Socket
Book a DemoInstallSign in
Socket

@itgenio/edik-reflection

Package Overview
Dependencies
Maintainers
4
Versions
45
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@itgenio/edik-reflection

Рефлексия

latest
npmnpm
Version
0.0.32
Version published
Maintainers
4
Created
Source

Reflection

  • зависимость nanoid для генерации ref id

Общее

Type.$$gemeta

Все данные об объекте хранятся в его типе в поле $$gemeta. Удобно хранить связанные с типом мета-данные в его прототипе.

Дальнейшим развитием может быть сохранение не по строчному ключу, а символьному (Symbol, так делает Mobx)

Какие мета-данные есть у типа?

  • name:string используется для поиска типа в качестве уникального идентификатора.

Можно было бы использовать имя конструктора, но после минификации оно становится не пригодным для использования.

  • displayName?:string для красивости
  • isComponent?:boolean компонент или нет (доп. атрибут для инспектора)
  • fields?:{ name:string }[] поля
  • inline?:boolean нужно ли инлайнить,т.е. вставлять объекты такого типа по месту не вынося в map.

Для типа SerializedRootObject есть поддержка версионирования. (текущая версия схемы будет лежать в поле __meta.v)

Про обычные и inline-типы

Библиотека позволяет сериализовывать циклические зависимости, а так же множественные ссылки на один объект и т.д.

Это работает благодаря тому, что все обработанные объекты помещяются в отдельный список. Перед тем, как обработать следующий объект, вначале проверяется, что он еще не обработан(т.е. его нет в списке).

Если объект есть в списке - значит мы уже обработали такой объект и можно идти дальше.

При этом, в поле будет записан идентификатор ссылки на этот объект. Т.е. сколько бы полей не ссылалось на данный объект, все они будут ссылаться на один конкретный экземпляр.

Важно: при копировании полей объекта такие ссылки на объекты не будут копироваться.

Пример:

class SomeComponent extends CBaseComponent {
  field: CBaseComponent;// предположим наш компонент ссылается на какой-то компонент.
}

const comp:SomeComponent = /*...*/;

const copy = comp.clone(); //копируем

assert(comp.field === copy.field);//!!! хотя сам компонент скопировался, ссылка при этом указывает на старый объект!

Такие объекты имеют Обычный тип.

В некоторых случаях хочется, чтобы объект не выносился в такой список и не мог иметь ссылок на себя кроме одной.

Для этого нужны inline-типы.

Самый простой пример - тип Color. Мы не хотим чтобы на него ссылались разные объекты, при этом при копировании создается новый экземпляр данного типа.

Пример:

class SomeComponent extends CBaseComponent {
  color: Color; //Color - inline-тип
}

const some:SomeComponent = ...;
const copy = some.copy(); //копируем

assert(some.color !== copy.color);//ссылки разные, т.к. цвет скопировался!

TODO

  • сократить размер?
    • если в массиве(+ set и map) все элементы - ссылки, то можно применить специальный флаг.(т.к. в большинстве случаев массивы однотипны)

Процесс регистрации

  • берем тип
  • регистрируем его поля в мету
  • далее идет по иерархии ниже и регистрируем следующий тип, если нужно.
  • так же регистрируем в общей хранилище Object.$$gemetamap : Map<string, Type> чтобы в дальнейшем из любого места могли получить тип по имени.

Если на верхнем уровне есть поле с именем, как и на нижних уровнях, то будет браться поле из верхнего уровня.

class CBase {
  id: string; //для CBase регаем id=>string
}

class CSome extends CBase {
  id: number; //для CSome регаем id=>number;
}

//getField(CSome,'id') => number;
//getField(CBase,'id') => string;

Early(ранняя) регистрация

Возможна такая ситуация, когда базовый класс не успел зарегистрироваться, но уже регистрируется наследник.

В таком случае наступает ранняя регистрация для базового типа. Это значит:

  • мы не знаем точно про доп. атрибуты и имя
  • мы знаем только о полях

Поэтому, для базового типа регистрируются поля и ставиться флаг, что была ранняя регистрация.

Сериализация

Поддержка и фичи

Типы:

  • type Primitives = string | number | boolean
  • Array<any>
  • Set<any>
  • Map<string, any>

Есть таблица

type ObjId = string;
type ObjData = any;

const objects = new Map<ObjId, ObjData>();

Все объекты(за исключением inline-типов) сериализуются в таблицу. Ссылки на такие объекты сериализуются как строки-указатели на ID объекта из таблицы.

Сериализация

У Serialized*** есть поле __meta.map хранящее все объекты, на которые могут ссылаться внутренние объекты.

Десериализация

Для объектов:

  • ID
  • тип
  • остальные данные

Тип при этом должен быть зареган в хранилище типов либо указан явно при десериализации.

Десериализация происходит в два этапа.

Первый этап

На первом этапе создаются экземпляры объектов. При этом берется тип из сериализованного объекта.

Второй этап

На втором этапе объекты связываются друг с другом

Объекты

Запоминаем ID и помечаем поле как ссылка на объект

Массивы

Запоминаем ID и помечаем, какие индексы как ссылки

Что не работает (так задумано)

Ссылки на общий raw object, raw array

import { Serializer } from './serializer';

const s1 = new Type();
const s2 = new Type();

const sharedRawObj = { a: '1' };

s1.raw = s2.raw = sharedRawObj; //вот так не работает!

const obj = { s1, s2 };

const dobj = Serializer.serialize(Serializer.deserialize(obj));

//после десериализации, dobj.s1.raw !== dobj.s2.raw, ссылка не сохранилась

PoC / Ideas

Иногда требуется сериализовать и десериализовать объект, но при этом, часть объектов, на которые ссылаемся, не находятся в иерархии и хочется сохранить ссылку на них после десериализации.

@gtype({ name: 'SomeType' })
class SomeType {
  @gserializable()
  id: string = 'randomId';

  @gserializable()
  externalLink?: SomeType;
}

const exernalObj = new SomeType();

const obj = new SomeType();
obj.externalLink = exernalObj;

Как сейчас:

const dobj = Serializer.deserialize(Serializer.serialize(obj));

assert(dobj.externalLink !== exernalObj); // сейчас вот так!

Предложение:

//serialize

const externalLinks = [externalObj];

const serialized = Serializer.serialize(obj, { externalLinks });

// deserialize

const dobj = Serializer.deserialize(serialized, { externalLinks: allObjsAndComps });

assert(dobj.externalLink === exernalObj);

interface ILinkable {
  id: string;
}

Подробнее:

Когда встречаем объект, который находится в externalLinks, подменяем тип на новый тип ExternalLink:

class ExternalLink {
  id: string;
}

FAQs

Package last updated on 16 May 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts