UNPKG

@safejs/serializer

Version:

Serializer is a superset of JSON that includes recursive objects, dates, regular expressions, functions and others which you also can expand by yourself

135 lines (121 loc) 4.37 kB
import { refs } from "../refs"; import { ExtraSerializersPreset, ExtraSerializerRef, UnifiedExtraSerializer, ExtraSerializerId, isConstantExtraSerializer, } from "../types"; interface Known { idx?: number; serialized?: any; include: boolean; } type IdentifiedExtraSerializer = UnifiedExtraSerializer & { id: ExtraSerializerId; ref: ExtraSerializerRef; }; class Knowns { nextIdx = 0; list = new Map<any, Known>(); include(ref: any, known: Partial<Known> = {}) { let v = this.list.get(ref); if (v) { Object.assign(v, known); } else { v = this.add(ref, known); } if (!v.include) { v.include = true; v.idx = this.nextIdx; this.nextIdx++; } return v; } has(ref: any) { return this.list.has(ref); } get(ref: any) { return this.list.get(ref); } add(ref: any, known: Partial<Known> = {}) { const v = { include: false, ...known }; this.list.set(ref, v); return v; } getIncludes() { return Array.from(this.list.values()) .filter((o) => o.include) .sort((a, b) => a.idx - b.idx) .map((o) => o.serialized); } } export class BaseSerializer { static serialize(value: any, extraSerializers: ExtraSerializersPreset = {}) { const serializers: IdentifiedExtraSerializer[] = Object.entries(extraSerializers).map(([id, s]) => ({ ...s, ref: {}, id, })); const knowns = BaseSerializer.getKnowns(value, serializers); const serialized = [value, ...knowns.getIncludes()]; return JSON.stringify(serialized, function (k) { const v = this[k]; if (this === serialized && k !== "0") { if (serializers.find((s) => s.ref === v)) { return serializers.find((s) => s.ref === v).id; } return v; } const knownValue = knowns.get(v); if (knownValue) { if (knownValue.include) return refs.create(knownValue.idx); return knownValue.serialized; } const serializer = BaseSerializer.findExtraSerializer(v, serializers); if (serializer) { const serialized = BaseSerializer.serializeByExtraSerializer(v, serializer); if (knowns.has(serialized)) { return refs.create(knowns.get(serialized).idx); } return BaseSerializer.serializeByExtraSerializer(v, serializer); } return v; }); } private static getKnowns(value: any, serializers: IdentifiedExtraSerializer[]) { const knowns = new Knowns(); JSON.stringify(value, function (k) { const v = this[k]; if (knowns.has(v)) { const known = knowns.include(v); return refs.create(known.idx); } if (refs.test(v)) { const known = knowns.include(v, { serialized: v }); return refs.create(known.idx); } const serializer = BaseSerializer.findExtraSerializer(v, serializers); let serialized = v; if (serializer) { knowns.include(serializer.ref, { serialized: serializer.ref }); serialized = BaseSerializer.serializeByExtraSerializer(v, serializer); } if ((typeof v === "object" && v !== null) || typeof v === "symbol") { knowns.add(v, { serialized }); } return serialized; }); return knowns; } private static findExtraSerializer(v: any, serializers: IdentifiedExtraSerializer[]) { for (const serializer of serializers) { if (serializer.determine(v)) { return serializer; } } } private static serializeByExtraSerializer(v: any, serializer: IdentifiedExtraSerializer) { return isConstantExtraSerializer(serializer) ? serializer.ref : [serializer.ref, serializer.serialize(v)]; } }