{"version":3,"sources":["../src/types/errors.ts","../src/validators/validation-context.ts","../src/validators/validation-utils.ts","../src/validators/bank-info.ts","../src/validators/service-code.ts","../src/validators/amount.ts","../src/validators/additional-data.ts","../src/validators/config-validator.ts","../src/validators/country.ts","../src/validators/currency.ts","../src/validators/merchant-category.ts","../src/types/decode.ts","../src/validators/field-validators.ts","../src/validators/crc-validator.ts","../src/validators/required-fields-validator.ts","../src/validators/business-rules-validator.ts","../src/validators/corruption-detector.ts","../src/validators/error-builder.ts","../src/validators/validate-with-options.ts","../src/validators/index.ts","../src/utils/encoding.ts","../src/utils/constants.ts","../src/generators/consumer-account-info.ts","../src/generators/additional-data.ts","../src/utils/crc.ts","../src/generators/vietqr.ts","../src/validators/qr-image-config.ts","../src/utils/image-encoder.ts","../src/generators/qr-image.ts","../src/parsers/tlv-parser.ts","../src/parsers/vietqr-parser.ts","../src/parsers/parse-with-options.ts","../src/parsers/index.ts","../src/decoders/image-validator.ts","../src/decoders/format-detector.ts","../src/decoders/qr-extractor.ts","../src/decoders/index.ts"],"names":["ValidationError","_ValidationError","field","value","rule","message","code","expectedFormat","AggregateValidationError","_AggregateValidationError","errors","e","QRGenerationError","_QRGenerationError","cause","ImageEncodingError","_ImageEncodingError","ValidationContext","config","error","validationFn","trimWhitespace","sanitizeForError","maxLength","str","containsNewlineOrTab","isAlphanumericASCII","validateBankBin","bankBin","trimmed","validateAccountNumber","accountNumber","validateCardNumber","cardNumber","VALID_SERVICE_CODES","validateServiceCode","serviceCode","validateAmount","amount","isDynamic","numericValue","validateBillNumber","billNumber","validatePurpose","purpose","validateMessage","validateReferenceLabel","referenceLabel","validateVietQRConfig","context","hasAccountNumber","hasCardNumber","hasAmount","validateCountry","country","validateCurrency","currency","validateMerchantCategory","merchantCategory","ValidationErrorCode","ValidationWarningCode","DecodingErrorType","isSuccessResult","result","isErrorResult","isDynamicQR","data","isStaticQR","MAX_IMAGE_SIZE","MAX_QR_STRING_LENGTH","FIELD_CONSTRAINTS","REQUIRED_VALUES","validateBankCode","validateCountryCode","validatePurposeCode","CRC_POLYNOMIAL","CRC_INITIAL","validateCRC","qrString","crcValue","dataForCRC","calculatedCRC","calculateCRC","verifyCRC","crc","buffer","i","byte","bit","REQUIRED_FIELDS","validateRequiredFields","fieldName","getExpectedFormat","validateBusinessRules","hasDynamicQRRecommendedFields","detectCorruption","indicators","hasCRCError","err","missingCriticalFields","isTruncated","detectTruncation","indicator","buildWarning","mergeErrors","errorArrays","seen","merged","key","sortErrors","priorityMap","a","b","priorityA","priorityB","validateWithOptions","options","skipCRCCheck","customFieldLimits","treatWarningsAsErrors","skipCorruptionDetection","allErrors","warnings","requiredFieldErrors","fieldErrors","bankCodeError","accountNumberError","validateAccountNumberWithLimit","currencyError","countryCodeError","amountError","validateAmountWithLimit","messageError","validateMessageWithLimit","purposeCodeError","validatePurposeCodeWithLimit","billNumberError","validateBillNumberWithLimit","merchantCategoryError","businessRuleErrors","crcError","isCorrupted","dedupedErrors","sortedErrors","warningErrors","w","baseError","maxByteLength","validate","crcFormatError","uniqueErrors","encodeField","id","length","encodeFieldWithDetails","encoded","NAPAS_GUID","DEFAULT_CURRENCY","DEFAULT_COUNTRY","DEFAULT_MCC","buildConsumerAccountInfo","guidField","binSubField","accountSubField","paymentProviderValue","paymentProviderField","serviceCodeField","consumerAccountValue","buildAdditionalData","subfields","crc16ccitt","providedCRC","dataWithoutCRC","generateVietQR","accountOrCardNumber","fields","field00","field01","consumerAccountField","field38Value","field38","field53","field54Encoded","field54","field58","field62Encoded","additionalDataField","field62Value","field62","field63","validateQRImageConfig","hexPattern","encodePNGImage","QRCode","encodeSVGImage","svgString","DEFAULT_CONFIG","generateQRImage","finalConfig","dataURI","base64Prefix","parseTLV","input","position","lengthStr","remainingString","remainingBuffer","completeness","extractVietQRFields","extractMerchantAccount","extractAdditionalData","merchantFields","paymentNetworkField","f","networkFields","bankCodeField","accountField","additionalFields","parseWithOptions","strictMode","extractPartialOnError","tlvResult","partialData","missingFields","parse","isValidImageSize","imageBuffer","validateImageBuffer","size","detectImageFormat","isSupportedFormat","format","decodeImageToPixels","png","PNG","jpegData","jpeg","extractQRString","imageData","decodeQR","decode","errorType","parseResult","decodeAndValidate","decodeResult"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Validation error with field-specific context\n */\nexport class ValidationError extends Error {\n  /**\n   * Field name that failed validation\n   */\n  readonly field: string;\n\n  /**\n   * Invalid value provided\n   */\n  readonly value: unknown;\n\n  /**\n   * Validation rule that failed\n   */\n  readonly rule: string;\n\n  /**\n   * Machine-readable error code for programmatic error handling\n   *\n   * Optional error code that categorizes the validation failure.\n   * Enables consumers to handle specific error types programmatically.\n   *\n   * @example\n   * ```typescript\n   * if (error.code === 'INVALID_BANK_BIN_LENGTH') {\n   *   // Handle bank BIN length error specifically\n   * }\n   * ```\n   */\n  readonly code?: string;\n\n  /**\n   * Expected format or constraint description\n   *\n   * Human-readable description of what format or value was expected.\n   * Helps developers understand the constraint that was violated.\n   *\n   * @example\n   * ```typescript\n   * console.error(`Expected: ${error.expectedFormat}, Received: ${error.value}`);\n   * // Output: \"Expected: 6 numeric digits, Received: 12345\"\n   * ```\n   */\n  readonly expectedFormat?: string;\n\n  constructor(\n    field: string,\n    value: unknown,\n    rule: string,\n    message: string,\n    code?: string,\n    expectedFormat?: string\n  ) {\n    super(message);\n    this.name = 'ValidationError';\n    this.field = field;\n    this.value = value;\n    this.rule = rule;\n    this.code = code;\n    this.expectedFormat = expectedFormat;\n\n    // Maintains proper stack trace for where our error was thrown (only available on V8)\n    if (typeof (Error as any).captureStackTrace === 'function') {\n      (Error as any).captureStackTrace(this, ValidationError);\n    }\n  }\n}\n\n/**\n * Aggregate validation error containing multiple validation failures\n */\nexport class AggregateValidationError extends Error {\n  /**\n   * Array of individual validation errors\n   */\n  readonly errors: ValidationError[];\n\n  constructor(errors: ValidationError[]) {\n    const message = `Validation failed with ${errors.length} error(s):\\n${errors\n      .map((e) => `  - ${e.field}: ${e.message}`)\n      .join('\\n')}`;\n    super(message);\n    this.name = 'AggregateValidationError';\n    this.errors = errors;\n\n    // Maintains proper stack trace for where our error was thrown (only available on V8)\n    if (typeof (Error as any).captureStackTrace === 'function') {\n      (Error as any).captureStackTrace(this, AggregateValidationError);\n    }\n  }\n}\n\n/**\n * Error thrown when QR code generation fails\n */\nexport class QRGenerationError extends Error {\n  /**\n   * Original error that caused the QR generation failure\n   */\n  readonly cause?: Error;\n\n  constructor(message: string, cause?: Error) {\n    super(message);\n    this.name = 'QRGenerationError';\n    this.cause = cause;\n\n    // Maintains proper stack trace for where our error was thrown (only available on V8)\n    if (typeof (Error as any).captureStackTrace === 'function') {\n      (Error as any).captureStackTrace(this, QRGenerationError);\n    }\n  }\n}\n\n/**\n * Error thrown when image encoding fails\n */\nexport class ImageEncodingError extends Error {\n  /**\n   * Original error that caused the image encoding failure\n   */\n  readonly cause?: Error;\n\n  constructor(message: string, cause?: Error) {\n    super(message);\n    this.name = 'ImageEncodingError';\n    this.cause = cause;\n\n    // Maintains proper stack trace for where our error was thrown (only available on V8)\n    if (typeof (Error as any).captureStackTrace === 'function') {\n      (Error as any).captureStackTrace(this, ImageEncodingError);\n    }\n  }\n}\n","/**\n * Validation context for collecting multiple validation errors\n *\n * This module provides a context-based error collection pattern that allows\n * validators to accumulate multiple errors before throwing, enabling better\n * user experience by showing all validation issues at once.\n *\n * @example\n * ```typescript\n * const context = new ValidationContext(config);\n *\n * context.tryValidate(() => validateBankBin(config.bankBin));\n * context.tryValidate(() => validateAccountNumber(config.accountNumber));\n *\n * context.throwIfErrors(); // Throws AggregateValidationError if any errors\n * ```\n */\n\nimport { ValidationError, AggregateValidationError } from '../types/errors.js';\nimport type { ValidationErrorCode } from './error-codes.js';\n\n/**\n * Context for collecting validation errors across multiple validation operations\n *\n * Allows validators to run without immediately throwing, collecting all errors\n * for comprehensive error reporting.\n */\nexport class ValidationContext {\n  /**\n   * Accumulated validation errors\n   */\n  readonly errors: ValidationError[] = [];\n\n  /**\n   * Configuration object being validated\n   */\n  readonly config: unknown;\n\n  /**\n   * Creates a new validation context\n   *\n   * @param config - The configuration object being validated\n   */\n  constructor(config: unknown) {\n    this.config = config;\n  }\n\n  /**\n   * Adds a validation error to the context\n   *\n   * @param error - The validation error to add\n   */\n  addError(error: ValidationError): void {\n    this.errors.push(error);\n  }\n\n  /**\n   * Executes a validation function and catches any ValidationError thrown\n   *\n   * If the validation function throws a ValidationError, it's added to the\n   * error collection. Other error types are re-thrown.\n   *\n   * @param validationFn - Function that performs validation (may throw ValidationError)\n   * @returns true if validation passed, false if ValidationError was caught\n   *\n   * @example\n   * ```typescript\n   * context.tryValidate(() => validateBankBin(config.bankBin));\n   * ```\n   */\n  tryValidate(validationFn: () => void): boolean {\n    try {\n      validationFn();\n      return true;\n    } catch (error) {\n      if (error instanceof ValidationError) {\n        this.addError(error);\n        return false;\n      }\n      // Re-throw non-validation errors\n      throw error;\n    }\n  }\n\n  /**\n   * Throws AggregateValidationError if any errors have been collected\n   *\n   * Should be called after all validations are complete to fail-fast if\n   * any validation errors occurred.\n   *\n   * @throws {AggregateValidationError} If one or more validation errors exist\n   *\n   * @example\n   * ```typescript\n   * context.tryValidate(() => validateBankBin(config.bankBin));\n   * context.tryValidate(() => validateAccountNumber(config.accountNumber));\n   * context.throwIfErrors(); // Throws if either validation failed\n   * ```\n   */\n  throwIfErrors(): void {\n    if (this.errors.length > 0) {\n      throw new AggregateValidationError(this.errors);\n    }\n  }\n\n  /**\n   * Checks if any errors have been collected\n   *\n   * @returns true if errors exist, false otherwise\n   */\n  hasErrors(): boolean {\n    return this.errors.length > 0;\n  }\n\n  /**\n   * Gets the number of errors collected\n   *\n   * @returns The error count\n   */\n  get errorCount(): number {\n    return this.errors.length;\n  }\n\n  /**\n   * Creates a ValidationError with enhanced properties\n   *\n   * Helper method to create ValidationError instances with code and expectedFormat\n   * properties for improved error reporting.\n   *\n   * @param field - The field name that failed validation\n   * @param value - The actual value that was invalid\n   * @param rule - The validation rule that failed\n   * @param message - Human-readable error message\n   * @param code - Machine-readable error code (optional)\n   * @param expectedFormat - Description of expected format (optional)\n   * @returns ValidationError instance\n   */\n  createError(\n    field: string,\n    value: unknown,\n    rule: string,\n    message: string,\n    code?: ValidationErrorCode,\n    expectedFormat?: string\n  ): ValidationError {\n    const error = new ValidationError(field, value, rule, message);\n\n    // Extend with additional properties\n    if (code !== undefined) {\n      Object.defineProperty(error, 'code', {\n        value: code,\n        enumerable: true,\n        writable: false,\n      });\n    }\n\n    if (expectedFormat !== undefined) {\n      Object.defineProperty(error, 'expectedFormat', {\n        value: expectedFormat,\n        enumerable: true,\n        writable: false,\n      });\n    }\n\n    return error;\n  }\n}\n","/**\n * Validation utility functions for robust input handling\n *\n * Provides enhanced trimming and sanitization beyond standard JavaScript methods\n * to handle edge cases like Unicode whitespace, special characters, and invalid inputs.\n */\n\n/**\n * Comprehensive whitespace trimming that handles Unicode whitespace characters\n *\n * Standard trim() only handles ASCII whitespace (\\t, \\n, \\r, space).\n * This function also handles Unicode whitespace like:\n * - \\u00A0 (non-breaking space)\n * - \\u2003 (em space)\n * - \\u3000 (ideographic space)\n * - and other Unicode whitespace categories\n *\n * @param value - String value to trim\n * @returns Trimmed string with all whitespace (including Unicode) removed from start/end\n *\n * @example\n * ```typescript\n * trimWhitespace('  hello  ');        // 'hello'\n * trimWhitespace('\\u00A0hello\\u00A0'); // 'hello'\n * trimWhitespace('\\u3000hello\\u3000'); // 'hello'\n * ```\n */\nexport function trimWhitespace(value: string): string {\n  // \\s matches all Unicode whitespace characters in JavaScript regex\n  // This includes standard whitespace and Unicode whitespace categories\n  return value.replace(/^\\s+|\\s+$/g, '');\n}\n\n/**\n * Check if a string contains only whitespace (including Unicode whitespace)\n *\n * @param value - String value to check\n * @returns true if string is empty or contains only whitespace\n *\n * @example\n * ```typescript\n * isWhitespaceOnly('   ');        // true\n * isWhitespaceOnly('\\u00A0');     // true\n * isWhitespaceOnly('hello');      // false\n * isWhitespaceOnly('  hi  ');     // false\n * ```\n */\nexport function isWhitespaceOnly(value: string): boolean {\n  return /^\\s*$/.test(value);\n}\n\n/**\n * Sanitize value for error messages - truncate long values and escape special characters\n *\n * Prevents error messages from becoming too verbose or exposing sensitive data.\n * Truncates long values and adds ellipsis.\n *\n * @param value - Value to sanitize\n * @param maxLength - Maximum length before truncation (default: 50)\n * @returns Sanitized string safe for error messages\n *\n * @example\n * ```typescript\n * sanitizeForError('short');                    // 'short'\n * sanitizeForError('a'.repeat(100));            // 'aaaa...' (truncated)\n * sanitizeForError('password123', 10);          // 'password...' (truncated at 10)\n * ```\n */\nexport function sanitizeForError(value: unknown, maxLength: number = 50): string {\n  if (value === null) return 'null';\n  if (value === undefined) return 'undefined';\n\n  let str: string;\n\n  if (typeof value === 'string') {\n    str = value;\n  } else if (typeof value === 'object') {\n    // For objects, show type instead of contents to avoid verbose errors\n    if (Array.isArray(value)) {\n      str = `[Array(${value.length})]`;\n    } else {\n      str = '[Object]';\n    }\n  } else {\n    str = String(value);\n  }\n\n  // Truncate if too long\n  if (str.length > maxLength) {\n    return str.substring(0, maxLength - 3) + '...';\n  }\n\n  return str;\n}\n\n/**\n * Check if a string contains newline or tab characters\n *\n * Useful for validating single-line text fields that should not contain\n * line breaks or tabs.\n *\n * @param value - String to check\n * @returns true if string contains newlines (\\n, \\r) or tabs (\\t)\n *\n * @example\n * ```typescript\n * containsNewlineOrTab('hello');           // false\n * containsNewlineOrTab('line1\\nline2');    // true\n * containsNewlineOrTab('text\\twith\\ttabs'); // true\n * ```\n */\nexport function containsNewlineOrTab(value: string): boolean {\n  return /[\\n\\r\\t]/.test(value);\n}\n\n/**\n * Validate that a string contains only ASCII alphanumeric characters\n *\n * Rejects Unicode letters, special characters, and non-ASCII digits.\n * Allows only A-Z, a-z, 0-9.\n *\n * @param value - String to validate\n * @returns true if string contains only ASCII alphanumeric characters\n *\n * @example\n * ```typescript\n * isAlphanumericASCII('ABC123');     // true\n * isAlphanumericASCII('ABC-123');    // false (hyphen)\n * isAlphanumericASCII('АБВ123');     // false (Cyrillic)\n * isAlphanumericASCII('中文123');    // false (Chinese)\n * ```\n */\nexport function isAlphanumericASCII(value: string): boolean {\n  return /^[A-Za-z0-9]+$/.test(value);\n}\n\n/**\n * Validate that a string contains only ASCII digits (0-9)\n *\n * Rejects Unicode digits like Arabic-Indic (٠-٩) or Devanagari (०-९).\n *\n * @param value - String to validate\n * @returns true if string contains only ASCII digits (0-9)\n *\n * @example\n * ```typescript\n * isNumericASCII('123456');      // true\n * isNumericASCII('١٢٣');         // false (Arabic-Indic digits)\n * isNumericASCII('९७०');         // false (Devanagari digits)\n * ```\n */\nexport function isNumericASCII(value: string): boolean {\n  return /^\\d+$/.test(value);\n}\n","import { ValidationError } from '../types/errors';\nimport { trimWhitespace, sanitizeForError, isAlphanumericASCII } from './validation-utils';\n\n/**\n * Validate bank BIN (Bank Identification Number)\n *\n * Per NAPAS IBFT v1.5.2 specification, bank BIN must be exactly 6 numeric digits.\n *\n * @param bankBin - Bank identification number to validate\n * @throws {ValidationError} If bankBin is invalid\n *\n * @example\n * ```typescript\n * validateBankBin('970403'); // Valid - Vietcombank\n * validateBankBin('970415'); // Valid - VietinBank\n * validateBankBin('97040');  // Throws - not 6 digits\n * validateBankBin('970@03'); // Throws - not numeric\n * ```\n */\nexport function validateBankBin(bankBin: unknown): asserts bankBin is string {\n  // Type check\n  if (typeof bankBin !== 'string') {\n    throw new ValidationError(\n      'bankBin',\n      bankBin,\n      'type',\n      'Bank BIN must be a string'\n    );\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(bankBin);\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'bankBin',\n      bankBin,\n      'required',\n      'Bank BIN is required',\n      'MISSING_REQUIRED_FIELD',\n      '6 numeric digits'\n    );\n  }\n\n  // Length check - must be exactly 6 digits\n  if (trimmed.length !== 6) {\n    throw new ValidationError(\n      'bankBin',\n      bankBin,\n      'length',\n      `Bank BIN must be exactly 6 digits. Expected: 6 numeric digits, Received: \"${sanitizeForError(trimmed, 20)}\" (${trimmed.length} ${trimmed.length === 1 ? 'digit' : 'digits'})`,\n      'INVALID_BANK_BIN_LENGTH',\n      '6 numeric digits'\n    );\n  }\n\n  // Format check - must be numeric only (ASCII digits 0-9, not Unicode digits)\n  if (!/^\\d{6}$/.test(trimmed)) {\n    throw new ValidationError(\n      'bankBin',\n      bankBin,\n      'format',\n      `Bank BIN must contain only numeric characters (0-9). Expected: 6 numeric digits, Received: \"${sanitizeForError(trimmed, 20)}\"`,\n      'INVALID_BANK_BIN_FORMAT',\n      '6 numeric digits'\n    );\n  }\n}\n\n/**\n * Validate account number\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Account numbers must be alphanumeric\n * - Maximum length: 19 characters\n * - Minimum length: 1 character\n *\n * @param accountNumber - Account number to validate\n * @throws {ValidationError} If accountNumber is invalid\n *\n * @example\n * ```typescript\n * validateAccountNumber('01234567');      // Valid\n * validateAccountNumber('ABC123XYZ');     // Valid - alphanumeric\n * validateAccountNumber('1234567890123456789'); // Valid - 19 chars (max)\n * validateAccountNumber('12345678901234567890'); // Throws - exceeds 19 chars\n * validateAccountNumber('0123-456');      // Throws - contains hyphen\n * ```\n */\nexport function validateAccountNumber(accountNumber: unknown): asserts accountNumber is string {\n  // Type check\n  if (typeof accountNumber !== 'string') {\n    throw new ValidationError(\n      'accountNumber',\n      accountNumber,\n      'type',\n      'Account number must be a string'\n    );\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(accountNumber);\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'accountNumber',\n      accountNumber,\n      'required',\n      'Account number is required',\n      'MISSING_REQUIRED_FIELD',\n      'Alphanumeric only (A-Z, a-z, 0-9)'\n    );\n  }\n\n  // Length check - maximum 19 characters per NAPAS spec\n  if (trimmed.length > 19) {\n    throw new ValidationError(\n      'accountNumber',\n      accountNumber,\n      'length',\n      `Account number must not exceed 19 characters. Expected: ≤ 19 characters, Received: \"${sanitizeForError(trimmed, 25)}\" (${trimmed.length} characters)`,\n      'ACCOUNT_NUMBER_TOO_LONG',\n      '≤ 19 characters'\n    );\n  }\n\n  // Format check - must be alphanumeric only (ASCII A-Z, a-z, 0-9, not Unicode)\n  if (!isAlphanumericASCII(trimmed)) {\n    throw new ValidationError(\n      'accountNumber',\n      accountNumber,\n      'format',\n      `Account number must contain only alphanumeric characters (A-Z, a-z, 0-9). Expected: Alphanumeric only (A-Z, a-z, 0-9), Received: \"${sanitizeForError(trimmed, 25)}\"`,\n      'INVALID_ACCOUNT_CHARACTERS',\n      'Alphanumeric only (A-Z, a-z, 0-9)'\n    );\n  }\n}\n\n/**\n * Validate card number\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Card numbers must be alphanumeric\n * - Maximum length: 19 characters\n * - Minimum length: 1 character\n *\n * @param cardNumber - Card number to validate\n * @throws {ValidationError} If cardNumber is invalid\n *\n * @example\n * ```typescript\n * validateCardNumber('9704031101234567');      // Valid\n * validateCardNumber('ABC123XYZ');             // Valid - alphanumeric\n * validateCardNumber('1234567890123456789');   // Valid - 19 chars (max)\n * validateCardNumber('12345678901234567890');  // Throws - exceeds 19 chars\n * validateCardNumber('9704-0311');             // Throws - contains hyphen\n * ```\n */\nexport function validateCardNumber(cardNumber: unknown): asserts cardNumber is string {\n  // Type check\n  if (typeof cardNumber !== 'string') {\n    throw new ValidationError(\n      'cardNumber',\n      cardNumber,\n      'type',\n      'Card number must be a string'\n    );\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(cardNumber);\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'cardNumber',\n      cardNumber,\n      'required',\n      'Card number is required',\n      'MISSING_REQUIRED_FIELD',\n      'Alphanumeric only (A-Z, a-z, 0-9)'\n    );\n  }\n\n  // Length check - maximum 19 characters per NAPAS spec\n  if (trimmed.length > 19) {\n    throw new ValidationError(\n      'cardNumber',\n      cardNumber,\n      'length',\n      `Card number must not exceed 19 characters. Expected: ≤ 19 characters, Received: \"${sanitizeForError(trimmed, 25)}\" (${trimmed.length} characters)`,\n      'CARD_NUMBER_TOO_LONG',\n      '≤ 19 characters'\n    );\n  }\n\n  // Format check - must be alphanumeric only (ASCII A-Z, a-z, 0-9, not Unicode)\n  if (!isAlphanumericASCII(trimmed)) {\n    throw new ValidationError(\n      'cardNumber',\n      cardNumber,\n      'format',\n      `Card number must contain only alphanumeric characters (A-Z, a-z, 0-9). Expected: Alphanumeric only (A-Z, a-z, 0-9), Received: \"${sanitizeForError(trimmed, 25)}\"`,\n      'INVALID_CARD_CHARACTERS',\n      'Alphanumeric only (A-Z, a-z, 0-9)'\n    );\n  }\n}\n","import { ValidationError } from '../types/errors';\nimport type { ServiceCode } from '../types';\nimport { sanitizeForError } from './validation-utils';\n\n/**\n * Valid NAPAS service codes per IBFT v1.5.2 specification\n */\nconst VALID_SERVICE_CODES: readonly ServiceCode[] = ['QRIBFTTA', 'QRIBFTTC'] as const;\n\n/**\n * Validate NAPAS service code\n *\n * Per NAPAS IBFT v1.5.2 specification, only two service codes are valid:\n * - QRIBFTTA: QR IBFT to Account (for bank account transfers)\n * - QRIBFTTC: QR IBFT to Card (for card-based transfers)\n *\n * Service codes are case-sensitive and must match exactly.\n *\n * @param serviceCode - Service code to validate\n * @throws {ValidationError} If serviceCode is not a valid NAPAS service code\n *\n * @example\n * ```typescript\n * validateServiceCode('QRIBFTTA'); // Valid - account transfer\n * validateServiceCode('QRIBFTTC'); // Valid - card transfer\n * validateServiceCode('qribftta'); // Throws - lowercase not allowed\n * validateServiceCode('INVALID');  // Throws - not a valid service code\n * ```\n */\nexport function validateServiceCode(serviceCode: unknown): asserts serviceCode is ServiceCode {\n  // Type check\n  if (typeof serviceCode !== 'string') {\n    throw new ValidationError(\n      'serviceCode',\n      serviceCode,\n      'type',\n      'Service code must be a string'\n    );\n  }\n\n  // Required check\n  if (serviceCode.length === 0) {\n    throw new ValidationError(\n      'serviceCode',\n      serviceCode,\n      'required',\n      'Service code is required',\n      'MISSING_REQUIRED_FIELD',\n      '\"QRIBFTTA\" or \"QRIBFTTC\"'\n    );\n  }\n\n  // Valid code check\n  if (!VALID_SERVICE_CODES.includes(serviceCode as ServiceCode)) {\n    throw new ValidationError(\n      'serviceCode',\n      serviceCode,\n      'enum',\n      `Invalid service code. Expected: \"QRIBFTTA\" or \"QRIBFTTC\", Received: \"${sanitizeForError(serviceCode, 20)}\"`,\n      'INVALID_SERVICE_CODE',\n      '\"QRIBFTTA\" or \"QRIBFTTC\"'\n    );\n  }\n}\n","import { ValidationError } from '../types/errors';\nimport { trimWhitespace, sanitizeForError } from './validation-utils';\n\n/**\n * Validate transaction amount\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Amount must be numeric with optional decimal point\n * - Maximum length: 13 characters (including decimal point)\n * - Minimum length: 1 character\n * - Must be positive (> 0)\n * - Cannot be negative or zero\n *\n * Dynamic QR codes (when `isDynamic` is true):\n * - Amount is REQUIRED (cannot be empty)\n * - Must be a positive value (> 0)\n *\n * Static QR codes (when `isDynamic` is false):\n * - Amount is OPTIONAL (can be empty for user-filled QR)\n * - If provided, must still meet all format requirements\n *\n * @param amount - Transaction amount to validate\n * @param isDynamic - Whether this is a dynamic QR (requires amount) or static QR (amount optional)\n * @throws {ValidationError} If amount is invalid\n *\n * @example\n * ```typescript\n * // Dynamic QR examples\n * validateAmount('180000', true);        // Valid - dynamic with amount\n * validateAmount('', true);              // Throws - dynamic requires amount\n * validateAmount('0', true);             // Throws - dynamic requires positive amount\n *\n * // Static QR examples\n * validateAmount('', false);             // Valid - static allows empty amount\n * validateAmount('180000', false);       // Valid - static with pre-filled amount\n * validateAmount('abc', false);          // Throws - invalid format even for static\n *\n * // General validation (backward compatible)\n * validateAmount('180000');              // Valid - integer amount\n * validateAmount('180000.50');           // Valid - decimal amount\n * validateAmount('1234567890123');       // Valid - 13 chars (max)\n * validateAmount('12345678901234');      // Throws - exceeds 13 chars\n * validateAmount('abc');                 // Throws - non-numeric\n * validateAmount('-100');                // Throws - negative\n * validateAmount('0');                   // Throws - zero not allowed\n * ```\n */\nexport function validateAmount(amount: unknown, isDynamic?: boolean): asserts amount is string {\n  // Type check\n  if (typeof amount !== 'string') {\n    throw new ValidationError('amount', amount, 'type', 'Amount must be a string');\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(amount);\n\n  // Dynamic QR specific validation - amount is REQUIRED\n  if (isDynamic === true && trimmed.length === 0) {\n    throw new ValidationError(\n      'amount',\n      amount,\n      'required',\n      'Dynamic QR codes require an amount. Expected: Positive numeric value, Received: empty string',\n      'INVALID_DYNAMIC_AMOUNT',\n      'Positive numeric value (e.g., 180000 or 180000.50)'\n    );\n  }\n\n  // Static QR - empty amount is allowed (user will fill in)\n  if (isDynamic === false && trimmed.length === 0) {\n    return; // Valid for static QR\n  }\n\n  // Required check (for backward compatibility when isDynamic is undefined)\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'amount',\n      amount,\n      'required',\n      'Amount is required',\n      'MISSING_REQUIRED_FIELD',\n      'Numeric with optional decimal (e.g., 180000 or 180000.50)'\n    );\n  }\n\n  // Length check - maximum 13 characters per NAPAS spec\n  if (trimmed.length > 13) {\n    throw new ValidationError(\n      'amount',\n      amount,\n      'length',\n      `Amount must not exceed 13 characters. Expected: ≤ 13 characters, Received: \"${sanitizeForError(trimmed, 20)}\" (${trimmed.length} ${trimmed.length === 1 ? 'character' : 'characters'})`,\n      'AMOUNT_TOO_LONG',\n      '≤ 13 characters'\n    );\n  }\n\n  // Format check - must be numeric with optional single decimal point (ASCII digits only)\n  // Valid: 123, 123.45, 0.5, 1234567890123\n  // Invalid: abc, 12.34.56, -100, 123., .5, 180,000 (thousand separator), ١٢٣ (Unicode digits)\n  if (!/^\\d+(\\.\\d+)?$/.test(trimmed)) {\n    throw new ValidationError(\n      'amount',\n      amount,\n      'format',\n      `Amount must be numeric with optional decimal point. Expected: Numeric with optional decimal (e.g., 180000 or 180000.50), Received: \"${sanitizeForError(trimmed, 20)}\"`,\n      'INVALID_AMOUNT_FORMAT',\n      'Numeric with optional decimal (e.g., 180000 or 180000.50)'\n    );\n  }\n\n  // Value check - must be positive (> 0)\n  const numericValue = parseFloat(trimmed);\n  if (numericValue <= 0) {\n    throw new ValidationError(\n      'amount',\n      amount,\n      'value',\n      `Amount must be positive. Expected: > 0, Received: \"${sanitizeForError(trimmed, 20)}\" (${numericValue})`,\n      'INVALID_AMOUNT_VALUE',\n      '> 0'\n    );\n  }\n}\n","import { ValidationError } from '../types/errors';\nimport { trimWhitespace, sanitizeForError, containsNewlineOrTab, isAlphanumericASCII } from './validation-utils';\n\n/**\n * Validate bill number\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Bill numbers must be alphanumeric\n * - Maximum length: 25 characters\n * - Minimum length: 1 character\n *\n * @param billNumber - Bill number to validate\n * @throws {ValidationError} If billNumber is invalid\n *\n * @example\n * ```typescript\n * validateBillNumber('NPS6869');       // Valid\n * validateBillNumber('INV123');        // Valid\n * validateBillNumber('A'.repeat(25));  // Valid - 25 chars (max)\n * validateBillNumber('A'.repeat(26));  // Throws - exceeds 25 chars\n * validateBillNumber('INV-123');       // Throws - contains hyphen\n * ```\n */\nexport function validateBillNumber(billNumber: unknown): asserts billNumber is string {\n  // Type check\n  if (typeof billNumber !== 'string') {\n    throw new ValidationError(\n      'billNumber',\n      billNumber,\n      'type',\n      'Bill number must be a string'\n    );\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(billNumber);\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'billNumber',\n      billNumber,\n      'required',\n      'Bill number is required',\n      'MISSING_REQUIRED_FIELD',\n      'Alphanumeric, hyphen, underscore allowed'\n    );\n  }\n\n  // Length check - maximum 25 characters per NAPAS spec\n  if (trimmed.length > 25) {\n    throw new ValidationError(\n      'billNumber',\n      billNumber,\n      'length',\n      `Bill number must not exceed 25 characters. Expected: ≤ 25 characters, Received: \"${sanitizeForError(trimmed, 30)}\" (${trimmed.length} ${trimmed.length === 1 ? 'character' : 'characters'})`,\n      'BILL_NUMBER_TOO_LONG',\n      '≤ 25 characters'\n    );\n  }\n\n  // Format check - alphanumeric with hyphens and underscores allowed (common in bill numbers)\n  // Updated to match real-world bill number formats\n  if (!/^[A-Za-z0-9_-]+$/.test(trimmed)) {\n    throw new ValidationError(\n      'billNumber',\n      billNumber,\n      'format',\n      `Bill number must contain only alphanumeric characters, hyphens, or underscores. Expected: Alphanumeric, hyphen, underscore allowed, Received: \"${sanitizeForError(trimmed, 30)}\"`,\n      'INVALID_BILL_CHARACTERS',\n      'Alphanumeric, hyphen, underscore allowed'\n    );\n  }\n}\n\n/**\n * Validate purpose/description\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Purpose can contain any characters\n * - Maximum length: 25 characters\n * - Minimum length: 1 character\n *\n * @param purpose - Purpose/description to validate\n * @throws {ValidationError} If purpose is invalid\n *\n * @example\n * ```typescript\n * validatePurpose('thanh toan don hang');  // Valid\n * validatePurpose('payment for order');     // Valid\n * validatePurpose('A'.repeat(25));          // Valid - 25 chars (max)\n * validatePurpose('A'.repeat(26));          // Throws - exceeds 25 chars\n * ```\n */\nexport function validatePurpose(purpose: unknown): asserts purpose is string {\n  // Type check\n  if (typeof purpose !== 'string') {\n    throw new ValidationError('purpose', purpose, 'type', 'Purpose must be a string');\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(purpose);\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'purpose',\n      purpose,\n      'required',\n      'Purpose is required',\n      'MISSING_REQUIRED_FIELD',\n      'Any characters (Unicode OK)'\n    );\n  }\n\n  // Length check - maximum 25 characters per NAPAS spec\n  if (trimmed.length > 25) {\n    throw new ValidationError(\n      'purpose',\n      purpose,\n      'length',\n      `Purpose must not exceed 25 characters. Expected: ≤ 25 characters, Received: \"${sanitizeForError(trimmed, 30)}...\" (${trimmed.length} ${trimmed.length === 1 ? 'character' : 'characters'})`,\n      'PURPOSE_TOO_LONG',\n      '≤ 25 characters'\n    );\n  }\n\n  // Format check - reject newlines and tabs (single-line text field)\n  if (containsNewlineOrTab(trimmed)) {\n    throw new ValidationError(\n      'purpose',\n      purpose,\n      'format',\n      `Purpose must not contain newline or tab characters. Expected: Single-line text, Received: \"${sanitizeForError(trimmed, 30)}\"`,\n      'INVALID_PURPOSE_FORMAT',\n      'Single-line text (no newlines or tabs)'\n    );\n  }\n\n  // No other format restriction - purpose can contain any characters per NAPAS spec\n}\n\n/**\n * Validate message field\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Message can contain any characters (Unicode supported)\n * - Maximum length: 500 characters\n * - Minimum length: 0 characters (optional field)\n *\n * @param message - Message to validate\n * @throws {ValidationError} If message is invalid\n *\n * @example\n * ```typescript\n * validateMessage('Thanh toán đơn hàng #123');  // Valid - Vietnamese\n * validateMessage('Payment for order');         // Valid - English\n * validateMessage('A'.repeat(500));             // Valid - 500 chars (max)\n * validateMessage('A'.repeat(501));             // Throws - exceeds 500 chars\n * ```\n */\nexport function validateMessage(message: unknown): asserts message is string {\n  // Type check\n  if (typeof message !== 'string') {\n    throw new ValidationError('message', message, 'type', 'Message must be a string');\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(message);\n\n  // Optional field - empty is allowed (skip required check)\n\n  // Length check - maximum 500 characters per NAPAS spec\n  if (trimmed.length > 500) {\n    throw new ValidationError(\n      'message',\n      message,\n      'length',\n      `Message must not exceed 500 characters. Expected: ≤ 500 characters, Received: \"${sanitizeForError(trimmed, 50)}...\" (${trimmed.length} ${trimmed.length === 1 ? 'character' : 'characters'})`,\n      'MESSAGE_TOO_LONG',\n      '≤ 500 characters'\n    );\n  }\n\n  // No format restriction - message can contain any characters per NAPAS spec\n}\n\n/**\n * Validate reference label\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Reference label must be alphanumeric\n * - Maximum length: 25 characters\n * - Minimum length: 0 characters (optional field)\n *\n * @param referenceLabel - Reference label to validate\n * @throws {ValidationError} If referenceLabel is invalid\n *\n * @example\n * ```typescript\n * validateReferenceLabel('REF123');        // Valid\n * validateReferenceLabel('ORDER001');      // Valid\n * validateReferenceLabel('A'.repeat(25));  // Valid - 25 chars (max)\n * validateReferenceLabel('A'.repeat(26));  // Throws - exceeds 25 chars\n * validateReferenceLabel('REF-123');       // Throws - contains hyphen\n * ```\n */\nexport function validateReferenceLabel(\n  referenceLabel: unknown\n): asserts referenceLabel is string {\n  // Type check\n  if (typeof referenceLabel !== 'string') {\n    throw new ValidationError(\n      'referenceLabel',\n      referenceLabel,\n      'type',\n      'Reference label must be a string'\n    );\n  }\n\n  // Trim whitespace (including Unicode whitespace)\n  const trimmed = trimWhitespace(referenceLabel);\n\n  // Optional field - empty is allowed, but whitespace-only is not\n  // If original was non-empty but trimmed is empty, it was whitespace-only\n  if (referenceLabel.length > 0 && trimmed.length === 0) {\n    throw new ValidationError(\n      'referenceLabel',\n      referenceLabel,\n      'format',\n      `Reference label cannot be whitespace only. Expected: Alphanumeric only (A-Z, a-z, 0-9) or empty, Received: \"${sanitizeForError(referenceLabel, 25)}\"`,\n      'INVALID_REFERENCE_CHARACTERS',\n      'Alphanumeric only (A-Z, a-z, 0-9)'\n    );\n  }\n\n  // Length check - maximum 25 characters per NAPAS spec\n  if (trimmed.length > 25) {\n    throw new ValidationError(\n      'referenceLabel',\n      referenceLabel,\n      'length',\n      `Reference label must not exceed 25 characters. Expected: ≤ 25 characters, Received: \"${sanitizeForError(trimmed, 30)}\" (${trimmed.length} ${trimmed.length === 1 ? 'character' : 'characters'})`,\n      'REFERENCE_LABEL_TOO_LONG',\n      '≤ 25 characters'\n    );\n  }\n\n  // Format check - must be alphanumeric only (A-Z, a-z, 0-9)\n  // Only validate if non-empty (optional field)\n  if (trimmed.length > 0 && !isAlphanumericASCII(trimmed)) {\n    throw new ValidationError(\n      'referenceLabel',\n      referenceLabel,\n      'format',\n      `Reference label must contain only alphanumeric characters. Expected: Alphanumeric only (A-Z, a-z, 0-9), Received: \"${sanitizeForError(trimmed, 30)}\"`,\n      'INVALID_REFERENCE_CHARACTERS',\n      'Alphanumeric only (A-Z, a-z, 0-9)'\n    );\n  }\n}\n","/**\n * Centralized VietQR configuration validator\n *\n * This module provides comprehensive validation for VietQRConfig objects,\n * including field-level, cross-field, and business rule validation with\n * multi-error collection for improved developer experience.\n *\n * @example\n * ```typescript\n * const config: VietQRConfig = {\n *   bankBin: '970403',\n *   serviceCode: 'QRIBFTTA',\n *   accountNumber: '01234567',\n *   initiationMethod: '11',\n * };\n *\n * validateVietQRConfig(config); // Throws if invalid, returns void if valid\n * ```\n */\n\nimport { ValidationContext } from './validation-context.js';\nimport { validateBankBin, validateAccountNumber, validateCardNumber } from './bank-info.js';\nimport { validateServiceCode } from './service-code.js';\nimport { validateAmount } from './amount.js';\nimport { validateBillNumber, validatePurpose, validateReferenceLabel } from './additional-data.js';\nimport type { VietQRConfig } from '../types/config.js';\n\n/**\n * Validates a complete VietQR configuration\n *\n * Performs comprehensive validation including:\n * - Required field presence (bankBin, serviceCode, accountNumber/cardNumber)\n * - Field format and constraint validation\n * - Cross-field business rules (account/card mutual exclusivity, service code dependencies)\n * - Dynamic vs static QR requirements (amount validation based on initiationMethod)\n * - Optional field validation (billNumber, purpose, referenceLabel)\n *\n * All validation errors are collected and reported together in an AggregateValidationError\n * for better developer experience.\n *\n * @param config - The VietQR configuration to validate\n * @throws {AggregateValidationError} If validation fails with one or more errors\n * @throws {ValidationError} If a single validation error occurs\n *\n * @example\n * ```typescript\n * // Valid static account transfer QR\n * validateVietQRConfig({\n *   bankBin: '970403',\n *   serviceCode: 'QRIBFTTA',\n *   accountNumber: '01234567',\n *   initiationMethod: '11',\n * });\n *\n * // Valid dynamic card transfer QR\n * validateVietQRConfig({\n *   bankBin: '970422',\n *   serviceCode: 'QRIBFTTC',\n *   cardNumber: '9704220112345678',\n *   initiationMethod: '12',\n *   amount: '180000',\n * });\n *\n * // Invalid config - throws AggregateValidationError with all errors\n * validateVietQRConfig({\n *   bankBin: '12345', // Too short\n *   serviceCode: 'INVALID', // Invalid code\n *   initiationMethod: '12', // Dynamic\n *   // Missing accountNumber/cardNumber and amount\n * });\n * ```\n */\nexport function validateVietQRConfig(config: VietQRConfig): void {\n  const context = new ValidationContext(config);\n\n  // T049: Required field validation\n  // Bank BIN is always required\n  context.tryValidate(() => validateBankBin(config.bankBin));\n\n  // Service code is always required\n  context.tryValidate(() => validateServiceCode(config.serviceCode));\n\n  // T050: Conditional field validation\n  // T051: Cross-field validation - account/card mutual exclusivity\n  const hasAccountNumber = config.accountNumber !== undefined && config.accountNumber !== '';\n  const hasCardNumber = config.cardNumber !== undefined && config.cardNumber !== '';\n\n  // Check mutual exclusivity\n  if (hasAccountNumber && hasCardNumber) {\n    const error = context.createError(\n      'config',\n      config,\n      'cross-field',\n      'Cannot provide both accountNumber and cardNumber. Use accountNumber for QRIBFTTA or cardNumber for QRIBFTTC.',\n      'BOTH_ACCOUNT_AND_CARD',\n      'Either accountNumber OR cardNumber (not both)'\n    );\n    context.addError(error);\n  }\n\n  // Check that at least one is provided\n  if (!hasAccountNumber && !hasCardNumber) {\n    const error = context.createError(\n      'config',\n      config,\n      'cross-field',\n      'Either accountNumber or cardNumber must be provided.',\n      'MISSING_ACCOUNT_OR_CARD',\n      'accountNumber OR cardNumber (one required)'\n    );\n    context.addError(error);\n  }\n\n  // T051: Service code dependencies\n  // QRIBFTTA requires accountNumber\n  if (config.serviceCode === 'QRIBFTTA' && !hasAccountNumber) {\n    const error = context.createError(\n      'accountNumber',\n      config.accountNumber,\n      'required',\n      'Service code QRIBFTTA requires accountNumber to be provided.',\n      'ACCOUNT_REQUIRED_FOR_QRIBFTTA',\n      'accountNumber required for QRIBFTTA'\n    );\n    context.addError(error);\n  }\n\n  // QRIBFTTC requires cardNumber\n  if (config.serviceCode === 'QRIBFTTC' && !hasCardNumber) {\n    const error = context.createError(\n      'cardNumber',\n      config.cardNumber,\n      'required',\n      'Service code QRIBFTTC requires cardNumber to be provided.',\n      'CARD_REQUIRED_FOR_QRIBFTTC',\n      'cardNumber required for QRIBFTTC'\n    );\n    context.addError(error);\n  }\n\n  // T052: Multi-error collection - validate all fields using tryValidate\n  // Validate accountNumber if provided\n  if (hasAccountNumber) {\n    context.tryValidate(() => validateAccountNumber(config.accountNumber!));\n  }\n\n  // Validate cardNumber if provided\n  if (hasCardNumber) {\n    context.tryValidate(() => validateCardNumber(config.cardNumber!));\n  }\n\n  // Dynamic vs Static QR validation\n  const isDynamic = config.initiationMethod === '12';\n  const hasAmount = config.amount !== undefined && config.amount !== '';\n\n  // Validate amount based on QR type\n  if (isDynamic) {\n    // Dynamic QR requires amount\n    context.tryValidate(() => validateAmount(config.amount || '', true));\n  } else if (hasAmount) {\n    // Static QR with optional amount - validate format if provided\n    context.tryValidate(() => validateAmount(config.amount!, false));\n  }\n\n  // Validate optional fields if provided\n  if (config.billNumber !== undefined && config.billNumber !== '') {\n    context.tryValidate(() => validateBillNumber(config.billNumber!));\n  }\n\n  if (config.purpose !== undefined && config.purpose !== '') {\n    context.tryValidate(() => validatePurpose(config.purpose!));\n  }\n\n  if (config.referenceLabel !== undefined && config.referenceLabel !== '') {\n    context.tryValidate(() => validateReferenceLabel(config.referenceLabel!));\n  }\n\n  // Throw if any errors were collected\n  context.throwIfErrors();\n}\n","import { ValidationError } from '../types/errors';\n\n/**\n * Validate country code\n *\n * Per NAPAS IBFT v1.5.2 specification, only Vietnam is supported:\n * - Country code must be \"VN\" (ISO 3166-1 alpha-2 code for Vietnam)\n * - Case-sensitive exact match required\n *\n * @param country - Country code to validate\n * @throws {ValidationError} If country is not \"VN\"\n *\n * @example\n * ```typescript\n * validateCountry('VN');  // Valid - Vietnam\n * validateCountry('vn');  // Throws - lowercase not allowed\n * validateCountry('US');  // Throws - only Vietnam supported\n * ```\n */\nexport function validateCountry(country: unknown): asserts country is 'VN' {\n  // Type check\n  if (typeof country !== 'string') {\n    throw new ValidationError(\n      'country',\n      country,\n      'type',\n      'Country must be a string'\n    );\n  }\n\n  // Trim whitespace\n  const trimmed = country.trim();\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'country',\n      country,\n      'required',\n      'Country is required',\n      'MISSING_REQUIRED_FIELD',\n      '\"VN\" (Vietnam)'\n    );\n  }\n\n  // Valid country check - only VN (Vietnam) is supported\n  if (trimmed !== 'VN') {\n    throw new ValidationError(\n      'country',\n      country,\n      'enum',\n      `Invalid country code. Expected: \"VN\" (Vietnam), Received: \"${trimmed}\"`,\n      'INVALID_COUNTRY_CODE',\n      '\"VN\" (Vietnam)'\n    );\n  }\n}\n","import { ValidationError } from '../types/errors';\n\n/**\n * Validate currency code\n *\n * Per NAPAS IBFT v1.5.2 specification, only VND (Vietnamese Dong) is supported:\n * - Currency code must be \"704\" (ISO 4217 numeric code for VND)\n * - Case-sensitive exact match required\n *\n * @param currency - Currency code to validate\n * @throws {ValidationError} If currency is not \"704\"\n *\n * @example\n * ```typescript\n * validateCurrency('704');  // Valid - VND\n * validateCurrency('840');  // Throws - USD not supported\n * validateCurrency('VND');  // Throws - must be numeric code\n * ```\n */\nexport function validateCurrency(currency: unknown): asserts currency is '704' {\n  // Type check\n  if (typeof currency !== 'string') {\n    throw new ValidationError(\n      'currency',\n      currency,\n      'type',\n      'Currency must be a string'\n    );\n  }\n\n  // Trim whitespace\n  const trimmed = currency.trim();\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'currency',\n      currency,\n      'required',\n      'Currency is required',\n      'MISSING_REQUIRED_FIELD',\n      '\"704\" (VND)'\n    );\n  }\n\n  // Valid currency check - only VND (704) is supported\n  if (trimmed !== '704') {\n    throw new ValidationError(\n      'currency',\n      currency,\n      'enum',\n      `Invalid currency code. Expected: \"704\" (VND), Received: \"${trimmed}\"`,\n      'INVALID_CURRENCY_CODE',\n      '\"704\" (VND)'\n    );\n  }\n}\n","import { ValidationError } from '../types/errors';\n\n/**\n * Validate merchant category code (MCC)\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - MCC must be exactly 4 numeric digits\n * - No leading/trailing spaces allowed (trimmed before validation)\n * - Valid range: 0000-9999\n *\n * Merchant Category Codes (MCC) are four-digit numbers assigned by card networks\n * to classify businesses by their primary trade, profession, or vocation.\n *\n * @param merchantCategory - Merchant category code to validate\n * @throws {ValidationError} If MCC is invalid\n *\n * @example\n * ```typescript\n * validateMerchantCategory('5411');  // Valid - Grocery stores\n * validateMerchantCategory('5812');  // Valid - Restaurants\n * validateMerchantCategory('0742');  // Valid - Veterinary services\n * validateMerchantCategory('541');   // Throws - too short\n * validateMerchantCategory('541A');  // Throws - non-numeric\n * ```\n */\nexport function validateMerchantCategory(merchantCategory: unknown): asserts merchantCategory is string {\n  // Type check\n  if (typeof merchantCategory !== 'string') {\n    throw new ValidationError(\n      'merchantCategory',\n      merchantCategory,\n      'type',\n      'Merchant category must be a string'\n    );\n  }\n\n  // Trim whitespace\n  const trimmed = merchantCategory.trim();\n\n  // Required check\n  if (trimmed.length === 0) {\n    throw new ValidationError(\n      'merchantCategory',\n      merchantCategory,\n      'required',\n      'Merchant category is required',\n      'MISSING_REQUIRED_FIELD',\n      '4 numeric digits'\n    );\n  }\n\n  // Length check - must be exactly 4 digits\n  if (trimmed.length !== 4) {\n    throw new ValidationError(\n      'merchantCategory',\n      merchantCategory,\n      'length',\n      `Merchant category must be exactly 4 digits. Expected: 4 numeric digits, Received: \"${trimmed}\" (${trimmed.length} ${trimmed.length === 1 ? 'digit' : 'digits'})`,\n      'INVALID_MCC_LENGTH',\n      '4 numeric digits'\n    );\n  }\n\n  // Format check - must be numeric\n  if (!/^\\d{4}$/.test(trimmed)) {\n    throw new ValidationError(\n      'merchantCategory',\n      merchantCategory,\n      'format',\n      `Merchant category must be numeric. Expected: 4 numeric digits, Received: \"${trimmed}\"`,\n      'INVALID_MCC_FORMAT',\n      '4 numeric digits'\n    );\n  }\n}\n","/**\n * VietQR Decoding and Validation Type Definitions\n *\n * This file contains TypeScript interface definitions for VietQR data parsing,\n * validation, and decoding operations based on NAPAS IBFT v1.5.2 specification.\n *\n * @module vietqr/decode\n * @version 1.0.0\n */\n\n// ============================================================================\n// Core Data Structures\n// ============================================================================\n\n/**\n * Represents complete parsed payment information from a VietQR code\n *\n * @remarks\n * All fields follow NAPAS IBFT v1.5.2 specification constraints.\n * Optional fields are undefined when not present in the QR data.\n */\nexport interface VietQRData {\n  // Payment Information\n  /** Bank identifier - BIN (6 digits) or CITAD code (8 characters) */\n  bankCode: string;\n\n  /** Bank account or card number (max 19 digits) */\n  accountNumber: string;\n\n  /** Transaction amount in VND (optional for static QR, required for dynamic) */\n  amount?: string;\n\n  /** ISO 4217 currency code - must be \"704\" (VND) */\n  currency: string;\n\n  // Additional Payment Data\n  /** Payment description/memo (max 500 characters, UTF-8) */\n  message?: string;\n\n  /** Transaction purpose code (max 25 characters) */\n  purposeCode?: string;\n\n  /** Bill or invoice reference number (max 25 characters) */\n  billNumber?: string;\n\n  // QR Metadata\n  /** QR code type: static ('11') or dynamic ('12') */\n  initiationMethod: 'static' | 'dynamic';\n\n  /** Merchant category code (4 digits) */\n  merchantCategory?: string;\n\n  /** ISO 3166-1 alpha-2 country code - must be \"VN\" */\n  countryCode: string;\n\n  // Technical Fields\n  /** EMV QR format version - must be \"01\" */\n  payloadFormatIndicator: string;\n\n  /** CRC-16-CCITT checksum (4 hexadecimal characters) */\n  crc: string;\n}\n\n/**\n * Result of validating VietQR data against NAPAS IBFT v1.5.2 specification\n *\n * @remarks\n * - isValid is true only when errors array is empty\n * - isCorrupted indicates data truncation or checksum mismatch\n * - Can have both isValid=false AND isCorrupted=true\n */\nexport interface ValidationResult {\n  /** Overall validation status (true if all checks pass) */\n  isValid: boolean;\n\n  /** Data corruption flag (truncated or checksum mismatch) */\n  isCorrupted: boolean;\n\n  /** Array of validation errors (empty if valid) */\n  errors: ValidationError[];\n\n  /** Non-critical issues (e.g., deprecated fields, unusual patterns) */\n  warnings?: ValidationWarning[];\n}\n\n/**\n * Specific validation failure with detailed context\n *\n * @remarks\n * actualValue field is sanitized to prevent sensitive data exposure:\n * - Account numbers: \"[REDACTED]\"\n * - Amounts: Shown (not sensitive per NAPAS)\n * - Messages: Truncated if >100 chars\n */\nexport interface ValidationError {\n  /** Name of field that failed validation */\n  field: string;\n\n  /** Machine-readable error code */\n  code: ValidationErrorCode;\n\n  /** Human-readable error description */\n  message: string;\n\n  /** Expected format or constraint */\n  expectedFormat?: string;\n\n  /** Actual value received (sanitized for security) */\n  actualValue?: string;\n}\n\n/**\n * Non-critical validation issue\n *\n * @remarks\n * Warnings inform developers of deprecated fields, unusual patterns,\n * or data quality issues that don't prevent processing.\n */\nexport interface ValidationWarning {\n  /** Name of field with warning */\n  field: string;\n\n  /** Machine-readable warning code */\n  code: ValidationWarningCode;\n\n  /** Human-readable warning description */\n  message: string;\n}\n\n/**\n * Error during QR string parsing or image decoding\n *\n * @remarks\n * Provides actionable error information when decode/parse operations fail.\n * Position field helps pinpoint exact location of parsing errors.\n */\nexport interface DecodingError {\n  /** Category of decoding error */\n  type: DecodingErrorType;\n\n  /** Human-readable error description */\n  message: string;\n\n  /** Field where parsing error occurred */\n  field?: string;\n\n  /** Character position in string where error occurred */\n  position?: number;\n}\n\n/**\n * Generic result wrapper for parse/decode operations\n *\n * @typeParam T - Type of successful result data (VietQRData or ValidationResult)\n *\n * @remarks\n * Invariants:\n * - If success = true: data is defined, error is undefined\n * - If success = false: error is defined, data is undefined\n */\nexport interface ParseResult<T> {\n  /** Operation success status */\n  success: boolean;\n\n  /** Result data (present if success = true) */\n  data?: T;\n\n  /** Error details (present if success = false) */\n  error?: DecodingError;\n}\n\n// ============================================================================\n// Enumerations\n// ============================================================================\n\n/**\n * Machine-readable validation error codes\n */\nexport enum ValidationErrorCode {\n  /** Required field not present */\n  MISSING_REQUIRED_FIELD = 'MISSING_REQUIRED_FIELD',\n\n  /** Field value doesn't match expected format */\n  INVALID_FORMAT = 'INVALID_FORMAT',\n\n  /** Field value exceeds maximum length */\n  LENGTH_EXCEEDED = 'LENGTH_EXCEEDED',\n\n  /** Field value below minimum length */\n  LENGTH_TOO_SHORT = 'LENGTH_TOO_SHORT',\n\n  /** Field contains illegal characters */\n  INVALID_CHARACTER = 'INVALID_CHARACTER',\n\n  /** CRC verification failed */\n  CHECKSUM_MISMATCH = 'CHECKSUM_MISMATCH',\n\n  /** Currency code is not VND (704) */\n  INVALID_CURRENCY = 'INVALID_CURRENCY',\n\n  /** Country code is not VN */\n  INVALID_COUNTRY = 'INVALID_COUNTRY',\n\n  /** Amount is negative or zero */\n  INVALID_AMOUNT = 'INVALID_AMOUNT',\n\n  /** Unrecognized EMV QR field ID */\n  UNKNOWN_FIELD = 'UNKNOWN_FIELD'\n}\n\n/**\n * Machine-readable validation warning codes\n */\nexport enum ValidationWarningCode {\n  /** Data appears truncated but usable */\n  PARTIAL_DATA = 'PARTIAL_DATA',\n\n  /** Field is deprecated in newer spec versions */\n  DEPRECATED_FIELD = 'DEPRECATED_FIELD',\n\n  /** Data pattern is valid but unusual */\n  UNUSUAL_PATTERN = 'UNUSUAL_PATTERN',\n\n  /** Recommended optional field is missing */\n  MISSING_OPTIONAL_FIELD = 'MISSING_OPTIONAL_FIELD'\n}\n\n/**\n * Categories of decoding errors\n */\nexport enum DecodingErrorType {\n  /** Failed to parse EMV QR TLV structure */\n  PARSE_ERROR = 'PARSE_ERROR',\n\n  /** QR string doesn't match EMV QR format */\n  INVALID_FORMAT = 'INVALID_FORMAT',\n\n  /** Failed to decode QR code from image */\n  IMAGE_DECODE_ERROR = 'IMAGE_DECODE_ERROR',\n\n  /** Image size exceeds 2MB limit */\n  SIZE_LIMIT_EXCEEDED = 'SIZE_LIMIT_EXCEEDED',\n\n  /** Image format not supported (only PNG/JPEG) */\n  UNSUPPORTED_FORMAT = 'UNSUPPORTED_FORMAT',\n\n  /** Image contains multiple QR codes (ambiguous) */\n  MULTIPLE_QR_CODES = 'MULTIPLE_QR_CODES',\n\n  /** No QR code detected in image */\n  NO_QR_CODE_FOUND = 'NO_QR_CODE_FOUND'\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Type guard to check if ParseResult is successful\n *\n * @param result - ParseResult to check\n * @returns true if result.success is true and data is defined\n */\nexport function isSuccessResult<T>(result: ParseResult<T>): result is { success: true; data: T; error: undefined } {\n  return result.success === true && result.data !== undefined;\n}\n\n/**\n * Type guard to check if ParseResult is an error\n *\n * @param result - ParseResult to check\n * @returns true if result.success is false and error is defined\n */\nexport function isErrorResult<T>(result: ParseResult<T>): result is { success: false; data: undefined; error: DecodingError } {\n  return result.success === false && result.error !== undefined;\n}\n\n/**\n * Type guard to check if VietQRData represents a dynamic QR code\n *\n * @param data - VietQRData to check\n * @returns true if initiationMethod is 'dynamic'\n */\nexport function isDynamicQR(data: VietQRData): boolean {\n  return data.initiationMethod === 'dynamic';\n}\n\n/**\n * Type guard to check if VietQRData represents a static QR code\n *\n * @param data - VietQRData to check\n * @returns true if initiationMethod is 'static'\n */\nexport function isStaticQR(data: VietQRData): boolean {\n  return data.initiationMethod === 'static';\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * Maximum image file size in bytes (2MB)\n */\nexport const MAX_IMAGE_SIZE = 2 * 1024 * 1024;\n\n/**\n * Maximum QR string length (typical QR codes are <4KB)\n */\nexport const MAX_QR_STRING_LENGTH = 4096;\n\n/**\n * Field length constraints per NAPAS IBFT v1.5.2\n */\nexport const FIELD_CONSTRAINTS = {\n  BANK_CODE_BIN_LENGTH: 6,\n  BANK_CODE_CITAD_LENGTH: 8,\n  ACCOUNT_NUMBER_MAX: 19,\n  AMOUNT_MAX: 13,\n  CURRENCY_LENGTH: 3,\n  MESSAGE_MAX: 500,\n  PURPOSE_CODE_MAX: 25,\n  BILL_NUMBER_MAX: 25,\n  COUNTRY_CODE_LENGTH: 2,\n  MERCHANT_CATEGORY_LENGTH: 4,\n  CRC_LENGTH: 4\n} as const;\n\n/**\n * Required constant values per NAPAS IBFT v1.5.2\n */\nexport const REQUIRED_VALUES = {\n  CURRENCY_VND: '704',\n  COUNTRY_CODE_VN: 'VN',\n  PAYLOAD_FORMAT_INDICATOR: '01',\n  INITIATION_STATIC: '11',\n  INITIATION_DYNAMIC: '12'\n} as const;\n","/**\n * VietQR Field Format Validators\n *\n * Individual field validation functions per NAPAS IBFT v1.5.2 specification.\n * Each validator returns null if valid, or ValidationError if invalid.\n *\n * @module validators/field-validators\n */\n\nimport type { ValidationError } from '../types/decode';\nimport { ValidationErrorCode, FIELD_CONSTRAINTS, REQUIRED_VALUES } from '../types/decode';\n\n/**\n * Validates bank code format (BIN or CITAD)\n *\n * @param value - Bank code to validate\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - BIN format: 6 numeric digits (e.g., \"970422\")\n * - CITAD format: 8 alphanumeric characters (e.g., \"VIETBANK\")\n */\nexport function validateBankCode(value: string | undefined): ValidationError | null {\n  if (value === undefined || value === null) {\n    return {\n      field: 'bankCode',\n      code: ValidationErrorCode.MISSING_REQUIRED_FIELD,\n      message: 'Bank code is required'\n    };\n  }\n\n  if (value.length === 0) {\n    return {\n      field: 'bankCode',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Bank code cannot be empty',\n      expectedFormat: '6 digits (BIN) or 8 alphanumeric (CITAD)'\n    };\n  }\n\n  // Check for illegal characters (anything other than alphanumeric)\n  if (!/^[a-zA-Z0-9]+$/.test(value)) {\n    return {\n      field: 'bankCode',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Bank code contains invalid characters',\n      expectedFormat: 'Alphanumeric only (no spaces or special characters)',\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  // Check for excessive length (resource exhaustion prevention) - only for extremely long values\n  const REASONABLE_MAX_LENGTH = 100;  // Security threshold for resource exhaustion\n  if (value.length > REASONABLE_MAX_LENGTH) {\n    return {\n      field: 'bankCode',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Bank code exceeds reasonable maximum length`,\n      expectedFormat: '6 digits (BIN) or 8 alphanumeric (CITAD)',\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  // Check length: must be exactly 6 or 8 characters\n  if (value.length !== FIELD_CONSTRAINTS.BANK_CODE_BIN_LENGTH &&\n      value.length !== FIELD_CONSTRAINTS.BANK_CODE_CITAD_LENGTH) {\n    return {\n      field: 'bankCode',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: `Bank code must be ${FIELD_CONSTRAINTS.BANK_CODE_BIN_LENGTH} or ${FIELD_CONSTRAINTS.BANK_CODE_CITAD_LENGTH} characters`,\n      expectedFormat: '6 digits (BIN) or 8 alphanumeric (CITAD)',\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  // If 6 characters, must be all numeric (BIN format)\n  if (value.length === FIELD_CONSTRAINTS.BANK_CODE_BIN_LENGTH && !/^\\d{6}$/.test(value)) {\n    return {\n      field: 'bankCode',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: '6-character bank code must be all digits (BIN format)',\n      expectedFormat: '6 numeric digits',\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates account number format\n *\n * @param value - Account number to validate\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - Must be numeric only\n * - Maximum 19 digits per NAPAS spec\n * - Minimum 1 digit\n */\nexport function validateAccountNumber(value: string | undefined): ValidationError | null {\n  if (value === undefined || value === null) {\n    return {\n      field: 'accountNumber',\n      code: ValidationErrorCode.MISSING_REQUIRED_FIELD,\n      message: 'Account number is required'\n    };\n  }\n\n  if (value.length === 0) {\n    return {\n      field: 'accountNumber',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Account number cannot be empty',\n      expectedFormat: '1-19 numeric digits'\n    };\n  }\n\n  // Check for special characters or spaces (more specific than letters)\n  if (/[^0-9A-Za-z]/.test(value)) {\n    return {\n      field: 'accountNumber',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Account number contains invalid characters',\n      expectedFormat: 'Numeric only (0-9)',\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  // Must be numeric only (catch letters)\n  if (!/^\\d+$/.test(value)) {\n    return {\n      field: 'accountNumber',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Account number must contain only digits',\n      expectedFormat: 'Numeric only (0-9)',\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  // Check length constraints\n  if (value.length > FIELD_CONSTRAINTS.ACCOUNT_NUMBER_MAX) {\n    return {\n      field: 'accountNumber',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Account number exceeds maximum length of ${FIELD_CONSTRAINTS.ACCOUNT_NUMBER_MAX} digits`,\n      expectedFormat: `1-${FIELD_CONSTRAINTS.ACCOUNT_NUMBER_MAX} digits`,\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates transaction amount format\n *\n * @param value - Amount to validate (optional field)\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - Numeric with optional decimal point\n * - Maximum 13 characters (including decimal)\n * - Must be positive (> 0)\n */\nexport function validateAmount(value: string | undefined): ValidationError | null {\n  // Amount is optional\n  if (value === undefined || value === null) {\n    return null;\n  }\n\n  if (value.length === 0) {\n    return {\n      field: 'amount',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Amount cannot be empty string if present',\n      expectedFormat: 'Numeric with optional decimal (e.g., \"50000\" or \"50000.50\")'\n    };\n  }\n\n  // Check if negative (most specific error)\n  if (value.startsWith('-')) {\n    return {\n      field: 'amount',\n      code: ValidationErrorCode.INVALID_AMOUNT,\n      message: 'Amount cannot be negative',\n      expectedFormat: 'Positive number > 0',\n      actualValue: value\n    };\n  }\n\n  // Check for special characters (currency symbols, commas, spaces) - but not letters\n  if (/[^\\d.A-Za-z]/.test(value)) {\n    return {\n      field: 'amount',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Amount contains invalid characters',\n      expectedFormat: 'Numeric with optional decimal (e.g., \"50000\" or \"50000.50\")',\n      actualValue: value\n    };\n  }\n\n  // Must be numeric with optional decimal point\n  if (!/^\\d+(\\.\\d+)?$/.test(value)) {\n    return {\n      field: 'amount',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Amount must be numeric with optional decimal',\n      expectedFormat: 'Numeric with optional decimal (e.g., \"50000\" or \"50000.50\")',\n      actualValue: value\n    };\n  }\n\n  // Check length constraint\n  if (value.length > FIELD_CONSTRAINTS.AMOUNT_MAX) {\n    return {\n      field: 'amount',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Amount exceeds maximum length of ${FIELD_CONSTRAINTS.AMOUNT_MAX} characters`,\n      expectedFormat: `Maximum ${FIELD_CONSTRAINTS.AMOUNT_MAX} characters`,\n      actualValue: value\n    };\n  }\n\n  // Must be positive\n  const numericValue = parseFloat(value);\n  if (numericValue <= 0) {\n    return {\n      field: 'amount',\n      code: ValidationErrorCode.INVALID_AMOUNT,\n      message: 'Amount must be greater than zero',\n      expectedFormat: 'Positive number > 0',\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates currency code (must be VND/704)\n *\n * @param value - Currency code to validate\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * Per NAPAS spec, only VND (code \"704\") is allowed\n */\nexport function validateCurrency(value: string | undefined): ValidationError | null {\n  if (value === undefined || value === null) {\n    return {\n      field: 'currency',\n      code: ValidationErrorCode.MISSING_REQUIRED_FIELD,\n      message: 'Currency is required'\n    };\n  }\n\n  // Check for wrong format/length first\n  if (value.length !== 3 || !/^\\d{3}$/.test(value)) {\n    return {\n      field: 'currency',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Currency code must be 3 digits',\n      expectedFormat: '3 numeric digits (ISO 4217)',\n      actualValue: value\n    };\n  }\n\n  if (value !== REQUIRED_VALUES.CURRENCY_VND) {\n    return {\n      field: 'currency',\n      code: ValidationErrorCode.INVALID_CURRENCY,\n      message: 'Currency must be VND (704)',\n      expectedFormat: REQUIRED_VALUES.CURRENCY_VND,\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates country code (must be VN)\n *\n * @param value - Country code to validate\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * Per NAPAS spec, only Vietnam (code \"VN\") is allowed\n */\nexport function validateCountryCode(value: string | undefined): ValidationError | null {\n  if (value === undefined || value === null) {\n    return {\n      field: 'countryCode',\n      code: ValidationErrorCode.MISSING_REQUIRED_FIELD,\n      message: 'Country code is required'\n    };\n  }\n\n  // Check for wrong length first\n  if (value.length !== 2) {\n    return {\n      field: 'countryCode',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Country code must be 2 characters',\n      expectedFormat: '2 letters (ISO 3166-1)',\n      actualValue: value\n    };\n  }\n\n  // Check if it's letters (format valid but wrong value should be INVALID_COUNTRY)\n  if (!/^[A-Za-z]{2}$/.test(value)) {\n    return {\n      field: 'countryCode',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Country code must be 2 letters',\n      expectedFormat: '2 letters (ISO 3166-1)',\n      actualValue: value\n    };\n  }\n\n  // Must be exactly \"VN\" (case-sensitive, per spec requirement for uppercase)\n  if (value !== REQUIRED_VALUES.COUNTRY_CODE_VN) {\n    return {\n      field: 'countryCode',\n      code: ValidationErrorCode.INVALID_COUNTRY,\n      message: 'Country code must be VN (uppercase)',\n      expectedFormat: REQUIRED_VALUES.COUNTRY_CODE_VN,\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates message/description field\n *\n * @param value - Message to validate (optional field)\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - UTF-8 encoded text\n * - Maximum 500 characters\n * - No control characters allowed\n */\nexport function validateMessage(value: string | undefined): ValidationError | null {\n  // Message is optional\n  if (value === undefined || value === null) {\n    return null;\n  }\n\n  // Check for control characters (reject all control chars for security)\n  if (/[\\x00-\\x1F\\x7F]/.test(value)) {\n    return {\n      field: 'message',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Message contains invalid control characters',\n      expectedFormat: 'UTF-8 text without control characters',\n      actualValue: value.length > 100 ? value.substring(0, 100) + '...' : value\n    };\n  }\n\n  // Check UTF-8 byte length (not character length)\n  const byteLength = Buffer.from(value, 'utf-8').length;\n  if (byteLength > FIELD_CONSTRAINTS.MESSAGE_MAX) {\n    return {\n      field: 'message',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Message exceeds maximum byte length of ${FIELD_CONSTRAINTS.MESSAGE_MAX}`,\n      expectedFormat: `Maximum ${FIELD_CONSTRAINTS.MESSAGE_MAX} bytes (UTF-8)`,\n      actualValue: value.length > 100 ? value.substring(0, 100) + '...' : value\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates purpose code field\n *\n * @param value - Purpose code to validate (optional field)\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - Alphanumeric characters only\n * - Maximum 25 characters\n */\nexport function validatePurposeCode(value: string | undefined): ValidationError | null {\n  // Purpose code is optional\n  if (value === undefined || value === null) {\n    return null;\n  }\n\n  // Allow empty string for optional fields\n  if (value.length === 0) {\n    return null;\n  }\n\n  // Must be alphanumeric only\n  if (!/^[a-zA-Z0-9]+$/.test(value)) {\n    return {\n      field: 'purposeCode',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Purpose code must be alphanumeric only',\n      expectedFormat: 'Alphanumeric (A-Z, a-z, 0-9)',\n      actualValue: value\n    };\n  }\n\n  // Check length constraint\n  if (value.length > FIELD_CONSTRAINTS.PURPOSE_CODE_MAX) {\n    return {\n      field: 'purposeCode',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Purpose code exceeds maximum length of ${FIELD_CONSTRAINTS.PURPOSE_CODE_MAX} characters`,\n      expectedFormat: `Maximum ${FIELD_CONSTRAINTS.PURPOSE_CODE_MAX} characters`,\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates bill/invoice number field\n *\n * @param value - Bill number to validate (optional field)\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - Alphanumeric characters only\n * - Maximum 25 characters\n */\nexport function validateBillNumber(value: string | undefined): ValidationError | null {\n  // Bill number is optional\n  if (value === undefined || value === null) {\n    return null;\n  }\n\n  // Allow empty string for optional fields\n  if (value.length === 0) {\n    return null;\n  }\n\n  // Must be alphanumeric only (allow hyphens for invoice formats like \"INV-2024\")\n  if (!/^[a-zA-Z0-9-]+$/.test(value)) {\n    return {\n      field: 'billNumber',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Bill number must be alphanumeric (hyphens allowed)',\n      expectedFormat: 'Alphanumeric with optional hyphens (A-Z, a-z, 0-9, -)',\n      actualValue: value\n    };\n  }\n\n  // Check length constraint\n  if (value.length > FIELD_CONSTRAINTS.BILL_NUMBER_MAX) {\n    return {\n      field: 'billNumber',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Bill number exceeds maximum length of ${FIELD_CONSTRAINTS.BILL_NUMBER_MAX} characters`,\n      expectedFormat: `Maximum ${FIELD_CONSTRAINTS.BILL_NUMBER_MAX} characters`,\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n\n/**\n * Validates merchant category code\n *\n * @param value - Merchant category code to validate (optional field)\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - Must be exactly 4 numeric digits\n * - ISO 18245 Merchant Category Codes\n */\nexport function validateMerchantCategory(value: string | undefined): ValidationError | null {\n  // Merchant category is optional\n  if (value === undefined || value === null) {\n    return null;\n  }\n\n  if (value.length === 0) {\n    return {\n      field: 'merchantCategory',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Merchant category cannot be empty string if present',\n      expectedFormat: '4 numeric digits'\n    };\n  }\n\n  // Check for special characters first (not alphanumeric)\n  if (/[^0-9A-Za-z]/.test(value)) {\n    return {\n      field: 'merchantCategory',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Merchant category contains invalid characters',\n      expectedFormat: '4 numeric digits only',\n      actualValue: value\n    };\n  }\n\n  // Must be exactly 4 numeric digits (catch letters as INVALID_FORMAT)\n  if (!/^\\d{4}$/.test(value)) {\n    return {\n      field: 'merchantCategory',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Merchant category must be exactly 4 digits',\n      expectedFormat: '4 numeric digits (e.g., \"5812\" for restaurant)',\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n","/**\n * VietQR CRC-16-CCITT Checksum Validator\n *\n * Implements CRC-16-CCITT checksum calculation and verification per NAPAS IBFT v1.5.2.\n * Uses polynomial 0x1021 with initial value 0xFFFF.\n *\n * @module validators/crc-validator\n */\n\nimport type { ValidationError } from '../types/decode';\nimport { ValidationErrorCode, FIELD_CONSTRAINTS } from '../types/decode';\n\n/**\n * CRC-16-CCITT polynomial (0x1021)\n */\nconst CRC_POLYNOMIAL = 0x1021;\n\n/**\n * Initial CRC value (0xFFFF)\n */\nconst CRC_INITIAL = 0xFFFF;\n\n/**\n * Validates CRC format and verifies checksum\n *\n * @param qrString - Complete QR string including CRC field\n * @param crcValue - CRC value extracted from Field 63\n * @returns null if valid, ValidationError if invalid\n *\n * @remarks\n * - CRC must be exactly 4 hexadecimal characters\n * - Case-insensitive validation (B7C9 = b7c9)\n * - Calculates CRC over QR string minus last 4 characters (the CRC itself)\n */\nexport function validateCRC(\n  qrString: string,\n  crcValue: string | undefined\n): ValidationError | null {\n  if (crcValue === undefined || crcValue === null) {\n    return {\n      field: 'crc',\n      code: ValidationErrorCode.MISSING_REQUIRED_FIELD,\n      message: 'CRC is required'\n    };\n  }\n\n  // Check format: must be exactly 4 characters\n  if (crcValue.length !== FIELD_CONSTRAINTS.CRC_LENGTH) {\n    return {\n      field: 'crc',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: `CRC must be exactly ${FIELD_CONSTRAINTS.CRC_LENGTH} hexadecimal characters`,\n      expectedFormat: '4 hexadecimal digits (e.g., \"B7C9\")',\n      actualValue: crcValue\n    };\n  }\n\n  // Check for non-hexadecimal characters\n  if (!/^[0-9A-Fa-f]{4}$/.test(crcValue)) {\n    // Distinguish between invalid characters vs wrong format\n    if (/^[0-9A-Za-z]{4}$/.test(crcValue)) {\n      return {\n        field: 'crc',\n        code: ValidationErrorCode.INVALID_FORMAT,\n        message: 'CRC contains non-hexadecimal characters',\n        expectedFormat: 'Hexadecimal only (0-9, A-F)',\n        actualValue: crcValue\n      };\n    }\n\n    return {\n      field: 'crc',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'CRC contains invalid characters',\n      expectedFormat: 'Hexadecimal only (0-9, A-F)',\n      actualValue: crcValue\n    };\n  }\n\n  // Calculate expected CRC\n  // Per EMV spec: CRC is calculated over everything EXCEPT the CRC value itself\n  // QR string format: ...6304XXXX where 6304 is Field 63 (length 04), XXXX is CRC\n  // Calculate over: ...6304 (include field ID and length, exclude CRC value)\n  const dataForCRC = qrString.substring(0, qrString.length - FIELD_CONSTRAINTS.CRC_LENGTH);\n  const calculatedCRC = calculateCRC(dataForCRC);\n\n  // Compare (case-insensitive)\n  if (calculatedCRC.toUpperCase() !== crcValue.toUpperCase()) {\n    return {\n      field: 'crc',\n      code: ValidationErrorCode.CHECKSUM_MISMATCH,\n      message: 'CRC verification failed - data may be corrupted or tampered',\n      expectedFormat: `Expected CRC: ${calculatedCRC}`,\n      actualValue: crcValue\n    };\n  }\n\n  return null;\n}\n\n/**\n * Verifies CRC of complete QR string\n *\n * @param qrString - Complete QR string including CRC field\n * @returns true if CRC is valid, false otherwise\n *\n * @remarks\n * Convenience function for quick boolean CRC checks without detailed error info.\n * For detailed error reporting, use validateCRC() instead.\n */\nexport function verifyCRC(qrString: string): boolean {\n  if (!qrString || qrString.length < 4) {\n    return false;\n  }\n\n  // Extract CRC from last 4 characters\n  const crcValue = qrString.substring(qrString.length - FIELD_CONSTRAINTS.CRC_LENGTH);\n\n  // Validate format\n  if (!/^[0-9A-Fa-f]{4}$/.test(crcValue)) {\n    return false;\n  }\n\n  // Calculate and compare\n  const dataForCRC = qrString.substring(0, qrString.length - FIELD_CONSTRAINTS.CRC_LENGTH);\n  const calculatedCRC = calculateCRC(dataForCRC);\n\n  return calculatedCRC.toUpperCase() === crcValue.toUpperCase();\n}\n\n/**\n * Calculates CRC-16-CCITT checksum for given data\n *\n * @param data - Input data string to calculate CRC for\n * @returns CRC value as 4-character uppercase hexadecimal string\n *\n * @remarks\n * Algorithm: CRC-16-CCITT\n * - Polynomial: 0x1021\n * - Initial value: 0xFFFF\n * - No final XOR\n * - Bit-by-bit processing\n *\n * This is the standard EMV QR CRC calculation per ISO/IEC 13239.\n */\nexport function calculateCRC(data: string): string {\n  let crc = CRC_INITIAL;\n\n  // Convert string to UTF-8 bytes for correct CRC calculation\n  const buffer = Buffer.from(data, 'utf-8');\n\n  // Process each byte of input data\n  for (let i = 0; i < buffer.length; i++) {\n    const byte = buffer[i];\n\n    if (byte === undefined) continue;\n\n    // XOR byte with high byte of CRC\n    crc ^= (byte << 8);\n\n    // Process all 8 bits\n    for (let bit = 0; bit < 8; bit++) {\n      if ((crc & 0x8000) !== 0) {\n        crc = (crc << 1) ^ CRC_POLYNOMIAL;\n      } else {\n        crc <<= 1;\n      }\n\n      // Keep CRC in 16-bit range\n      crc &= 0xFFFF;\n    }\n  }\n\n  // Convert to 4-character uppercase hex string\n  return crc.toString(16).toUpperCase().padStart(4, '0');\n}\n","/**\n * VietQR Required Fields Validator\n *\n * Validates that all mandatory fields per NAPAS IBFT v1.5.2 are present.\n * Checks for undefined/null required fields and returns comprehensive errors.\n *\n * @module validators/required-fields-validator\n */\n\nimport type { VietQRData, ValidationError } from '../types/decode';\nimport { ValidationErrorCode } from '../types/decode';\n\n/**\n * List of required fields per NAPAS IBFT v1.5.2 specification\n */\nconst REQUIRED_FIELDS: (keyof VietQRData)[] = [\n  'payloadFormatIndicator',\n  'initiationMethod',\n  'bankCode',\n  'accountNumber',\n  'currency',\n  'countryCode',\n  'crc'\n];\n\n/**\n * Validates that all required fields are present and non-empty\n *\n * @param data - VietQR data to validate\n * @returns Array of validation errors (empty if all required fields present)\n *\n * @remarks\n * Required fields per NAPAS IBFT v1.5.2:\n * - payloadFormatIndicator (Field 00)\n * - initiationMethod (Field 01)\n * - bankCode (Field 38.01.00)\n * - accountNumber (Field 38.01.01)\n * - currency (Field 53)\n * - countryCode (Field 58)\n * - crc (Field 63)\n *\n * Optional fields:\n * - amount (Field 54) - required for dynamic QR, optional for static\n * - message (Field 62.08)\n * - purposeCode (Field 62.07)\n * - billNumber (Field 62.05 or 62.09)\n * - merchantCategory (Field 52)\n */\nexport function validateRequiredFields(data: Partial<VietQRData>): ValidationError[] {\n  const errors: ValidationError[] = [];\n\n  for (const fieldName of REQUIRED_FIELDS) {\n    const value = data[fieldName];\n\n    if (value === undefined || value === null) {\n      errors.push({\n        field: fieldName,\n        code: ValidationErrorCode.MISSING_REQUIRED_FIELD,\n        message: `Required field '${fieldName}' is missing`,\n        expectedFormat: getExpectedFormat(fieldName)\n      });\n      continue;\n    }\n\n    // Check for empty strings (required fields cannot be empty)\n    if (typeof value === 'string' && value.length === 0) {\n      errors.push({\n        field: fieldName,\n        code: ValidationErrorCode.INVALID_FORMAT,\n        message: `Required field '${fieldName}' cannot be empty`,\n        expectedFormat: getExpectedFormat(fieldName),\n        actualValue: '[empty string]'\n      });\n    }\n  }\n\n  return errors;\n}\n\n/**\n * Gets expected format description for a field\n *\n * @param fieldName - Name of field\n * @returns Human-readable format description\n */\nfunction getExpectedFormat(fieldName: keyof VietQRData): string {\n  const formats: Record<string, string> = {\n    payloadFormatIndicator: '\"01\"',\n    initiationMethod: '\"static\" or \"dynamic\"',\n    bankCode: '6-digit BIN or 8-character CITAD',\n    accountNumber: '1-19 digit account number',\n    currency: '\"704\" (VND)',\n    countryCode: '\"VN\"',\n    crc: '4 hexadecimal characters'\n  };\n\n  return formats[fieldName] || 'See NAPAS IBFT v1.5.2 specification';\n}\n","/**\n * VietQR Business Rules Validator\n *\n * Validates business logic rules per NAPAS IBFT v1.5.2 specification.\n * Checks currency, country code, payload format, and dynamic QR requirements.\n *\n * @module validators/business-rules-validator\n */\n\nimport type { VietQRData, ValidationError } from '../types/decode';\nimport { ValidationErrorCode, REQUIRED_VALUES } from '../types/decode';\n\n/**\n * Validates all business rules defined in NAPAS IBFT v1.5.2\n *\n * @param data - VietQR data to validate\n * @returns Array of validation errors (empty if all rules pass)\n *\n * @remarks\n * Business rules checked:\n * 1. Currency must be VND (704)\n * 2. Country code must be VN\n * 3. Payload format indicator must be \"01\"\n * 4. Dynamic QR should have amount (warning if missing)\n * 5. Initiation method must be valid (\"static\" or \"dynamic\")\n */\nexport function validateBusinessRules(data: VietQRData): ValidationError[] {\n  const errors: ValidationError[] = [];\n\n  // Rule 1: Currency must be VND (704)\n  if (data.currency !== REQUIRED_VALUES.CURRENCY_VND) {\n    errors.push({\n      field: 'currency',\n      code: ValidationErrorCode.INVALID_CURRENCY,\n      message: 'Only Vietnamese Dong (VND) currency is supported',\n      expectedFormat: REQUIRED_VALUES.CURRENCY_VND,\n      actualValue: data.currency\n    });\n  }\n\n  // Rule 2: Country code must be VN\n  if (data.countryCode !== REQUIRED_VALUES.COUNTRY_CODE_VN) {\n    errors.push({\n      field: 'countryCode',\n      code: ValidationErrorCode.INVALID_COUNTRY,\n      message: 'Only Vietnam (VN) country code is supported',\n      expectedFormat: REQUIRED_VALUES.COUNTRY_CODE_VN,\n      actualValue: data.countryCode\n    });\n  }\n\n  // Rule 3: Payload format indicator must be \"01\"\n  if (data.payloadFormatIndicator !== REQUIRED_VALUES.PAYLOAD_FORMAT_INDICATOR) {\n    errors.push({\n      field: 'payloadFormatIndicator',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Payload format indicator must be \"01\"',\n      expectedFormat: REQUIRED_VALUES.PAYLOAD_FORMAT_INDICATOR,\n      actualValue: data.payloadFormatIndicator\n    });\n  }\n\n  // Rule 4: Initiation method must be valid\n  if (data.initiationMethod !== 'static' && data.initiationMethod !== 'dynamic') {\n    errors.push({\n      field: 'initiationMethod',\n      code: ValidationErrorCode.INVALID_FORMAT,\n      message: 'Initiation method must be \"static\" or \"dynamic\"',\n      expectedFormat: '\"static\" or \"dynamic\"',\n      actualValue: data.initiationMethod as string\n    });\n  }\n\n  // Rule 5: Dynamic QR validation\n  // Note: Dynamic QR SHOULD have amount, but it's not strictly required by spec\n  // We don't add an error here, just a note for future warning implementation\n\n  return errors;\n}\n\n/**\n * Checks if dynamic QR has recommended fields\n *\n * @param data - VietQR data to check\n * @returns true if dynamic QR has amount, false otherwise\n *\n * @remarks\n * This is used for generating warnings (not errors).\n * Dynamic QR codes typically should include an amount, but it's not\n * strictly required by the NAPAS specification.\n */\nexport function hasDynamicQRRecommendedFields(data: VietQRData): boolean {\n  if (data.initiationMethod === 'dynamic') {\n    return data.amount !== undefined && data.amount.length > 0;\n  }\n  return true; // Not applicable for static QR\n}\n","/**\n * VietQR Data Corruption Detector\n *\n * Detects data corruption indicators such as truncation and checksum failures.\n * Sets the isCorrupted flag in validation results per NAPAS IBFT v1.5.2.\n *\n * @module validators/corruption-detector\n */\n\nimport type { VietQRData, ValidationError } from '../types/decode';\nimport { ValidationErrorCode } from '../types/decode';\nimport { verifyCRC } from './crc-validator';\n\n/**\n * Detects data corruption indicators in VietQR data\n *\n * @param data - Parsed VietQR data\n * @param qrString - Original QR string\n * @param errors - Existing validation errors\n * @returns true if corruption detected, false otherwise\n *\n * @remarks\n * Corruption indicators:\n * 1. CRC checksum mismatch (data tampering or transmission error)\n * 2. Missing required fields (possible truncation)\n * 3. QR string truncation (incomplete data)\n * 4. Length mismatches between declared and actual field values\n *\n * Note: Corrupted data can still be partially valid.\n * isCorrupted flag alerts consumers to potential data integrity issues.\n */\nexport function detectCorruption(\n  data: Partial<VietQRData>,\n  qrString: string,\n  errors: ValidationError[]\n): boolean {\n  const indicators: boolean[] = [];\n\n  // Indicator 1: CRC checksum failure\n  const hasCRCError = errors.some(err => err.code === ValidationErrorCode.CHECKSUM_MISMATCH);\n  indicators.push(hasCRCError);\n\n  // Indicator 2: Missing critical required fields suggests truncation\n  const requiredFields = ['bankCode', 'accountNumber', 'currency', 'countryCode'];\n  const missingCriticalFields = requiredFields.some(field => {\n    const value = data[field as keyof VietQRData];\n    return value === undefined || value === null || (typeof value === 'string' && value.length === 0);\n  });\n  indicators.push(missingCriticalFields);\n\n  // Indicator 3: QR string appears truncated (no CRC field or incomplete)\n  const isTruncated = detectTruncation(qrString, data);\n  indicators.push(isTruncated);\n\n  // Indicator 4: CRC field exists but checksum fails (data corruption)\n  if (data.crc && data.crc.length === 4 && !verifyCRC(qrString)) {\n    indicators.push(true);\n  }\n\n  // Return true if ANY corruption indicator is present\n  return indicators.some(indicator => indicator === true);\n}\n\n/**\n * Detects if QR string appears truncated\n *\n * @param qrString - Original QR string\n * @param data - Parsed data\n * @returns true if truncation detected, false otherwise\n *\n * @remarks\n * Truncation indicators:\n * - QR string doesn't end with Field 63 (CRC)\n * - QR string is unusually short (<50 characters)\n * - CRC field is missing or incomplete\n */\nfunction detectTruncation(qrString: string, data: Partial<VietQRData>): boolean {\n  // Minimum viable QR string should be at least 50 characters\n  // (00|02|01 + 01|02|11 + Field 38 ~40 chars + 53|03|704 + 58|02|VN + 63|04|XXXX)\n  if (qrString.length < 50) {\n    return true;\n  }\n\n  // QR string should end with Field 63 (CRC): 6304XXXX\n  if (!qrString.match(/6304[0-9A-Fa-f]{4}$/)) {\n    return true;\n  }\n\n  // CRC field should be present in parsed data\n  if (!data.crc || data.crc.length !== 4) {\n    return true;\n  }\n\n  return false;\n}\n\n/**\n * Checks if corruption is recoverable\n *\n * @param data - Parsed VietQR data\n * @param errors - Validation errors\n * @returns true if data is usable despite corruption, false if unusable\n *\n * @remarks\n * Recoverable corruption:\n * - CRC mismatch but all required fields present and valid\n * - Optional fields missing/truncated but core payment info intact\n *\n * Unrecoverable corruption:\n * - Missing bank code or account number\n * - Invalid currency or country code\n * - Critical format errors\n */\nexport function isRecoverableCorruption(\n  data: Partial<VietQRData>,\n  errors: ValidationError[]\n): boolean {\n  // Check for critical field errors\n  const criticalErrors = errors.filter(err =>\n    err.code === ValidationErrorCode.MISSING_REQUIRED_FIELD &&\n    (err.field === 'bankCode' ||\n     err.field === 'accountNumber' ||\n     err.field === 'currency' ||\n     err.field === 'countryCode')\n  );\n\n  // If critical fields are missing, corruption is unrecoverable\n  if (criticalErrors.length > 0) {\n    return false;\n  }\n\n  // Check if core payment fields are present and valid\n  const hasCoreFields = Boolean(\n    data.bankCode && data.bankCode.length > 0 &&\n    data.accountNumber && data.accountNumber.length > 0 &&\n    data.currency && data.currency.length > 0 &&\n    data.countryCode && data.countryCode.length > 0\n  );\n\n  // Corruption is recoverable if core fields are present\n  return hasCoreFields;\n}\n","/**\n * VietQR Validation Error Builder\n *\n * Utility functions for constructing validation errors with proper sanitization.\n * Prevents sensitive data exposure in error messages per security requirements.\n *\n * @module validators/error-builder\n */\n\nimport type { ValidationError, ValidationWarning } from '../types/decode';\nimport { ValidationErrorCode, ValidationWarningCode } from '../types/decode';\n\n/**\n * Sensitive field names that should be redacted in error messages\n */\nconst SENSITIVE_FIELDS = new Set(['accountNumber', 'bankCode']);\n\n/**\n * Fields that should be truncated if too long in error messages\n */\nconst TRUNCATABLE_FIELDS = new Set(['message', 'purposeCode']);\n\n/**\n * Maximum length for field values in error messages\n */\nconst MAX_ERROR_VALUE_LENGTH = 100;\n\n/**\n * Builds a validation error with automatic value sanitization\n *\n * @param field - Name of field with error\n * @param code - Error code\n * @param message - Human-readable error message\n * @param options - Optional error details\n * @returns Sanitized validation error\n *\n * @remarks\n * Sanitization rules:\n * - Account numbers: always \"[REDACTED]\"\n * - Bank codes: always \"[REDACTED]\"\n * - Messages: truncated to 100 chars if longer\n * - Other fields: shown as-is (amounts, codes, etc.)\n */\nexport function buildError(\n  field: string,\n  code: ValidationErrorCode,\n  message: string,\n  options?: {\n    expectedFormat?: string;\n    actualValue?: string;\n  }\n): ValidationError {\n  const error: ValidationError = {\n    field,\n    code,\n    message\n  };\n\n  if (options?.expectedFormat) {\n    error.expectedFormat = options.expectedFormat;\n  }\n\n  if (options?.actualValue !== undefined) {\n    error.actualValue = sanitizeValue(field, options.actualValue);\n  }\n\n  return error;\n}\n\n/**\n * Sanitizes field value for safe display in error messages\n *\n * @param field - Name of field\n * @param value - Original field value\n * @returns Sanitized value safe for error messages\n *\n * @remarks\n * Prevents sensitive data exposure and limits message size.\n * Per NAPAS security guidelines, financial account data must not\n * appear in logs or error messages.\n */\nexport function sanitizeValue(field: string, value: string): string {\n  // Always redact sensitive financial data\n  if (SENSITIVE_FIELDS.has(field)) {\n    return '[REDACTED]';\n  }\n\n  // Truncate long values\n  if (TRUNCATABLE_FIELDS.has(field) && value.length > MAX_ERROR_VALUE_LENGTH) {\n    return value.substring(0, MAX_ERROR_VALUE_LENGTH) + '...';\n  }\n\n  // Truncate any value longer than max length\n  if (value.length > MAX_ERROR_VALUE_LENGTH) {\n    return value.substring(0, MAX_ERROR_VALUE_LENGTH) + '...';\n  }\n\n  return value;\n}\n\n/**\n * Builds a validation warning\n *\n * @param field - Name of field with warning\n * @param code - Warning code\n * @param message - Human-readable warning message\n * @returns Validation warning\n *\n * @remarks\n * Warnings are non-critical issues that don't prevent processing.\n * Examples: deprecated fields, unusual patterns, missing optional fields.\n */\nexport function buildWarning(\n  field: string,\n  code: ValidationWarningCode,\n  message: string\n): ValidationWarning {\n  return {\n    field,\n    code,\n    message\n  };\n}\n\n/**\n * Merges validation error arrays, removing duplicates\n *\n * @param errorArrays - Arrays of validation errors to merge\n * @returns Deduplicated merged error array\n *\n * @remarks\n * Deduplication based on field + code combination.\n * Preserves first occurrence of each unique error.\n */\nexport function mergeErrors(...errorArrays: ValidationError[][]): ValidationError[] {\n  const seen = new Set<string>();\n  const merged: ValidationError[] = [];\n\n  for (const errors of errorArrays) {\n    for (const error of errors) {\n      const key = `${error.field}:${error.code}`;\n      if (!seen.has(key)) {\n        seen.add(key);\n        merged.push(error);\n      }\n    }\n  }\n\n  return merged;\n}\n\n/**\n * Sorts validation errors by severity and field name\n *\n * @param errors - Errors to sort\n * @returns Sorted error array\n *\n * @remarks\n * Sort priority:\n * 1. MISSING_REQUIRED_FIELD (highest priority)\n * 2. CHECKSUM_MISMATCH\n * 3. INVALID_CURRENCY, INVALID_COUNTRY\n * 4. INVALID_FORMAT\n * 5. Other errors\n * Within same priority, sorted alphabetically by field name.\n */\nexport function sortErrors(errors: ValidationError[]): ValidationError[] {\n  const priorityMap: Record<ValidationErrorCode, number> = {\n    [ValidationErrorCode.MISSING_REQUIRED_FIELD]: 1,\n    [ValidationErrorCode.CHECKSUM_MISMATCH]: 2,\n    [ValidationErrorCode.INVALID_CURRENCY]: 3,\n    [ValidationErrorCode.INVALID_COUNTRY]: 3,\n    [ValidationErrorCode.INVALID_FORMAT]: 4,\n    [ValidationErrorCode.LENGTH_EXCEEDED]: 5,\n    [ValidationErrorCode.LENGTH_TOO_SHORT]: 5,\n    [ValidationErrorCode.INVALID_CHARACTER]: 5,\n    [ValidationErrorCode.INVALID_AMOUNT]: 5,\n    [ValidationErrorCode.UNKNOWN_FIELD]: 6\n  };\n\n  return [...errors].sort((a, b) => {\n    // First, sort by priority\n    const priorityA = priorityMap[a.code] || 999;\n    const priorityB = priorityMap[b.code] || 999;\n\n    if (priorityA !== priorityB) {\n      return priorityA - priorityB;\n    }\n\n    // Within same priority, sort alphabetically by field\n    return a.field.localeCompare(b.field);\n  });\n}\n","/**\n * Advanced validation options for VietQR data\n *\n * @module validators/validate-with-options\n */\n\nimport type {\n  VietQRData,\n  ValidationResult,\n  ValidationError as VietQRValidationError,\n  ValidationWarning\n} from '../types/decode';\nimport { ValidationWarningCode, ValidationErrorCode } from '../types/decode';\n\n// Import validators\nimport { validateRequiredFields } from './required-fields-validator';\nimport { validateBusinessRules } from './business-rules-validator';\nimport { validateCRC } from './crc-validator';\nimport { detectCorruption } from './corruption-detector';\nimport { mergeErrors, sortErrors, buildWarning } from './error-builder';\n\n// Import field validators\nimport {\n  validateBankCode,\n  validateAccountNumber,\n  validateAmount,\n  validateCurrency,\n  validateCountryCode,\n  validateMessage,\n  validatePurposeCode,\n  validateBillNumber,\n  validateMerchantCategory\n} from './field-validators';\n\n/**\n * Custom field length limits for validation\n */\nexport interface CustomFieldLimits {\n  /** Maximum account number length (default: 19) */\n  accountNumberMax?: number;\n  /** Maximum amount length (default: 13) */\n  amountMax?: number;\n  /** Maximum message byte length (default: 500) */\n  messageMax?: number;\n  /** Maximum purpose code length (default: 25) */\n  purposeCodeMax?: number;\n  /** Maximum bill number length (default: 25) */\n  billNumberMax?: number;\n}\n\n/**\n * Advanced validation options for validateWithOptions\n */\nexport interface ValidationOptions {\n  /**\n   * Skip CRC checksum verification\n   * Useful for testing or when CRC has been previously verified\n   * @default false\n   */\n  skipCRCCheck?: boolean;\n\n  /**\n   * Custom field length limits\n   * Allows stricter or more lenient validation\n   */\n  customFieldLimits?: CustomFieldLimits;\n\n  /**\n   * Treat warnings as errors\n   * @default false\n   */\n  treatWarningsAsErrors?: boolean;\n\n  /**\n   * Skip corruption detection\n   * @default false\n   */\n  skipCorruptionDetection?: boolean;\n}\n\n/**\n * Validate parsed VietQR data with advanced options\n *\n * @param data - Parsed VietQR data to validate\n * @param qrString - Original QR string (required for CRC verification)\n * @param options - Advanced validation options\n * @returns Validation result with errors, warnings, and corruption flag\n *\n * @remarks\n * - Supports skipping CRC verification for performance\n * - Allows custom field length limits\n * - Can treat warnings as errors for strict validation\n *\n * @example\n * ```typescript\n * // Skip CRC check for performance\n * const result1 = validateWithOptions(data, qrString, {\n *   skipCRCCheck: true\n * });\n *\n * // Custom field limits\n * const result2 = validateWithOptions(data, qrString, {\n *   customFieldLimits: {\n *     accountNumberMax: 15,\n *     messageMax: 200\n *   }\n * });\n *\n * // Treat warnings as errors\n * const result3 = validateWithOptions(data, qrString, {\n *   treatWarningsAsErrors: true\n * });\n * ```\n */\nexport function validateWithOptions(\n  data: VietQRData,\n  qrString: string,\n  options: ValidationOptions = {}\n): ValidationResult {\n  const {\n    skipCRCCheck = false,\n    customFieldLimits,\n    treatWarningsAsErrors = false,\n    skipCorruptionDetection = false\n  } = options;\n\n  const allErrors: VietQRValidationError[] = [];\n  const warnings: ValidationWarning[] = [];\n\n  // Step 1: Validate required fields presence\n  const requiredFieldErrors = validateRequiredFields(data);\n  allErrors.push(...requiredFieldErrors);\n\n  // Step 2: Validate individual field formats with custom limits\n  const fieldErrors: VietQRValidationError[] = [];\n\n  // Required fields\n  const bankCodeError = validateBankCode(data.bankCode);\n  if (bankCodeError) fieldErrors.push(bankCodeError);\n\n  // Account number validation with custom limit\n  if (customFieldLimits?.accountNumberMax) {\n    const accountNumberError = validateAccountNumberWithLimit(\n      data.accountNumber,\n      customFieldLimits.accountNumberMax\n    );\n    if (accountNumberError) fieldErrors.push(accountNumberError);\n  } else {\n    const accountNumberError = validateAccountNumber(data.accountNumber);\n    if (accountNumberError) fieldErrors.push(accountNumberError);\n  }\n\n  const currencyError = validateCurrency(data.currency);\n  if (currencyError) fieldErrors.push(currencyError);\n\n  const countryCodeError = validateCountryCode(data.countryCode);\n  if (countryCodeError) fieldErrors.push(countryCodeError);\n\n  // Optional fields with custom limits\n  if (data.amount !== undefined) {\n    if (customFieldLimits?.amountMax) {\n      const amountError = validateAmountWithLimit(\n        data.amount,\n        customFieldLimits.amountMax\n      );\n      if (amountError) fieldErrors.push(amountError);\n    } else {\n      const amountError = validateAmount(data.amount);\n      if (amountError) fieldErrors.push(amountError);\n    }\n  }\n\n  if (data.message !== undefined) {\n    if (customFieldLimits?.messageMax) {\n      const messageError = validateMessageWithLimit(\n        data.message,\n        customFieldLimits.messageMax\n      );\n      if (messageError) fieldErrors.push(messageError);\n    } else {\n      const messageError = validateMessage(data.message);\n      if (messageError) fieldErrors.push(messageError);\n    }\n  }\n\n  if (data.purposeCode !== undefined) {\n    if (customFieldLimits?.purposeCodeMax) {\n      const purposeCodeError = validatePurposeCodeWithLimit(\n        data.purposeCode,\n        customFieldLimits.purposeCodeMax\n      );\n      if (purposeCodeError) fieldErrors.push(purposeCodeError);\n    } else {\n      const purposeCodeError = validatePurposeCode(data.purposeCode);\n      if (purposeCodeError) fieldErrors.push(purposeCodeError);\n    }\n  }\n\n  if (data.billNumber !== undefined) {\n    if (customFieldLimits?.billNumberMax) {\n      const billNumberError = validateBillNumberWithLimit(\n        data.billNumber,\n        customFieldLimits.billNumberMax\n      );\n      if (billNumberError) fieldErrors.push(billNumberError);\n    } else {\n      const billNumberError = validateBillNumber(data.billNumber);\n      if (billNumberError) fieldErrors.push(billNumberError);\n    }\n  }\n\n  if (data.merchantCategory !== undefined) {\n    const merchantCategoryError = validateMerchantCategory(data.merchantCategory);\n    if (merchantCategoryError) fieldErrors.push(merchantCategoryError);\n  }\n\n  allErrors.push(...fieldErrors);\n\n  // Step 3: Validate business rules\n  const businessRuleErrors = validateBusinessRules(data);\n  allErrors.push(...businessRuleErrors);\n\n  // Step 4: Validate CRC (if not skipped)\n  if (!skipCRCCheck && qrString && qrString.length > 0) {\n    const crcError = validateCRC(qrString, data.crc);\n    if (crcError) allErrors.push(crcError);\n  }\n\n  // Step 5: Detect corruption (if not skipped)\n  const isCorrupted = skipCorruptionDetection\n    ? false\n    : detectCorruption(data, qrString, allErrors);\n\n  // Step 6: Add warnings\n  // Dynamic QR should typically include amount\n  if (isDynamicQR(data) && !data.amount) {\n    warnings.push(\n      buildWarning(\n        'amount',\n        ValidationWarningCode.MISSING_OPTIONAL_FIELD,\n        'Dynamic QR codes typically include an amount. Consider adding Field 54.'\n      )\n    );\n  }\n\n  // Step 7: Merge and sort errors\n  const dedupedErrors = mergeErrors(allErrors);\n  const sortedErrors = sortErrors(dedupedErrors);\n\n  // Step 8: Convert warnings to errors if requested\n  if (treatWarningsAsErrors && warnings.length > 0) {\n    const warningErrors: VietQRValidationError[] = warnings.map(w => ({\n      field: w.field,\n      code: ValidationErrorCode.MISSING_REQUIRED_FIELD,\n      message: w.message\n    }));\n    sortedErrors.push(...warningErrors);\n  }\n\n  return {\n    isValid: sortedErrors.length === 0,\n    isCorrupted,\n    errors: sortedErrors,\n    warnings: warnings.length > 0 ? warnings : undefined\n  };\n}\n\n// Helper function to check if QR is dynamic\nfunction isDynamicQR(data: VietQRData): boolean {\n  return data.initiationMethod === 'dynamic';\n}\n\n// Custom limit validation helpers\nfunction validateAccountNumberWithLimit(\n  value: string | undefined,\n  maxLength: number\n): VietQRValidationError | null {\n  const baseError = validateAccountNumber(value);\n  if (baseError) return baseError;\n\n  if (value && value.length > maxLength) {\n    return {\n      field: 'accountNumber',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Account number exceeds custom maximum length of ${maxLength} digits`,\n      expectedFormat: `1-${maxLength} digits`,\n      actualValue: '[REDACTED]'\n    };\n  }\n\n  return null;\n}\n\nfunction validateAmountWithLimit(\n  value: string | undefined,\n  maxLength: number\n): VietQRValidationError | null {\n  const baseError = validateAmount(value);\n  if (baseError) return baseError;\n\n  if (value && value.length > maxLength) {\n    return {\n      field: 'amount',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Amount exceeds custom maximum length of ${maxLength} characters`,\n      expectedFormat: `Maximum ${maxLength} characters`,\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n\nfunction validateMessageWithLimit(\n  value: string | undefined,\n  maxByteLength: number\n): VietQRValidationError | null {\n  if (value === null || value === undefined) return null;\n\n  // Check for control characters\n  if (/[\\x00-\\x1F\\x7F]/.test(value)) {\n    return {\n      field: 'message',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Message contains invalid control characters',\n      expectedFormat: 'UTF-8 text without control characters',\n      actualValue: value.length > 100 ? value.substring(0, 100) + '...' : value\n    };\n  }\n\n  const byteLength = Buffer.from(value, 'utf-8').length;\n  if (byteLength > maxByteLength) {\n    return {\n      field: 'message',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Message exceeds custom maximum byte length of ${maxByteLength}`,\n      expectedFormat: `Maximum ${maxByteLength} bytes (UTF-8)`,\n      actualValue: value.length > 100 ? value.substring(0, 100) + '...' : value\n    };\n  }\n\n  return null;\n}\n\nfunction validatePurposeCodeWithLimit(\n  value: string | undefined,\n  maxLength: number\n): VietQRValidationError | null {\n  if (value === null || value === undefined || value.length === 0) return null;\n\n  if (!/^[a-zA-Z0-9]+$/.test(value)) {\n    return {\n      field: 'purposeCode',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Purpose code must be alphanumeric only',\n      expectedFormat: 'Alphanumeric (A-Z, a-z, 0-9)',\n      actualValue: value\n    };\n  }\n\n  if (value.length > maxLength) {\n    return {\n      field: 'purposeCode',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Purpose code exceeds custom maximum length of ${maxLength} characters`,\n      expectedFormat: `Maximum ${maxLength} characters`,\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n\nfunction validateBillNumberWithLimit(\n  value: string | undefined,\n  maxLength: number\n): VietQRValidationError | null {\n  if (value === null || value === undefined || value.length === 0) return null;\n\n  if (!/^[a-zA-Z0-9-]+$/.test(value)) {\n    return {\n      field: 'billNumber',\n      code: ValidationErrorCode.INVALID_CHARACTER,\n      message: 'Bill number must be alphanumeric (hyphens allowed)',\n      expectedFormat: 'Alphanumeric with optional hyphens (A-Z, a-z, 0-9, -)',\n      actualValue: value\n    };\n  }\n\n  if (value.length > maxLength) {\n    return {\n      field: 'billNumber',\n      code: ValidationErrorCode.LENGTH_EXCEEDED,\n      message: `Bill number exceeds custom maximum length of ${maxLength} characters`,\n      expectedFormat: `Maximum ${maxLength} characters`,\n      actualValue: value\n    };\n  }\n\n  return null;\n}\n","// ============================================================================\n// VietQR Generation Validation (Feature 004 - Input Validation)\n// ============================================================================\n\n// Re-export comprehensive config validator\nexport { validateVietQRConfig } from './config-validator.js';\n\n// Re-export individual field validators for QR generation\nexport { validateBankBin, validateAccountNumber as validateAccountNumberForGeneration, validateCardNumber } from './bank-info.js';\nexport { validateServiceCode } from './service-code.js';\nexport { validateAmount as validateAmountForGeneration } from './amount.js';\nexport { validateBillNumber as validateBillNumberForGeneration, validatePurpose, validateReferenceLabel, validateMessage as validateMessageForGeneration } from './additional-data.js';\nexport { validateCountry as validateCountryCodeForGeneration } from './country.js';\nexport { validateCurrency as validateCurrencyCodeForGeneration } from './currency.js';\nexport { validateMerchantCategory as validateMerchantCategoryCodeForGeneration } from './merchant-category.js';\n\n// Re-export validation utilities\nexport { trimWhitespace, sanitizeForError, isWhitespaceOnly, containsNewlineOrTab, isAlphanumericASCII, isNumericASCII } from './validation-utils.js';\n\n// Re-export validation context for multi-error collection\nexport { ValidationContext } from './validation-context.js';\n\n// Re-export error codes\nexport { type ValidationErrorCode } from './error-codes.js';\n\n// ============================================================================\n// VietQR Data Validation (Feature 003 - Decoding & Validation)\n// ============================================================================\n\nimport type { VietQRData, ValidationResult, ValidationError as VietQRValidationError, ValidationWarning } from '../types/decode';\nimport { ValidationWarningCode } from '../types/decode';\n\n// Import field validators\nimport {\n  validateBankCode,\n  validateAccountNumber as validateParsedAccountNumber,\n  validateAmount as validateParsedAmount,\n  validateCurrency,\n  validateCountryCode,\n  validateMessage,\n  validatePurposeCode,\n  validateBillNumber as validateParsedBillNumber,\n  validateMerchantCategory\n} from './field-validators';\n\nimport { validateCRC } from './crc-validator';\nimport { validateRequiredFields } from './required-fields-validator';\nimport { validateBusinessRules, hasDynamicQRRecommendedFields } from './business-rules-validator';\nimport { detectCorruption } from './corruption-detector';\nimport { mergeErrors, sortErrors, buildWarning } from './error-builder';\n\n/**\n * Validates parsed VietQR data against NAPAS IBFT v1.5.2 specification\n *\n * @param data - Parsed VietQR data to validate\n * @param qrString - Original QR string (required for CRC verification)\n * @returns Validation result with errors, warnings, and corruption flag\n *\n * @remarks\n * This function validates decoded QR data, not generation config.\n * For validating QR generation configuration, use validateVietQRConfig().\n *\n * @example\n * ```typescript\n * const parseResult = parse(qrString);\n * if (parseResult.success) {\n *   const validation = validate(parseResult.data, qrString);\n *   if (validation.isValid) {\n *     // Process payment\n *   } else {\n *     console.error('Validation errors:', validation.errors);\n *   }\n * }\n * ```\n */\nexport function validate(data: VietQRData, qrString: string): ValidationResult {\n  const allErrors: VietQRValidationError[] = [];\n  const warnings: ValidationWarning[] = [];\n\n  // Step 1: Validate required fields presence\n  const requiredFieldErrors = validateRequiredFields(data);\n  allErrors.push(...requiredFieldErrors);\n\n  // Step 2: Validate individual field formats\n  const fieldErrors: VietQRValidationError[] = [];\n\n  // Required fields\n  const bankCodeError = validateBankCode(data.bankCode);\n  if (bankCodeError) fieldErrors.push(bankCodeError);\n\n  const accountNumberError = validateParsedAccountNumber(data.accountNumber);\n  if (accountNumberError) fieldErrors.push(accountNumberError);\n\n  const currencyError = validateCurrency(data.currency);\n  if (currencyError) fieldErrors.push(currencyError);\n\n  const countryCodeError = validateCountryCode(data.countryCode);\n  if (countryCodeError) fieldErrors.push(countryCodeError);\n\n  // Optional fields (only validate if present)\n  if (data.amount !== undefined) {\n    const amountError = validateParsedAmount(data.amount);\n    if (amountError) fieldErrors.push(amountError);\n  }\n\n  if (data.message !== undefined) {\n    const messageError = validateMessage(data.message);\n    if (messageError) fieldErrors.push(messageError);\n  }\n\n  if (data.purposeCode !== undefined) {\n    const purposeCodeError = validatePurposeCode(data.purposeCode);\n    if (purposeCodeError) fieldErrors.push(purposeCodeError);\n  }\n\n  if (data.billNumber !== undefined) {\n    const billNumberError = validateParsedBillNumber(data.billNumber);\n    if (billNumberError) fieldErrors.push(billNumberError);\n  }\n\n  if (data.merchantCategory !== undefined) {\n    const merchantCategoryError = validateMerchantCategory(data.merchantCategory);\n    if (merchantCategoryError) fieldErrors.push(merchantCategoryError);\n  }\n\n  allErrors.push(...fieldErrors);\n\n  // Step 3: Validate business rules\n  const businessRuleErrors = validateBusinessRules(data);\n  allErrors.push(...businessRuleErrors);\n\n  // Step 4: Verify CRC checksum (only if qrString is provided)\n  // When qrString is empty, only format validation is performed, not checksum verification\n  if (qrString && qrString.length > 0) {\n    const crcError = validateCRC(qrString, data.crc);\n    if (crcError) {\n      allErrors.push(crcError);\n    }\n  } else if (data.crc) {\n    // When no qrString provided, only validate CRC format\n    const crcFormatError = validateCRC('6304' + data.crc, data.crc);\n    if (crcFormatError && crcFormatError.code !== 'CHECKSUM_MISMATCH') {\n      // Only add non-checksum errors (format errors)\n      allErrors.push(crcFormatError);\n    }\n  }\n\n  // Step 5: Detect data corruption\n  const isCorrupted = detectCorruption(data, qrString, allErrors);\n\n  // Step 6: Generate warnings for optional/recommended fields\n  if (!hasDynamicQRRecommendedFields(data)) {\n    warnings.push(buildWarning(\n      'amount',\n      ValidationWarningCode.MISSING_OPTIONAL_FIELD,\n      'Dynamic QR codes typically include an amount. Consider adding Field 54.'\n    ));\n  }\n\n  // Deduplicate and sort errors\n  const uniqueErrors = mergeErrors(allErrors);\n  const sortedErrors = sortErrors(uniqueErrors);\n\n  // Determine overall validation status\n  const isValid = sortedErrors.length === 0;\n\n  return {\n    isValid,\n    isCorrupted,\n    errors: sortedErrors,\n    warnings: warnings.length > 0 ? warnings : undefined\n  };\n}\n\n// Re-export validator functions for advanced usage\nexport {\n  validateBankCode,\n  validateCurrency,\n  validateCountryCode,\n  validateMessage,\n  validatePurposeCode,\n  validateMerchantCategory\n} from './field-validators';\n\n// Export aliased functions using local imports (for decoding validation)\nexport { validateParsedAccountNumber as validateAccountNumber };\nexport { validateParsedAmount as validateAmount };\nexport { validateParsedBillNumber as validateBillNumber };\n\nexport { validateCRC, verifyCRC, calculateCRC } from './crc-validator';\nexport { validateRequiredFields } from './required-fields-validator';\nexport { validateBusinessRules } from './business-rules-validator';\nexport { detectCorruption, isRecoverableCorruption } from './corruption-detector';\nexport { buildError, buildWarning, sanitizeValue, mergeErrors, sortErrors } from './error-builder';\nexport { validateFieldLength, validateMaxLength, validateMinLength, validateExactLength } from './length-validators';\n\n// Export advanced validation options\nexport { validateWithOptions, type ValidationOptions, type CustomFieldLimits } from './validate-with-options';\n","import type { QRField } from '../types/data';\n\n/**\n * Encode EMVCo field using ID/Length/Value format\n *\n * @param id - 2-digit field identifier\n * @param value - Field value\n * @returns Encoded field string (empty if value is empty)\n *\n * @example\n * ```typescript\n * const encoded = encodeField('00', '01');\n * console.log(encoded); // \"000201\"\n * ```\n *\n * @internal\n */\nexport function encodeField(id: string, value: string): string {\n  if (!value) {\n    return '';\n  }\n\n  const length = value.length.toString().padStart(2, '0');\n  const encoded = `${id}${length}${value}`;\n\n  return encoded;\n}\n\n/**\n * Encode EMVCo field and return detailed field object\n *\n * @param id - 2-digit field identifier\n * @param value - Field value\n * @returns QRField object with id, length, value, and encoded string\n *\n * @internal\n */\nexport function encodeFieldWithDetails(id: string, value: string): QRField | null {\n  if (!value) {\n    return null;\n  }\n\n  const length = value.length.toString().padStart(2, '0');\n  const encoded = `${id}${length}${value}`;\n\n  return {\n    id,\n    length,\n    value,\n    encoded,\n  };\n}\n","/**\n * NAPAS GUID for Vietnam QR payment system\n */\nexport const NAPAS_GUID = 'A000000727';\n\n/**\n * Default currency code for Vietnamese Dong (ISO 4217)\n */\nexport const DEFAULT_CURRENCY = '704';\n\n/**\n * Default country code for Vietnam (ISO 3166-1 alpha-2)\n */\nexport const DEFAULT_COUNTRY = 'VN';\n\n/**\n * Default merchant category code\n */\nexport const DEFAULT_MCC = '0000';\n\n/**\n * Payload format indicator (always \"01\" for EMVCo)\n */\nexport const PAYLOAD_FORMAT_INDICATOR = '01';\n","import { encodeField } from '../utils/encoding';\nimport { NAPAS_GUID } from '../utils/constants';\nimport type { ServiceCode } from '../types';\n\n/**\n * Build Consumer Account Information field (ID 38)\n *\n * Per NAPAS IBFT v1.5.2 specification, this field contains:\n * - Sub-field 00: NAPAS GUID (A000000727)\n * - Sub-field 01: Payment provider information\n *   - Sub-sub-field 00: Bank BIN (6 digits)\n *   - Sub-sub-field 01: Account number or card number (max 19 chars)\n * - Sub-field 02: Service code (QRIBFTTA or QRIBFTTC)\n *\n * Structure: ID 38 → {00: GUID, 01: {00: BIN, 01: Account}, 02: ServiceCode}\n *\n * @param bankBin - 6-digit bank identification number\n * @param accountNumber - Account number or card number (max 19 characters)\n * @param serviceCode - Service code (QRIBFTTA for account, QRIBFTTC for card)\n * @returns Encoded consumer account information field (ID 38)\n *\n * @example\n * ```typescript\n * const field = buildConsumerAccountInfo('970403', '01234567', 'QRIBFTTA');\n * console.log(field);\n * // \"38570010A00000072701270006970403011200110123456780208QRIBFTTA\"\n * //  ^^   ID 38\n * //    ^^  Length 57\n * //      0010A000000727 - Sub-field 00 (GUID)\n * //                    0127... - Sub-field 01 (Bank info)\n * //                            0208QRIBFTTA - Sub-field 02 (Service code)\n * ```\n */\nexport function buildConsumerAccountInfo(\n  bankBin: string,\n  accountNumber: string,\n  serviceCode: ServiceCode\n): string {\n  // Sub-field 00: NAPAS GUID\n  const guidField = encodeField('00', NAPAS_GUID);\n\n  // Sub-field 01: Payment provider information\n  // Contains nested sub-fields for bank BIN and account number\n  const binSubField = encodeField('00', bankBin);\n  const accountSubField = encodeField('01', accountNumber);\n  const paymentProviderValue = binSubField + accountSubField;\n  const paymentProviderField = encodeField('01', paymentProviderValue);\n\n  // Sub-field 02: Service code\n  const serviceCodeField = encodeField('02', serviceCode);\n\n  // Combine all sub-fields\n  const consumerAccountValue = guidField + paymentProviderField + serviceCodeField;\n\n  // Return ID 38 with encoded sub-fields\n  return encodeField('38', consumerAccountValue);\n}\n","import { encodeField } from '../utils/encoding';\n\n/**\n * Build Additional Data field (ID 62) for dynamic QR codes\n *\n * Per NAPAS IBFT v1.5.2 specification:\n * - Field 62 contains nested subfields for bill number and purpose\n * - Subfield 01: Bill number (max 25 chars, alphanumeric)\n * - Subfield 08: Purpose/description (max 25 chars, any characters)\n *\n * Format: \"62\" + Length + (Subfield 01 + Subfield 08)\n *\n * @param billNumber - Bill/invoice number (optional)\n * @param purpose - Transaction purpose/description (optional)\n * @returns Complete Field 62 in EMVCo ID/Length/Value format\n *\n * @example\n * ```typescript\n * // NAPAS reference example\n * buildAdditionalData('NPS6869', 'thanh toan don hang');\n * // Returns: \"62340107NPS68690819thanh toan don hang\"\n * // Breakdown:\n * //   62 = Field ID\n * //   34 = Total length of subfields\n * //   0107NPS6869 = Subfield 01 (bill number)\n * //   0819thanh toan don hang = Subfield 08 (purpose)\n * ```\n */\nexport function buildAdditionalData(billNumber?: string, purpose?: string): string {\n  // Build subfields\n  let subfields = '';\n\n  // Subfield 01: Bill number\n  if (billNumber && billNumber.trim().length > 0) {\n    subfields += encodeField('01', billNumber.trim());\n  }\n\n  // Subfield 08: Purpose/description\n  if (purpose && purpose.trim().length > 0) {\n    subfields += encodeField('08', purpose.trim());\n  }\n\n  // If no subfields, return empty string\n  if (subfields.length === 0) {\n    return '';\n  }\n\n  // Build Field 62 with total length of all subfields\n  return encodeField('62', subfields);\n}\n","import { crc16ccitt } from 'crc';\n\n/**\n * Calculate CRC-16-CCITT checksum for data string\n *\n * Per EMVCo specification, the CRC is calculated over the entire data string\n * including the CRC field ID \"63\" and length \"04\" but with CRC value as empty.\n * The data parameter should already include \"6304\" at the end.\n *\n * @param data - Data string ending with \"6304\" placeholder\n * @returns 4-character uppercase hexadecimal CRC\n *\n * @example\n * ```typescript\n * const dataWithPlaceholder = '00020101021138570010A00000072701270006970403011200110123456780208QRIBFTTA53037045802VN6304';\n * const crc = calculateCRC(dataWithPlaceholder);\n * console.log(crc); // \"F4E5\"\n * // Complete QR data: dataWithPlaceholder + crc\n * ```\n *\n * @remarks\n * Uses CRC-16-CCITT algorithm (ISO/IEC 13239, polynomial 0x1021, initial 0xFFFF).\n * Implementation uses the 'crc' npm package's crc16ccitt function which matches\n * the NAPAS specification requirements for VietQR payment QR codes.\n */\nexport function calculateCRC(data: string): string {\n  const crcValue = crc16ccitt(data);\n  return crcValue.toString(16).toUpperCase().padStart(4, '0');\n}\n\n/**\n * Verify CRC-16-CCITT checksum of VietQR string\n *\n * @param qrString - Complete EMV QR formatted VietQR string (must include CRC field)\n * @returns true if CRC is valid, false otherwise\n *\n * @example\n * ```typescript\n * const validQR = '00020101...6304F4E5';\n * const isValid = verifyCRC(validQR); // true\n *\n * const invalidQR = '00020101...63040000';\n * const isInvalid = verifyCRC(invalidQR); // false\n * ```\n *\n * @remarks\n * Uses CRC-16-CCITT algorithm (polynomial 0x1021) per EMVCo spec.\n * Compares calculated CRC with CRC field in QR string.\n */\nexport function verifyCRC(qrString: string): boolean {\n  // Extract CRC from end of string (last 4 characters)\n  if (qrString.length < 4) {\n    return false;\n  }\n\n  const providedCRC = qrString.slice(-4);\n\n  // Calculate CRC on string without the CRC value (but with field ID \"6304\")\n  const dataWithoutCRC = qrString.slice(0, -4);\n  const calculatedCRC = calculateCRC(dataWithoutCRC);\n\n  return providedCRC.toUpperCase() === calculatedCRC;\n}\n","import { validateVietQRConfig } from '../validators';\nimport { buildConsumerAccountInfo } from './consumer-account-info';\nimport { buildAdditionalData } from './additional-data';\nimport { encodeFieldWithDetails } from '../utils/encoding';\nimport { calculateCRC } from '../utils/crc';\nimport { DEFAULT_CURRENCY, DEFAULT_COUNTRY } from '../utils/constants';\nimport type { VietQRConfig } from '../types/config';\nimport type { VietQRData, QRField } from '../types/data';\n\n/**\n * Generate VietQR data string for static or dynamic QR code\n *\n * Creates an EMVCo-compliant QR data string per NAPAS IBFT v1.5.2 specification.\n * The generated string is ready to be encoded into a QR code image.\n *\n * Supports both static and dynamic QR codes:\n * - Static QR: User enters amount manually (initiation method \"11\")\n * - Dynamic QR: Amount pre-filled (initiation method \"12\")\n *\n * Supports both account and card transfers:\n * - QRIBFTTA: Account transfer (requires accountNumber)\n * - QRIBFTTC: Card transfer (requires cardNumber)\n *\n * @param config - VietQR configuration\n * @param config.bankBin - 6-digit bank identification number\n * @param config.accountNumber - Account number for QRIBFTTA (max 19 characters, alphanumeric)\n * @param config.cardNumber - Card number for QRIBFTTC (max 19 characters, alphanumeric)\n * @param config.serviceCode - Service code (QRIBFTTA for account, QRIBFTTC for card)\n * @param config.amount - Transaction amount for dynamic QR (max 13 characters, numeric with optional decimal)\n * @param config.billNumber - Bill/invoice number (max 25 characters, alphanumeric)\n * @param config.purpose - Transaction purpose/description (max 25 characters)\n * @returns VietQR data with raw string, CRC, and field breakdown\n *\n * @throws {AggregateValidationError} If any parameter fails validation\n *\n * @example\n * ```typescript\n * // Generate static account QR\n * const accountResult = generateVietQR({\n *   bankBin: '970403',\n *   accountNumber: '01234567',\n *   serviceCode: 'QRIBFTTA'\n * });\n *\n * // Generate dynamic account QR with amount\n * const dynamicResult = generateVietQR({\n *   bankBin: '970403',\n *   accountNumber: '0011012345678',\n *   serviceCode: 'QRIBFTTA',\n *   amount: '180000',\n *   billNumber: 'NPS6869',\n *   purpose: 'thanh toan don hang'\n * });\n *\n * // Generate static card QR\n * const cardResult = generateVietQR({\n *   bankBin: '970403',\n *   cardNumber: '9704031101234567',\n *   serviceCode: 'QRIBFTTC'\n * });\n * ```\n */\nexport function generateVietQR(config: VietQRConfig): VietQRData {\n  // Validate all input parameters (aggregate validation - collects all errors)\n  validateVietQRConfig(config);\n\n  // Determine if this is a dynamic QR (has amount)\n  const isDynamic = config.amount !== undefined && config.amount.trim().length > 0;\n\n  // Trim whitespace from inputs\n  const bankBin = config.bankBin.trim();\n  const accountOrCardNumber = (config.accountNumber || config.cardNumber || '').trim();\n  const serviceCode = config.serviceCode;\n\n  // Track all fields for debugging\n  const fields: QRField[] = [];\n\n  // Field 00: Payload Format Indicator (always \"01\")\n  const field00 = encodeFieldWithDetails('00', '01');\n  if (field00) fields.push(field00);\n\n  // Field 01: Point of Initiation Method\n  // \"11\" = Static QR (user enters amount)\n  // \"12\" = Dynamic QR (amount pre-filled)\n  const initiationMethod = isDynamic ? '12' : '11';\n  const field01 = encodeFieldWithDetails('01', initiationMethod);\n  if (field01) fields.push(field01);\n\n  // Field 38: Consumer Account Information (NAPAS-specific)\n  const consumerAccountField = buildConsumerAccountInfo(bankBin, accountOrCardNumber, serviceCode);\n  const field38Value = consumerAccountField.substring(4); // Remove \"38LL\" prefix for value extraction\n  const field38 = encodeFieldWithDetails('38', field38Value);\n  if (field38) fields.push(field38);\n\n  // Field 53: Transaction Currency (704 = VND)\n  const field53 = encodeFieldWithDetails('53', DEFAULT_CURRENCY);\n  if (field53) fields.push(field53);\n\n  // Field 54: Transaction Amount (only for dynamic QR)\n  let field54Encoded = '';\n  if (isDynamic && config.amount) {\n    const field54 = encodeFieldWithDetails('54', config.amount.trim());\n    if (field54) {\n      fields.push(field54);\n      field54Encoded = field54.encoded;\n    }\n  }\n\n  // Field 58: Country Code (VN = Vietnam)\n  const field58 = encodeFieldWithDetails('58', DEFAULT_COUNTRY);\n  if (field58) fields.push(field58);\n\n  // Field 62: Additional Data (for dynamic QR with bill number or purpose)\n  let field62Encoded = '';\n  if (config.billNumber || config.purpose) {\n    const additionalDataField = buildAdditionalData(config.billNumber, config.purpose);\n    if (additionalDataField) {\n      const field62Value = additionalDataField.substring(4); // Remove \"62LL\" prefix\n      const field62 = encodeFieldWithDetails('62', field62Value);\n      if (field62) {\n        fields.push(field62);\n        field62Encoded = additionalDataField;\n      }\n    }\n  }\n\n  // Build data string without CRC\n  let dataWithoutCRC = field00?.encoded || '';\n  dataWithoutCRC += field01?.encoded || '';\n  dataWithoutCRC += consumerAccountField;\n  dataWithoutCRC += field53?.encoded || '';\n  dataWithoutCRC += field54Encoded; // Amount field (dynamic QR only)\n  dataWithoutCRC += field58?.encoded || '';\n  dataWithoutCRC += field62Encoded; // Additional data (if present)\n\n  // Add CRC placeholder\n  dataWithoutCRC += '6304'; // CRC field ID and length\n\n  // Calculate CRC\n  const crc = calculateCRC(dataWithoutCRC);\n\n  // Add CRC field to fields list\n  const field63 = encodeFieldWithDetails('63', crc);\n  if (field63) fields.push(field63);\n\n  // Complete QR data string\n  const rawData = dataWithoutCRC + crc;\n\n  return {\n    rawData,\n    crc,\n    fields,\n  };\n}\n","import { ValidationError, AggregateValidationError } from '../types/errors';\nimport type { QRImageConfig } from '../types/qr-image';\n\n/**\n * Validates QR image configuration\n *\n * Performs comprehensive validation of all QRImageConfig fields according to\n * functional requirements FR-011 and FR-012.\n *\n * @param config - QR image configuration to validate\n * @throws {AggregateValidationError} If multiple validation errors occur\n * @throws {ValidationError} If a single validation error occurs\n */\nexport function validateQRImageConfig(config: QRImageConfig): void {\n  const errors: ValidationError[] = [];\n\n  // FR-011: Required field validation - data must be non-empty string\n  if (!config.data || typeof config.data !== 'string' || config.data.trim() === '') {\n    errors.push(\n      new ValidationError(\n        'data',\n        config.data,\n        'required',\n        'data must be a non-empty string'\n      )\n    );\n  }\n\n  // Optional: format validation\n  if (config.format !== undefined) {\n    const validFormats = ['png', 'svg'];\n    if (!validFormats.includes(config.format)) {\n      errors.push(\n        new ValidationError(\n          'format',\n          config.format,\n          'enum',\n          'format must be either \"png\" or \"svg\"'\n        )\n      );\n    }\n  }\n\n  // FR-012: size validation (50-1000 pixels)\n  if (config.size !== undefined) {\n    if (!Number.isInteger(config.size)) {\n      errors.push(\n        new ValidationError(\n          'size',\n          config.size,\n          'type',\n          'size must be an integer'\n        )\n      );\n    } else if (config.size < 50 || config.size > 1000) {\n      errors.push(\n        new ValidationError(\n          'size',\n          config.size,\n          'range',\n          'size must be between 50 and 1000 pixels'\n        )\n      );\n    }\n  }\n\n  // Optional: errorCorrectionLevel validation\n  if (config.errorCorrectionLevel !== undefined) {\n    const validLevels = ['L', 'M', 'Q', 'H'];\n    if (!validLevels.includes(config.errorCorrectionLevel)) {\n      errors.push(\n        new ValidationError(\n          'errorCorrectionLevel',\n          config.errorCorrectionLevel,\n          'enum',\n          'errorCorrectionLevel must be one of: L, M, Q, H'\n        )\n      );\n    }\n  }\n\n  // Optional: margin validation (non-negative)\n  if (config.margin !== undefined) {\n    if (!Number.isInteger(config.margin)) {\n      errors.push(\n        new ValidationError(\n          'margin',\n          config.margin,\n          'type',\n          'margin must be an integer'\n        )\n      );\n    } else if (config.margin < 0) {\n      errors.push(\n        new ValidationError(\n          'margin',\n          config.margin,\n          'range',\n          'margin must be a non-negative integer'\n        )\n      );\n    }\n  }\n\n  // Optional: color validation\n  if (config.color) {\n    const hexPattern = /^#[0-9A-Fa-f]{6}$/;\n\n    if (config.color.dark && !hexPattern.test(config.color.dark)) {\n      errors.push(\n        new ValidationError(\n          'color.dark',\n          config.color.dark,\n          'pattern',\n          'color.dark must be a valid 6-digit hex color (e.g., #000000)'\n        )\n      );\n    }\n\n    if (config.color.light && !hexPattern.test(config.color.light)) {\n      errors.push(\n        new ValidationError(\n          'color.light',\n          config.color.light,\n          'pattern',\n          'color.light must be a valid 6-digit hex color (e.g., #FFFFFF)'\n        )\n      );\n    }\n  }\n\n  // Throw aggregate error if multiple validation errors\n  if (errors.length > 1) {\n    throw new AggregateValidationError(errors);\n  }\n\n  // Throw single error if only one validation error\n  if (errors.length === 1) {\n    throw errors[0];\n  }\n}\n","import QRCode from 'qrcode';\nimport { ImageEncodingError } from '../types/errors';\nimport type { ErrorCorrectionLevel, QRColorOptions } from '../types/qr-image';\n\n/**\n * Options for PNG/SVG encoding\n */\nexport interface ImageEncodingOptions {\n  size?: number;\n  margin?: number;\n  errorCorrectionLevel?: ErrorCorrectionLevel;\n  color?: QRColorOptions;\n}\n\n/**\n * Result of base64 encoding with data URI\n */\nexport interface Base64Result {\n  /**\n   * Base64-encoded string (without data URI prefix)\n   */\n  base64: string;\n\n  /**\n   * Complete data URI for direct embedding\n   */\n  dataURI: string;\n}\n\n/**\n * Encodes QR data as PNG image with base64 data URI\n *\n * Wraps qrcode.toDataURL() to generate PNG images with configurable parameters.\n *\n * @param data - QR data string to encode\n * @param options - Encoding options (size, margin, errorCorrectionLevel, color)\n * @returns PNG data URI string\n * @throws {ImageEncodingError} If PNG encoding fails\n */\nexport async function encodePNGImage(\n  data: string,\n  options: ImageEncodingOptions = {}\n): Promise<string> {\n  try {\n    const dataURI = await QRCode.toDataURL(data, {\n      type: 'image/png',\n      width: options.size,\n      margin: options.margin,\n      errorCorrectionLevel: options.errorCorrectionLevel,\n      color: options.color\n        ? {\n            dark: options.color.dark,\n            light: options.color.light,\n          }\n        : undefined,\n    });\n\n    return dataURI;\n  } catch (error) {\n    throw new ImageEncodingError(\n      `Failed to encode PNG image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n      error instanceof Error ? error : undefined\n    );\n  }\n}\n\n/**\n * Encodes QR data as SVG image with base64 data URI\n *\n * Wraps qrcode.toString() to generate SVG images with configurable parameters.\n *\n * @param data - QR data string to encode\n * @param options - Encoding options (size, margin, errorCorrectionLevel, color)\n * @returns SVG data URI string\n * @throws {ImageEncodingError} If SVG encoding fails\n */\nexport async function encodeSVGImage(\n  data: string,\n  options: ImageEncodingOptions = {}\n): Promise<string> {\n  try {\n    const svgString = await QRCode.toString(data, {\n      type: 'svg',\n      width: options.size,\n      margin: options.margin,\n      errorCorrectionLevel: options.errorCorrectionLevel,\n      color: options.color\n        ? {\n            dark: options.color.dark,\n            light: options.color.light,\n          }\n        : undefined,\n    });\n\n    // Convert SVG string to base64 data URI\n    const base64SVG = Buffer.from(svgString).toString('base64');\n    const dataURI = `data:image/svg+xml;base64,${base64SVG}`;\n\n    return dataURI;\n  } catch (error) {\n    throw new ImageEncodingError(\n      `Failed to encode SVG image: ${error instanceof Error ? error.message : 'Unknown error'}`,\n      error instanceof Error ? error : undefined\n    );\n  }\n}\n\n/**\n * Encodes string or buffer to base64 with data URI\n *\n * Utility function for base64 encoding with automatic data URI generation.\n *\n * @param input - String or Buffer to encode\n * @param mimeType - MIME type for data URI (e.g., 'text/plain', 'image/png')\n * @returns Base64 string and data URI\n */\nexport function encodeToBase64(input: string | Buffer, mimeType: string): Base64Result {\n  const base64 = Buffer.from(input).toString('base64');\n  const dataURI = `data:${mimeType};base64,${base64}`;\n\n  return {\n    base64,\n    dataURI,\n  };\n}\n","import { validateQRImageConfig } from '../validators/qr-image-config';\nimport { encodePNGImage, encodeSVGImage } from '../utils/image-encoder';\nimport { QRGenerationError } from '../types/errors';\nimport type { QRImageConfig, QRImageResult } from '../types/qr-image';\n\n/**\n * Default configuration for QR image generation\n * Per constitution assumptions and data-model.md\n */\nconst DEFAULT_CONFIG = {\n  format: 'png' as const,\n  size: 300,\n  errorCorrectionLevel: 'M' as const,\n  margin: 4,\n  color: {\n    dark: '#000000',\n    light: '#FFFFFF',\n  },\n};\n\n/**\n * Generates QR code image from VietQR data string\n *\n * Converts VietQR data strings into base64-encoded PNG or SVG images\n * suitable for web embedding, API responses, or further processing.\n *\n * @param config - QR image generation configuration\n * @returns QR image result with base64 string, data URI, and metadata\n * @throws {ValidationError} If configuration is invalid\n * @throws {QRGenerationError} If QR code generation fails\n * @throws {ImageEncodingError} If image encoding fails\n *\n * @example\n * ```typescript\n * // Generate PNG QR image\n * const result = await generateQRImage({\n *   data: vietQRData,\n *   format: 'png',\n *   size: 300\n * });\n *\n * // Use in HTML\n * <img src={result.dataURI} alt=\"Payment QR Code\" />\n * ```\n */\nexport async function generateQRImage(config: QRImageConfig): Promise<QRImageResult> {\n  // Step 1: Validate configuration (throws ValidationError if invalid)\n  validateQRImageConfig(config);\n\n  // Step 2: Apply defaults\n  const finalConfig = {\n    data: config.data,\n    format: config.format ?? DEFAULT_CONFIG.format,\n    size: config.size ?? DEFAULT_CONFIG.size,\n    errorCorrectionLevel: config.errorCorrectionLevel ?? DEFAULT_CONFIG.errorCorrectionLevel,\n    margin: config.margin ?? DEFAULT_CONFIG.margin,\n    color: config.color ?? DEFAULT_CONFIG.color,\n  };\n\n  // Step 3: Generate QR image based on format\n  let dataURI: string;\n\n  try {\n    if (finalConfig.format === 'svg') {\n      // SVG path\n      dataURI = await encodeSVGImage(finalConfig.data, {\n        size: finalConfig.size,\n        margin: finalConfig.margin,\n        errorCorrectionLevel: finalConfig.errorCorrectionLevel,\n        color: finalConfig.color,\n      });\n    } else {\n      // PNG path (default)\n      dataURI = await encodePNGImage(finalConfig.data, {\n        size: finalConfig.size,\n        margin: finalConfig.margin,\n        errorCorrectionLevel: finalConfig.errorCorrectionLevel,\n        color: finalConfig.color,\n      });\n    }\n  } catch (error) {\n    // Handle QR capacity errors (data too long for error correction level)\n    if (error instanceof Error &&\n        (error.message.includes('too much data') ||\n         error.message.includes('too big to be stored'))) {\n      throw new QRGenerationError(\n        `Data too long for QR code capacity at error correction level ${finalConfig.errorCorrectionLevel}. Try using a lower error correction level (L or M) or reducing data size.`,\n        error\n      );\n    }\n    // Re-throw other errors as-is (ImageEncodingError, etc.)\n    throw error;\n  }\n\n  // Step 4: Extract base64 from data URI\n  const base64Prefix =\n    finalConfig.format === 'svg' ? 'data:image/svg+xml;base64,' : 'data:image/png;base64,';\n  const base64 = dataURI.replace(base64Prefix, '');\n\n  // Step 5: Return result with metadata\n  return {\n    base64,\n    dataURI,\n    format: finalConfig.format,\n    size: finalConfig.size,\n    errorCorrectionLevel: finalConfig.errorCorrectionLevel,\n  };\n}\n","/**\n * EMV QR TLV (Tag-Length-Value) parser\n *\n * Parses EMV QR formatted strings using Tag-Length-Value encoding structure\n *\n * @module parsers/tlv-parser\n */\n\nimport { DecodingErrorType } from '../types/decode';\n\nexport interface TLVField {\n  /** 2-digit field identifier */\n  id: string;\n  /** Field value length */\n  length: number;\n  /** Field value content */\n  value: string;\n}\n\nexport interface TLVParseResult {\n  /** Parse operation succeeded */\n  success: boolean;\n  /** Extracted TLV fields */\n  fields: TLVField[];\n  /** Data appears truncated or corrupted */\n  isCorrupted: boolean;\n  /** Error details if parse failed */\n  error?: {\n    type: DecodingErrorType;\n    message: string;\n    position?: number;\n  };\n}\n\n/**\n * Parse EMV QR TLV formatted string\n *\n * @param input - EMV QR string with TLV encoding\n * @returns Parse result with extracted fields or error\n *\n * @remarks\n * TLV format: Each field is ID (2 digits) + Length (2 digits) + Value (variable)\n * Example: \"000201\" → ID=00, Length=02, Value=\"01\"\n */\nexport function parseTLV(input: string): TLVParseResult {\n  if (!input || input.length === 0) {\n    return {\n      success: false,\n      fields: [],\n      isCorrupted: false,\n      error: {\n        type: DecodingErrorType.INVALID_FORMAT,\n        message: 'Input string is empty',\n        position: 0\n      }\n    };\n  }\n\n  const fields: TLVField[] = [];\n  let position = 0;\n  let isCorrupted = false;\n\n  while (position < input.length) {\n    // Need at least 4 characters for ID + Length\n    if (position + 4 > input.length) {\n      // Incomplete field structure at end\n      isCorrupted = true;\n      break;\n    }\n\n    // Extract field ID (2 digits)\n    const id = input.substring(position, position + 2);\n    if (!/^\\d{2}$/.test(id)) {\n      return {\n        success: false,\n        fields: [],\n        isCorrupted: false,\n        error: {\n          type: DecodingErrorType.INVALID_FORMAT,\n          message: `Invalid field ID at position ${position}: expected 2 digits, got \"${id}\"`,\n          position\n        }\n      };\n    }\n    position += 2;\n\n    // Extract length (2 digits)\n    const lengthStr = input.substring(position, position + 2);\n    if (!/^\\d{2}$/.test(lengthStr)) {\n      return {\n        success: false,\n        fields: [],\n        isCorrupted: false,\n        error: {\n          type: DecodingErrorType.INVALID_FORMAT,\n          message: `Invalid length at position ${position}: expected 2 digits, got \"${lengthStr}\"`,\n          position\n        }\n      };\n    }\n    const length = parseInt(lengthStr, 10);\n    position += 2;\n\n    // Extract value (length is byte count for UTF-8)\n    // Convert remaining string to buffer to handle UTF-8 byte lengths correctly\n    const remainingString = input.substring(position);\n    const remainingBuffer = Buffer.from(remainingString, 'utf-8');\n\n    if (length > remainingBuffer.length) {\n      // Value is truncated - not enough data to extract\n      // Decision logic:\n      // 1. If we've already successfully parsed some fields → treat as corruption (partial success)\n      // 2. If this is the FIRST field AND we have < 50% data → parse error (too incomplete)\n      // 3. Otherwise → corruption (might be usable)\n\n      const completeness = remainingBuffer.length / length;\n      const hasPartialData = fields.length > 0;\n\n      if (!hasPartialData && completeness < 0.5) {\n        // First field is severely truncated - parse error\n        return {\n          success: false,\n          fields: [],\n          isCorrupted: false,\n          error: {\n            type: DecodingErrorType.PARSE_ERROR,\n            message: `Incomplete field value: declared length ${length} but only ${remainingBuffer.length} bytes available`,\n            position\n          }\n        };\n      }\n\n      // Either have prior fields OR sufficient data - treat as corruption\n      isCorrupted = true;\n      break;\n    }\n\n    // Extract exact byte count and convert back to string\n    const valueBuffer = remainingBuffer.subarray(0, length);\n    const value = valueBuffer.toString('utf-8');\n\n    // Advance position by the number of JavaScript characters consumed\n    position += value.length;\n\n    fields.push({ id, length, value });\n  }\n\n  return {\n    success: true,\n    fields,\n    isCorrupted\n  };\n}\n","/**\n * VietQR field extractor\n *\n * Extracts VietQR payment fields from parsed TLV structure\n *\n * @module parsers/vietqr-parser\n */\n\nimport type { TLVField } from './tlv-parser';\nimport { parseTLV } from './tlv-parser';\n\n/**\n * Partial VietQR data (fields may be missing)\n */\nexport interface PartialVietQRData {\n  payloadFormatIndicator?: string;\n  initiationMethod?: 'static' | 'dynamic';\n  bankCode?: string;\n  accountNumber?: string;\n  amount?: string;\n  currency?: string;\n  message?: string;\n  purposeCode?: string;\n  billNumber?: string;\n  merchantCategory?: string;\n  countryCode?: string;\n  crc?: string;\n}\n\n/**\n * Extract VietQR fields from parsed TLV fields\n *\n * @param fields - Array of TLV fields from parser\n * @returns Partial VietQR data object with extracted fields\n *\n * @remarks\n * Maps EMV QR field IDs to VietQR data structure:\n * - Field 00: Payload Format Indicator\n * - Field 01: Initiation Method (11=static, 12=dynamic)\n * - Field 38: Merchant Account Information (contains bank code, account number)\n * - Field 52: Merchant Category Code\n * - Field 53: Transaction Currency\n * - Field 54: Transaction Amount\n * - Field 58: Country Code\n * - Field 62: Additional Data (contains message, purpose code, bill number)\n * - Field 63: CRC\n */\nexport function extractVietQRFields(fields: TLVField[]): PartialVietQRData {\n  const result: PartialVietQRData = {};\n\n  for (const field of fields) {\n    switch (field.id) {\n      case '00':\n        // Payload Format Indicator\n        result.payloadFormatIndicator = field.value;\n        break;\n\n      case '01':\n        // Point of Initiation Method\n        if (field.value === '11') {\n          result.initiationMethod = 'static';\n        } else if (field.value === '12') {\n          result.initiationMethod = 'dynamic';\n        }\n        break;\n\n      case '38':\n        // Merchant Account Information (nested TLV structure)\n        extractMerchantAccount(field.value, result);\n        break;\n\n      case '52':\n        // Merchant Category Code\n        result.merchantCategory = field.value;\n        break;\n\n      case '53':\n        // Transaction Currency\n        result.currency = field.value;\n        break;\n\n      case '54':\n        // Transaction Amount\n        result.amount = field.value;\n        break;\n\n      case '58':\n        // Country Code\n        result.countryCode = field.value;\n        break;\n\n      case '62':\n        // Additional Data Field Template (nested TLV)\n        extractAdditionalData(field.value, result);\n        break;\n\n      case '63':\n        // CRC\n        result.crc = field.value;\n        break;\n\n      // Ignore unknown field IDs (forward compatibility)\n    }\n  }\n\n  return result;\n}\n\n/**\n * Extract bank code and account number from merchant account field\n *\n * @param value - Merchant account field value (nested TLV)\n * @param result - Result object to populate\n *\n * @remarks\n * Merchant account field contains:\n * - Sub-field 00: Globally Unique Identifier (GUID)\n * - Sub-field 01: Payment network specific data\n *   - Sub-sub-field 00: Bank identifier (BIN or CITAD)\n *   - Sub-sub-field 01: Account/card number\n *   - Sub-sub-field 02: Service code (QRIBFTTA for VietQR)\n */\nfunction extractMerchantAccount(value: string, result: PartialVietQRData): void {\n  const merchantFields = parseTLV(value);\n\n  if (!merchantFields.success) {\n    return;\n  }\n\n  // Look for sub-field 01 (Payment network specific)\n  const paymentNetworkField = merchantFields.fields.find(f => f.id === '01');\n  if (!paymentNetworkField) {\n    return;\n  }\n\n  // Parse nested structure\n  const networkFields = parseTLV(paymentNetworkField.value);\n  if (!networkFields.success) {\n    return;\n  }\n\n  // Extract bank code (sub-field 00)\n  const bankCodeField = networkFields.fields.find(f => f.id === '00');\n  if (bankCodeField) {\n    result.bankCode = bankCodeField.value;\n  }\n\n  // Extract account number (sub-field 01)\n  const accountField = networkFields.fields.find(f => f.id === '01');\n  if (accountField) {\n    result.accountNumber = accountField.value;\n  }\n}\n\n/**\n * Extract message, purpose code, and bill number from additional data field\n *\n * @param value - Additional data field value (nested TLV)\n * @param result - Result object to populate\n *\n * @remarks\n * Additional data field contains:\n * - Sub-field 08: Bill Number\n * - Sub-field 07: Purpose of Transaction\n * - Sub-field 09: Additional Consumer Data Request (bill reference)\n */\nfunction extractAdditionalData(value: string, result: PartialVietQRData): void {\n  const additionalFields = parseTLV(value);\n\n  if (!additionalFields.success) {\n    return;\n  }\n\n  for (const field of additionalFields.fields) {\n    switch (field.id) {\n      case '08':\n        // Bill Number / Message\n        result.message = field.value;\n        break;\n\n      case '07':\n        // Purpose of Transaction\n        result.purposeCode = field.value;\n        break;\n\n      case '09':\n        // Additional Consumer Data Request (Bill Reference)\n        result.billNumber = field.value;\n        break;\n    }\n  }\n}\n","/**\n * Advanced parsing options for VietQR strings\n *\n * @module parsers/parse-with-options\n */\n\nimport { parseTLV } from './tlv-parser';\nimport { extractVietQRFields } from './vietqr-parser';\nimport type { VietQRData, ParseResult, DecodingError } from '../types/decode';\nimport { DecodingErrorType, MAX_QR_STRING_LENGTH } from '../types/decode';\n\n/**\n * Advanced parsing options for parseWithOptions\n */\nexport interface ParseOptions {\n  /**\n   * Strict mode - reject QR strings with any missing required fields\n   * @default false\n   */\n  strictMode?: boolean;\n\n  /**\n   * Extract partial data even when errors occur\n   * When true, returns available fields even if parsing fails\n   * @default false\n   */\n  extractPartialOnError?: boolean;\n\n  /**\n   * Maximum allowed QR string length\n   * @default 4096\n   */\n  maxLength?: number;\n}\n\n/**\n * Parse VietQR string with advanced options\n *\n * @param qrString - EMV QR formatted VietQR string to parse\n * @param options - Advanced parsing options\n * @returns ParseResult containing VietQRData on success or DecodingError on failure\n *\n * @remarks\n * - Supports strict mode for rejecting incomplete data\n * - Can extract partial data from corrupted QR strings\n * - Allows custom maximum length validation\n *\n * @example\n * ```typescript\n * // Strict mode - reject any incomplete data\n * const strictResult = parseWithOptions(qrString, { strictMode: true });\n *\n * // Extract partial data from corrupted QR\n * const partialResult = parseWithOptions(corruptedQR, {\n *   extractPartialOnError: true\n * });\n *\n * // Custom length limit\n * const limitedResult = parseWithOptions(qrString, {\n *   maxLength: 2000\n * });\n * ```\n */\nexport function parseWithOptions(\n  qrString: string,\n  options: ParseOptions = {}\n): ParseResult<VietQRData> {\n  const {\n    strictMode = false,\n    extractPartialOnError = false,\n    maxLength = MAX_QR_STRING_LENGTH\n  } = options;\n\n  // Validate input\n  if (!qrString || typeof qrString !== 'string') {\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.INVALID_FORMAT,\n        message: 'QR string is required and must be a string'\n      }\n    };\n  }\n\n  if (qrString.length > maxLength) {\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.PARSE_ERROR,\n        message: `QR string exceeds maximum length (${maxLength} characters)`\n      }\n    };\n  }\n\n  // Parse TLV structure\n  const tlvResult = parseTLV(qrString);\n\n  if (!tlvResult.success) {\n    // If extractPartialOnError is enabled and we have some fields, return them\n    if (extractPartialOnError && tlvResult.fields && tlvResult.fields.length > 0) {\n      const partialData = extractVietQRFields(tlvResult.fields);\n\n      if (Object.keys(partialData).length > 0) {\n        return {\n          success: true,\n          data: partialData as VietQRData\n        };\n      }\n    }\n\n    return {\n      success: false,\n      error: tlvResult.error as DecodingError\n    };\n  }\n\n  // Extract VietQR fields\n  const partialData = extractVietQRFields(tlvResult.fields);\n\n  // Strict mode validation - require ALL essential fields\n  if (strictMode) {\n    const requiredFields = [\n      'payloadFormatIndicator',\n      'initiationMethod',\n      'bankCode',\n      'accountNumber',\n      'currency',\n      'countryCode',\n      'crc'\n    ];\n\n    const missingFields = requiredFields.filter(\n      field => !partialData[field as keyof typeof partialData]\n    );\n\n    if (missingFields.length > 0) {\n      return {\n        success: false,\n        error: {\n          type: DecodingErrorType.INVALID_FORMAT,\n          message: `Strict mode: Missing required fields: ${missingFields.join(', ')}`\n        }\n      };\n    }\n\n    // Also reject if data appears corrupted\n    if (tlvResult.isCorrupted) {\n      return {\n        success: false,\n        error: {\n          type: DecodingErrorType.INVALID_FORMAT,\n          message: 'Strict mode: QR data appears corrupted or truncated'\n        }\n      };\n    }\n  }\n\n  // Check minimum required fields (non-strict mode)\n  if (!partialData.payloadFormatIndicator || !partialData.initiationMethod) {\n    // If extractPartialOnError is enabled and we have some data, return it\n    if (extractPartialOnError && Object.keys(partialData).length > 0) {\n      return {\n        success: true,\n        data: partialData as VietQRData\n      };\n    }\n\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.INVALID_FORMAT,\n        message: 'Missing required EMV QR fields (payload format or initiation method)'\n      }\n    };\n  }\n\n  // Return parsed data\n  return {\n    success: true,\n    data: partialData as VietQRData\n  };\n}\n","/**\n * VietQR string parser - Main entry point\n *\n * @module parsers\n */\n\nimport { parseTLV } from './tlv-parser';\nimport { extractVietQRFields } from './vietqr-parser';\nimport type { VietQRData, ParseResult, DecodingError } from '../types/decode';\nimport { DecodingErrorType, MAX_QR_STRING_LENGTH } from '../types/decode';\n\n// Export advanced parsing options\nexport { parseWithOptions, type ParseOptions } from './parse-with-options';\n\n/**\n * Parse VietQR string data into structured payment information\n *\n * @param qrString - EMV QR formatted VietQR string to parse\n * @returns ParseResult containing VietQRData on success or DecodingError on failure\n *\n * @remarks\n * - Performs TLV (Tag-Length-Value) parsing per EMV QR specification\n * - Extracts all VietQR fields from the string\n * - Handles truncated/corrupted data by extracting available fields\n * - Does NOT perform validation (use validate() for that)\n *\n * @example\n * ```typescript\n * const result = parse(qrString);\n * if (result.success) {\n *   console.log('Bank:', result.data.bankCode);\n *   console.log('Amount:', result.data.amount);\n * } else {\n *   console.error('Parse failed:', result.error.message);\n * }\n * ```\n */\nexport function parse(qrString: string): ParseResult<VietQRData> {\n  // Validate input\n  if (!qrString || typeof qrString !== 'string') {\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.INVALID_FORMAT,\n        message: 'QR string is required and must be a string'\n      }\n    };\n  }\n\n  if (qrString.length > MAX_QR_STRING_LENGTH) {\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.PARSE_ERROR,\n        message: `QR string exceeds maximum length (${MAX_QR_STRING_LENGTH} characters)`\n      }\n    };\n  }\n\n  // Parse TLV structure\n  const tlvResult = parseTLV(qrString);\n\n  if (!tlvResult.success) {\n    return {\n      success: false,\n      error: tlvResult.error as DecodingError\n    };\n  }\n\n  // Extract VietQR fields\n  const partialData = extractVietQRFields(tlvResult.fields);\n\n  // Check if we have minimum required fields\n  if (!partialData.payloadFormatIndicator || !partialData.initiationMethod) {\n    // Even if corrupted, try to return what we have if possible\n    if (tlvResult.isCorrupted && Object.keys(partialData).length > 0) {\n      // Return partial data (validation will flag issues)\n      return {\n        success: true,\n        data: partialData as VietQRData\n      };\n    }\n\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.INVALID_FORMAT,\n        message: 'Missing required EMV QR fields (payload format or initiation method)'\n      }\n    };\n  }\n\n  // Return parsed data (may be incomplete if corrupted)\n  // Validation should be performed separately\n  return {\n    success: true,\n    data: partialData as VietQRData\n  };\n}\n","/**\n * Image validation utilities for QR code decoding\n *\n * @module decoders/image-validator\n */\n\nimport { MAX_IMAGE_SIZE } from '../types/decode';\n\n/**\n * Check if image buffer is within size limits\n *\n * @param imageBuffer - Image data to validate\n * @returns true if buffer size ≤ 2MB, false otherwise\n *\n * @remarks\n * - 2MB limit prevents resource exhaustion attacks\n * - Mobile-optimized size that handles typical smartphone photos (1-4MB compressed)\n * - Used for pre-validation before attempting QR decode\n *\n * @example\n * ```typescript\n * if (!isValidImageSize(imageBuffer)) {\n *   throw new Error('Image exceeds 2MB limit');\n * }\n * ```\n */\nexport function isValidImageSize(imageBuffer: Buffer | Uint8Array): boolean {\n  if (!imageBuffer || !imageBuffer.length) {\n    return false;\n  }\n\n  return imageBuffer.length <= MAX_IMAGE_SIZE;\n}\n\n/**\n * Validate image buffer and return size in bytes\n *\n * @param imageBuffer - Image data to validate\n * @returns Size in bytes if valid\n * @throws {Error} If buffer is invalid or exceeds size limit\n */\nexport function validateImageBuffer(imageBuffer: Buffer | Uint8Array): number {\n  if (!imageBuffer) {\n    throw new Error('Image buffer is required');\n  }\n\n  if (!(imageBuffer instanceof Buffer) && !(imageBuffer instanceof Uint8Array)) {\n    throw new Error('Image buffer must be Buffer or Uint8Array');\n  }\n\n  const size = imageBuffer.length;\n\n  if (size === 0) {\n    throw new Error('Image buffer is empty');\n  }\n\n  if (size > MAX_IMAGE_SIZE) {\n    throw new Error(`Image size ${size} bytes exceeds maximum ${MAX_IMAGE_SIZE} bytes (2MB)`);\n  }\n\n  return size;\n}\n","/**\n * Image format detection using magic bytes\n *\n * @module decoders/format-detector\n */\n\nexport type ImageFormat = 'png' | 'jpeg' | 'unknown';\n\n/**\n * Detect image format from buffer using magic bytes/file signatures\n *\n * @param imageBuffer - Image data to analyze\n * @returns Image format ('png', 'jpeg', or 'unknown')\n *\n * @remarks\n * Detection based on file magic bytes:\n * - PNG: Starts with `89 50 4E 47 0D 0A 1A 0A` (8 bytes)\n * - JPEG: Starts with `FF D8 FF` (3 bytes minimum)\n *\n * @example\n * ```typescript\n * const format = detectImageFormat(buffer);\n * if (format === 'unknown') {\n *   throw new Error('Unsupported format, use PNG or JPEG');\n * }\n * ```\n */\nexport function detectImageFormat(imageBuffer: Buffer | Uint8Array): ImageFormat {\n  if (!imageBuffer || imageBuffer.length < 3) {\n    return 'unknown';\n  }\n\n  // PNG magic bytes: 89 50 4E 47 0D 0A 1A 0A\n  // Check first 4 bytes for PNG signature\n  if (\n    imageBuffer[0] === 0x89 &&\n    imageBuffer[1] === 0x50 &&\n    imageBuffer[2] === 0x4e &&\n    imageBuffer[3] === 0x47\n  ) {\n    return 'png';\n  }\n\n  // JPEG magic bytes: FF D8 FF\n  // Check SOI (Start of Image) marker\n  if (\n    imageBuffer[0] === 0xff &&\n    imageBuffer[1] === 0xd8 &&\n    imageBuffer[2] === 0xff\n  ) {\n    return 'jpeg';\n  }\n\n  return 'unknown';\n}\n\n/**\n * Check if image format is supported for QR decoding\n *\n * @param format - Image format to check\n * @returns true if format is supported (png or jpeg)\n */\nexport function isSupportedFormat(format: ImageFormat): format is 'png' | 'jpeg' {\n  return format === 'png' || format === 'jpeg';\n}\n","/**\n * QR code extraction from image buffers\n *\n * @module decoders/qr-extractor\n */\n\nimport { decodeQR } from 'qr/decode.js';\nimport { PNG } from 'pngjs';\nimport jpeg from 'jpeg-js';\nimport { detectImageFormat, isSupportedFormat } from './format-detector';\n\n/**\n * Decode image buffer to raw pixel data\n *\n * @param imageBuffer - PNG or JPEG image data\n * @returns Image object with width, height, and RGBA pixel data\n */\nfunction decodeImageToPixels(imageBuffer: Buffer | Uint8Array): {\n  width: number;\n  height: number;\n  data: Uint8Array;\n} {\n  // Ensure we have a Buffer\n  const buffer = Buffer.from(imageBuffer);\n\n  // Detect format\n  const format = detectImageFormat(buffer);\n\n  if (!isSupportedFormat(format)) {\n    throw new Error('Unsupported image format');\n  }\n\n  if (format === 'png') {\n    // Decode PNG\n    const png = PNG.sync.read(buffer);\n    return {\n      width: png.width,\n      height: png.height,\n      data: png.data\n    };\n  } else {\n    // Decode JPEG\n    const jpegData = jpeg.decode(buffer);\n    return {\n      width: jpegData.width,\n      height: jpegData.height,\n      data: jpegData.data\n    };\n  }\n}\n\n/**\n * Extract QR code string from image buffer\n *\n * @param imageBuffer - PNG or JPEG image data\n * @returns Decoded QR string\n * @throws {Error} If no QR code found or decoding fails\n *\n * @remarks\n * - Uses `qr` package by paulmillr for QR decoding\n * - Supports PNG and JPEG formats\n * - If multiple QR codes present, extracts the first/largest one\n * - Returns raw QR string data without validation\n *\n * @example\n * ```typescript\n * try {\n *   const qrString = extractQRString(imageBuffer);\n *   console.log('Decoded:', qrString);\n * } catch (error) {\n *   console.error('No QR code found');\n * }\n * ```\n */\nexport function extractQRString(imageBuffer: Buffer | Uint8Array): string {\n  try {\n    // Step 1: Decode image to raw pixels\n    const imageData = decodeImageToPixels(imageBuffer);\n\n    // Step 2: Decode QR code from pixel data\n    const qrString = decodeQR(imageData);\n\n    if (!qrString || qrString.length === 0) {\n      throw new Error('QR code decoded but contained no data');\n    }\n\n    return qrString;\n  } catch (error) {\n    // Re-throw with more context\n    if (error instanceof Error) {\n      if (error.message.includes('No QR code') || error.message.includes('contained no data')) {\n        throw error;\n      }\n      throw new Error(`Failed to decode QR code: ${error.message}`);\n    }\n    throw new Error('Failed to decode QR code from image');\n  }\n}\n\n/**\n * Check if buffer contains a valid QR code (without extracting)\n *\n * @param imageBuffer - Image data to check\n * @returns true if QR code detected, false otherwise\n *\n * @remarks\n * This is a lightweight check that attempts to detect QR presence\n * without full decoding. Useful for pre-validation.\n */\nexport function hasQRCode(imageBuffer: Buffer | Uint8Array): boolean {\n  try {\n    const imageData = decodeImageToPixels(imageBuffer);\n    const result = decodeQR(imageData);\n    return result !== null && result !== undefined;\n  } catch {\n    return false;\n  }\n}\n","/**\n * VietQR image decoding - Main entry point\n *\n * @module decoders\n */\n\nimport { parse } from '../parsers';\nimport { validate } from '../validators';\nimport type { VietQRData, ParseResult, ValidationResult } from '../types/decode';\nimport { DecodingErrorType } from '../types/decode';\nimport { validateImageBuffer } from './image-validator';\nimport { detectImageFormat, isSupportedFormat } from './format-detector';\nimport { extractQRString } from './qr-extractor';\n\n/**\n * Decode QR code image to extract VietQR string, then parse to structured data\n *\n * @param imageBuffer - Image data as Buffer (Node.js) or Uint8Array (browser)\n * @returns ParseResult containing VietQRData on success or DecodingError on failure\n *\n * @remarks\n * - Supports PNG and JPEG image formats\n * - Enforces 2MB maximum file size (rejects larger images before decoding)\n * - Extracts first/largest QR code if multiple codes present\n * - Automatically parses extracted QR string to VietQRData\n * - Does NOT perform validation (use decodeAndValidate() for combined operation)\n *\n * @example\n * ```typescript\n * import fs from 'fs/promises';\n * import { decode } from 'vietqr-ts';\n *\n * const imageBuffer = await fs.readFile('qr-code.png');\n * const result = decode(imageBuffer);\n *\n * if (result.success) {\n *   console.log('Bank:', result.data.bankCode);\n *   console.log('Account:', result.data.accountNumber);\n *   console.log('Amount:', result.data.amount);\n * } else {\n *   console.error('Decode failed:', result.error.message);\n * }\n * ```\n */\nexport function decode(imageBuffer: Buffer | Uint8Array): ParseResult<VietQRData> {\n  // Step 1: Validate image buffer and size\n  try {\n    validateImageBuffer(imageBuffer);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : 'Invalid image buffer';\n\n    // Determine error type based on message\n    let errorType = DecodingErrorType.IMAGE_DECODE_ERROR;\n    if (message.includes('exceeds maximum')) {\n      errorType = DecodingErrorType.SIZE_LIMIT_EXCEEDED;\n    }\n\n    return {\n      success: false,\n      error: {\n        type: errorType,\n        message\n      }\n    };\n  }\n\n  // Step 2: Detect and validate image format\n  const format = detectImageFormat(imageBuffer);\n\n  if (!isSupportedFormat(format)) {\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.UNSUPPORTED_FORMAT,\n        message: 'Unsupported image format. Only PNG and JPEG are supported.'\n      }\n    };\n  }\n\n  // Step 3: Extract QR string from image\n  let qrString: string;\n  try {\n    qrString = extractQRString(imageBuffer);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : 'Failed to decode QR code';\n\n    // Determine error type based on message\n    let errorType = DecodingErrorType.IMAGE_DECODE_ERROR;\n    if (message.includes('No QR code')) {\n      errorType = DecodingErrorType.NO_QR_CODE_FOUND;\n    }\n\n    return {\n      success: false,\n      error: {\n        type: errorType,\n        message\n      }\n    };\n  }\n\n  // Step 4: Parse QR string to VietQRData\n  const parseResult = parse(qrString);\n\n  if (!parseResult.success) {\n    // Pass through parse errors\n    return parseResult;\n  }\n\n  // Return successfully parsed data\n  return parseResult;\n}\n\n/**\n * Decode QR code image, parse to structured data, and validate\n *\n * @param imageBuffer - Image data as Buffer (Node.js) or Uint8Array (browser)\n * @returns ParseResult containing ValidationResult on success or DecodingError on failure\n *\n * @remarks\n * This is a convenience function that combines decode() and validate():\n * 1. Decode QR image to string\n * 2. Parse string to VietQRData\n * 3. Validate parsed data\n * 4. Return ValidationResult\n *\n * If decoding or parsing fails, returns ParseResult with DecodingError.\n * If successful, returns ParseResult with ValidationResult.\n *\n * @example\n * ```typescript\n * import { decodeAndValidate } from 'vietqr-ts';\n *\n * const result = decodeAndValidate(imageBuffer);\n *\n * if (result.success) {\n *   if (result.data.isValid) {\n *     console.log('✓ Valid VietQR payment');\n *     // Safe to process payment\n *   } else {\n *     console.warn('✗ Invalid VietQR data');\n *     result.data.errors.forEach(err => {\n *       console.error(`  ${err.field}: ${err.message}`);\n *     });\n *   }\n * } else {\n *   console.error('Decode failed:', result.error.message);\n * }\n * ```\n */\nexport function decodeAndValidate(imageBuffer: Buffer | Uint8Array): ParseResult<ValidationResult> {\n  // Step 1: Decode and parse image\n  const decodeResult = decode(imageBuffer);\n\n  if (!decodeResult.success) {\n    // Return decode/parse errors\n    return {\n      success: false,\n      error: decodeResult.error\n    };\n  }\n\n  // Step 2: Validate parsed data\n  // Note: validate() requires the original qrString for CRC verification\n  // Since we already decoded and parsed successfully, we have valid data\n  // We validate with empty qrString (validates format only, not CRC from image)\n  if (!decodeResult.data) {\n    return {\n      success: false,\n      error: {\n        type: DecodingErrorType.PARSE_ERROR,\n        message: 'Decoded data is undefined'\n      }\n    };\n  }\n\n  const validationResult = validate(decodeResult.data, '');\n\n  // Return validation result wrapped in ParseResult\n  return {\n    success: true,\n    data: validationResult\n  };\n}\n\n// Re-export utilities for convenience\nexport { isValidImageSize } from './image-validator';\nexport { detectImageFormat } from './format-detector';\nexport type { ImageFormat } from './format-detector';\n"]}