agsamantha/node_modules/langchain/dist/experimental/masking/parser.js
2024-10-02 15:15:21 -05:00

130 lines
6.1 KiB
JavaScript

/**
* MaskingParser class for handling the masking and rehydrating of messages.
*/
export class MaskingParser {
constructor(config = {}) {
Object.defineProperty(this, "transformers", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "state", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "config", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.transformers = config.transformers ?? [];
this.state = new Map();
this.config = config;
}
/**
* Adds a transformer to the parser.
* @param transformer - An instance of a class extending MaskingTransformer.
*/
addTransformer(transformer) {
this.transformers.push(transformer);
}
/**
* Getter method for retrieving the current state.
* @returns The current state map.
*/
getState() {
return this.state;
}
/**
* Masks the provided message using the added transformers.
* This method sequentially applies each transformer's masking logic to the message.
* It utilizes a state map to track original values corresponding to their masked versions.
*
* @param message - The message to be masked.
* @returns A masked version of the message.
* @throws {TypeError} If the message is not a string.
* @throws {Error} If no transformers are added.
*/
async mask(message) {
// If onMaskingStart is a function, handle it accordingly
if (this.config.onMaskingStart) {
await this.config.onMaskingStart(message);
}
// Check if there are any transformers added to the parser. If not, throw an error
// as masking requires at least one transformer to apply its logic.
if (this.transformers.length === 0) {
throw new Error("MaskingParser.mask Error: No transformers have been added. Please add at least one transformer before parsing.");
}
if (typeof message !== "string") {
throw new TypeError("MaskingParser.mask Error: The 'message' argument must be a string.");
}
// Initialize the variable to hold the progressively masked message.
// It starts as the original message and gets transformed by each transformer.
let processedMessage = message;
// Iterate through each transformer and apply their transform method.
for (const transformer of this.transformers) {
// Transform the message and get the transformer's state changes, ensuring no direct mutation of the shared state.
const [transformedMessage, transformerState] = await transformer.transform(processedMessage, new Map(this.state));
// Update the processed message for subsequent transformers.
processedMessage = transformedMessage;
// Merge state changes from the transformer into the parser's state.
// This accumulates all transformations' effects on the state.
transformerState.forEach((value, key) => this.state.set(key, value));
}
// Handle onMaskingEnd callback
if (this.config.onMaskingEnd) {
await this.config.onMaskingEnd(processedMessage);
}
// Return the fully masked message after all transformers have been applied.
return processedMessage;
}
/**
* Rehydrates a masked message back to its original form.
* This method sequentially applies the rehydration logic of each added transformer in reverse order.
* It relies on the state map to correctly map the masked values back to their original values.
*
* The rehydration process is essential for restoring the original content of a message
* that has been transformed (masked) by the transformers. This process is the inverse of the masking process.
*
* @param message - The masked message to be rehydrated.
* @returns The original (rehydrated) version of the message.
*/
async rehydrate(message, state) {
// Handle onRehydratingStart callback
if (this.config.onRehydratingStart) {
await this.config.onRehydratingStart(message);
}
if (typeof message !== "string") {
throw new TypeError("MaskingParser.rehydrate Error: The 'message' argument must be a string.");
}
// Check if any transformers have been added to the parser.
// If no transformers are present, throw an error as rehydration requires at least one transformer.
if (this.transformers.length === 0) {
throw new Error("MaskingParser.rehydrate Error: No transformers have been added. Please add at least one transformer before rehydrating.");
}
// eslint-disable-next-line no-instanceof/no-instanceof
if (state && !(state instanceof Map)) {
throw new TypeError("MaskingParser.rehydrate Error: The 'state' argument, if provided, must be an instance of Map.");
}
const rehydrationState = state || this.state; // Use provided state or fallback to internal state
// Initialize the rehydratedMessage with the input masked message.
// This variable will undergo rehydration by each transformer in reverse order.
let rehydratedMessage = message;
// Use a reverse for...of loop to accommodate asynchronous rehydrate methods
const reversedTransformers = this.transformers.slice().reverse();
for (const transformer of reversedTransformers) {
// Check if the result is a Promise and use await, otherwise use it directly
rehydratedMessage = await transformer.rehydrate(rehydratedMessage, rehydrationState);
}
// Handle onRehydratingEnd callback
if (this.config.onRehydratingEnd) {
await this.config.onRehydratingEnd(rehydratedMessage);
}
// Return the fully rehydrated message after all transformers have been applied.
return rehydratedMessage;
}
}