97 lines
3.6 KiB
TypeScript
97 lines
3.6 KiB
TypeScript
import { binToHex, hexToBin } from '@bitauth/libauth';
|
|
|
|
/**
|
|
* Matches a bigint encoded in Extended JSON format: `<bigint: 123n>`.
|
|
*/
|
|
const EXTENDED_JSON_BIGINT_PATTERN = /^<bigint: (?<bigint>[+-]?[0-9]+)n>$/u;
|
|
|
|
/**
|
|
* Matches a Uint8Array encoded in Extended JSON format: `<uint8array: abcd>`.
|
|
*/
|
|
const EXTENDED_JSON_UINT8ARRAY_PATTERN = /^<uint8array: (?<hex>[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 `<bigint: 123n>`.
|
|
* - Uint8Arrays are encoded as `<uint8array: abcd>`.
|
|
* 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 `<uint8array: ${binToHex(value)}>`;
|
|
}
|
|
|
|
if (typeof value === 'bigint') {
|
|
return `<bigint: ${value.toString()}n>`;
|
|
}
|
|
|
|
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);
|
|
};
|