refactor for new architecture
This commit is contained in:
parent
5ef148193e
commit
a163468b7f
43 changed files with 880 additions and 1463 deletions
16
dist/de/generic.d.ts
vendored
16
dist/de/generic.d.ts
vendored
|
@ -1,16 +0,0 @@
|
|||
import { Deserialize, Deserializer, IterableAccess, MapAccess, Visitor } from './interface';
|
||||
export declare class GenericSeed<T> implements Deserialize<T> {
|
||||
static deserialize<T, D extends Deserializer>(deserializer: D): T;
|
||||
deserialize<D extends Deserializer>(deserializer: D): T;
|
||||
}
|
||||
export declare class GenericVisitor<T> implements Visitor<T> {
|
||||
visitString(value: string): T;
|
||||
visitNumber(value: number): T;
|
||||
visitBigInt(value: bigint): T;
|
||||
visitBoolean(value: boolean): T;
|
||||
visitSymbol(value: symbol): T;
|
||||
visitNull(): T;
|
||||
visitObject(access: MapAccess): T;
|
||||
visitClass(name: string, value: MapAccess): T;
|
||||
visitIterable(access: IterableAccess): T;
|
||||
}
|
107
dist/de/generic.js
vendored
107
dist/de/generic.js
vendored
|
@ -1,107 +0,0 @@
|
|||
"use strict";
|
||||
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
||||
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
||||
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
||||
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
||||
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
||||
var _, done = false;
|
||||
for (var i = decorators.length - 1; i >= 0; i--) {
|
||||
var context = {};
|
||||
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
||||
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
||||
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
||||
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
||||
if (kind === "accessor") {
|
||||
if (result === void 0) continue;
|
||||
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
||||
if (_ = accept(result.get)) descriptor.get = _;
|
||||
if (_ = accept(result.set)) descriptor.set = _;
|
||||
if (_ = accept(result.init)) initializers.unshift(_);
|
||||
}
|
||||
else if (_ = accept(result)) {
|
||||
if (kind === "field") initializers.unshift(_);
|
||||
else descriptor[key] = _;
|
||||
}
|
||||
}
|
||||
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
||||
done = true;
|
||||
};
|
||||
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
||||
var useValue = arguments.length > 2;
|
||||
for (var i = 0; i < initializers.length; i++) {
|
||||
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
||||
}
|
||||
return useValue ? value : void 0;
|
||||
};
|
||||
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
||||
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
||||
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GenericVisitor = exports.GenericSeed = void 0;
|
||||
const utils_1 = require("../utils");
|
||||
let GenericSeed = (() => {
|
||||
let _classDecorators = [(0, utils_1.staticImplements)()];
|
||||
let _classDescriptor;
|
||||
let _classExtraInitializers = [];
|
||||
let _classThis;
|
||||
var GenericSeed = _classThis = class {
|
||||
static deserialize(deserializer) {
|
||||
return deserializer.deserializeAny(new GenericVisitor());
|
||||
}
|
||||
deserialize(deserializer) {
|
||||
return GenericSeed.deserialize(deserializer);
|
||||
}
|
||||
};
|
||||
__setFunctionName(_classThis, "GenericSeed");
|
||||
(() => {
|
||||
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
||||
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
||||
GenericSeed = _classThis = _classDescriptor.value;
|
||||
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
||||
__runInitializers(_classThis, _classExtraInitializers);
|
||||
})();
|
||||
return GenericSeed = _classThis;
|
||||
})();
|
||||
exports.GenericSeed = GenericSeed;
|
||||
class GenericVisitor {
|
||||
visitString(value) {
|
||||
return value;
|
||||
}
|
||||
visitNumber(value) {
|
||||
return value;
|
||||
}
|
||||
visitBigInt(value) {
|
||||
return value;
|
||||
}
|
||||
visitBoolean(value) {
|
||||
return value;
|
||||
}
|
||||
visitSymbol(value) {
|
||||
return value;
|
||||
}
|
||||
visitNull() {
|
||||
return null;
|
||||
}
|
||||
visitObject(access) {
|
||||
const result = {};
|
||||
let entry;
|
||||
while ((entry = access.nextEntry())) {
|
||||
result[entry[0]] = entry[1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
visitClass(name, value) {
|
||||
console.log(name, '!!!!!!');
|
||||
return this.visitObject(value);
|
||||
}
|
||||
visitIterable(access) {
|
||||
const result = new Array(access.sizeHint());
|
||||
let element;
|
||||
while ((element = access.nextElement())) {
|
||||
result.push(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.GenericVisitor = GenericVisitor;
|
112
dist/de/impl.d.ts
vendored
112
dist/de/impl.d.ts
vendored
|
@ -1,7 +1,107 @@
|
|||
import { SerdeOptions } from '../options';
|
||||
import { Deserialize, Deserializer } from './interface';
|
||||
type DeserializeConstructor<T> = Deserialize<T> & {
|
||||
new (): Deserialize<T>;
|
||||
};
|
||||
export declare function deserializeWith<T, D extends Deserializer, E extends DeserializeConstructor<T>>(deserializer: D, into: E, options: SerdeOptions): T;
|
||||
import { Registry } from '../registry';
|
||||
import { Constructor } from '../utils';
|
||||
type Nullable<T> = T | undefined;
|
||||
export declare class IterResult {
|
||||
static Next<T>(value: T): IteratorResult<T>;
|
||||
static Done<T>(): IteratorResult<T>;
|
||||
}
|
||||
export interface IMapAccess {
|
||||
nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>;
|
||||
nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>;
|
||||
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]>;
|
||||
nextKey<T>(): IteratorResult<T>;
|
||||
nextValue<V>(): IteratorResult<V>;
|
||||
nextEntry<K, V>(): IteratorResult<[K, V]>;
|
||||
sizeHint?(): Nullable<number>;
|
||||
}
|
||||
export declare abstract class MapAccess {
|
||||
abstract nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>;
|
||||
abstract nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>;
|
||||
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]>;
|
||||
abstract nextKey<T>(): IteratorResult<T>;
|
||||
abstract nextValue<V>(): IteratorResult<V>;
|
||||
nextEntry<K, V>(): IteratorResult<[K, V]>;
|
||||
}
|
||||
export interface IIterableAccess {
|
||||
nextElement<T>(): IteratorResult<T>;
|
||||
sizeHint?(): Nullable<number>;
|
||||
}
|
||||
export declare abstract class IterableAccess implements IIterableAccess {
|
||||
abstract nextElement<T>(): IteratorResult<T>;
|
||||
}
|
||||
export interface IVisitor<T> {
|
||||
visitBoolean(value: boolean): T;
|
||||
visitNumber(value: number): T;
|
||||
visitBigInt(value: bigint): T;
|
||||
visitString(value: string): T;
|
||||
visitSymbol(value: symbol): T;
|
||||
visitNull(): T;
|
||||
visitObject(access: IMapAccess): T;
|
||||
visitIterable(access: IIterableAccess): T;
|
||||
}
|
||||
export declare class GenericSeed<T> {
|
||||
readonly visitor: IVisitor<T>;
|
||||
constructor(visitor?: IVisitor<T>);
|
||||
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor?: IVisitor<T>): T;
|
||||
deserialize<D extends IDeserializer>(deserializer: D): T;
|
||||
}
|
||||
export declare class GenericVisitor<T> implements IVisitor<T> {
|
||||
visitBoolean(value: boolean): T;
|
||||
visitNumber(value: number): T;
|
||||
visitBigInt(value: bigint): T;
|
||||
visitString(value: string): T;
|
||||
visitSymbol(value: symbol): T;
|
||||
visitNull(): T;
|
||||
visitObject(access: IMapAccess): T;
|
||||
visitIterable(access: IIterableAccess): T;
|
||||
}
|
||||
export interface IDeserializer {
|
||||
deserializeAny<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeString<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeFunction<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
}
|
||||
export declare class ForwardMapAccess extends MapAccess {
|
||||
private readonly keys;
|
||||
private readonly values;
|
||||
private kindex;
|
||||
private vindex;
|
||||
constructor(keys: string[], values: any[]);
|
||||
static fromObject(obj: object): ForwardMapAccess;
|
||||
nextKeySeed<T, K extends Deserialize>(_seed: K): IteratorResult<T>;
|
||||
nextValueSeed<T, V extends Deserialize>(_seed: V): IteratorResult<T>;
|
||||
nextKey<T>(): IteratorResult<T>;
|
||||
nextValue<V>(): IteratorResult<V>;
|
||||
}
|
||||
export declare class ForwardIterableAccess extends IterableAccess {
|
||||
private readonly items;
|
||||
private index;
|
||||
constructor(items: any[]);
|
||||
nextElement<T>(): IteratorResult<T>;
|
||||
}
|
||||
export declare class Forward implements IDeserializer {
|
||||
private readonly value;
|
||||
constructor(value: any);
|
||||
static with(value: any): Forward;
|
||||
deserializeAny<T, V extends IVisitor<T>>(_visitor: V): T;
|
||||
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeString<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T;
|
||||
deserializeFunction<T, V extends IVisitor<T>>(_visitor: V): T;
|
||||
}
|
||||
export interface Deserialize {
|
||||
<T>(deserializer: IDeserializer): T;
|
||||
}
|
||||
export declare function deserialize<T, D extends IDeserializer>(deserializer: D, into: Constructor<T>, registry?: Registry): T;
|
||||
export {};
|
||||
|
|
244
dist/de/impl.js
vendored
244
dist/de/impl.js
vendored
|
@ -1,21 +1,233 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.deserializeWith = deserializeWith;
|
||||
const options_1 = require("../options");
|
||||
const generic_1 = require("./generic");
|
||||
function deserializeWith(deserializer, into, options) {
|
||||
const visitor = new generic_1.GenericVisitor();
|
||||
const target = new into();
|
||||
const obj = deserializer.deserializeClass(into.name, visitor);
|
||||
const newObject = {};
|
||||
for (const property in target) {
|
||||
const name = options.getPropertyName(property, options_1.Stage.Deserialize);
|
||||
const value = obj[name] || options.getDefault(property);
|
||||
newObject[property] = value;
|
||||
delete obj[name];
|
||||
exports.Forward = exports.ForwardIterableAccess = exports.ForwardMapAccess = exports.GenericVisitor = exports.GenericSeed = exports.IterableAccess = exports.MapAccess = exports.IterResult = void 0;
|
||||
exports.deserialize = deserialize;
|
||||
const registry_1 = require("../registry");
|
||||
class IterResult {
|
||||
static Next(value) {
|
||||
return { done: false, value };
|
||||
}
|
||||
if (options.options.denyUnknownFields && Object.keys(obj).length > 0) {
|
||||
throw new TypeError(`Unexpected fields: ${Object.keys(obj).join(', ')}`);
|
||||
static Done() {
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
}
|
||||
exports.IterResult = IterResult;
|
||||
class MapAccess {
|
||||
nextEntrySeed(kseed, vseed) {
|
||||
const key = this.nextKeySeed(kseed);
|
||||
if (!key.done) {
|
||||
const value = this.nextValueSeed(vseed);
|
||||
if (!value.done) {
|
||||
return IterResult.Next([key.value, value.value]);
|
||||
}
|
||||
}
|
||||
return IterResult.Done();
|
||||
}
|
||||
nextEntry() {
|
||||
const key = this.nextKey();
|
||||
if (!key.done) {
|
||||
const value = this.nextValue();
|
||||
if (!value.done) {
|
||||
return IterResult.Next([key.value, value.value]);
|
||||
}
|
||||
}
|
||||
return IterResult.Done();
|
||||
}
|
||||
}
|
||||
exports.MapAccess = MapAccess;
|
||||
class IterableAccess {
|
||||
}
|
||||
exports.IterableAccess = IterableAccess;
|
||||
class GenericSeed {
|
||||
constructor(visitor = new GenericVisitor()) {
|
||||
Object.defineProperty(this, "visitor", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
this.visitor = visitor;
|
||||
}
|
||||
static deserialize(deserializer, visitor = new GenericVisitor()) {
|
||||
return deserializer.deserializeAny(visitor);
|
||||
}
|
||||
deserialize(deserializer) {
|
||||
return GenericSeed.deserialize(deserializer, this.visitor);
|
||||
}
|
||||
}
|
||||
exports.GenericSeed = GenericSeed;
|
||||
class GenericVisitor {
|
||||
visitBoolean(value) {
|
||||
return value;
|
||||
}
|
||||
visitNumber(value) {
|
||||
return value;
|
||||
}
|
||||
visitBigInt(value) {
|
||||
return value;
|
||||
}
|
||||
visitString(value) {
|
||||
return value;
|
||||
}
|
||||
visitSymbol(value) {
|
||||
return value;
|
||||
}
|
||||
visitNull() {
|
||||
return null;
|
||||
}
|
||||
visitObject(access) {
|
||||
const result = [];
|
||||
let entry;
|
||||
while ((entry = access.nextEntry()) && !entry.done) {
|
||||
result.push(entry.value);
|
||||
}
|
||||
return Object.fromEntries(result);
|
||||
}
|
||||
visitIterable(access) {
|
||||
const result = [];
|
||||
let element;
|
||||
while ((element = access.nextElement())) {
|
||||
result.push(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.GenericVisitor = GenericVisitor;
|
||||
class ForwardMapAccess extends MapAccess {
|
||||
constructor(keys, values) {
|
||||
super();
|
||||
Object.defineProperty(this, "keys", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "values", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "kindex", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 0
|
||||
});
|
||||
Object.defineProperty(this, "vindex", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 0
|
||||
});
|
||||
this.keys = keys;
|
||||
this.values = values;
|
||||
}
|
||||
static fromObject(obj) {
|
||||
return new ForwardMapAccess(Object.keys(obj), Object.values(obj));
|
||||
}
|
||||
nextKeySeed(_seed) {
|
||||
return this.nextKey();
|
||||
}
|
||||
nextValueSeed(_seed) {
|
||||
return this.nextValue();
|
||||
}
|
||||
nextKey() {
|
||||
if (this.kindex < this.keys.length) {
|
||||
return IterResult.Next(this.keys[this.kindex++]);
|
||||
}
|
||||
else {
|
||||
return IterResult.Done();
|
||||
}
|
||||
}
|
||||
nextValue() {
|
||||
if (this.vindex < this.values.length) {
|
||||
return IterResult.Next(this.values[this.vindex++]);
|
||||
}
|
||||
else {
|
||||
return IterResult.Done();
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.ForwardMapAccess = ForwardMapAccess;
|
||||
class ForwardIterableAccess extends IterableAccess {
|
||||
constructor(items) {
|
||||
super();
|
||||
Object.defineProperty(this, "items", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "index", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: 0
|
||||
});
|
||||
this.items = items;
|
||||
}
|
||||
nextElement() {
|
||||
if (this.index < this.items.length) {
|
||||
return IterResult.Next(this.items[this.index++]);
|
||||
}
|
||||
else {
|
||||
return IterResult.Done();
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.ForwardIterableAccess = ForwardIterableAccess;
|
||||
class Forward {
|
||||
constructor(value) {
|
||||
Object.defineProperty(this, "value", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
this.value = value;
|
||||
}
|
||||
static with(value) {
|
||||
return new this(value);
|
||||
}
|
||||
deserializeAny(_visitor) {
|
||||
throw new Error("Can't forward to deserializeAny");
|
||||
}
|
||||
deserializeBoolean(visitor) {
|
||||
return visitor.visitBoolean(this.value);
|
||||
}
|
||||
deserializeNumber(visitor) {
|
||||
return visitor.visitNumber(this.value);
|
||||
}
|
||||
deserializeBigInt(visitor) {
|
||||
return visitor.visitBigInt(this.value);
|
||||
}
|
||||
deserializeString(visitor) {
|
||||
return visitor.visitString(this.value);
|
||||
}
|
||||
deserializeSymbol(visitor) {
|
||||
return visitor.visitSymbol(this.value);
|
||||
}
|
||||
deserializeNull(visitor) {
|
||||
return visitor.visitNull();
|
||||
}
|
||||
deserializeObject(visitor) {
|
||||
return visitor.visitObject(ForwardMapAccess.fromObject(this.value));
|
||||
}
|
||||
deserializeIterable(visitor) {
|
||||
return visitor.visitIterable(new ForwardIterableAccess(this.value));
|
||||
}
|
||||
deserializeFunction(_visitor) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
exports.Forward = Forward;
|
||||
function deserialize(deserializer, into, registry = registry_1.GlobalRegistry) {
|
||||
const de = registry.deserializers.get(into);
|
||||
if (de == null) {
|
||||
throw new ReferenceError(`No deserializer for ${into.name}`);
|
||||
}
|
||||
else {
|
||||
return de(deserializer);
|
||||
}
|
||||
return Object.assign(target, newObject);
|
||||
}
|
||||
|
|
3
dist/de/index.d.ts
vendored
3
dist/de/index.d.ts
vendored
|
@ -1,3 +0,0 @@
|
|||
export * from './interface';
|
||||
export * from './generic';
|
||||
export * from './mixin';
|
19
dist/de/index.js
vendored
19
dist/de/index.js
vendored
|
@ -1,19 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./interface"), exports);
|
||||
__exportStar(require("./generic"), exports);
|
||||
__exportStar(require("./mixin"), exports);
|
53
dist/de/interface.d.ts
vendored
53
dist/de/interface.d.ts
vendored
|
@ -1,53 +0,0 @@
|
|||
import { Nullable } from '../utils';
|
||||
export interface MapAccess {
|
||||
nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>;
|
||||
nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>;
|
||||
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]>;
|
||||
nextKey<T>(): Nullable<T>;
|
||||
nextValue<T>(): Nullable<T>;
|
||||
nextEntry<K, V>(): Nullable<[K, V]>;
|
||||
}
|
||||
export declare abstract class DefaultMapAccessImpl implements MapAccess {
|
||||
abstract nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>;
|
||||
abstract nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>;
|
||||
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]>;
|
||||
nextKey<T>(): Nullable<T>;
|
||||
nextValue<T>(): Nullable<T>;
|
||||
nextEntry<K, V>(): Nullable<[K, V]>;
|
||||
}
|
||||
export interface IterableAccess {
|
||||
nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>;
|
||||
nextElement<T>(): Nullable<T>;
|
||||
sizeHint(): number;
|
||||
}
|
||||
export declare abstract class DefaultIterableAccessImpl implements IterableAccess {
|
||||
abstract nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>;
|
||||
nextElement<T>(): Nullable<T>;
|
||||
sizeHint(): number;
|
||||
}
|
||||
export interface Visitor<T> {
|
||||
visitString(value: string): T;
|
||||
visitNumber(value: number): T;
|
||||
visitBigInt(value: bigint): T;
|
||||
visitBoolean(value: boolean): T;
|
||||
visitSymbol(value: symbol): T;
|
||||
visitNull(): T;
|
||||
visitObject(value: MapAccess): T;
|
||||
visitClass(name: string, value: MapAccess): T;
|
||||
visitIterable?(value: IterableAccess): T;
|
||||
}
|
||||
export interface Deserializer {
|
||||
deserializeAny<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeString<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeNumber<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeBigInt<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeBoolean<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeSymbol<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeNull<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeObject<T, V extends Visitor<T>>(visitor: V): T;
|
||||
deserializeClass<T, V extends Visitor<T>>(name: string, visitor: V): T;
|
||||
deserializeIterable<T, V extends Visitor<T>>(visitor: V): T;
|
||||
}
|
||||
export interface Deserialize<T> {
|
||||
deserialize<D extends Deserializer>(deserializer: D): T;
|
||||
}
|
32
dist/de/interface.js
vendored
32
dist/de/interface.js
vendored
|
@ -1,32 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DefaultIterableAccessImpl = exports.DefaultMapAccessImpl = void 0;
|
||||
const generic_1 = require("./generic");
|
||||
class DefaultMapAccessImpl {
|
||||
nextEntrySeed(kseed, vseed) {
|
||||
const key = this.nextKeySeed(kseed);
|
||||
if (key !== undefined) {
|
||||
const value = this.nextValueSeed(vseed);
|
||||
if (value !== undefined) {
|
||||
return [key, value];
|
||||
}
|
||||
}
|
||||
}
|
||||
nextKey() {
|
||||
return this.nextValueSeed((generic_1.GenericSeed));
|
||||
}
|
||||
nextValue() {
|
||||
return this.nextValueSeed((generic_1.GenericSeed));
|
||||
}
|
||||
nextEntry() {
|
||||
return this.nextEntrySeed((generic_1.GenericSeed), (generic_1.GenericSeed));
|
||||
}
|
||||
}
|
||||
exports.DefaultMapAccessImpl = DefaultMapAccessImpl;
|
||||
class DefaultIterableAccessImpl {
|
||||
nextElement() {
|
||||
return this.nextElementSeed((generic_1.GenericSeed));
|
||||
}
|
||||
sizeHint() { return 0; }
|
||||
}
|
||||
exports.DefaultIterableAccessImpl = DefaultIterableAccessImpl;
|
9
dist/de/mixin.d.ts
vendored
9
dist/de/mixin.d.ts
vendored
|
@ -1,9 +0,0 @@
|
|||
import { Constructor } from '../utils';
|
||||
import { Deserializer } from './interface';
|
||||
export declare function deserialize<T, C extends Constructor>(constructor: C): {
|
||||
new (...args: any[]): {
|
||||
[x: string]: any;
|
||||
};
|
||||
name: string;
|
||||
deserialize<D extends Deserializer>(deserializer: D): T;
|
||||
} & C;
|
76
dist/de/mixin.js
vendored
76
dist/de/mixin.js
vendored
|
@ -1,76 +0,0 @@
|
|||
"use strict";
|
||||
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
||||
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
||||
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
||||
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
||||
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
||||
var _, done = false;
|
||||
for (var i = decorators.length - 1; i >= 0; i--) {
|
||||
var context = {};
|
||||
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
||||
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
||||
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
||||
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
||||
if (kind === "accessor") {
|
||||
if (result === void 0) continue;
|
||||
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
||||
if (_ = accept(result.get)) descriptor.get = _;
|
||||
if (_ = accept(result.set)) descriptor.set = _;
|
||||
if (_ = accept(result.init)) initializers.unshift(_);
|
||||
}
|
||||
else if (_ = accept(result)) {
|
||||
if (kind === "field") initializers.unshift(_);
|
||||
else descriptor[key] = _;
|
||||
}
|
||||
}
|
||||
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
||||
done = true;
|
||||
};
|
||||
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
||||
var useValue = arguments.length > 2;
|
||||
for (var i = 0; i < initializers.length; i++) {
|
||||
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
||||
}
|
||||
return useValue ? value : void 0;
|
||||
};
|
||||
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
||||
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
||||
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.deserialize = deserialize;
|
||||
const utils_1 = require("../utils");
|
||||
const impl_1 = require("./impl");
|
||||
function deserialize(constructor) {
|
||||
let Deserializable = (() => {
|
||||
let _classDecorators = [(0, utils_1.staticImplements)()];
|
||||
let _classDescriptor;
|
||||
let _classExtraInitializers = [];
|
||||
let _classThis;
|
||||
let _classSuper = constructor;
|
||||
var Deserializable = _classThis = class extends _classSuper {
|
||||
static deserialize(deserializer) {
|
||||
return (0, impl_1.deserializeWith)(deserializer, this, this[Symbol.metadata].serde);
|
||||
}
|
||||
};
|
||||
__setFunctionName(_classThis, "Deserializable");
|
||||
(() => {
|
||||
var _a;
|
||||
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
|
||||
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
||||
Deserializable = _classThis = _classDescriptor.value;
|
||||
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
||||
})();
|
||||
Object.defineProperty(_classThis, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: constructor.name
|
||||
});
|
||||
(() => {
|
||||
__runInitializers(_classThis, _classExtraInitializers);
|
||||
})();
|
||||
return Deserializable = _classThis;
|
||||
})();
|
||||
return Deserializable;
|
||||
}
|
4
dist/decorator.d.ts
vendored
4
dist/decorator.d.ts
vendored
|
@ -1,4 +0,0 @@
|
|||
import { ContainerOptions, PropertyOptions } from './options';
|
||||
import { Registry } from './registry';
|
||||
export declare function serde(options: ContainerOptions | PropertyOptions): (target: any, context: any) => void;
|
||||
export declare function register(registry?: Registry): (target: any, _context: any) => void;
|
33
dist/decorator.js
vendored
33
dist/decorator.js
vendored
|
@ -1,33 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.serde = serde;
|
||||
exports.register = register;
|
||||
const options_1 = require("./options");
|
||||
const registry_1 = require("./registry");
|
||||
function decorateContainer(target, context, options) {
|
||||
const meta = context.metadata;
|
||||
const serde = (meta.serde || new options_1.SerdeOptions(target));
|
||||
serde.target = target;
|
||||
serde.options = options;
|
||||
meta.serde = serde;
|
||||
}
|
||||
function decorateProperty(target, context, options) {
|
||||
const meta = context.metadata;
|
||||
const serde = (meta.serde || new options_1.SerdeOptions(target));
|
||||
serde.properties.set(context.name, options);
|
||||
meta.serde = serde;
|
||||
}
|
||||
function serde(options) {
|
||||
return function (target, context) {
|
||||
if (target == null) {
|
||||
decorateProperty(target, context, options);
|
||||
}
|
||||
else
|
||||
decorateContainer(target, context, options);
|
||||
};
|
||||
}
|
||||
function register(registry = registry_1.GlobalRegistry) {
|
||||
return function (target, _context) {
|
||||
registry.add(target);
|
||||
};
|
||||
}
|
7
dist/index.d.ts
vendored
7
dist/index.d.ts
vendored
|
@ -1,6 +1,5 @@
|
|||
export * as ser from './ser/index';
|
||||
export * as de from './de/index';
|
||||
export * as ser from './ser/impl';
|
||||
export * as de from './de/impl';
|
||||
export * from './case';
|
||||
export * from './registry';
|
||||
export * from './decorator';
|
||||
export * from './options';
|
||||
export * from './utils';
|
||||
|
|
7
dist/index.js
vendored
7
dist/index.js
vendored
|
@ -37,9 +37,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.de = exports.ser = void 0;
|
||||
exports.ser = __importStar(require("./ser/index"));
|
||||
exports.de = __importStar(require("./de/index"));
|
||||
exports.ser = __importStar(require("./ser/impl"));
|
||||
exports.de = __importStar(require("./de/impl"));
|
||||
__exportStar(require("./case"), exports);
|
||||
__exportStar(require("./registry"), exports);
|
||||
__exportStar(require("./decorator"), exports);
|
||||
__exportStar(require("./options"), exports);
|
||||
__exportStar(require("./utils"), exports);
|
||||
|
|
63
dist/options.d.ts
vendored
63
dist/options.d.ts
vendored
|
@ -1,63 +0,0 @@
|
|||
import { CaseConvention } from './case';
|
||||
import { Deserializer } from './de';
|
||||
import { Serializer } from './ser';
|
||||
import { Registry } from './registry';
|
||||
export interface RenameOptions {
|
||||
serialize?: string;
|
||||
deserialize?: string;
|
||||
}
|
||||
export interface RenameAllOptions {
|
||||
serialize?: CaseConvention;
|
||||
deserialize?: CaseConvention;
|
||||
}
|
||||
export interface ContainerOptions {
|
||||
default?: () => any;
|
||||
denyUnknownFields?: boolean;
|
||||
expecting?: string;
|
||||
rename?: RenameOptions | string;
|
||||
renameAll?: RenameAllOptions | CaseConvention;
|
||||
registry?: Registry;
|
||||
tag?: string;
|
||||
content?: string;
|
||||
untagged?: boolean;
|
||||
}
|
||||
export interface ConditionalSkipOptions {
|
||||
if: (value: any) => boolean;
|
||||
}
|
||||
export interface SkipOptions {
|
||||
serializing?: ConditionalSkipOptions | boolean;
|
||||
deserializing?: ConditionalSkipOptions | boolean;
|
||||
}
|
||||
export type CustomSerializer = <T, V, S extends Serializer<T>>(value: V, serializer: S) => T;
|
||||
export type CustomDeserializer = <T, D extends Deserializer>(deserializer: D) => T;
|
||||
export interface PropertyOptions {
|
||||
aliases?: Set<string>;
|
||||
default?: () => any;
|
||||
flatten?: boolean;
|
||||
rename?: RenameOptions | string;
|
||||
skip?: SkipOptions | ConditionalSkipOptions | boolean;
|
||||
}
|
||||
export declare const Stage: Readonly<{
|
||||
readonly Serialize: 0;
|
||||
readonly Deserialize: 1;
|
||||
}>;
|
||||
export type Stage = typeof Stage[keyof typeof Stage];
|
||||
export declare class SerdeOptions {
|
||||
target: any;
|
||||
readonly options: ContainerOptions;
|
||||
readonly properties: Map<string, PropertyOptions>;
|
||||
get registry(): Registry;
|
||||
constructor(options?: ContainerOptions, properties?: Map<string, PropertyOptions>);
|
||||
static from(target: any): SerdeOptions;
|
||||
getClassName(stage: Stage): any;
|
||||
private getPropertyRename;
|
||||
private getPropertyCase;
|
||||
getPropertyName(property: string, stage: Stage): string;
|
||||
getSerializationName(property: string): string;
|
||||
getDeserializationName(property: string): string;
|
||||
getDefault(property: string): any;
|
||||
private isConditionalSkip;
|
||||
shouldSkip(property: string, value: any, stage: Stage): boolean;
|
||||
shouldSkipSerializing(property: string, value: any): boolean;
|
||||
shouldSkipDeserializing(property: string, value: any): boolean;
|
||||
}
|
155
dist/options.js
vendored
155
dist/options.js
vendored
|
@ -1,155 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.SerdeOptions = exports.Stage = void 0;
|
||||
const case_1 = require("./case");
|
||||
const utils_1 = require("./utils");
|
||||
const registry_1 = require("./registry");
|
||||
exports.Stage = Object.freeze({
|
||||
Serialize: 0,
|
||||
Deserialize: 1
|
||||
});
|
||||
class SerdeOptions {
|
||||
get registry() {
|
||||
return this.options.registry || registry_1.GlobalRegistry;
|
||||
}
|
||||
constructor(options = {}, properties = new Map()) {
|
||||
Object.defineProperty(this, "target", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "options", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
Object.defineProperty(this, "properties", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: void 0
|
||||
});
|
||||
this.options = options;
|
||||
this.properties = properties;
|
||||
}
|
||||
static from(target) {
|
||||
return new this(target);
|
||||
}
|
||||
getClassName(stage) {
|
||||
var _a, _b;
|
||||
if ((0, utils_1.isString)(this.options.rename)) {
|
||||
return this.options.rename;
|
||||
}
|
||||
else if (stage === exports.Stage.Serialize && (0, utils_1.isString)((_a = this.options.rename) === null || _a === void 0 ? void 0 : _a.serialize)) {
|
||||
return this.options.rename.serialize;
|
||||
}
|
||||
else if (stage === exports.Stage.Deserialize && (0, utils_1.isString)((_b = this.options.rename) === null || _b === void 0 ? void 0 : _b.deserialize)) {
|
||||
return this.options.rename.deserialize;
|
||||
}
|
||||
else {
|
||||
return this.target.constructor.name;
|
||||
}
|
||||
}
|
||||
getPropertyRename(property, stage, options) {
|
||||
var _a, _b;
|
||||
if (options != null) {
|
||||
if ((0, utils_1.isString)(options.rename)) {
|
||||
return options.rename;
|
||||
}
|
||||
else if (stage === exports.Stage.Serialize && (0, utils_1.isString)((_a = options.rename) === null || _a === void 0 ? void 0 : _a.serialize)) {
|
||||
return options.rename.serialize;
|
||||
}
|
||||
else if (stage === exports.Stage.Deserialize && (0, utils_1.isString)((_b = options.rename) === null || _b === void 0 ? void 0 : _b.deserialize)) {
|
||||
return options.rename.deserialize;
|
||||
}
|
||||
}
|
||||
return property;
|
||||
}
|
||||
getPropertyCase(name, stage) {
|
||||
var _a, _b;
|
||||
if ((0, utils_1.isNumber)(this.options.renameAll)) {
|
||||
return (0, case_1.convertCase)(name, this.options.renameAll);
|
||||
}
|
||||
else if (stage === exports.Stage.Serialize && (0, utils_1.isNumber)((_a = this.options.renameAll) === null || _a === void 0 ? void 0 : _a.serialize)) {
|
||||
return (0, case_1.convertCase)(name, this.options.renameAll.serialize);
|
||||
}
|
||||
else if (stage === exports.Stage.Deserialize && (0, utils_1.isNumber)((_b = this.options.renameAll) === null || _b === void 0 ? void 0 : _b.deserialize)) {
|
||||
return (0, case_1.convertCase)(name, this.options.renameAll.deserialize);
|
||||
}
|
||||
else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
getPropertyName(property, stage) {
|
||||
const options = this.properties.get(property);
|
||||
const name = options != null ? this.getPropertyRename(property, stage, options) : property;
|
||||
return this.getPropertyCase(name, stage);
|
||||
}
|
||||
getSerializationName(property) {
|
||||
return this.getPropertyName(property, exports.Stage.Serialize);
|
||||
}
|
||||
getDeserializationName(property) {
|
||||
return this.getPropertyName(property, exports.Stage.Deserialize);
|
||||
}
|
||||
getDefault(property) {
|
||||
const options = this.properties.get(property);
|
||||
if (options && (0, utils_1.isFunction)(options.default)) {
|
||||
return options.default();
|
||||
}
|
||||
else if ((0, utils_1.isFunction)(this.options.default)) {
|
||||
return this.options.default();
|
||||
}
|
||||
}
|
||||
//getCustomImpl(property: string, stage: Stage) {
|
||||
// const options = this.properties.get(property)
|
||||
// if (options != null) {
|
||||
// if (stage === Stage.Serialize && isFunction(options.serializeWith)) {
|
||||
// return options.serializeWith
|
||||
// } else if (stage === Stage.Deserialize && isFunction(options.deserializeWith)) {
|
||||
// return options.deserializeWith
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//getSerializer(property: string): Nullable<CustomSerializer> {
|
||||
// return this.getCustomImpl(property, Stage.Serialize) as CustomSerializer
|
||||
//}
|
||||
//getDeserializer(property: string): Nullable<CustomDeserializer> {
|
||||
// return this.getCustomImpl(property, Stage.Deserialize) as CustomDeserializer
|
||||
//}
|
||||
isConditionalSkip(skip) {
|
||||
return 'if' in skip && (0, utils_1.isFunction)(skip.if);
|
||||
}
|
||||
shouldSkip(property, value, stage) {
|
||||
const options = this.properties.get(property);
|
||||
if (options != null && options.skip != null) {
|
||||
if (typeof options.skip === 'boolean') {
|
||||
return options.skip;
|
||||
}
|
||||
else if (this.isConditionalSkip(options.skip)) {
|
||||
return options.skip.if(value);
|
||||
}
|
||||
else if (stage === exports.Stage.Serialize && typeof options.skip.serializing === 'boolean') {
|
||||
return options.skip.serializing;
|
||||
}
|
||||
else if (stage === exports.Stage.Serialize && this.isConditionalSkip(options.skip.serializing)) {
|
||||
return options.skip.serializing.if(value);
|
||||
}
|
||||
else if (stage === exports.Stage.Deserialize && typeof options.skip.deserializing === 'boolean') {
|
||||
return options.skip.deserializing;
|
||||
}
|
||||
else if (stage === exports.Stage.Deserialize && this.isConditionalSkip(options.skip.deserializing)) {
|
||||
return options.skip.deserializing.if(value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
shouldSkipSerializing(property, value) {
|
||||
return this.shouldSkip(property, value, exports.Stage.Serialize);
|
||||
}
|
||||
shouldSkipDeserializing(property, value) {
|
||||
return this.shouldSkip(property, value, exports.Stage.Deserialize);
|
||||
}
|
||||
}
|
||||
exports.SerdeOptions = SerdeOptions;
|
12
dist/registry.d.ts
vendored
12
dist/registry.d.ts
vendored
|
@ -1,7 +1,11 @@
|
|||
import { Constructor } from './utils';
|
||||
import { Deserialize } from './de/impl';
|
||||
import { Serialize } from './ser/impl';
|
||||
export declare class Registry {
|
||||
registeredClasses: Map<string, Constructor>;
|
||||
add(ctor: Constructor, key?: string): void;
|
||||
get(name: string): Constructor | undefined;
|
||||
serializers: Map<Function, Serialize<any>>;
|
||||
deserializers: Map<Function, Deserialize>;
|
||||
registerSerializer<T>(ctor: Function, serialize: Serialize<T>): void;
|
||||
registerDeserializer(ctor: Function, deserialize: Deserialize): void;
|
||||
}
|
||||
export declare const GlobalRegistry: Registry;
|
||||
export declare const registerSerializer: <T>(ctor: Function, serialize: Serialize<T>) => void;
|
||||
export declare const registerDeserializer: (ctor: Function, deserialize: Deserialize) => void;
|
||||
|
|
20
dist/registry.js
vendored
20
dist/registry.js
vendored
|
@ -1,21 +1,29 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GlobalRegistry = exports.Registry = void 0;
|
||||
exports.registerDeserializer = exports.registerSerializer = exports.GlobalRegistry = exports.Registry = void 0;
|
||||
class Registry {
|
||||
constructor() {
|
||||
Object.defineProperty(this, "registeredClasses", {
|
||||
Object.defineProperty(this, "serializers", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: new Map()
|
||||
});
|
||||
Object.defineProperty(this, "deserializers", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: new Map()
|
||||
});
|
||||
}
|
||||
add(ctor, key = ctor.name) {
|
||||
this.registeredClasses.set(key, ctor);
|
||||
registerSerializer(ctor, serialize) {
|
||||
this.serializers.set(ctor, serialize);
|
||||
}
|
||||
get(name) {
|
||||
return this.registeredClasses.get(name);
|
||||
registerDeserializer(ctor, deserialize) {
|
||||
this.deserializers.set(ctor, deserialize);
|
||||
}
|
||||
}
|
||||
exports.Registry = Registry;
|
||||
exports.GlobalRegistry = new Registry();
|
||||
exports.registerSerializer = exports.GlobalRegistry.registerSerializer.bind(exports.GlobalRegistry);
|
||||
exports.registerDeserializer = exports.GlobalRegistry.registerDeserializer.bind(exports.GlobalRegistry);
|
||||
|
|
50
dist/ser/impl.d.ts
vendored
50
dist/ser/impl.d.ts
vendored
|
@ -1,4 +1,46 @@
|
|||
import { Serializable, Serializer } from './interface';
|
||||
import { SerdeOptions } from '../options';
|
||||
import { Nullable } from '../utils';
|
||||
export declare function serializeWith<T>(serializer: Serializer<T>, value: Serializable, optionsGetter?: (value: any) => Nullable<SerdeOptions>): Nullable<T>;
|
||||
export interface ISerializeObject<T> {
|
||||
serializeKey(key: string): void;
|
||||
serializeValue<U>(value: U): void;
|
||||
serializeEntry<U>(key: string, value: U): void;
|
||||
end(): T;
|
||||
}
|
||||
export declare abstract class SerializeObject<T> implements ISerializeObject<T> {
|
||||
abstract serializeKey(key: string): void;
|
||||
abstract serializeValue<U>(value: U): void;
|
||||
abstract end(): T;
|
||||
serializeEntry<U>(key: string, value: U): void;
|
||||
}
|
||||
export interface ISerializeIterable<T> {
|
||||
serializeElement<U>(value: U): void;
|
||||
end(): T;
|
||||
}
|
||||
export declare abstract class SerializeIterable<T> implements ISerializeIterable<T> {
|
||||
abstract serializeElement<U>(value: U): void;
|
||||
abstract end(): T;
|
||||
}
|
||||
export interface ISerializer<T> {
|
||||
serializeAny(value: any): T;
|
||||
serializeBoolean(value: boolean): T;
|
||||
serializeNumber(value: number): T;
|
||||
serializeBigInt(value: bigint): T;
|
||||
serializeString(value: string): T;
|
||||
serializeSymbol(value: symbol): T;
|
||||
serializeNull(): T;
|
||||
serializeObject(): ISerializeObject<T>;
|
||||
serializeClass(name: string): ISerializeObject<T>;
|
||||
}
|
||||
export declare class Serializer<T> implements ISerializer<T> {
|
||||
serializeAny(_value: any): T;
|
||||
serializeBoolean(_value: boolean): T;
|
||||
serializeNumber(_value: number): T;
|
||||
serializeBigInt(_value: bigint): T;
|
||||
serializeString(_value: string): T;
|
||||
serializeSymbol(_value: symbol): T;
|
||||
serializeNull(): T;
|
||||
serializeObject(): ISerializeObject<T>;
|
||||
serializeClass(_name: string): ISerializeObject<T>;
|
||||
}
|
||||
export interface Serialize<T> {
|
||||
<U, S extends Serializer<U>>(serializer: S, value: T): U;
|
||||
}
|
||||
export declare function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V): T;
|
||||
|
|
121
dist/ser/impl.js
vendored
121
dist/ser/impl.js
vendored
|
@ -1,63 +1,78 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.serializeWith = serializeWith;
|
||||
const options_1 = require("../options");
|
||||
const utils_1 = require("../utils");
|
||||
const unhandledType = (serializer, value) => new TypeError(`'${serializer.constructor.name}' has no method for value type '${typeof value}'`);
|
||||
function serializeEntries(serializer, value, options) {
|
||||
var _a;
|
||||
let state;
|
||||
for (const [key, val] of value) {
|
||||
if (options === null || options === void 0 ? void 0 : options.shouldSkipSerializing(key, val)) {
|
||||
continue;
|
||||
}
|
||||
const name = (_a = options === null || options === void 0 ? void 0 : options.getPropertyName(key, options_1.Stage.Serialize)) !== null && _a !== void 0 ? _a : key;
|
||||
state = serializer.serializeKey(name);
|
||||
state = serializer.serializeValue(val);
|
||||
exports.Serializer = exports.SerializeIterable = exports.SerializeObject = void 0;
|
||||
exports.serialize = serialize;
|
||||
class SerializeObject {
|
||||
serializeEntry(key, value) {
|
||||
this.serializeKey(key);
|
||||
this.serializeValue(value);
|
||||
}
|
||||
}
|
||||
exports.SerializeObject = SerializeObject;
|
||||
class SerializeIterable {
|
||||
}
|
||||
exports.SerializeIterable = SerializeIterable;
|
||||
class Serializer {
|
||||
serializeAny(_value) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
serializeBoolean(_value) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializeNumber(_value) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializeBigInt(_value) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializeString(_value) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializeSymbol(_value) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializeNull() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializeObject() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
serializeClass(_name) {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
exports.Serializer = Serializer;
|
||||
const isPlainObject = (value) => (value === null || value === void 0 ? void 0 : value.constructor) === Object;
|
||||
class UnhandledTypeError extends TypeError {
|
||||
constructor(serializer, value) {
|
||||
super(`unhandled type: "${typeof value}" for serializer ${serializer.constructor.name}`);
|
||||
}
|
||||
}
|
||||
function serializeObject(serializer, obj) {
|
||||
for (const key in obj) {
|
||||
serializer.serializeEntry(key, obj[key]);
|
||||
}
|
||||
return serializer.end();
|
||||
}
|
||||
function serializeClass(serializer, value, options) {
|
||||
const ser = serializer.serializeClass(value.constructor.name);
|
||||
return serializeEntries(ser, Object.entries(value), options);
|
||||
function serializeClass(serializer, value) {
|
||||
const name = value.constructor.name;
|
||||
const ser = serializer.serializeClass(name);
|
||||
return serializeObject(ser, value);
|
||||
}
|
||||
function serializeObject(serializer, value, options) {
|
||||
const ser = serializer.serializeObject();
|
||||
return serializeEntries(ser, Object.entries(value), options);
|
||||
}
|
||||
function serializeIter(serializer, value, options) {
|
||||
let state;
|
||||
for (const val of value) {
|
||||
state = serializer.serializeElement(val);
|
||||
}
|
||||
return serializer.end();
|
||||
}
|
||||
function defaultOptions(value) {
|
||||
return value.constructor[Symbol.metadata].serde;
|
||||
}
|
||||
// dispatches in the order of serializeType -> serializeAny -> throw TypeError
|
||||
function serializeWith(serializer, value, optionsGetter = defaultOptions) {
|
||||
// prepare fallback methods
|
||||
const serializeAny = (0, utils_1.orElse)(serializer, serializer.serializeAny, (value) => unhandledType(serializer, value));
|
||||
const serialize = (0, utils_1.ifNull)(serializer, serializeAny, value);
|
||||
function serialize(serializer, value) {
|
||||
switch (typeof value) {
|
||||
case 'string': return serialize(serializer.serializeString);
|
||||
case 'number': return serialize(serializer.serializeNumber);
|
||||
case 'bigint': return serialize(serializer.serializeBigInt);
|
||||
case 'boolean': return serialize(serializer.serializeBoolean);
|
||||
case 'symbol': return serialize(serializer.serializeSymbol);
|
||||
case 'undefined': return serialize(serializer.serializeNull);
|
||||
case 'object':
|
||||
const options = optionsGetter(value);
|
||||
if ((0, utils_1.isIterable)(value) && (0, utils_1.isFunction)(serializer.serializeIterable)) {
|
||||
return serializeIter(serializer.serializeIterable(), value, options);
|
||||
case "string": return serializer.serializeString(value);
|
||||
case "number": return serializer.serializeNumber(value);
|
||||
case "bigint": return serializer.serializeBigInt(value);
|
||||
case "boolean": return serializer.serializeBoolean(value);
|
||||
case "symbol": return serializer.serializeSymbol(value);
|
||||
case "undefined": return serializer.serializeNull();
|
||||
case "object":
|
||||
switch (true) {
|
||||
case value == null: return serializer.serializeNull();
|
||||
case !isPlainObject(value): return serializeClass(serializer, value);
|
||||
default: return serializeObject(serializer.serializeObject(), value);
|
||||
}
|
||||
else if (!(0, utils_1.isPlainObject)(value)) {
|
||||
return serializeClass(serializer, value, options);
|
||||
}
|
||||
else if ((0, utils_1.isFunction)(serializer.serializeObject)) {
|
||||
return serializeObject(serializer, value, options);
|
||||
} // deliberate fallthrough when the above fail
|
||||
default: return serializeAny(value);
|
||||
default: throw new UnhandledTypeError(serializer, value);
|
||||
}
|
||||
}
|
||||
|
|
4
dist/ser/index.d.ts
vendored
4
dist/ser/index.d.ts
vendored
|
@ -1,4 +0,0 @@
|
|||
import '@tsmetadata/polyfill';
|
||||
export * from './interface';
|
||||
export * from './mixin';
|
||||
export * from './impl';
|
20
dist/ser/index.js
vendored
20
dist/ser/index.js
vendored
|
@ -1,20 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
require("@tsmetadata/polyfill");
|
||||
__exportStar(require("./interface"), exports);
|
||||
__exportStar(require("./mixin"), exports);
|
||||
__exportStar(require("./impl"), exports);
|
31
dist/ser/interface.d.ts
vendored
31
dist/ser/interface.d.ts
vendored
|
@ -1,31 +0,0 @@
|
|||
import { Primitive, ToString } from "../utils";
|
||||
export interface ObjectSerializer<T = void> {
|
||||
serializeKey<U extends Serializable>(key: U): T;
|
||||
serializeValue<U extends Serializable>(value: U): T;
|
||||
end(): T;
|
||||
}
|
||||
export interface IterableSerializer<T = void> {
|
||||
serializeElement<U extends Serializable>(element: U): T;
|
||||
end(): T;
|
||||
}
|
||||
interface TypeSerializer<T> {
|
||||
serializeString(value: string): T;
|
||||
serializeNumber(value: number): T;
|
||||
serializeBigInt(value: bigint): T;
|
||||
serializeBoolean(value: boolean): T;
|
||||
serializeSymbol(value: Symbol): T;
|
||||
serializeNull(): T;
|
||||
serializeObject(): ObjectSerializer<T>;
|
||||
serializeIterable?(): IterableSerializer<T>;
|
||||
serializeClass(name: PropertyKey): ObjectSerializer<T>;
|
||||
}
|
||||
interface AnySerializer<T> {
|
||||
serializeAny?(value?: any): T;
|
||||
}
|
||||
export declare function isGenericSerializer(value: any): boolean;
|
||||
export type Serializer<T> = Partial<TypeSerializer<T>> & Partial<AnySerializer<T>>;
|
||||
export type Serializable = Primitive | ToString | Serialize;
|
||||
export interface Serialize {
|
||||
serialize<T, S extends Serializer<T>>(serializer: S): T;
|
||||
}
|
||||
export {};
|
21
dist/ser/interface.js
vendored
21
dist/ser/interface.js
vendored
|
@ -1,21 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.isGenericSerializer = isGenericSerializer;
|
||||
const utils_1 = require("../utils");
|
||||
const TypeSerializerMethods = [
|
||||
'serializeString',
|
||||
'serializeNumber',
|
||||
'serializeBigInt',
|
||||
'serializeBoolean',
|
||||
'serializeSymbol',
|
||||
//'serializeMap',
|
||||
'serializeIterable',
|
||||
'serializeNull',
|
||||
'serializeObject',
|
||||
'serializeClass',
|
||||
];
|
||||
const AnySerializerMethods = ['serializeAny'];
|
||||
function isGenericSerializer(value) {
|
||||
return AnySerializerMethods.every(k => (0, utils_1.isFunction)(value[k])) &&
|
||||
TypeSerializerMethods.every(k => !(0, utils_1.isFunction)(value[k]));
|
||||
}
|
9
dist/ser/mixin.d.ts
vendored
9
dist/ser/mixin.d.ts
vendored
|
@ -1,9 +0,0 @@
|
|||
import { Constructor } from '../utils';
|
||||
import { Serializer } from './interface';
|
||||
export declare function serialize<T extends Constructor>(constructor: T): {
|
||||
new (...args: any[]): {
|
||||
[x: string]: any;
|
||||
serialize<U>(serializer: Serializer<U>): U;
|
||||
};
|
||||
name: string;
|
||||
} & T;
|
25
dist/ser/mixin.js
vendored
25
dist/ser/mixin.js
vendored
|
@ -1,25 +0,0 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.serialize = serialize;
|
||||
const impl_1 = require("./impl");
|
||||
const interface_1 = require("./interface");
|
||||
function serialize(constructor) {
|
||||
class Serializable extends constructor {
|
||||
serialize(serializer) {
|
||||
// shortcut for serializers with only the serializeAny method
|
||||
if ((0, interface_1.isGenericSerializer)(serializer)) {
|
||||
return serializer.serializeAny(this);
|
||||
}
|
||||
else {
|
||||
return (0, impl_1.serializeWith)(serializer, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
Object.defineProperty(Serializable, "name", {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: constructor.name
|
||||
});
|
||||
return Serializable;
|
||||
}
|
13
dist/utils.d.ts
vendored
13
dist/utils.d.ts
vendored
|
@ -13,16 +13,3 @@ export declare function isIterable(value: any): value is Iterable<any>;
|
|||
export declare function isString(value: any): value is string;
|
||||
export declare function isNumber(value: any): value is number;
|
||||
export type Constructor<T = any> = new (...args: any[]) => T;
|
||||
export declare function orElse(thisArg: any, a: Nullable<Function>, b: Function): (...args: any) => any;
|
||||
export declare function ifNull(thisArg: any, b: Function, ...args: any): (a: Nullable<Function>) => any;
|
||||
export declare function mixin<U = any>(impl: Function): <TBase extends Constructor<U>>(constructor: TBase) => TBase;
|
||||
type AnyFunc = (...arg: any) => any;
|
||||
type PipeArgs<F extends AnyFunc[], Acc extends AnyFunc[] = []> = F extends [
|
||||
(...args: infer A) => infer B
|
||||
] ? [...Acc, (...args: A) => B] : F extends [(...args: infer A) => any, ...infer Tail] ? Tail extends [(arg: infer B) => any, ...any[]] ? PipeArgs<Tail, [...Acc, (...args: A) => B]> : Acc : Acc;
|
||||
type LastFnReturnType<F extends Array<AnyFunc>, Else = never> = F extends [
|
||||
...any[],
|
||||
(...arg: any) => infer R
|
||||
] ? R : Else;
|
||||
export declare function pipe<FirstFn extends AnyFunc, F extends AnyFunc[]>(arg: Parameters<FirstFn>[0], firstFn: FirstFn, ...fns: PipeArgs<F> extends F ? F : PipeArgs<F>): LastFnReturnType<F, ReturnType<FirstFn>>;
|
||||
export {};
|
||||
|
|
32
dist/utils.js
vendored
32
dist/utils.js
vendored
|
@ -6,10 +6,6 @@ exports.isFunction = isFunction;
|
|||
exports.isIterable = isIterable;
|
||||
exports.isString = isString;
|
||||
exports.isNumber = isNumber;
|
||||
exports.orElse = orElse;
|
||||
exports.ifNull = ifNull;
|
||||
exports.mixin = mixin;
|
||||
exports.pipe = pipe;
|
||||
function staticImplements() {
|
||||
return (constructor) => { constructor; };
|
||||
}
|
||||
|
@ -28,31 +24,3 @@ function isString(value) {
|
|||
function isNumber(value) {
|
||||
return !isNaN(value);
|
||||
}
|
||||
function orElse(thisArg, a, b) {
|
||||
return function (...args) {
|
||||
const fn = a != null ? a : b;
|
||||
return fn.apply(thisArg, args);
|
||||
};
|
||||
}
|
||||
function ifNull(thisArg, b, ...args) {
|
||||
return function (a) {
|
||||
return orElse(thisArg, a, b).apply(thisArg, args);
|
||||
};
|
||||
}
|
||||
function applyMixins(derivedCtor, constructors) {
|
||||
constructors.forEach((baseCtor) => {
|
||||
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
|
||||
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
|
||||
Object.create(null));
|
||||
});
|
||||
});
|
||||
}
|
||||
function mixin(impl) {
|
||||
return function (constructor) {
|
||||
applyMixins(constructor, [impl]);
|
||||
return constructor;
|
||||
};
|
||||
}
|
||||
function pipe(arg, firstFn, ...fns) {
|
||||
return fns.reduce((acc, fn) => fn(acc), firstFn(arg));
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
"default": "./dist/index.js"
|
||||
},
|
||||
"./ser": {
|
||||
"types": "./dist/ser/index.d.ts",
|
||||
"default": "./dist/ser/index.js"
|
||||
"types": "./dist/ser/impl.d.ts",
|
||||
"default": "./dist/ser/impl.js"
|
||||
},
|
||||
"./de": {
|
||||
"types": "./dist/de/index.d.ts",
|
||||
"default": "./dist/de/index.js"
|
||||
"types": "./dist/de/impl.d.ts",
|
||||
"default": "./dist/de/impl.js"
|
||||
},
|
||||
"./utils": {
|
||||
"types": "./dist/utils.d.ts",
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import { staticImplements } from '../utils'
|
||||
import { Deserialize, Deserializer, IterableAccess, MapAccess, Visitor } from './interface'
|
||||
|
||||
@staticImplements<Deserialize<any>>()
|
||||
export class GenericSeed<T> implements Deserialize<T> {
|
||||
static deserialize<T, D extends Deserializer>(deserializer: D): T {
|
||||
return deserializer.deserializeAny(new GenericVisitor<T>())
|
||||
}
|
||||
|
||||
deserialize<D extends Deserializer>(deserializer: D): T {
|
||||
return GenericSeed.deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
export class GenericVisitor<T> implements Visitor<T> {
|
||||
visitString(value: string): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitNumber(value: number): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitBigInt(value: bigint): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitBoolean(value: boolean): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitSymbol(value: symbol): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitNull(): T {
|
||||
return null as T
|
||||
}
|
||||
|
||||
visitObject(access: MapAccess): T {
|
||||
const result: Record<PropertyKey, any> = {}
|
||||
let entry
|
||||
|
||||
while ((entry = access.nextEntry<string, any>())) {
|
||||
result[entry[0]] = entry[1]
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO: use global registry to deserialize classes
|
||||
visitClass(name: string, access: MapAccess): T {
|
||||
console.log(name, '!!!!!!')
|
||||
return this.visitObject(access)
|
||||
}
|
||||
|
||||
visitIterable(access: IterableAccess): T {
|
||||
const result = new Array(access.sizeHint())
|
||||
let element
|
||||
|
||||
while ((element = access.nextElement())) {
|
||||
result.push(element)
|
||||
}
|
||||
|
||||
return result as T
|
||||
}
|
||||
}
|
||||
|
303
src/de/impl.ts
303
src/de/impl.ts
|
@ -1,30 +1,287 @@
|
|||
import { SerdeOptions, Stage } from '../options'
|
||||
import { GenericVisitor } from './generic'
|
||||
import { Deserialize, Deserializer } from './interface'
|
||||
import { GlobalRegistry, Registry } from '../registry'
|
||||
import { Constructor } from '../utils'
|
||||
|
||||
type DeserializeConstructor<T> = Deserialize<T> & { new(): Deserialize<T> }
|
||||
type Nullable<T> = T | undefined
|
||||
|
||||
export function deserializeWith<T, D extends Deserializer, E extends DeserializeConstructor<T>>(deserializer: D, into: E, options: SerdeOptions): T {
|
||||
const visitor = new GenericVisitor<T>()
|
||||
// options to figure out properties/fields during deserialization:
|
||||
// offer schema decorator
|
||||
// parse fields from javascript with acorn
|
||||
// allow no-param constructor for initialization - this is what it does now
|
||||
const target = new into()
|
||||
const obj = deserializer.deserializeClass(into.name, visitor) as any
|
||||
const newObject = {} as any
|
||||
|
||||
for (const property in target) {
|
||||
const name = options.getPropertyName(property, Stage.Deserialize)
|
||||
const value = obj[name] || options.getDefault(property)
|
||||
newObject[property] = value
|
||||
delete obj[name]
|
||||
export class IterResult {
|
||||
static Next<T>(value: T): IteratorResult<T> {
|
||||
return { done: false, value }
|
||||
}
|
||||
|
||||
if (options.options.denyUnknownFields && Object.keys(obj).length > 0) {
|
||||
throw new TypeError(`Unexpected fields: ${Object.keys(obj).join(', ')}`)
|
||||
static Done<T>(): IteratorResult<T> {
|
||||
return { done: true, value: undefined }
|
||||
}
|
||||
}
|
||||
|
||||
export interface IMapAccess {
|
||||
nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>
|
||||
nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>
|
||||
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]>
|
||||
nextKey<T>(): IteratorResult<T>
|
||||
nextValue<V>(): IteratorResult<V>
|
||||
nextEntry<K, V>(): IteratorResult<[K, V]>
|
||||
sizeHint?(): Nullable<number>
|
||||
}
|
||||
|
||||
export abstract class MapAccess {
|
||||
abstract nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>
|
||||
abstract nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>
|
||||
|
||||
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]> {
|
||||
const key = this.nextKeySeed(kseed) as IteratorResult<TK>
|
||||
|
||||
if (!key.done) {
|
||||
const value = this.nextValueSeed(vseed) as IteratorResult<TV>
|
||||
|
||||
if (!value.done) {
|
||||
return IterResult.Next([key.value, value.value])
|
||||
}
|
||||
}
|
||||
|
||||
return IterResult.Done()
|
||||
}
|
||||
|
||||
abstract nextKey<T>(): IteratorResult<T>
|
||||
abstract nextValue<V>(): IteratorResult<V>
|
||||
|
||||
nextEntry<K, V>(): IteratorResult<[K, V]> {
|
||||
const key = this.nextKey() as IteratorResult<K>
|
||||
|
||||
if (!key.done) {
|
||||
const value = this.nextValue() as IteratorResult<V>
|
||||
|
||||
if (!value.done) {
|
||||
return IterResult.Next([key.value, value.value])
|
||||
}
|
||||
}
|
||||
|
||||
return IterResult.Done()
|
||||
}
|
||||
}
|
||||
|
||||
export interface IIterableAccess {
|
||||
nextElement<T>(): IteratorResult<T>
|
||||
sizeHint?(): Nullable<number>
|
||||
}
|
||||
|
||||
export abstract class IterableAccess implements IIterableAccess {
|
||||
abstract nextElement<T>(): IteratorResult<T>
|
||||
}
|
||||
|
||||
export interface IVisitor<T> {
|
||||
visitBoolean(value: boolean): T
|
||||
visitNumber(value: number): T
|
||||
visitBigInt(value: bigint): T
|
||||
visitString(value: string): T
|
||||
visitSymbol(value: symbol): T
|
||||
visitNull(): T
|
||||
visitObject(access: IMapAccess): T
|
||||
visitIterable(access: IIterableAccess): T
|
||||
}
|
||||
|
||||
export class GenericSeed<T> {
|
||||
readonly visitor: IVisitor<T>
|
||||
|
||||
constructor(visitor: IVisitor<T> = new GenericVisitor()) {
|
||||
this.visitor = visitor
|
||||
}
|
||||
|
||||
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor: IVisitor<T> = new GenericVisitor()): T {
|
||||
return deserializer.deserializeAny(visitor)
|
||||
}
|
||||
|
||||
deserialize<D extends IDeserializer>(deserializer: D): T {
|
||||
return GenericSeed.deserialize(deserializer, this.visitor)
|
||||
}
|
||||
}
|
||||
|
||||
export class GenericVisitor<T> implements IVisitor<T> {
|
||||
visitBoolean(value: boolean): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitNumber(value: number): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitBigInt(value: bigint): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitString(value: string): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitSymbol(value: symbol): T {
|
||||
return value as T
|
||||
}
|
||||
|
||||
visitNull(): T {
|
||||
return null as T
|
||||
}
|
||||
|
||||
visitObject(access: IMapAccess): T {
|
||||
const result = []
|
||||
let entry
|
||||
|
||||
while ((entry = access.nextEntry()) && !entry.done) {
|
||||
result.push(entry.value)
|
||||
}
|
||||
|
||||
return Object.fromEntries(result)
|
||||
}
|
||||
|
||||
visitIterable(access: IIterableAccess): T {
|
||||
const result = []
|
||||
let element
|
||||
|
||||
while ((element = access.nextElement())) {
|
||||
result.push(element)
|
||||
}
|
||||
|
||||
return result as T
|
||||
}
|
||||
}
|
||||
|
||||
export interface IDeserializer {
|
||||
deserializeAny<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeString<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T
|
||||
deserializeFunction<T, V extends IVisitor<T>>(visitor: V): T
|
||||
}
|
||||
|
||||
export class ForwardMapAccess extends MapAccess {
|
||||
private readonly keys: string[]
|
||||
private readonly values: any[]
|
||||
|
||||
private kindex: number = 0
|
||||
private vindex: number = 0
|
||||
|
||||
constructor(keys: string[], values: any[]) {
|
||||
super()
|
||||
this.keys = keys
|
||||
this.values = values
|
||||
}
|
||||
|
||||
static fromObject(obj: object): ForwardMapAccess {
|
||||
return new ForwardMapAccess(
|
||||
Object.keys(obj),
|
||||
Object.values(obj)
|
||||
)
|
||||
}
|
||||
|
||||
nextKeySeed<T, K extends Deserialize>(_seed: K): IteratorResult<T> {
|
||||
return this.nextKey()
|
||||
}
|
||||
|
||||
nextValueSeed<T, V extends Deserialize>(_seed: V): IteratorResult<T> {
|
||||
return this.nextValue()
|
||||
}
|
||||
|
||||
nextKey<T>(): IteratorResult<T> {
|
||||
if (this.kindex < this.keys.length) {
|
||||
return IterResult.Next(this.keys[this.kindex++]) as IteratorResult<T>
|
||||
} else {
|
||||
return IterResult.Done()
|
||||
}
|
||||
}
|
||||
|
||||
nextValue<V>(): IteratorResult<V> {
|
||||
if (this.vindex < this.values.length) {
|
||||
return IterResult.Next(this.values[this.vindex++]) as IteratorResult<V>
|
||||
} else {
|
||||
return IterResult.Done()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ForwardIterableAccess extends IterableAccess {
|
||||
private readonly items: any[]
|
||||
private index: number = 0
|
||||
|
||||
constructor(items: any[]) {
|
||||
super()
|
||||
this.items = items
|
||||
}
|
||||
|
||||
nextElement<T>(): IteratorResult<T> {
|
||||
if (this.index < this.items.length) {
|
||||
return IterResult.Next(this.items[this.index++]) as IteratorResult<T>
|
||||
} else {
|
||||
return IterResult.Done()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Forward implements IDeserializer {
|
||||
private readonly value: any
|
||||
|
||||
constructor(value: any) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
static with(value: any): Forward {
|
||||
return new this(value)
|
||||
}
|
||||
|
||||
deserializeAny<T, V extends IVisitor<T>>(_visitor: V): T {
|
||||
throw new Error("Can't forward to deserializeAny")
|
||||
}
|
||||
|
||||
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitBoolean(this.value)
|
||||
}
|
||||
|
||||
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitNumber(this.value)
|
||||
}
|
||||
|
||||
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitBigInt(this.value)
|
||||
}
|
||||
|
||||
deserializeString<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitString(this.value)
|
||||
}
|
||||
|
||||
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitSymbol(this.value)
|
||||
}
|
||||
|
||||
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitNull()
|
||||
}
|
||||
|
||||
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitObject(ForwardMapAccess.fromObject(this.value))
|
||||
}
|
||||
|
||||
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T {
|
||||
return visitor.visitIterable(new ForwardIterableAccess(this.value))
|
||||
}
|
||||
|
||||
deserializeFunction<T, V extends IVisitor<T>>(_visitor: V): T {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
export interface Deserialize {
|
||||
<T>(deserializer: IDeserializer): T
|
||||
}
|
||||
|
||||
export function deserialize<T, D extends IDeserializer>(deserializer: D, into: Constructor<T>, registry: Registry = GlobalRegistry): T {
|
||||
const de = registry.deserializers.get(into)
|
||||
|
||||
if (de == null) {
|
||||
throw new ReferenceError(`No deserializer for ${into.name}`)
|
||||
} else {
|
||||
return de(deserializer)
|
||||
}
|
||||
|
||||
return Object.assign(target, newObject)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
export * from './interface'
|
||||
export * from './generic'
|
||||
export * from './mixin'
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
import { Nullable } from '../utils'
|
||||
import { GenericSeed } from './generic'
|
||||
|
||||
export interface MapAccess {
|
||||
nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>
|
||||
nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>
|
||||
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]>
|
||||
nextKey<T>(): Nullable<T>
|
||||
nextValue<T>(): Nullable<T>
|
||||
nextEntry<K, V>(): Nullable<[K, V]>
|
||||
}
|
||||
|
||||
export abstract class DefaultMapAccessImpl implements MapAccess {
|
||||
abstract nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>
|
||||
abstract nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>
|
||||
|
||||
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]> {
|
||||
const key = this.nextKeySeed(kseed) as Nullable<TK>
|
||||
if (key !== undefined) {
|
||||
const value = this.nextValueSeed(vseed) as Nullable<TV>
|
||||
|
||||
if (value !== undefined) {
|
||||
return [key, value]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextKey<T>(): Nullable<T> {
|
||||
return this.nextValueSeed(GenericSeed<T>)
|
||||
}
|
||||
|
||||
nextValue<T>(): Nullable<T> {
|
||||
return this.nextValueSeed(GenericSeed<T>)
|
||||
}
|
||||
|
||||
nextEntry<K, V>(): Nullable<[K, V]> {
|
||||
return this.nextEntrySeed(GenericSeed<K>, GenericSeed<V>)
|
||||
}
|
||||
}
|
||||
|
||||
export interface IterableAccess {
|
||||
nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>
|
||||
nextElement<T>(): Nullable<T>
|
||||
sizeHint(): number
|
||||
}
|
||||
|
||||
export abstract class DefaultIterableAccessImpl implements IterableAccess {
|
||||
abstract nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>
|
||||
|
||||
nextElement<T>(): Nullable<T> {
|
||||
return this.nextElementSeed(GenericSeed<T>)
|
||||
}
|
||||
|
||||
sizeHint(): number { return 0 }
|
||||
}
|
||||
|
||||
export interface Visitor<T> {
|
||||
visitString(value: string): T
|
||||
visitNumber(value: number): T
|
||||
visitBigInt(value: bigint): T
|
||||
visitBoolean(value: boolean): T
|
||||
visitSymbol(value: symbol): T
|
||||
visitNull(): T
|
||||
visitObject(value: MapAccess): T
|
||||
visitClass(name: string, value: MapAccess): T
|
||||
visitIterable?(value: IterableAccess): T
|
||||
}
|
||||
|
||||
export interface Deserializer {
|
||||
deserializeAny<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeString<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeNumber<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeBigInt<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeBoolean<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeSymbol<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeNull<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeObject<T, V extends Visitor<T>>(visitor: V): T
|
||||
deserializeClass<T, V extends Visitor<T>>(name: string, visitor: V): T
|
||||
deserializeIterable<T, V extends Visitor<T>>(visitor: V): T
|
||||
}
|
||||
|
||||
export interface Deserialize<T> {
|
||||
deserialize<D extends Deserializer>(deserializer: D): T
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
import { Constructor, staticImplements } from '../utils'
|
||||
import { deserializeWith } from './impl'
|
||||
import { Deserialize, Deserializer } from './interface'
|
||||
|
||||
export function deserialize<T, C extends Constructor>(constructor: C) {
|
||||
@staticImplements<Deserialize<T>>()
|
||||
class Deserializable extends constructor {
|
||||
static name = constructor.name
|
||||
|
||||
static deserialize<D extends Deserializer>(deserializer: D): T {
|
||||
return deserializeWith(deserializer, this, (this as any)[Symbol.metadata].serde)
|
||||
}
|
||||
}
|
||||
|
||||
return Deserializable
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
import { ContainerOptions, PropertyOptions, SerdeOptions } from './options'
|
||||
import { GlobalRegistry, Registry } from './registry'
|
||||
|
||||
function decorateContainer(target: any, context: any, options: ContainerOptions) {
|
||||
const meta = context.metadata
|
||||
const serde = (meta.serde || new SerdeOptions(target))
|
||||
serde.target = target
|
||||
serde.options = options
|
||||
meta.serde = serde
|
||||
}
|
||||
|
||||
function decorateProperty(target: any, context: any, options: PropertyOptions) {
|
||||
const meta = context.metadata
|
||||
const serde = (meta.serde || new SerdeOptions(target))
|
||||
serde.properties.set(context.name, options)
|
||||
meta.serde = serde
|
||||
}
|
||||
|
||||
|
||||
export function serde(options: ContainerOptions | PropertyOptions) {
|
||||
return function(target: any, context: any) {
|
||||
if (target == null) {
|
||||
decorateProperty(target, context, options as PropertyOptions)
|
||||
} else
|
||||
decorateContainer(target, context, options as ContainerOptions)
|
||||
}
|
||||
}
|
||||
|
||||
export function register(registry: Registry = GlobalRegistry) {
|
||||
return function(target: any, _context: any) {
|
||||
registry.add(target)
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
export * as ser from './ser/index'
|
||||
export * as de from './de/index'
|
||||
export * as ser from './ser/impl'
|
||||
export * as de from './de/impl'
|
||||
export * from './case'
|
||||
export * from './registry'
|
||||
export * from './decorator'
|
||||
export * from './options'
|
||||
export * from './utils'
|
||||
|
||||
|
|
192
src/options.ts
192
src/options.ts
|
@ -1,192 +0,0 @@
|
|||
import { CaseConvention, convertCase } from './case'
|
||||
import { Deserializer } from './de'
|
||||
import { Serializer } from './ser'
|
||||
import { isFunction, isNumber, isString } from './utils'
|
||||
import { GlobalRegistry, Registry } from './registry'
|
||||
|
||||
|
||||
export interface RenameOptions {
|
||||
serialize?: string
|
||||
deserialize?: string
|
||||
}
|
||||
|
||||
export interface RenameAllOptions {
|
||||
serialize?: CaseConvention
|
||||
deserialize?: CaseConvention
|
||||
}
|
||||
|
||||
export interface ContainerOptions {
|
||||
// deserialization only
|
||||
default?: () => any
|
||||
// deserialization only
|
||||
denyUnknownFields?: boolean
|
||||
expecting?: string
|
||||
rename?: RenameOptions | string
|
||||
renameAll?: RenameAllOptions | CaseConvention
|
||||
registry?: Registry
|
||||
tag?: string
|
||||
content?: string
|
||||
untagged?: boolean
|
||||
}
|
||||
|
||||
export interface ConditionalSkipOptions {
|
||||
if: (value: any) => boolean
|
||||
}
|
||||
|
||||
export interface SkipOptions {
|
||||
serializing?: ConditionalSkipOptions | boolean
|
||||
deserializing?: ConditionalSkipOptions | boolean
|
||||
}
|
||||
|
||||
export type CustomSerializer = <T, V, S extends Serializer<T>>(value: V, serializer: S) => T
|
||||
export type CustomDeserializer = <T, D extends Deserializer>(deserializer: D) => T
|
||||
|
||||
export interface PropertyOptions {
|
||||
aliases?: Set<string>
|
||||
// deserialization only
|
||||
default?: () => any
|
||||
flatten?: boolean
|
||||
rename?: RenameOptions | string
|
||||
//serializeWith?: CustomSerializer
|
||||
//deserializeWith: CustomDeserializer
|
||||
skip?: SkipOptions | ConditionalSkipOptions | boolean
|
||||
}
|
||||
|
||||
export const Stage = Object.freeze({
|
||||
Serialize: 0,
|
||||
Deserialize: 1
|
||||
} as const)
|
||||
|
||||
export type Stage = typeof Stage[keyof typeof Stage]
|
||||
|
||||
export class SerdeOptions {
|
||||
target: any
|
||||
readonly options: ContainerOptions
|
||||
readonly properties: Map<string, PropertyOptions>
|
||||
|
||||
get registry() {
|
||||
return this.options.registry || GlobalRegistry
|
||||
}
|
||||
|
||||
constructor(options: ContainerOptions = {}, properties: Map<string, PropertyOptions> = new Map()) {
|
||||
this.options = options
|
||||
this.properties = properties
|
||||
}
|
||||
|
||||
static from(target: any) {
|
||||
return new this(target)
|
||||
}
|
||||
|
||||
getClassName(stage: Stage) {
|
||||
if (isString(this.options.rename)) {
|
||||
return this.options.rename
|
||||
} else if (stage === Stage.Serialize && isString(this.options.rename?.serialize)) {
|
||||
return this.options.rename.serialize
|
||||
} else if (stage === Stage.Deserialize && isString(this.options.rename?.deserialize)) {
|
||||
return this.options.rename.deserialize
|
||||
} else {
|
||||
return this.target.constructor.name
|
||||
}
|
||||
}
|
||||
|
||||
private getPropertyRename(property: string, stage: Stage, options: PropertyOptions) {
|
||||
if (options != null) {
|
||||
if (isString(options.rename)) {
|
||||
return options.rename
|
||||
} else if (stage === Stage.Serialize && isString(options.rename?.serialize)) {
|
||||
return options.rename.serialize
|
||||
} else if (stage === Stage.Deserialize && isString(options.rename?.deserialize)) {
|
||||
return options.rename.deserialize
|
||||
}
|
||||
}
|
||||
|
||||
return property
|
||||
}
|
||||
|
||||
private getPropertyCase(name: string, stage: Stage) {
|
||||
if (isNumber(this.options.renameAll)) {
|
||||
return convertCase(name, this.options.renameAll)
|
||||
} else if (stage === Stage.Serialize && isNumber(this.options.renameAll?.serialize)) {
|
||||
return convertCase(name, this.options.renameAll.serialize)
|
||||
} else if (stage === Stage.Deserialize && isNumber(this.options.renameAll?.deserialize)) {
|
||||
return convertCase(name, this.options.renameAll.deserialize)
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
getPropertyName(property: string, stage: Stage) {
|
||||
const options = this.properties.get(property)
|
||||
const name = options != null ? this.getPropertyRename(property, stage, options) : property
|
||||
return this.getPropertyCase(name, stage)
|
||||
}
|
||||
|
||||
getSerializationName(property: string) {
|
||||
return this.getPropertyName(property, Stage.Serialize)
|
||||
}
|
||||
|
||||
getDeserializationName(property: string) {
|
||||
return this.getPropertyName(property, Stage.Deserialize)
|
||||
}
|
||||
|
||||
getDefault(property: string) {
|
||||
const options = this.properties.get(property)
|
||||
if (options && isFunction(options.default)) {
|
||||
return options.default()
|
||||
} else if (isFunction(this.options.default)) {
|
||||
return this.options.default()
|
||||
}
|
||||
}
|
||||
|
||||
//getCustomImpl(property: string, stage: Stage) {
|
||||
// const options = this.properties.get(property)
|
||||
// if (options != null) {
|
||||
// if (stage === Stage.Serialize && isFunction(options.serializeWith)) {
|
||||
// return options.serializeWith
|
||||
// } else if (stage === Stage.Deserialize && isFunction(options.deserializeWith)) {
|
||||
// return options.deserializeWith
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
//getSerializer(property: string): Nullable<CustomSerializer> {
|
||||
// return this.getCustomImpl(property, Stage.Serialize) as CustomSerializer
|
||||
//}
|
||||
|
||||
//getDeserializer(property: string): Nullable<CustomDeserializer> {
|
||||
// return this.getCustomImpl(property, Stage.Deserialize) as CustomDeserializer
|
||||
//}
|
||||
|
||||
private isConditionalSkip(skip: any): skip is ConditionalSkipOptions {
|
||||
return 'if' in skip && isFunction(skip.if)
|
||||
}
|
||||
|
||||
shouldSkip(property: string, value: any, stage: Stage): boolean {
|
||||
const options = this.properties.get(property)
|
||||
if (options != null && options.skip != null) {
|
||||
if (typeof options.skip === 'boolean') {
|
||||
return options.skip
|
||||
} else if (this.isConditionalSkip(options.skip)) {
|
||||
return options.skip.if(value)
|
||||
} else if (stage === Stage.Serialize && typeof options.skip.serializing === 'boolean') {
|
||||
return options.skip.serializing
|
||||
} else if (stage === Stage.Serialize && this.isConditionalSkip(options.skip.serializing)) {
|
||||
return options.skip.serializing.if(value)
|
||||
} else if (stage === Stage.Deserialize && typeof options.skip.deserializing === 'boolean') {
|
||||
return options.skip.deserializing
|
||||
} else if (stage === Stage.Deserialize && this.isConditionalSkip(options.skip.deserializing)) {
|
||||
return options.skip.deserializing.if(value)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
shouldSkipSerializing(property: string, value: any): boolean {
|
||||
return this.shouldSkip(property, value, Stage.Serialize)
|
||||
}
|
||||
|
||||
shouldSkipDeserializing(property: string, value: any): boolean {
|
||||
return this.shouldSkip(property, value, Stage.Deserialize)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,20 @@
|
|||
import { Constructor } from './utils'
|
||||
import { Deserialize } from './de/impl'
|
||||
import { Serialize } from './ser/impl'
|
||||
|
||||
export class Registry {
|
||||
registeredClasses: Map<string, Constructor> = new Map()
|
||||
serializers: Map<Function, Serialize<any>> = new Map()
|
||||
deserializers: Map<Function, Deserialize> = new Map()
|
||||
|
||||
add(ctor: Constructor, key: string = ctor.name) {
|
||||
this.registeredClasses.set(key, ctor)
|
||||
registerSerializer<T>(ctor: Function, serialize: Serialize<T>) {
|
||||
this.serializers.set(ctor, serialize)
|
||||
}
|
||||
|
||||
get(name: string) {
|
||||
return this.registeredClasses.get(name)
|
||||
registerDeserializer(ctor: Function, deserialize: Deserialize) {
|
||||
this.deserializers.set(ctor, deserialize)
|
||||
}
|
||||
}
|
||||
|
||||
export const GlobalRegistry = new Registry()
|
||||
export const registerSerializer = GlobalRegistry.registerSerializer.bind(GlobalRegistry)
|
||||
export const registerDeserializer = GlobalRegistry.registerDeserializer.bind(GlobalRegistry)
|
||||
|
||||
|
|
172
src/ser/impl.ts
172
src/ser/impl.ts
|
@ -1,80 +1,122 @@
|
|||
import { IterableSerializer, ObjectSerializer, Serializable, Serializer } from './interface'
|
||||
import { SerdeOptions, Stage } from '../options'
|
||||
import { ifNull, isFunction, isIterable, isPlainObject, Nullable, orElse } from '../utils'
|
||||
export interface ISerializeObject<T> {
|
||||
serializeKey(key: string): void
|
||||
serializeValue<U>(value: U): void
|
||||
serializeEntry<U>(key: string, value: U): void
|
||||
end(): T
|
||||
}
|
||||
|
||||
const unhandledType = (serializer: any, value: any) => new TypeError(`'${serializer.constructor.name}' has no method for value type '${typeof value}'`)
|
||||
export abstract class SerializeObject<T> implements ISerializeObject<T> {
|
||||
abstract serializeKey(key: string): void
|
||||
abstract serializeValue<U>(value: U): void
|
||||
abstract end(): T
|
||||
|
||||
function serializeEntries<T, K extends string, V extends Serializable, E extends Iterable<[K, V]>>(serializer: ObjectSerializer<T>, value: E, options?: SerdeOptions) {
|
||||
let state
|
||||
serializeEntry<U>(key: string, value: U): void {
|
||||
this.serializeKey(key)
|
||||
this.serializeValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, val] of value) {
|
||||
if (options?.shouldSkipSerializing(key, val)) {
|
||||
continue
|
||||
}
|
||||
export interface ISerializeIterable<T> {
|
||||
serializeElement<U>(value: U): void
|
||||
end(): T
|
||||
}
|
||||
|
||||
const name = options?.getPropertyName(key as string, Stage.Serialize) ?? key
|
||||
state = serializer.serializeKey(name)
|
||||
state = serializer.serializeValue(val)
|
||||
export abstract class SerializeIterable<T> implements ISerializeIterable<T> {
|
||||
abstract serializeElement<U>(value: U): void
|
||||
abstract end(): T
|
||||
}
|
||||
|
||||
export interface ISerializer<T> {
|
||||
serializeAny(value: any): T
|
||||
serializeBoolean(value: boolean): T
|
||||
serializeNumber(value: number): T
|
||||
serializeBigInt(value: bigint): T
|
||||
serializeString(value: string): T
|
||||
serializeSymbol(value: symbol): T
|
||||
serializeNull(): T
|
||||
serializeObject(): ISerializeObject<T>
|
||||
serializeClass(name: string): ISerializeObject<T>
|
||||
}
|
||||
|
||||
export class Serializer<T> implements ISerializer<T> {
|
||||
serializeAny(_value: any): T {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
serializeBoolean(_value: boolean): T {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
serializeNumber(_value: number): T {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
serializeBigInt(_value: bigint): T {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
serializeString(_value: string): T {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
serializeSymbol(_value: symbol): T {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
serializeNull(): T {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
serializeObject(): ISerializeObject<T> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
serializeClass(_name: string): ISerializeObject<T> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
||||
|
||||
export interface Serialize<T> {
|
||||
<U, S extends Serializer<U>>(serializer: S, value: T): U
|
||||
}
|
||||
|
||||
const isPlainObject = (value: any): value is object => value?.constructor === Object
|
||||
|
||||
class UnhandledTypeError extends TypeError {
|
||||
constructor(serializer: Serializer<unknown>, value: any) {
|
||||
super(`unhandled type: "${typeof value}" for serializer ${serializer.constructor.name}`)
|
||||
}
|
||||
}
|
||||
|
||||
function serializeObject<T, V extends object, S extends ISerializeObject<T>>(serializer: S, obj: V): T {
|
||||
for (const key in obj) {
|
||||
serializer.serializeEntry(key, obj[key])
|
||||
}
|
||||
|
||||
return serializer.end()
|
||||
}
|
||||
|
||||
function serializeClass<T, K extends string, V extends Serializable, R extends Record<K, V>>(serializer: Serializer<T>, value: R, options?: SerdeOptions) {
|
||||
const ser = serializer.serializeClass!(value.constructor.name)
|
||||
return serializeEntries(ser, Object.entries(value) as Iterable<[K, V]>, options)
|
||||
function serializeClass<T, V extends object, S extends Serializer<T>>(serializer: S, value: V): T {
|
||||
const name = value.constructor.name
|
||||
const ser = serializer.serializeClass(name)
|
||||
return serializeObject(ser, value)
|
||||
}
|
||||
|
||||
|
||||
function serializeObject<T, K extends string, V extends Serializable, R extends Record<K, V>>(serializer: Serializer<T>, value: R, options?: SerdeOptions) {
|
||||
const ser = serializer.serializeObject!()
|
||||
return serializeEntries(ser, Object.entries(value) as Iterable<[K, V]>, options)
|
||||
}
|
||||
|
||||
function serializeIter<T, V extends Iterable<any>>(serializer: IterableSerializer<T>, value: V, options?: SerdeOptions) {
|
||||
let state
|
||||
|
||||
for (const val of value) {
|
||||
state = serializer.serializeElement(val)
|
||||
}
|
||||
|
||||
return serializer.end()
|
||||
}
|
||||
|
||||
function defaultOptions(value: any) {
|
||||
return value.constructor[Symbol.metadata].serde
|
||||
}
|
||||
|
||||
// dispatches in the order of serializeType -> serializeAny -> throw TypeError
|
||||
export function serializeWith<T>(serializer: Serializer<T>, value: Serializable, optionsGetter: (value: any) => Nullable<SerdeOptions> = defaultOptions): Nullable<T> {
|
||||
// prepare fallback methods
|
||||
const serializeAny = orElse(
|
||||
serializer,
|
||||
serializer.serializeAny,
|
||||
(value: Serializable) => unhandledType(serializer, value)
|
||||
)
|
||||
|
||||
const serialize = ifNull(serializer, serializeAny, value)
|
||||
|
||||
export function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V): T {
|
||||
switch (typeof value) {
|
||||
case 'string': return serialize(serializer.serializeString)
|
||||
case 'number': return serialize(serializer.serializeNumber)
|
||||
case 'bigint': return serialize(serializer.serializeBigInt)
|
||||
case 'boolean': return serialize(serializer.serializeBoolean)
|
||||
case 'symbol': return serialize(serializer.serializeSymbol)
|
||||
case 'undefined': return serialize(serializer.serializeNull)
|
||||
|
||||
case 'object':
|
||||
const options = optionsGetter(value)
|
||||
if (isIterable(value) && isFunction(serializer.serializeIterable)) {
|
||||
return serializeIter(serializer.serializeIterable(), value, options)
|
||||
} else if (!isPlainObject(value)) {
|
||||
return serializeClass(serializer, value as Record<PropertyKey, any>, options)
|
||||
} else if (isFunction(serializer.serializeObject)) {
|
||||
return serializeObject(serializer, value as Record<PropertyKey, any>, options)
|
||||
} // deliberate fallthrough when the above fail
|
||||
|
||||
default: return serializeAny(value)
|
||||
case "string": return serializer.serializeString(value)
|
||||
case "number": return serializer.serializeNumber(value)
|
||||
case "bigint": return serializer.serializeBigInt(value)
|
||||
case "boolean": return serializer.serializeBoolean(value)
|
||||
case "symbol": return serializer.serializeSymbol(value)
|
||||
case "undefined": return serializer.serializeNull()
|
||||
case "object":
|
||||
switch (true) {
|
||||
case value == null: return serializer.serializeNull()
|
||||
case !isPlainObject(value): return serializeClass(serializer, value)
|
||||
default: return serializeObject(serializer.serializeObject(), value)
|
||||
}
|
||||
default: throw new UnhandledTypeError(serializer, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import '@tsmetadata/polyfill'
|
||||
|
||||
export * from './interface'
|
||||
export * from './mixin'
|
||||
export * from './impl'
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import { isFunction, Primitive, ToString } from "../utils"
|
||||
|
||||
export interface ObjectSerializer<T = void> {
|
||||
serializeKey<U extends Serializable>(key: U): T
|
||||
serializeValue<U extends Serializable>(value: U): T
|
||||
end(): T
|
||||
}
|
||||
|
||||
export interface IterableSerializer<T = void> {
|
||||
serializeElement<U extends Serializable>(element: U): T
|
||||
end(): T
|
||||
}
|
||||
|
||||
const TypeSerializerMethods = [
|
||||
'serializeString',
|
||||
'serializeNumber',
|
||||
'serializeBigInt',
|
||||
'serializeBoolean',
|
||||
'serializeSymbol',
|
||||
//'serializeMap',
|
||||
'serializeIterable',
|
||||
'serializeNull',
|
||||
'serializeObject',
|
||||
'serializeClass',
|
||||
] as const
|
||||
|
||||
interface TypeSerializer<T> {
|
||||
serializeString(value: string): T
|
||||
serializeNumber(value: number): T
|
||||
serializeBigInt(value: bigint): T
|
||||
serializeBoolean(value: boolean): T
|
||||
serializeSymbol(value: Symbol): T
|
||||
serializeNull(): T
|
||||
serializeObject(): ObjectSerializer<T>
|
||||
// serializeMap?(): ObjectSerializer<T>
|
||||
serializeIterable?(): IterableSerializer<T>
|
||||
serializeClass(name: PropertyKey): ObjectSerializer<T>
|
||||
}
|
||||
|
||||
const AnySerializerMethods = ['serializeAny']
|
||||
|
||||
interface AnySerializer<T> {
|
||||
serializeAny?(value?: any): T
|
||||
}
|
||||
|
||||
export function isGenericSerializer(value: any): boolean {
|
||||
return AnySerializerMethods.every(k => isFunction(value[k])) &&
|
||||
TypeSerializerMethods.every(k => !isFunction(value[k]))
|
||||
}
|
||||
|
||||
export type Serializer<T> = Partial<TypeSerializer<T>> & Partial<AnySerializer<T>>
|
||||
|
||||
export type Serializable = Primitive | ToString | Serialize
|
||||
|
||||
export interface Serialize {
|
||||
serialize<T, S extends Serializer<T>>(serializer: S): T
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
import { Constructor } from '../utils'
|
||||
import { serializeWith } from './impl'
|
||||
import { isGenericSerializer, Serializer } from './interface'
|
||||
|
||||
export function serialize<T extends Constructor>(constructor: T) {
|
||||
class Serializable extends constructor implements Serializable {
|
||||
static name = constructor.name
|
||||
serialize<U>(serializer: Serializer<U>): U {
|
||||
// shortcut for serializers with only the serializeAny method
|
||||
if (isGenericSerializer(serializer)) {
|
||||
return serializer.serializeAny!(this) as U
|
||||
} else {
|
||||
return serializeWith(serializer, this) as U
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Serializable
|
||||
}
|
58
src/utils.ts
58
src/utils.ts
|
@ -36,61 +36,3 @@ export function isNumber(value: any): value is number {
|
|||
|
||||
export type Constructor<T = any> = new (...args: any[]) => T
|
||||
|
||||
export function orElse(thisArg: any, a: Nullable<Function>, b: Function) {
|
||||
return function(...args: any) {
|
||||
const fn = a != null ? a : b
|
||||
return fn.apply(thisArg, args)
|
||||
}
|
||||
}
|
||||
|
||||
export function ifNull(thisArg: any, b: Function, ...args: any) {
|
||||
return function(a: Nullable<Function>) {
|
||||
return orElse(thisArg, a, b).apply(thisArg, args)
|
||||
}
|
||||
}
|
||||
|
||||
function applyMixins(derivedCtor: any, constructors: any[]) {
|
||||
constructors.forEach((baseCtor) => {
|
||||
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
|
||||
Object.defineProperty(
|
||||
derivedCtor.prototype,
|
||||
name,
|
||||
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
|
||||
Object.create(null)
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function mixin<U = any>(impl: Function) {
|
||||
return function <TBase extends Constructor<U>>(constructor: TBase) {
|
||||
applyMixins(constructor, [impl])
|
||||
return constructor
|
||||
}
|
||||
}
|
||||
|
||||
type AnyFunc = (...arg: any) => any
|
||||
|
||||
type PipeArgs<F extends AnyFunc[], Acc extends AnyFunc[] = []> = F extends [
|
||||
(...args: infer A) => infer B
|
||||
]
|
||||
? [...Acc, (...args: A) => B]
|
||||
: F extends [(...args: infer A) => any, ...infer Tail]
|
||||
? Tail extends [(arg: infer B) => any, ...any[]]
|
||||
? PipeArgs<Tail, [...Acc, (...args: A) => B]>
|
||||
: Acc
|
||||
: Acc
|
||||
|
||||
type LastFnReturnType<F extends Array<AnyFunc>, Else = never> = F extends [
|
||||
...any[],
|
||||
(...arg: any) => infer R
|
||||
] ? R : Else
|
||||
|
||||
export function pipe<FirstFn extends AnyFunc, F extends AnyFunc[]>(
|
||||
arg: Parameters<FirstFn>[0],
|
||||
firstFn: FirstFn,
|
||||
...fns: PipeArgs<F> extends F ? F : PipeArgs<F>
|
||||
): LastFnReturnType<F, ReturnType<FirstFn>> {
|
||||
return (fns as AnyFunc[]).reduce((acc, fn) => fn(acc), firstFn(arg))
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue