{"version":3,"file":"regex_masking_transformer.cjs","names":["MaskingTransformer"],"sources":["../../../src/experimental/masking/regex_masking_transformer.ts"],"sourcesContent":["import { MaskingTransformer } from \"./transformer.js\";\nimport type { HashFunction, MaskingPattern } from \"./types.js\";\n/**\n * RegexMaskingTransformer class for masking and rehydrating messages with Regex.\n */\nexport class RegexMaskingTransformer extends MaskingTransformer {\n  private patterns: { [key: string]: MaskingPattern };\n\n  private hashFunction: HashFunction;\n\n  /**\n   * Constructs a RegexMaskingTransformer with given patterns and an optional hash function.\n   * Validates the provided patterns to ensure they conform to the expected structure.\n   *\n   * @param patterns - An object containing masking patterns. Each pattern should include\n   *                   a regular expression (`regex`) and optionally a `replacement` string\n   *                   or a `mask` function.\n   * @param hashFunction - An optional custom hash function to be used for masking.\n   */\n  constructor(\n    patterns: { [key: string]: MaskingPattern },\n    hashFunction?: HashFunction\n  ) {\n    super();\n    // Validates the provided masking patterns before initializing the transformer.\n    // This ensures that each pattern has a valid regular expression.\n    this.validatePatterns(patterns);\n\n    // Assigns the validated patterns and the hash function to the transformer.\n    // If no custom hash function is provided, the default hash function is used.\n    this.patterns = patterns;\n    this.hashFunction = hashFunction || this.defaultHashFunction;\n  }\n\n  /**\n   * Validates the given masking patterns to ensure each pattern has a valid regular expression.\n   * Throws an error if any pattern is found to be invalid.\n   *\n   * @param patterns - The patterns object to validate.\n   */\n  private validatePatterns(patterns: { [key: string]: MaskingPattern }) {\n    for (const key of Object.keys(patterns)) {\n      const pattern = patterns[key];\n      // Checks that each pattern is an object and has a regex property that is an instance of RegExp.\n      // Throws an error if these conditions are not met, indicating an invalid pattern configuration.\n      if (\n        !pattern ||\n        typeof pattern !== \"object\" ||\n        // oxlint-disable-next-line no-instanceof/no-instanceof\n        !(pattern.regex instanceof RegExp)\n      ) {\n        throw new Error(\"Invalid pattern configuration.\");\n      }\n    }\n  }\n\n  /**\n   * Masks content in a message based on the defined patterns.\n   * @param message - The message to be masked.\n   * @param state - The current state containing original values.\n   * @returns A tuple of the masked message and the updated state.\n   */\n  async transform(\n    message: string,\n    state: Map<string, string>\n  ): Promise<[string, Map<string, string>]> {\n    if (typeof message !== \"string\") {\n      throw new TypeError(\n        \"RegexMaskingTransformer.transform Error: The 'message' argument must be a string.\"\n      );\n    }\n\n    // oxlint-disable-next-line no-instanceof/no-instanceof\n    if (!(state instanceof Map)) {\n      throw new TypeError(\n        \"RegexMaskingTransformer.transform Error: The 'state' argument must be an instance of Map.\"\n      );\n    }\n\n    // Holds the progressively masked message\n    let processedMessage = message;\n\n    // Initialize original values map with the current state or a new map\n    const originalValues = state || new Map<string, string>();\n\n    // Iterate over each pattern defined in the transformer\n    for (const key of Object.keys(this.patterns)) {\n      const pattern = this.patterns[key];\n\n      // Apply the current pattern's regex to the message\n      processedMessage = processedMessage.replace(pattern.regex, (match) => {\n        // Determine the masked value: use the mask function if provided, else use the replacement string,\n        // else use the hash function.\n        const maskedValue = pattern.mask\n          ? pattern.mask(match)\n          : (pattern.replacement ?? this.hashFunction(match));\n\n        // Store the mapping of the masked value to the original value (match)\n        originalValues.set(maskedValue, match);\n\n        // Return the masked value to replace the original value in the message\n        return maskedValue;\n      });\n    }\n\n    // Return the fully masked message and the state map with all original values\n    // Wrap the synchronous return values in Promise.resolve() to maintain compatibility\n    // with the MaskingParser's expectation of a Promise return type.\n    return [processedMessage, originalValues];\n  }\n\n  /**\n   * Rehydrates a masked message back to its original form using the provided state.\n   * @param message - The masked message to be rehydrated.\n   * @param state - The state map containing mappings of masked values to their original values.\n   * @returns The rehydrated (original) message.\n   */\n  async rehydrate(\n    message: string,\n    state: Map<string, string>\n  ): Promise<string> {\n    if (typeof message !== \"string\") {\n      throw new TypeError(\n        \"RegexMaskingTransformer.rehydrate Error: The 'message' argument must be a string.\"\n      );\n    }\n\n    // oxlint-disable-next-line no-instanceof/no-instanceof\n    if (!(state instanceof Map)) {\n      throw new TypeError(\n        \"RegexMaskingTransformer.rehydrate Error: The 'state' argument must be an instance of Map.\"\n      );\n    }\n\n    // Convert the state map to an array and use reduce to sequentially replace masked values with original values.\n    const rehydratedMessage = Array.from(state).reduce(\n      (msg, [masked, original]) => {\n        // Escape special characters in the masked string to ensure it can be used in a regular expression safely.\n        // This is necessary because masked values might contain characters that have special meanings in regex.\n        const escapedMasked = masked.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n\n        // Replace all instances of the escaped masked value in the message with the original value.\n        // The 'g' flag in the RegExp ensures that all occurrences of the masked value are replaced.\n        return msg.replace(new RegExp(escapedMasked, \"g\"), original);\n      },\n      message\n    );\n\n    return rehydratedMessage;\n  }\n\n  /**\n   * Default hash function for creating unique hash values.\n   * @param input - The input string to hash.\n   * @returns The resulting hash as a string.\n   */\n  private defaultHashFunction(input: string): string {\n    let hash = 0;\n    // Iterate over each character in the input string\n    for (let i = 0; i < input.length; i += 1) {\n      // Get ASCII value of the character\n      const char = input.charCodeAt(i);\n      // Combine the current hash with the new character and ensure it remains a 32-bit integer\n      hash = (hash << 5) - hash + char;\n      // Bitwise OR operation to convert to a 32-bit integer.\n      // This is a common technique to ensure the final hash value stays within the 32-bit limit,\n      // effectively wrapping the value when it becomes too large.\n      hash |= 0;\n    }\n\n    // Convert the numerical hash value to a string and return\n    return hash.toString();\n  }\n}\n"],"mappings":";;;;;AAKA,IAAa,0BAAb,cAA6CA,oBAAAA,mBAAmB;CAC9D;CAEA;;;;;;;;;;CAWA,YACE,UACA,cACA;AACA,SAAO;AAGP,OAAK,iBAAiB,SAAS;AAI/B,OAAK,WAAW;AAChB,OAAK,eAAe,gBAAgB,KAAK;;;;;;;;CAS3C,iBAAyB,UAA6C;AACpE,OAAK,MAAM,OAAO,OAAO,KAAK,SAAS,EAAE;GACvC,MAAM,UAAU,SAAS;AAGzB,OACE,CAAC,WACD,OAAO,YAAY,YAEnB,EAAE,QAAQ,iBAAiB,QAE3B,OAAM,IAAI,MAAM,iCAAiC;;;;;;;;;CAWvD,MAAM,UACJ,SACA,OACwC;AACxC,MAAI,OAAO,YAAY,SACrB,OAAM,IAAI,UACR,oFACD;AAIH,MAAI,EAAE,iBAAiB,KACrB,OAAM,IAAI,UACR,4FACD;EAIH,IAAI,mBAAmB;EAGvB,MAAM,iBAAiB,yBAAS,IAAI,KAAqB;AAGzD,OAAK,MAAM,OAAO,OAAO,KAAK,KAAK,SAAS,EAAE;GAC5C,MAAM,UAAU,KAAK,SAAS;AAG9B,sBAAmB,iBAAiB,QAAQ,QAAQ,QAAQ,UAAU;IAGpE,MAAM,cAAc,QAAQ,OACxB,QAAQ,KAAK,MAAM,GAClB,QAAQ,eAAe,KAAK,aAAa,MAAM;AAGpD,mBAAe,IAAI,aAAa,MAAM;AAGtC,WAAO;KACP;;AAMJ,SAAO,CAAC,kBAAkB,eAAe;;;;;;;;CAS3C,MAAM,UACJ,SACA,OACiB;AACjB,MAAI,OAAO,YAAY,SACrB,OAAM,IAAI,UACR,oFACD;AAIH,MAAI,EAAE,iBAAiB,KACrB,OAAM,IAAI,UACR,4FACD;AAiBH,SAb0B,MAAM,KAAK,MAAM,CAAC,QACzC,KAAK,CAAC,QAAQ,cAAc;GAG3B,MAAM,gBAAgB,OAAO,QAAQ,uBAAuB,OAAO;AAInE,UAAO,IAAI,QAAQ,IAAI,OAAO,eAAe,IAAI,EAAE,SAAS;KAE9D,QACD;;;;;;;CAUH,oBAA4B,OAAuB;EACjD,IAAI,OAAO;AAEX,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;GAExC,MAAM,OAAO,MAAM,WAAW,EAAE;AAEhC,WAAQ,QAAQ,KAAK,OAAO;AAI5B,WAAQ;;AAIV,SAAO,KAAK,UAAU"}