Compare commits
3 commits
d749044351
...
a163468b7f
Author | SHA1 | Date | |
---|---|---|---|
a163468b7f | |||
5ef148193e | |||
69e56d2d74 |
43 changed files with 880 additions and 1445 deletions
15
dist/de/generic.d.ts
vendored
15
dist/de/generic.d.ts
vendored
|
@ -1,15 +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;
|
|
||||||
visitIterable(access: IterableAccess): T;
|
|
||||||
}
|
|
103
dist/de/generic.js
vendored
103
dist/de/generic.js
vendored
|
@ -1,103 +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;
|
|
||||||
}
|
|
||||||
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 { Registry } from '../registry';
|
||||||
import { Deserialize, Deserializer } from './interface';
|
import { Constructor } from '../utils';
|
||||||
type DeserializeConstructor<T> = Deserialize<T> & {
|
type Nullable<T> = T | undefined;
|
||||||
new (): Deserialize<T>;
|
export declare class IterResult {
|
||||||
};
|
static Next<T>(value: T): IteratorResult<T>;
|
||||||
export declare function deserializeWith<T, D extends Deserializer, E extends DeserializeConstructor<T>>(deserializer: D, into: E, options: SerdeOptions): 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 {};
|
export {};
|
||||||
|
|
244
dist/de/impl.js
vendored
244
dist/de/impl.js
vendored
|
@ -1,21 +1,233 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.deserializeWith = deserializeWith;
|
exports.Forward = exports.ForwardIterableAccess = exports.ForwardMapAccess = exports.GenericVisitor = exports.GenericSeed = exports.IterableAccess = exports.MapAccess = exports.IterResult = void 0;
|
||||||
const options_1 = require("../options");
|
exports.deserialize = deserialize;
|
||||||
const generic_1 = require("./generic");
|
const registry_1 = require("../registry");
|
||||||
function deserializeWith(deserializer, into, options) {
|
class IterResult {
|
||||||
const visitor = new generic_1.GenericVisitor();
|
static Next(value) {
|
||||||
const obj = deserializer.deserializeObject(visitor);
|
return { done: false, value };
|
||||||
const target = new into();
|
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
if (options.options.denyUnknownFields && Object.keys(obj).length > 0) {
|
static Done() {
|
||||||
throw new TypeError(`Unexpected fields: ${Object.keys(obj).join(', ')}`);
|
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);
|
|
52
dist/de/interface.d.ts
vendored
52
dist/de/interface.d.ts
vendored
|
@ -1,52 +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;
|
|
||||||
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, fields: 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, constructor[Symbol.metadata]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
__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.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, options);
|
|
||||||
meta.serde = serde;
|
|
||||||
}
|
|
||||||
function serde(options) {
|
|
||||||
return function (target, context) {
|
|
||||||
if (context != 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 ser from './ser/impl';
|
||||||
export * as de from './de/index';
|
export * as de from './de/impl';
|
||||||
export * from './case';
|
export * from './case';
|
||||||
export * from './registry';
|
export * from './registry';
|
||||||
export * from './decorator';
|
export * from './utils';
|
||||||
export * from './options';
|
|
||||||
|
|
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 });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.de = exports.ser = void 0;
|
exports.de = exports.ser = void 0;
|
||||||
exports.ser = __importStar(require("./ser/index"));
|
exports.ser = __importStar(require("./ser/impl"));
|
||||||
exports.de = __importStar(require("./de/index"));
|
exports.de = __importStar(require("./de/impl"));
|
||||||
__exportStar(require("./case"), exports);
|
__exportStar(require("./case"), exports);
|
||||||
__exportStar(require("./registry"), exports);
|
__exportStar(require("./registry"), exports);
|
||||||
__exportStar(require("./decorator"), exports);
|
__exportStar(require("./utils"), exports);
|
||||||
__exportStar(require("./options"), 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 {
|
|
||||||
alias?: 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 {
|
|
||||||
private readonly target;
|
|
||||||
readonly options: ContainerOptions;
|
|
||||||
readonly properties: Map<string, PropertyOptions>;
|
|
||||||
get registry(): Registry;
|
|
||||||
constructor(target: any, 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;
|
|
||||||
}
|
|
156
dist/options.js
vendored
156
dist/options.js
vendored
|
@ -1,156 +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(target, 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.target = target;
|
|
||||||
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 {
|
export declare class Registry {
|
||||||
registeredClasses: Map<string, Constructor>;
|
serializers: Map<Function, Serialize<any>>;
|
||||||
add(ctor: Constructor, key?: string): void;
|
deserializers: Map<Function, Deserialize>;
|
||||||
get(name: string): Constructor | undefined;
|
registerSerializer<T>(ctor: Function, serialize: Serialize<T>): void;
|
||||||
|
registerDeserializer(ctor: Function, deserialize: Deserialize): void;
|
||||||
}
|
}
|
||||||
export declare const GlobalRegistry: Registry;
|
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";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.GlobalRegistry = exports.Registry = void 0;
|
exports.registerDeserializer = exports.registerSerializer = exports.GlobalRegistry = exports.Registry = void 0;
|
||||||
class Registry {
|
class Registry {
|
||||||
constructor() {
|
constructor() {
|
||||||
Object.defineProperty(this, "registeredClasses", {
|
Object.defineProperty(this, "serializers", {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
value: new Map()
|
||||||
|
});
|
||||||
|
Object.defineProperty(this, "deserializers", {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
writable: true,
|
writable: true,
|
||||||
value: new Map()
|
value: new Map()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
add(ctor, key = ctor.name) {
|
registerSerializer(ctor, serialize) {
|
||||||
this.registeredClasses.set(key, ctor);
|
this.serializers.set(ctor, serialize);
|
||||||
}
|
}
|
||||||
get(name) {
|
registerDeserializer(ctor, deserialize) {
|
||||||
return this.registeredClasses.get(name);
|
this.deserializers.set(ctor, deserialize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.Registry = Registry;
|
exports.Registry = Registry;
|
||||||
exports.GlobalRegistry = new 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';
|
export interface ISerializeObject<T> {
|
||||||
import { SerdeOptions } from '../options';
|
serializeKey(key: string): void;
|
||||||
import { Nullable } from '../utils';
|
serializeValue<U>(value: U): void;
|
||||||
export declare function serializeWith<T>(serializer: Serializer<T>, value: Serializable, optionsGetter?: (value: any) => Nullable<SerdeOptions>): Nullable<T>;
|
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;
|
||||||
|
|
118
dist/ser/impl.js
vendored
118
dist/ser/impl.js
vendored
|
@ -1,62 +1,78 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.serializeWith = serializeWith;
|
exports.Serializer = exports.SerializeIterable = exports.SerializeObject = void 0;
|
||||||
const options_1 = require("../options");
|
exports.serialize = serialize;
|
||||||
const utils_1 = require("../utils");
|
class SerializeObject {
|
||||||
const unhandledType = (serializer, value) => new TypeError(`'${serializer.constructor.name}' has no method for value type '${typeof value}'`);
|
serializeEntry(key, value) {
|
||||||
function serializeEntries(serializer, value, options) {
|
this.serializeKey(key);
|
||||||
var _a;
|
this.serializeValue(value);
|
||||||
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);
|
exports.SerializeObject = SerializeObject;
|
||||||
state = serializer.serializeValue(val);
|
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();
|
return serializer.end();
|
||||||
}
|
}
|
||||||
function serializeClass(serializer, value, options) {
|
function serializeClass(serializer, value) {
|
||||||
const classSerializer = serializer.serializeClass(value.constructor.name);
|
const name = value.constructor.name;
|
||||||
return serializeEntries(classSerializer, Object.entries(value), options);
|
const ser = serializer.serializeClass(name);
|
||||||
|
return serializeObject(ser, value);
|
||||||
}
|
}
|
||||||
function serializeObject(serializer, value, options) {
|
function serialize(serializer, value) {
|
||||||
return serializeEntries(serializer.serializeObject(), 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];
|
|
||||||
}
|
|
||||||
// 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);
|
|
||||||
switch (typeof value) {
|
switch (typeof value) {
|
||||||
case 'string': return serialize(serializer.serializeString);
|
case "string": return serializer.serializeString(value);
|
||||||
case 'number': return serialize(serializer.serializeNumber);
|
case "number": return serializer.serializeNumber(value);
|
||||||
case 'bigint': return serialize(serializer.serializeBigInt);
|
case "bigint": return serializer.serializeBigInt(value);
|
||||||
case 'boolean': return serialize(serializer.serializeBoolean);
|
case "boolean": return serializer.serializeBoolean(value);
|
||||||
case 'symbol': return serialize(serializer.serializeSymbol);
|
case "symbol": return serializer.serializeSymbol(value);
|
||||||
case 'undefined': return serialize(serializer.serializeNull);
|
case "undefined": return serializer.serializeNull();
|
||||||
case 'object':
|
case "object":
|
||||||
const options = optionsGetter(value);
|
switch (true) {
|
||||||
if ((0, utils_1.isIterable)(value) && (0, utils_1.isFunction)(serializer.serializeIterable)) {
|
case value == null: return serializer.serializeNull();
|
||||||
return serializeIter(serializer.serializeIterable(), value, options);
|
case !isPlainObject(value): return serializeClass(serializer, value);
|
||||||
|
default: return serializeObject(serializer.serializeObject(), value);
|
||||||
}
|
}
|
||||||
if (!(0, utils_1.isPlainObject)(value)) {
|
default: throw new UnhandledTypeError(serializer, 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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 isString(value: any): value is string;
|
||||||
export declare function isNumber(value: any): value is number;
|
export declare function isNumber(value: any): value is number;
|
||||||
export type Constructor<T = any> = new (...args: any[]) => T;
|
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.isIterable = isIterable;
|
||||||
exports.isString = isString;
|
exports.isString = isString;
|
||||||
exports.isNumber = isNumber;
|
exports.isNumber = isNumber;
|
||||||
exports.orElse = orElse;
|
|
||||||
exports.ifNull = ifNull;
|
|
||||||
exports.mixin = mixin;
|
|
||||||
exports.pipe = pipe;
|
|
||||||
function staticImplements() {
|
function staticImplements() {
|
||||||
return (constructor) => { constructor; };
|
return (constructor) => { constructor; };
|
||||||
}
|
}
|
||||||
|
@ -28,31 +24,3 @@ function isString(value) {
|
||||||
function isNumber(value) {
|
function isNumber(value) {
|
||||||
return !isNaN(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"
|
"default": "./dist/index.js"
|
||||||
},
|
},
|
||||||
"./ser": {
|
"./ser": {
|
||||||
"types": "./dist/ser/index.d.ts",
|
"types": "./dist/ser/impl.d.ts",
|
||||||
"default": "./dist/ser/index.js"
|
"default": "./dist/ser/impl.js"
|
||||||
},
|
},
|
||||||
"./de": {
|
"./de": {
|
||||||
"types": "./dist/de/index.d.ts",
|
"types": "./dist/de/impl.d.ts",
|
||||||
"default": "./dist/de/index.js"
|
"default": "./dist/de/impl.js"
|
||||||
},
|
},
|
||||||
"./utils": {
|
"./utils": {
|
||||||
"types": "./dist/utils.d.ts",
|
"types": "./dist/utils.d.ts",
|
||||||
|
|
|
@ -1,62 +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
|
|
||||||
}
|
|
||||||
|
|
||||||
visitIterable(access: IterableAccess): T {
|
|
||||||
const result = new Array(access.sizeHint())
|
|
||||||
let element
|
|
||||||
|
|
||||||
while ((element = access.nextElement())) {
|
|
||||||
result.push(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result as T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
299
src/de/impl.ts
299
src/de/impl.ts
|
@ -1,26 +1,287 @@
|
||||||
import { SerdeOptions, Stage } from '../options'
|
import { GlobalRegistry, Registry } from '../registry'
|
||||||
import { GenericVisitor } from './generic'
|
import { Constructor } from '../utils'
|
||||||
import { Deserialize, Deserializer } from './interface'
|
|
||||||
|
|
||||||
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 {
|
export class IterResult {
|
||||||
const visitor = new GenericVisitor<T>()
|
static Next<T>(value: T): IteratorResult<T> {
|
||||||
const obj = deserializer.deserializeObject(visitor) as any
|
return { done: false, value }
|
||||||
const target = new into()
|
|
||||||
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]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.options.denyUnknownFields && Object.keys(obj).length > 0) {
|
static Done<T>(): IteratorResult<T> {
|
||||||
throw new TypeError(`Unexpected fields: ${Object.keys(obj).join(', ')}`)
|
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,83 +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
|
|
||||||
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, fields: 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, (constructor as any)[Symbol.metadata])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.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, options)
|
|
||||||
meta.serde = serde
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export function serde(options: ContainerOptions | PropertyOptions) {
|
|
||||||
return function(target: any, context: any) {
|
|
||||||
if (context != 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 ser from './ser/impl'
|
||||||
export * as de from './de/index'
|
export * as de from './de/impl'
|
||||||
export * from './case'
|
export * from './case'
|
||||||
export * from './registry'
|
export * from './registry'
|
||||||
export * from './decorator'
|
export * from './utils'
|
||||||
export * from './options'
|
|
||||||
|
|
||||||
|
|
193
src/options.ts
193
src/options.ts
|
@ -1,193 +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 {
|
|
||||||
alias?: 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 {
|
|
||||||
private readonly target: any
|
|
||||||
readonly options: ContainerOptions
|
|
||||||
readonly properties: Map<string, PropertyOptions>
|
|
||||||
|
|
||||||
get registry() {
|
|
||||||
return this.options.registry || GlobalRegistry
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(target: any, options: ContainerOptions = {}, properties: Map<string, PropertyOptions> = new Map()) {
|
|
||||||
this.target = target
|
|
||||||
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 {
|
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) {
|
registerSerializer<T>(ctor: Function, serialize: Serialize<T>) {
|
||||||
this.registeredClasses.set(key, ctor)
|
this.serializers.set(ctor, serialize)
|
||||||
}
|
}
|
||||||
|
|
||||||
get(name: string) {
|
registerDeserializer(ctor: Function, deserialize: Deserialize) {
|
||||||
return this.registeredClasses.get(name)
|
this.deserializers.set(ctor, deserialize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GlobalRegistry = new Registry()
|
export const GlobalRegistry = new Registry()
|
||||||
|
export const registerSerializer = GlobalRegistry.registerSerializer.bind(GlobalRegistry)
|
||||||
|
export const registerDeserializer = GlobalRegistry.registerDeserializer.bind(GlobalRegistry)
|
||||||
|
|
||||||
|
|
168
src/ser/impl.ts
168
src/ser/impl.ts
|
@ -1,78 +1,122 @@
|
||||||
import { IterableSerializer, ObjectSerializer, Serializable, Serializer } from './interface'
|
export interface ISerializeObject<T> {
|
||||||
import { SerdeOptions, Stage } from '../options'
|
serializeKey(key: string): void
|
||||||
import { ifNull, isFunction, isIterable, isPlainObject, Nullable, orElse } from '../utils'
|
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) {
|
serializeEntry<U>(key: string, value: U): void {
|
||||||
let state
|
this.serializeKey(key)
|
||||||
|
this.serializeValue(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const [key, val] of value) {
|
export interface ISerializeIterable<T> {
|
||||||
if (options?.shouldSkipSerializing(key, val)) {
|
serializeElement<U>(value: U): void
|
||||||
continue
|
end(): T
|
||||||
|
}
|
||||||
|
|
||||||
|
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.")
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = options?.getPropertyName(key as string, Stage.Serialize) ?? key
|
serializeBoolean(_value: boolean): T {
|
||||||
state = serializer.serializeKey(name)
|
throw new Error('Method not implemented.')
|
||||||
state = serializer.serializeValue(val)
|
}
|
||||||
|
|
||||||
|
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()
|
return serializer.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeClass<T, K extends string, V extends Serializable, R extends Record<K, V>>(serializer: Serializer<T>, value: R, options?: SerdeOptions) {
|
function serializeClass<T, V extends object, S extends Serializer<T>>(serializer: S, value: V): T {
|
||||||
const classSerializer = serializer.serializeClass!(value.constructor.name)
|
const name = value.constructor.name
|
||||||
return serializeEntries(classSerializer, Object.entries(value) as Iterable<[K, V]>, options)
|
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) {
|
export function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V): T {
|
||||||
return serializeEntries(serializer.serializeObject!(), 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]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
switch (typeof value) {
|
switch (typeof value) {
|
||||||
case 'string': return serialize(serializer.serializeString)
|
case "string": return serializer.serializeString(value)
|
||||||
case 'number': return serialize(serializer.serializeNumber)
|
case "number": return serializer.serializeNumber(value)
|
||||||
case 'bigint': return serialize(serializer.serializeBigInt)
|
case "bigint": return serializer.serializeBigInt(value)
|
||||||
case 'boolean': return serialize(serializer.serializeBoolean)
|
case "boolean": return serializer.serializeBoolean(value)
|
||||||
case 'symbol': return serialize(serializer.serializeSymbol)
|
case "symbol": return serializer.serializeSymbol(value)
|
||||||
case 'undefined': return serialize(serializer.serializeNull)
|
case "undefined": return serializer.serializeNull()
|
||||||
|
case "object":
|
||||||
case 'object':
|
switch (true) {
|
||||||
const options = optionsGetter(value)
|
case value == null: return serializer.serializeNull()
|
||||||
if (isIterable(value) && isFunction(serializer.serializeIterable)) {
|
case !isPlainObject(value): return serializeClass(serializer, value)
|
||||||
return serializeIter(serializer.serializeIterable(), value, options)
|
default: return serializeObject(serializer.serializeObject(), value)
|
||||||
} if (!isPlainObject(value)) {
|
}
|
||||||
return serializeClass(serializer, value as Record<PropertyKey, any>, options)
|
default: throw new UnhandledTypeError(serializer, value)
|
||||||
} else if (isFunction(serializer.serializeObject)) {
|
|
||||||
return serializeObject(serializer, value as Record<PropertyKey, any>, options)
|
|
||||||
} // deliberate fallthrough when the above fail
|
|
||||||
|
|
||||||
default: return serializeAny(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 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