From 32b41dd07af23dbde97be2436218030f48ca3175 Mon Sep 17 00:00:00 2001 From: Hide_D Date: Sun, 20 Aug 2023 07:55:54 +0000 Subject: [PATCH] josonify --- server/util/jsonify.ts | 134 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 server/util/jsonify.ts diff --git a/server/util/jsonify.ts b/server/util/jsonify.ts new file mode 100644 index 0000000..10cb5d5 --- /dev/null +++ b/server/util/jsonify.ts @@ -0,0 +1,134 @@ +import { every, isString } from "lodash-es"; +import { symbol } from "zod"; + +type JsonifiableLite = + string | number | boolean | bigint | null | undefined | + Date | ArrayBuffer | SharedArrayBuffer | ArrayBufferView | + JsonifiableLite[] | { [key: string]: JsonifiableLite }; + +export type Jsonifiable = + JsonifiableLite | + Jsonifiable[] | + Map | + Set | + { jsonify(): Jsonifiable } | + { [key: string]: Jsonifiable }; + +export type Jsonified = + T extends string ? string : + T extends number ? number : + T extends boolean ? boolean : + T extends bigint ? string : + T extends null ? null : + T extends undefined ? undefined : + T extends Date ? string : + T extends ArrayBuffer ? string : + T extends SharedArrayBuffer ? string : + T extends ArrayBufferView ? string : + T extends (infer K extends Jsonifiable)[] ? Jsonified[] : + T extends Map ? ( + K extends string ? { [key in string]: Jsonified } : [Jsonified, Jsonified][]) : + T extends Set ? Jsonified[] : + T extends object ? ( + T extends { jsonify(): infer V extends Jsonifiable } ? Jsonified : //HACK: jsonify()는 Jsonified여야 함 + {[key in keyof T]: Jsonified}): //FIXME: keyof T에 symbol을 받을 수 없어야 함 + T extends unknown ? unknown : + never; + + +/** + * Convert any object to JSON-safe object + */ +export function jsonify(item: T): Jsonified { + if (typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return item as any; + } + + if (typeof item === 'bigint') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return item.toString() as any; + } + + if (item === undefined || item === null) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return item as any; + } + + if (item instanceof Date) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return item.toISOString() as any; + } + + if (item instanceof ArrayBuffer || item instanceof SharedArrayBuffer) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return Buffer.from(item).toString('base64') as any; + } + + if (ArrayBuffer.isView(item)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return Buffer.from(item.buffer, item.byteOffset, item.byteLength).toString('base64') as any; + } + + if (Array.isArray(item)) { + //HACK: depth hack + const result: Jsonified[] = []; + for (const v of item) { + result.push(jsonify(v as string) as Jsonified); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return result as any; + } + + if (item instanceof Map) { + //HACK: depth hack + + const onlyStringKey = every(item.keys, isString); + if (onlyStringKey) { + const result: { [key: string]: Jsonified } = {}; + for (const [k, v] of item.entries()) { + result[k as string] = jsonify(v as string) as Jsonified; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return result as any; + } + + const result: [Jsonified, Jsonified][] = []; + for (const [k, v] of item.entries()) { + result.push([jsonify(k as string), jsonify(v as string)] as [Jsonified, Jsonified]); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return result as any; + } + + if (item instanceof Set) { + const result: Jsonified[] = []; + for (const v of item.values()) { + result.push(jsonify(v as string) as Jsonified); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return result as any; + } + + if (typeof item === 'object') { + if (typeof (item as { jsonify(): unknown }).jsonify === 'function') { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (item as { jsonify(): unknown }).jsonify() as any; + } + + const result: { [key: string]: Jsonified } = {}; + for (const [k, v] of Object.entries(item)) { + if (typeof k !== 'string') { + continue; + } + if (typeof v === 'function') { + continue; + } + result[k] = jsonify(v as string) as Jsonified; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return result as any; + } + + throw new Error(`jsonify: invalid type ${typeof item}`); +} \ No newline at end of file