122 lines
3.3 KiB
TypeScript
122 lines
3.3 KiB
TypeScript
|
import * as plugins from './smartjson.plugins.js';
|
||
|
|
||
|
// Define interfaces and types for better type checking and readability
|
||
|
interface IBufferLike {
|
||
|
type: 'Buffer';
|
||
|
data: string | any[]; // `any[]` for array data representation
|
||
|
}
|
||
|
|
||
|
interface IEncodedBuffer {
|
||
|
type: 'EncodedBuffer',
|
||
|
data: string;
|
||
|
}
|
||
|
|
||
|
type TParseReviver = (this: any, key: string, value: any) => any;
|
||
|
type TParseReplacer = (this: any, key: string, value: any) => any;
|
||
|
|
||
|
// Utility functions to handle base64 encoding/decoding in a cross-platform way
|
||
|
function base64Encode(data: Uint8Array): string {
|
||
|
return btoa(String.fromCharCode(...data));
|
||
|
}
|
||
|
|
||
|
function base64Decode(str: string): Uint8Array {
|
||
|
return new Uint8Array(Array.from(atob(str)).map((char) => char.charCodeAt(0)));
|
||
|
}
|
||
|
|
||
|
// Main functionality with cross-platform support
|
||
|
function stringify(value: any, space?: string | number): string {
|
||
|
return JSON.stringify(value, replacer, space);
|
||
|
}
|
||
|
|
||
|
function parse(text: string): any {
|
||
|
return JSON.parse(text, reviver);
|
||
|
}
|
||
|
|
||
|
const replacer: TParseReplacer = (key, value) => {
|
||
|
// Check if value is IBufferLike
|
||
|
if (isBufferLike(value)) {
|
||
|
let bufferData: Uint8Array;
|
||
|
|
||
|
// Handle IBufferLike objects with a .data property
|
||
|
if ('data' in value && isArray(value.data)) {
|
||
|
if (value.data.length > 0) {
|
||
|
bufferData = new Uint8Array(value.data);
|
||
|
} else {
|
||
|
return ''; // Return empty string for empty data arrays
|
||
|
}
|
||
|
}
|
||
|
// Handle Uint8Array directly
|
||
|
else if (value instanceof Uint8Array) {
|
||
|
bufferData = value;
|
||
|
} else {
|
||
|
// If not a recognized format, return value as is
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
// Encode the bufferData (Uint8Array) to base64
|
||
|
const base64Data = 'base64:' + base64Encode(bufferData);
|
||
|
return {
|
||
|
type: 'EncodedBuffer',
|
||
|
data: base64Data,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
// Return value unchanged if not buffer-like
|
||
|
return value;
|
||
|
};
|
||
|
|
||
|
const reviver: TParseReviver = (key, value) => {
|
||
|
if (isEncodedBuffer(value)) {
|
||
|
if (isString(value.data) && value.data.startsWith('base64:')) {
|
||
|
// Correctly slice the 'base64:' prefix before decoding
|
||
|
const base64Data = value.data.slice(7); // Skip 'base64:' prefix
|
||
|
const buffer = base64Decode(base64Data);
|
||
|
// Assuming the rest of your application can work directly with Uint8Array,
|
||
|
// otherwise, you might need to convert it to another format
|
||
|
return buffer;
|
||
|
}
|
||
|
}
|
||
|
return value;
|
||
|
};
|
||
|
|
||
|
function isEncodedBuffer(x: any): x is IEncodedBuffer {
|
||
|
return isObject(x) && (x as any).type === 'EncodedBuffer' && isString((x as any).data);
|
||
|
}
|
||
|
|
||
|
function isBufferLike(x: any): x is IBufferLike | Uint8Array {
|
||
|
return (
|
||
|
(isObject(x) &&
|
||
|
((x as any).type === 'Buffer' &&
|
||
|
(isArray((x as any).data) || isString((x as any).data)))) ||
|
||
|
x instanceof Uint8Array
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* We use this function to check if a value is an array
|
||
|
* @param x
|
||
|
* @returns
|
||
|
*/
|
||
|
function isArray(x: any): x is any[] {
|
||
|
return Array.isArray(x);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* We use this function to check if a value is a string
|
||
|
* @param x
|
||
|
* @returns
|
||
|
*/
|
||
|
function isString(x: any): x is string {
|
||
|
return typeof x === 'string';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* We use this function to check if a value is an object
|
||
|
* @param x
|
||
|
*/
|
||
|
function isObject(x: any): x is object {
|
||
|
return typeof x === 'object' && x !== null;
|
||
|
}
|
||
|
|
||
|
export { stringify, parse, replacer, reviver };
|