import { binToHex, hexToBin } from '@bitauth/libauth'; /** * Matches a bigint encoded in Extended JSON format: ``. */ const EXTENDED_JSON_BIGINT_PATTERN = /^[+-]?[0-9]+)n>$/u; /** * Matches a Uint8Array encoded in Extended JSON format: ``. */ const EXTENDED_JSON_UINT8ARRAY_PATTERN = /^[0-9a-f]*)>$/u; /** * The JSON replacer that encodes `bigint` and `Uint8Array` values in Extended JSON format, * compatible with the format expected by `extendedJsonReviver`. * * - BigInts are encoded as ``. * - Uint8Arrays are encoded as ``. * All other values pass through unchanged and if any incompatible type is encountered, an error is thrown. * * Note: To use this function, pass it as the second argument to `JSON.stringify` when serializing data. * * Note to developers: Libauth's `stringify` is the replacer. It also serializes functions and symbols, * which we do not support. Passing it would let templates include those values, but revival would then fail. * This module provides a dedicated replacer and reviver so serialization and deserialization stay aligned. * * @param _propertyKey The property key being serialized, required by the `JSON.stringify` replacer but not used here. * @param value The value to encode or pass through unchanged. * @returns The encoded string */ export const extendedJsonReplacer = (_propertyKey: string, value: unknown): unknown => { if (value instanceof Uint8Array) { return ``; } if (typeof value === 'bigint') { return ``; } return value; }; /** * The JSON reviver that reconstructs `bigint` and `Uint8Array` values encoded by `extendedJsonReplacer`. * * Note: To use this function, pass it as the second argument to `JSON.parse` when deserializing data. * * @param _propertyKey The property key being deserialized, required by the `JSON.parse` reviver but not used here. * @param value The value to reconstruct or pass through unchanged. * @returns The reconstructed value */ export const extendedJsonReviver = (_propertyKey: string, value: unknown): unknown => { // If the value is not a string, return the original value if (typeof value !== 'string') { return value; } // Match the bigint pattern const bigintPatternMatch = value.match(EXTENDED_JSON_BIGINT_PATTERN); // If the value matches the bigint pattern, return the reconstructed bigint if (bigintPatternMatch) { return BigInt(bigintPatternMatch.groups!.bigint); } // Match the Uint8Array pattern const uint8arrayPatternMatch = value.match(EXTENDED_JSON_UINT8ARRAY_PATTERN); // If the value matches the Uint8Array pattern, return the reconstructed Uint8Array if (uint8arrayPatternMatch) { return hexToBin(uint8arrayPatternMatch.groups!.hex); } // If the value does not match either pattern, return the original value return value; }; /** * Serializes an object to a string using the {@link extendedJsonReplacer}. * * @param object The object to serialize. * @returns The string representation of the object in Extended JSON format. */ export const toExtendedJson = (object: unknown): string => { return JSON.stringify(object, extendedJsonReplacer); }; /** * Deserializes a string to an object using the {@link extendedJsonReviver}. * * @param serializedObject The string to deserialize. * @returns The object reconstructed from the string. */ export const fromExtendedJson = (serializedObject: string): unknown => { return JSON.parse(serializedObject, extendedJsonReviver); };