you know what fuck all the options
This commit is contained in:
parent
ac02e19606
commit
62e9563b8a
18 changed files with 139 additions and 706 deletions
22
dist/de/generic.d.ts
vendored
22
dist/de/generic.d.ts
vendored
|
@ -1,4 +1,3 @@
|
||||||
import { SerdeOptions } from '../option';
|
|
||||||
import { IDeserializer, IIterableAccess, IMapAccess, IVisitor } from './interface';
|
import { IDeserializer, IIterableAccess, IMapAccess, IVisitor } from './interface';
|
||||||
export declare class GenericSeed<T> {
|
export declare class GenericSeed<T> {
|
||||||
readonly visitor: IVisitor<T>;
|
readonly visitor: IVisitor<T>;
|
||||||
|
@ -6,10 +5,10 @@ export declare class GenericSeed<T> {
|
||||||
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor?: IVisitor<T>): T;
|
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor?: IVisitor<T>): T;
|
||||||
deserialize<D extends IDeserializer>(deserializer: D): T;
|
deserialize<D extends IDeserializer>(deserializer: D): T;
|
||||||
}
|
}
|
||||||
export declare class ProxyVisitor<T> implements IVisitor<T> {
|
export declare class Visitor<T> implements IVisitor<T> {
|
||||||
private overrides?;
|
private overrides?;
|
||||||
private options?;
|
constructor(overrides?: Partial<IVisitor<T>>);
|
||||||
constructor(overrides?: Partial<IVisitor<T>>, options?: SerdeOptions);
|
static from<T>(visitor: Partial<IVisitor<T>>): IVisitor<T>;
|
||||||
visitBoolean(value: boolean): T;
|
visitBoolean(value: boolean): T;
|
||||||
visitNumber(value: number): T;
|
visitNumber(value: number): T;
|
||||||
visitBigInt(value: bigint): T;
|
visitBigInt(value: bigint): T;
|
||||||
|
@ -19,18 +18,3 @@ export declare class ProxyVisitor<T> implements IVisitor<T> {
|
||||||
visitObject(access: IMapAccess): T;
|
visitObject(access: IMapAccess): T;
|
||||||
visitIterable(access: IIterableAccess): T;
|
visitIterable(access: IIterableAccess): T;
|
||||||
}
|
}
|
||||||
export declare class ProxyDeserializer implements IDeserializer {
|
|
||||||
private readonly deserializer;
|
|
||||||
private readonly options?;
|
|
||||||
constructor(deserializer: IDeserializer, options?: SerdeOptions);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
191
dist/de/generic.js
vendored
191
dist/de/generic.js
vendored
|
@ -1,9 +1,10 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.ProxyDeserializer = exports.ProxyVisitor = exports.GenericSeed = void 0;
|
exports.Visitor = exports.GenericSeed = void 0;
|
||||||
const utils_1 = require("../utils");
|
const utils_1 = require("../utils");
|
||||||
|
const interface_1 = require("./interface");
|
||||||
class GenericSeed {
|
class GenericSeed {
|
||||||
constructor(visitor = new ProxyVisitor()) {
|
constructor(visitor = new Visitor()) {
|
||||||
Object.defineProperty(this, "visitor", {
|
Object.defineProperty(this, "visitor", {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
|
@ -12,7 +13,7 @@ class GenericSeed {
|
||||||
});
|
});
|
||||||
this.visitor = visitor;
|
this.visitor = visitor;
|
||||||
}
|
}
|
||||||
static deserialize(deserializer, visitor = new ProxyVisitor()) {
|
static deserialize(deserializer, visitor = new Visitor()) {
|
||||||
return deserializer.deserializeAny(visitor);
|
return deserializer.deserializeAny(visitor);
|
||||||
}
|
}
|
||||||
deserialize(deserializer) {
|
deserialize(deserializer) {
|
||||||
|
@ -20,127 +21,21 @@ class GenericSeed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.GenericSeed = GenericSeed;
|
exports.GenericSeed = GenericSeed;
|
||||||
class ProxyMapAccess {
|
class Visitor {
|
||||||
constructor(access, options) {
|
constructor(overrides) {
|
||||||
Object.defineProperty(this, "access", {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
value: void 0
|
|
||||||
});
|
|
||||||
Object.defineProperty(this, "options", {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
value: void 0
|
|
||||||
});
|
|
||||||
if (access instanceof ProxyMapAccess) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
this.access = access;
|
|
||||||
this.options = options;
|
|
||||||
}
|
|
||||||
wrapResponse(result) {
|
|
||||||
var _a, _b, _c, _d;
|
|
||||||
switch (true) {
|
|
||||||
default:
|
|
||||||
case result.done: return result;
|
|
||||||
case (0, utils_1.isString)(result.value): {
|
|
||||||
const key = (_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.getDeserializationPropertyName(result.value)) !== null && _b !== void 0 ? _b : result.value;
|
|
||||||
return utils_1.IterResult.Next(key);
|
|
||||||
}
|
|
||||||
case Array.isArray(result.value): {
|
|
||||||
const [alias, value] = result.value;
|
|
||||||
const key = (_d = (_c = this.options) === null || _c === void 0 ? void 0 : _c.getDeserializationPropertyName(alias)) !== null && _d !== void 0 ? _d : alias;
|
|
||||||
return utils_1.IterResult.Next([
|
|
||||||
key,
|
|
||||||
value
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shouldSkipEntry(entry) {
|
|
||||||
var _a;
|
|
||||||
return (_a = this.options) === null || _a === void 0 ? void 0 : _a.shouldSkipDeserialization(entry.value[0], entry.value[1]);
|
|
||||||
}
|
|
||||||
nextKeySeed(seed) {
|
|
||||||
return this.wrapResponse(this.access.nextKeySeed(seed));
|
|
||||||
}
|
|
||||||
nextValueSeed(seed) {
|
|
||||||
return this.access.nextValueSeed(seed);
|
|
||||||
}
|
|
||||||
nextEntrySeed(kseed, vseed) {
|
|
||||||
const response = this.wrapResponse(this.access.nextEntrySeed(kseed, vseed));
|
|
||||||
if (!response.done && this.shouldSkipEntry(response)) {
|
|
||||||
return this.nextEntrySeed(kseed, vseed);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nextKey() {
|
|
||||||
return this.wrapResponse(this.access.nextKey());
|
|
||||||
}
|
|
||||||
nextValue() {
|
|
||||||
return this.access.nextValue();
|
|
||||||
}
|
|
||||||
nextEntry() {
|
|
||||||
const response = this.wrapResponse(this.access.nextEntry());
|
|
||||||
if (!response.done && this.shouldSkipEntry(response)) {
|
|
||||||
return this.nextEntry();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sizeHint() {
|
|
||||||
var _a, _b;
|
|
||||||
return (_b = (_a = this.access).sizeHint) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
||||||
}
|
|
||||||
*generate(next) {
|
|
||||||
let item;
|
|
||||||
while ((item = next()) && !item.done) {
|
|
||||||
yield item.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keys(seed) {
|
|
||||||
return this.generate(seed == null ?
|
|
||||||
this.nextKey.bind(this) :
|
|
||||||
this.nextKeySeed.bind(this, seed));
|
|
||||||
}
|
|
||||||
values(seed) {
|
|
||||||
return this.generate(seed == null ?
|
|
||||||
this.nextValue.bind(this) :
|
|
||||||
this.nextValueSeed.bind(this, seed));
|
|
||||||
}
|
|
||||||
entries(kseed, vseed) {
|
|
||||||
return this.generate(kseed == null || vseed == null ?
|
|
||||||
this.nextEntry.bind(this) :
|
|
||||||
this.nextEntrySeed.bind(this, kseed, vseed));
|
|
||||||
}
|
|
||||||
[Symbol.iterator]() {
|
|
||||||
return this.entries();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class ProxyVisitor {
|
|
||||||
constructor(overrides, options) {
|
|
||||||
Object.defineProperty(this, "overrides", {
|
Object.defineProperty(this, "overrides", {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
writable: true,
|
writable: true,
|
||||||
value: void 0
|
value: void 0
|
||||||
});
|
});
|
||||||
Object.defineProperty(this, "options", {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
value: void 0
|
|
||||||
});
|
|
||||||
if (overrides instanceof ProxyVisitor) {
|
|
||||||
return overrides;
|
|
||||||
}
|
|
||||||
this.overrides = overrides;
|
this.overrides = overrides;
|
||||||
this.options = options;
|
}
|
||||||
|
static from(visitor) {
|
||||||
|
if (visitor instanceof Visitor || (0, interface_1.isVisitor)(visitor)) {
|
||||||
|
return visitor;
|
||||||
|
}
|
||||||
|
return new this(visitor);
|
||||||
}
|
}
|
||||||
visitBoolean(value) {
|
visitBoolean(value) {
|
||||||
var _a, _b, _c;
|
var _a, _b, _c;
|
||||||
|
@ -168,14 +63,12 @@ class ProxyVisitor {
|
||||||
}
|
}
|
||||||
visitObject(access) {
|
visitObject(access) {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
const proxy = new ProxyMapAccess(access, this.options);
|
|
||||||
if ((0, utils_1.isFunction)((_a = this.overrides) === null || _a === void 0 ? void 0 : _a.visitObject)) {
|
if ((0, utils_1.isFunction)((_a = this.overrides) === null || _a === void 0 ? void 0 : _a.visitObject)) {
|
||||||
return (_b = this.overrides) === null || _b === void 0 ? void 0 : _b.visitObject(proxy);
|
return (_b = this.overrides) === null || _b === void 0 ? void 0 : _b.visitObject(access);
|
||||||
}
|
}
|
||||||
const result = [];
|
const result = [];
|
||||||
let entry;
|
for (const entry of access) {
|
||||||
while ((entry = proxy.nextEntry()) && !entry.done) {
|
result.push(entry);
|
||||||
result.push(entry.value);
|
|
||||||
}
|
}
|
||||||
return Object.fromEntries(result);
|
return Object.fromEntries(result);
|
||||||
}
|
}
|
||||||
|
@ -185,60 +78,10 @@ class ProxyVisitor {
|
||||||
return (_b = this.overrides) === null || _b === void 0 ? void 0 : _b.visitIterable(access);
|
return (_b = this.overrides) === null || _b === void 0 ? void 0 : _b.visitIterable(access);
|
||||||
}
|
}
|
||||||
const result = [];
|
const result = [];
|
||||||
let element;
|
for (const element of access) {
|
||||||
while ((element = access.nextElement())) {
|
|
||||||
result.push(element);
|
result.push(element);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exports.ProxyVisitor = ProxyVisitor;
|
exports.Visitor = Visitor;
|
||||||
class ProxyDeserializer {
|
|
||||||
constructor(deserializer, options) {
|
|
||||||
Object.defineProperty(this, "deserializer", {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
value: void 0
|
|
||||||
});
|
|
||||||
Object.defineProperty(this, "options", {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
value: void 0
|
|
||||||
});
|
|
||||||
this.deserializer = deserializer;
|
|
||||||
this.options = options;
|
|
||||||
}
|
|
||||||
deserializeAny(visitor) {
|
|
||||||
return this.deserializer.deserializeAny(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeBoolean(visitor) {
|
|
||||||
return this.deserializer.deserializeBoolean(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeNumber(visitor) {
|
|
||||||
return this.deserializer.deserializeNumber(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeBigInt(visitor) {
|
|
||||||
return this.deserializer.deserializeBigInt(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeString(visitor) {
|
|
||||||
return this.deserializer.deserializeString(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeSymbol(visitor) {
|
|
||||||
return this.deserializer.deserializeSymbol(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeNull(visitor) {
|
|
||||||
return this.deserializer.deserializeNull(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeObject(visitor) {
|
|
||||||
return this.deserializer.deserializeObject(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeIterable(visitor) {
|
|
||||||
return this.deserializer.deserializeIterable(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
deserializeFunction(visitor) {
|
|
||||||
return this.deserializer.deserializeFunction(new ProxyVisitor(visitor, this.options));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exports.ProxyDeserializer = ProxyDeserializer;
|
|
||||||
|
|
4
dist/de/impl.js
vendored
4
dist/de/impl.js
vendored
|
@ -2,14 +2,12 @@
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.deserialize = deserialize;
|
exports.deserialize = deserialize;
|
||||||
const registry_1 = require("../registry");
|
const registry_1 = require("../registry");
|
||||||
const generic_1 = require("./generic");
|
|
||||||
function deserialize(deserializer, into, registry = registry_1.GlobalRegistry) {
|
function deserialize(deserializer, into, registry = registry_1.GlobalRegistry) {
|
||||||
var _a;
|
|
||||||
const de = registry.deserializers.get(into);
|
const de = registry.deserializers.get(into);
|
||||||
if (de == null) {
|
if (de == null) {
|
||||||
throw new ReferenceError(`No deserializer for ${into.name}`);
|
throw new ReferenceError(`No deserializer for ${into.name}`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return de(new generic_1.ProxyDeserializer(deserializer, (_a = into === null || into === void 0 ? void 0 : into[Symbol.metadata]) === null || _a === void 0 ? void 0 : _a.serde));
|
return de(deserializer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
dist/de/interface.d.ts
vendored
3
dist/de/interface.d.ts
vendored
|
@ -28,10 +28,13 @@ export declare abstract class MapAccess {
|
||||||
export interface IIterableAccess {
|
export interface IIterableAccess {
|
||||||
nextElement<T>(): IteratorResult<T>;
|
nextElement<T>(): IteratorResult<T>;
|
||||||
sizeHint?(): Nullable<number>;
|
sizeHint?(): Nullable<number>;
|
||||||
|
[Symbol.iterator]<T>(): Iterator<T>;
|
||||||
}
|
}
|
||||||
export declare abstract class IterableAccess implements IIterableAccess {
|
export declare abstract class IterableAccess implements IIterableAccess {
|
||||||
abstract nextElement<T>(): IteratorResult<T>;
|
abstract nextElement<T>(): IteratorResult<T>;
|
||||||
|
[Symbol.iterator]<T>(): Iterator<T>;
|
||||||
}
|
}
|
||||||
|
export declare function isVisitor<T>(visitor: any): visitor is IVisitor<T>;
|
||||||
export interface IVisitor<T> {
|
export interface IVisitor<T> {
|
||||||
visitBoolean(value: boolean): T;
|
visitBoolean(value: boolean): T;
|
||||||
visitNumber(value: number): T;
|
visitNumber(value: number): T;
|
||||||
|
|
21
dist/de/interface.js
vendored
21
dist/de/interface.js
vendored
|
@ -1,6 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
Object.defineProperty(exports, "__esModule", { value: true });
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||||||
exports.IterableAccess = exports.MapAccess = void 0;
|
exports.IterableAccess = exports.MapAccess = void 0;
|
||||||
|
exports.isVisitor = isVisitor;
|
||||||
const utils_1 = require("../utils");
|
const utils_1 = require("../utils");
|
||||||
class MapAccess {
|
class MapAccess {
|
||||||
nextEntrySeed(kseed, vseed) {
|
nextEntrySeed(kseed, vseed) {
|
||||||
|
@ -25,7 +26,7 @@ class MapAccess {
|
||||||
}
|
}
|
||||||
*generate(next) {
|
*generate(next) {
|
||||||
let item;
|
let item;
|
||||||
while ((item = next())) {
|
while ((item = next()) && !item.done) {
|
||||||
yield item.value;
|
yield item.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,5 +51,23 @@ class MapAccess {
|
||||||
}
|
}
|
||||||
exports.MapAccess = MapAccess;
|
exports.MapAccess = MapAccess;
|
||||||
class IterableAccess {
|
class IterableAccess {
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
return {
|
||||||
|
next: this.nextElement
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.IterableAccess = IterableAccess;
|
exports.IterableAccess = IterableAccess;
|
||||||
|
const VisitorMethods = Object.freeze([
|
||||||
|
'visitBoolean',
|
||||||
|
'visitNumber',
|
||||||
|
'visitBigInt',
|
||||||
|
'visitString',
|
||||||
|
'visitSymbol',
|
||||||
|
'visitNull',
|
||||||
|
'visitObject',
|
||||||
|
'visitIterable'
|
||||||
|
]);
|
||||||
|
function isVisitor(visitor) {
|
||||||
|
return VisitorMethods.every(method => method in visitor);
|
||||||
|
}
|
||||||
|
|
8
dist/index.d.ts
vendored
8
dist/index.d.ts
vendored
|
@ -1,8 +1,4 @@
|
||||||
import '@tsmetadata/polyfill';
|
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 './decorator';
|
|
||||||
export * from './option';
|
|
||||||
export * from './case';
|
|
||||||
export * from './registry';
|
export * from './registry';
|
||||||
export * from './utils';
|
export * from './utils';
|
||||||
|
|
8
dist/index.js
vendored
8
dist/index.js
vendored
|
@ -37,11 +37,7 @@ 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;
|
||||||
require("@tsmetadata/polyfill");
|
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("./decorator"), exports);
|
|
||||||
__exportStar(require("./option"), exports);
|
|
||||||
__exportStar(require("./case"), exports);
|
|
||||||
__exportStar(require("./registry"), exports);
|
__exportStar(require("./registry"), exports);
|
||||||
__exportStar(require("./utils"), exports);
|
__exportStar(require("./utils"), exports);
|
||||||
|
|
17
dist/option.d.ts
vendored
17
dist/option.d.ts
vendored
|
@ -1,5 +1,5 @@
|
||||||
import { Deserialize } from './de';
|
import { IDeserializer } from './de';
|
||||||
import { Serialize } from './ser';
|
import { ISerializer } from './ser';
|
||||||
import { CaseConvention } from './case';
|
import { CaseConvention } from './case';
|
||||||
import { Nullable } from './utils';
|
import { Nullable } from './utils';
|
||||||
export interface RenameOptions {
|
export interface RenameOptions {
|
||||||
|
@ -25,14 +25,19 @@ export interface SkipOptions {
|
||||||
serialize?: Predicate | boolean;
|
serialize?: Predicate | boolean;
|
||||||
deserialize?: Predicate | boolean;
|
deserialize?: Predicate | boolean;
|
||||||
}
|
}
|
||||||
|
type CustomSerializer = <T, U, S extends ISerializer<T>>(value: U, serializer: S) => T;
|
||||||
|
type CustomDeserializer = <T, D extends IDeserializer>(deserializer: D) => T;
|
||||||
|
export interface CustomSerdeOptions {
|
||||||
|
serialize?: CustomSerializer;
|
||||||
|
deserialize?: CustomDeserializer;
|
||||||
|
}
|
||||||
export interface PropertyOptions {
|
export interface PropertyOptions {
|
||||||
alias?: string | string[];
|
alias?: string | string[];
|
||||||
default?: () => any;
|
default?: () => any;
|
||||||
flatten?: boolean;
|
flatten?: boolean;
|
||||||
rename?: RenameOptions | string;
|
rename?: RenameOptions | string;
|
||||||
skip?: SkipOptions | Predicate | boolean;
|
skip?: SkipOptions | Predicate | boolean;
|
||||||
serializeWith?: Serialize<any>;
|
with?: CustomSerdeOptions;
|
||||||
deserializeWith?: Deserialize<any>;
|
|
||||||
}
|
}
|
||||||
export declare const Stage: Readonly<{
|
export declare const Stage: Readonly<{
|
||||||
readonly Serialize: 0;
|
readonly Serialize: 0;
|
||||||
|
@ -67,5 +72,9 @@ export declare class SerdeOptions {
|
||||||
shouldSkipSerialization(property: string, value: any): boolean;
|
shouldSkipSerialization(property: string, value: any): boolean;
|
||||||
shouldSkipDeserialization(property: string, value: any): boolean;
|
shouldSkipDeserialization(property: string, value: any): boolean;
|
||||||
defaultFor(property: string): any;
|
defaultFor(property: string): any;
|
||||||
|
hasCustomSerializer(property: string): boolean;
|
||||||
|
hasCustomDeserializer(property: string): boolean;
|
||||||
|
useCustomSerializer<T, U, S extends ISerializer<U>>(serializer: S, property: string, value: T): Nullable<U>;
|
||||||
|
useCustomDeserializer<T, U, D extends IDeserializer>(deserializer: D, property: string): Nullable<U>;
|
||||||
}
|
}
|
||||||
export {};
|
export {};
|
||||||
|
|
20
dist/option.js
vendored
20
dist/option.js
vendored
|
@ -205,5 +205,25 @@ class SerdeOptions {
|
||||||
return this.container.default();
|
return this.container.default();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hasCustomSerializer(property) {
|
||||||
|
var _a;
|
||||||
|
const options = this.properties.get(property);
|
||||||
|
return (0, utils_1.isFunction)((_a = options === null || options === void 0 ? void 0 : options.with) === null || _a === void 0 ? void 0 : _a.serialize);
|
||||||
|
}
|
||||||
|
hasCustomDeserializer(property) {
|
||||||
|
var _a;
|
||||||
|
const options = this.properties.get(property);
|
||||||
|
return (0, utils_1.isFunction)((_a = options === null || options === void 0 ? void 0 : options.with) === null || _a === void 0 ? void 0 : _a.deserialize);
|
||||||
|
}
|
||||||
|
useCustomSerializer(serializer, property, value) {
|
||||||
|
var _a, _b;
|
||||||
|
const options = this.properties.get(property);
|
||||||
|
return (_b = (_a = options === null || options === void 0 ? void 0 : options.with) === null || _a === void 0 ? void 0 : _a.serialize) === null || _b === void 0 ? void 0 : _b.call(_a, value, serializer);
|
||||||
|
}
|
||||||
|
useCustomDeserializer(deserializer, property) {
|
||||||
|
var _a, _b;
|
||||||
|
const options = this.properties.get(property);
|
||||||
|
return (_b = (_a = options === null || options === void 0 ? void 0 : options.with) === null || _a === void 0 ? void 0 : _a.deserialize) === null || _b === void 0 ? void 0 : _b.call(_a, deserializer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.SerdeOptions = SerdeOptions;
|
exports.SerdeOptions = SerdeOptions;
|
||||||
|
|
4
dist/ser/impl.d.ts
vendored
4
dist/ser/impl.d.ts
vendored
|
@ -1,4 +1,2 @@
|
||||||
import { ISerializer } from './interface';
|
import { ISerializer } from './interface';
|
||||||
import { Nullable } from '../utils';
|
export declare function serialize<T, V, S extends ISerializer<T>>(serializer: S, value: V): T;
|
||||||
import { SerdeOptions } from '../option';
|
|
||||||
export declare function serialize<T, V, S extends ISerializer<T>>(serializer: S, value: V, optionsGetter?: (value: V) => Nullable<SerdeOptions>): T;
|
|
||||||
|
|
22
dist/ser/impl.js
vendored
22
dist/ser/impl.js
vendored
|
@ -7,26 +7,19 @@ class UnhandledTypeError extends TypeError {
|
||||||
super(`unhandled type: '${typeof value}' for serializer ${serializer.constructor.name}`);
|
super(`unhandled type: '${typeof value}' for serializer ${serializer.constructor.name}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function serializeObject(serializer, obj, options) {
|
function serializeObject(serializer, obj) {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
const value = obj[key];
|
const value = obj[key];
|
||||||
if (!(options === null || options === void 0 ? void 0 : options.shouldSkipSerialization(key, value))) {
|
serializer.serializeEntry(key, value);
|
||||||
const name = (options === null || options === void 0 ? void 0 : options.getSerializationPropertyName(key)) || key;
|
|
||||||
serializer.serializeEntry(name, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return serializer.end();
|
return serializer.end();
|
||||||
}
|
}
|
||||||
function serializeClass(serializer, value, options) {
|
function serializeClass(serializer, value) {
|
||||||
const name = value.constructor.name;
|
const name = value.constructor.name;
|
||||||
const ser = serializer.serializeClass(name);
|
const ser = serializer.serializeClass(name);
|
||||||
return serializeObject(ser, value, options);
|
return serializeObject(ser, value);
|
||||||
}
|
}
|
||||||
const defaultGetter = (value) => {
|
function serialize(serializer, value) {
|
||||||
var _a;
|
|
||||||
return (_a = value.constructor[Symbol.metadata]) === null || _a === void 0 ? void 0 : _a.serde;
|
|
||||||
};
|
|
||||||
function serialize(serializer, value, optionsGetter = defaultGetter) {
|
|
||||||
switch (typeof value) {
|
switch (typeof value) {
|
||||||
case 'string': return serializer.serializeString(value);
|
case 'string': return serializer.serializeString(value);
|
||||||
case 'number': return serializer.serializeNumber(value);
|
case 'number': return serializer.serializeNumber(value);
|
||||||
|
@ -35,11 +28,10 @@ function serialize(serializer, value, optionsGetter = defaultGetter) {
|
||||||
case 'symbol': return serializer.serializeSymbol(value);
|
case 'symbol': return serializer.serializeSymbol(value);
|
||||||
case 'undefined': return serializer.serializeNull();
|
case 'undefined': return serializer.serializeNull();
|
||||||
case 'object':
|
case 'object':
|
||||||
const options = optionsGetter(value);
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case value == null: return serializer.serializeNull();
|
case value == null: return serializer.serializeNull();
|
||||||
case !(0, utils_1.isPlainObject)(value): return serializeClass(serializer, value, options);
|
case !(0, utils_1.isPlainObject)(value): return serializeClass(serializer, value);
|
||||||
default: return serializeObject(serializer.serializeObject(), value, options);
|
default: return serializeObject(serializer.serializeObject(), value);
|
||||||
}
|
}
|
||||||
default: throw new UnhandledTypeError(serializer, value);
|
default: throw new UnhandledTypeError(serializer, value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
import { SerdeOptions } from '../option'
|
import { isFunction } from '../utils'
|
||||||
import { isFunction, isString, IterResult, Nullable } from '../utils'
|
import { IDeserializer, IIterableAccess, IMapAccess, isVisitor, IVisitor } from './interface'
|
||||||
import { Deserialize, IDeserializer, IIterableAccess, IMapAccess, IVisitor } from './interface'
|
|
||||||
|
|
||||||
export class GenericSeed<T> {
|
export class GenericSeed<T> {
|
||||||
readonly visitor: IVisitor<T>
|
readonly visitor: IVisitor<T>
|
||||||
|
|
||||||
constructor(visitor: IVisitor<T> = new ProxyVisitor()) {
|
constructor(visitor: IVisitor<T> = new Visitor()) {
|
||||||
this.visitor = visitor
|
this.visitor = visitor
|
||||||
}
|
}
|
||||||
|
|
||||||
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor: IVisitor<T> = new ProxyVisitor()): T {
|
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor: IVisitor<T> = new Visitor()): T {
|
||||||
return deserializer.deserializeAny(visitor)
|
return deserializer.deserializeAny(visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,131 +17,19 @@ export class GenericSeed<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ProxyMapAccess implements IMapAccess {
|
export class Visitor<T> implements IVisitor<T> {
|
||||||
private access!: IMapAccess
|
|
||||||
private options?: SerdeOptions
|
|
||||||
|
|
||||||
constructor(access: IMapAccess, options?: SerdeOptions) {
|
|
||||||
if (access instanceof ProxyMapAccess) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
this.access = access
|
|
||||||
this.options = options
|
|
||||||
}
|
|
||||||
|
|
||||||
private wrapResponse<T, I extends IteratorResult<T> = IteratorResult<T>>(result: I): I {
|
|
||||||
switch (true) {
|
|
||||||
default:
|
|
||||||
case result.done: return result
|
|
||||||
|
|
||||||
case isString(result.value): {
|
|
||||||
const key = this.options?.getDeserializationPropertyName(result.value) ?? result.value
|
|
||||||
return IterResult.Next(key) as I
|
|
||||||
}
|
|
||||||
case Array.isArray(result.value): {
|
|
||||||
const [alias, value] = result.value
|
|
||||||
const key = this.options?.getDeserializationPropertyName(alias) ?? alias
|
|
||||||
return IterResult.Next([
|
|
||||||
key,
|
|
||||||
value
|
|
||||||
]) as I
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldSkipEntry<K, V>(entry: IteratorResult<[K, V]>) {
|
|
||||||
return this.options?.shouldSkipDeserialization(entry.value[0] as string, entry.value[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
nextKeySeed<T, K extends Deserialize<T>>(seed: K): IteratorResult<T> {
|
|
||||||
return this.wrapResponse<T>(this.access.nextKeySeed(seed))
|
|
||||||
}
|
|
||||||
|
|
||||||
nextValueSeed<T, V extends Deserialize<T>>(seed: V): IteratorResult<T> {
|
|
||||||
return this.access.nextValueSeed(seed)
|
|
||||||
}
|
|
||||||
|
|
||||||
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): IteratorResult<[TK, TV]> {
|
|
||||||
const response = this.wrapResponse<[TK, TV]>(this.access.nextEntrySeed(kseed, vseed))
|
|
||||||
|
|
||||||
if (!response.done && this.shouldSkipEntry(response)) {
|
|
||||||
return this.nextEntrySeed(kseed, vseed)
|
|
||||||
} else {
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nextKey<T>(): IteratorResult<T> {
|
|
||||||
return this.wrapResponse<T>(this.access.nextKey())
|
|
||||||
}
|
|
||||||
|
|
||||||
nextValue<V>(): IteratorResult<V> {
|
|
||||||
return this.access.nextValue()
|
|
||||||
}
|
|
||||||
|
|
||||||
nextEntry<K, V>(): IteratorResult<[K, V]> {
|
|
||||||
const response = this.wrapResponse<[K, V]>(this.access.nextEntry())
|
|
||||||
|
|
||||||
if (!response.done && this.shouldSkipEntry(response)) {
|
|
||||||
return this.nextEntry()
|
|
||||||
} else {
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sizeHint?(): Nullable<number> {
|
|
||||||
return this.access.sizeHint?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
private *generate<T>(next: () => IteratorResult<T>): Iterator<T> {
|
|
||||||
let item: IteratorResult<T>
|
|
||||||
|
|
||||||
while ((item = next()) && !item.done) {
|
|
||||||
yield item.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
keys<T, K extends Deserialize<T>>(seed?: K): Iterator<T> {
|
|
||||||
return this.generate(
|
|
||||||
seed == null ?
|
|
||||||
this.nextKey.bind(this) :
|
|
||||||
this.nextKeySeed.bind(this, seed) as any
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
values<T, V extends Deserialize<T>>(seed?: V): Iterator<T> {
|
|
||||||
return this.generate(
|
|
||||||
seed == null ?
|
|
||||||
this.nextValue.bind(this) :
|
|
||||||
this.nextValueSeed.bind(this, seed) as any
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
entries<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed?: K, vseed?: V): Iterator<[TK, TV]> {
|
|
||||||
return this.generate(
|
|
||||||
kseed == null || vseed == null ?
|
|
||||||
this.nextEntry.bind(this) :
|
|
||||||
this.nextEntrySeed.bind(this, kseed, vseed) as any
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
[Symbol.iterator]<K, V>(): Iterator<[K, V]> {
|
|
||||||
return this.entries() as Iterator<[K, V]>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ProxyVisitor<T> implements IVisitor<T> {
|
|
||||||
private overrides?: Partial<IVisitor<T>>
|
private overrides?: Partial<IVisitor<T>>
|
||||||
private options?: SerdeOptions
|
|
||||||
|
|
||||||
constructor(overrides?: Partial<IVisitor<T>>, options?: SerdeOptions) {
|
constructor(overrides?: Partial<IVisitor<T>>) {
|
||||||
if (overrides instanceof ProxyVisitor) {
|
this.overrides = overrides
|
||||||
return overrides
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.overrides = overrides
|
static from<T>(visitor: Partial<IVisitor<T>>): IVisitor<T> {
|
||||||
this.options = options
|
if (visitor instanceof Visitor || isVisitor(visitor)) {
|
||||||
|
return visitor
|
||||||
|
}
|
||||||
|
|
||||||
|
return new this(visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
visitBoolean(value: boolean): T {
|
visitBoolean(value: boolean): T {
|
||||||
|
@ -170,17 +57,14 @@ export class ProxyVisitor<T> implements IVisitor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
visitObject(access: IMapAccess): T {
|
visitObject(access: IMapAccess): T {
|
||||||
const proxy = new ProxyMapAccess(access, this.options)
|
|
||||||
|
|
||||||
if (isFunction(this.overrides?.visitObject)) {
|
if (isFunction(this.overrides?.visitObject)) {
|
||||||
return this.overrides?.visitObject(proxy)
|
return this.overrides?.visitObject(access)
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = []
|
const result = []
|
||||||
let entry
|
|
||||||
|
|
||||||
while ((entry = proxy.nextEntry()) && !entry.done) {
|
for (const entry of access) {
|
||||||
result.push(entry.value)
|
result.push(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.fromEntries(result)
|
return Object.fromEntries(result)
|
||||||
|
@ -192,9 +76,8 @@ export class ProxyVisitor<T> implements IVisitor<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = []
|
const result = []
|
||||||
let element
|
|
||||||
|
|
||||||
while ((element = access.nextElement())) {
|
for (const element of access) {
|
||||||
result.push(element)
|
result.push(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,53 +85,3 @@ export class ProxyVisitor<T> implements IVisitor<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProxyDeserializer implements IDeserializer {
|
|
||||||
private readonly deserializer: IDeserializer
|
|
||||||
private readonly options?: SerdeOptions
|
|
||||||
|
|
||||||
constructor(deserializer: IDeserializer, options?: SerdeOptions) {
|
|
||||||
this.deserializer = deserializer
|
|
||||||
this.options = options
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeAny<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeAny(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeBoolean(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeNumber(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeBigInt(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeString<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeString(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeSymbol(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeNull(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeObject(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeIterable(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
|
|
||||||
deserializeFunction<T, V extends IVisitor<T>>(visitor: V): T {
|
|
||||||
return this.deserializer.deserializeFunction(new ProxyVisitor(visitor, this.options))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { GlobalRegistry, Registry } from '../registry'
|
import { GlobalRegistry, Registry } from '../registry'
|
||||||
import { Constructor } from '../utils'
|
import { Constructor } from '../utils'
|
||||||
import { ProxyDeserializer } from './generic'
|
|
||||||
import { IDeserializer } from './interface'
|
import { IDeserializer } from './interface'
|
||||||
|
|
||||||
export function deserialize<T, D extends IDeserializer>(deserializer: D, into: Constructor<T>, registry: Registry = GlobalRegistry): T {
|
export function deserialize<T, D extends IDeserializer>(deserializer: D, into: Constructor<T>, registry: Registry = GlobalRegistry): T {
|
||||||
|
@ -9,7 +8,7 @@ export function deserialize<T, D extends IDeserializer>(deserializer: D, into: C
|
||||||
if (de == null) {
|
if (de == null) {
|
||||||
throw new ReferenceError(`No deserializer for ${into.name}`)
|
throw new ReferenceError(`No deserializer for ${into.name}`)
|
||||||
} else {
|
} else {
|
||||||
return de(new ProxyDeserializer(deserializer, (into as any)?.[Symbol.metadata]?.serde))
|
return de(deserializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,11 @@ export abstract class MapAccess {
|
||||||
private *generate<T>(next: () => IteratorResult<T>): Iterator<T> {
|
private *generate<T>(next: () => IteratorResult<T>): Iterator<T> {
|
||||||
let item
|
let item
|
||||||
|
|
||||||
while ((item = next())) {
|
while ((item = next()) && !item.done) {
|
||||||
yield item.value
|
yield item.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
keys<T, K extends Deserialize<T>>(seed?: K): Iterator<T> {
|
keys<T, K extends Deserialize<T>>(seed?: K): Iterator<T> {
|
||||||
return this.generate(
|
return this.generate(
|
||||||
seed == null ?
|
seed == null ?
|
||||||
|
@ -90,12 +89,33 @@ export abstract class MapAccess {
|
||||||
export interface IIterableAccess {
|
export interface IIterableAccess {
|
||||||
nextElement<T>(): IteratorResult<T>
|
nextElement<T>(): IteratorResult<T>
|
||||||
sizeHint?(): Nullable<number>
|
sizeHint?(): Nullable<number>
|
||||||
|
[Symbol.iterator]<T>(): Iterator<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class IterableAccess implements IIterableAccess {
|
export abstract class IterableAccess implements IIterableAccess {
|
||||||
abstract nextElement<T>(): IteratorResult<T>
|
abstract nextElement<T>(): IteratorResult<T>
|
||||||
|
|
||||||
|
*[Symbol.iterator]<T>(): Iterator<T> {
|
||||||
|
return {
|
||||||
|
next: this.nextElement
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VisitorMethods = Object.freeze([
|
||||||
|
'visitBoolean',
|
||||||
|
'visitNumber',
|
||||||
|
'visitBigInt',
|
||||||
|
'visitString',
|
||||||
|
'visitSymbol',
|
||||||
|
'visitNull',
|
||||||
|
'visitObject',
|
||||||
|
'visitIterable'
|
||||||
|
] as const)
|
||||||
|
|
||||||
|
export function isVisitor<T>(visitor: any): visitor is IVisitor<T> {
|
||||||
|
return VisitorMethods.every(method => method in visitor)
|
||||||
|
}
|
||||||
export interface IVisitor<T> {
|
export interface IVisitor<T> {
|
||||||
visitBoolean(value: boolean): T
|
visitBoolean(value: boolean): T
|
||||||
visitNumber(value: number): T
|
visitNumber(value: number): T
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { ContainerOptions, PropertyOptions, SerdeOptions } from './option'
|
|
||||||
import { Nullable } from './utils'
|
|
||||||
|
|
||||||
const ContextKind = Object.freeze({
|
|
||||||
Class: 'class',
|
|
||||||
Method: 'method',
|
|
||||||
Getter: 'getter',
|
|
||||||
Setter: 'setter',
|
|
||||||
Field: 'field',
|
|
||||||
Accessor: 'accessor'
|
|
||||||
} as const)
|
|
||||||
|
|
||||||
function decorateClass(target: Function, context: DecoratorContext, options: ContainerOptions) {
|
|
||||||
const serde = context.metadata.serde as Nullable<SerdeOptions>
|
|
||||||
|
|
||||||
context.metadata.serde = new SerdeOptions(
|
|
||||||
target,
|
|
||||||
options,
|
|
||||||
serde?.properties
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function decorateField(context: DecoratorContext, options: PropertyOptions) {
|
|
||||||
const serde = (context.metadata.serde as SerdeOptions) || new SerdeOptions(undefined)
|
|
||||||
serde.properties.set(context.name as string, options)
|
|
||||||
context.metadata.serde = serde
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serde(options: ContainerOptions | PropertyOptions) {
|
|
||||||
return function(target: any, context: DecoratorContext) {
|
|
||||||
switch (context.kind) {
|
|
||||||
case ContextKind.Class:
|
|
||||||
decorateClass(target, context, options)
|
|
||||||
break
|
|
||||||
|
|
||||||
case ContextKind.Field:
|
|
||||||
decorateField(context, options)
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
import '@tsmetadata/polyfill'
|
export * as ser from './ser/index'
|
||||||
|
export * as de from './de/index'
|
||||||
export * as ser from './ser/impl'
|
|
||||||
export * as de from './de/impl'
|
|
||||||
export * from './decorator'
|
|
||||||
export * from './option'
|
|
||||||
export * from './case'
|
|
||||||
export * from './registry'
|
export * from './registry'
|
||||||
export * from './utils'
|
export * from './utils'
|
||||||
|
|
||||||
|
|
219
src/option.ts
219
src/option.ts
|
@ -1,219 +0,0 @@
|
||||||
import { Deserialize } from './de'
|
|
||||||
import { Serialize } from './ser'
|
|
||||||
import { CaseConvention, convertCase } from './case'
|
|
||||||
import { isFunction, isIterable, isNumber, isString, Nullable } from './utils'
|
|
||||||
|
|
||||||
export interface RenameOptions {
|
|
||||||
serialize?: string
|
|
||||||
deserialize?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RenameAllOptions {
|
|
||||||
serialize?: CaseConvention
|
|
||||||
deserialize?: CaseConvention
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ContainerOptions {
|
|
||||||
rename?: RenameOptions | string
|
|
||||||
renameAll?: RenameAllOptions | CaseConvention
|
|
||||||
default?: () => any
|
|
||||||
denyUnknownFields?: boolean
|
|
||||||
tag?: string
|
|
||||||
untagged?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Predicate {
|
|
||||||
<T>(value: T): boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SkipOptions {
|
|
||||||
serialize?: Predicate | boolean
|
|
||||||
deserialize?: Predicate | boolean
|
|
||||||
}
|
|
||||||
export interface PropertyOptions {
|
|
||||||
alias?: string | string[]
|
|
||||||
default?: () => any
|
|
||||||
flatten?: boolean
|
|
||||||
rename?: RenameOptions | string
|
|
||||||
skip?: SkipOptions | Predicate | boolean
|
|
||||||
serializeWith?: Serialize<any>
|
|
||||||
deserializeWith?: Deserialize<any>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Stage = Object.freeze({
|
|
||||||
Serialize: 0,
|
|
||||||
Deserialize: 1
|
|
||||||
} as const)
|
|
||||||
|
|
||||||
export class PropertyMap extends Map<string, PropertyOptions> {
|
|
||||||
private readonly options: SerdeOptions
|
|
||||||
private readonly aliases: Map<string, string> = new Map()
|
|
||||||
|
|
||||||
constructor(options: SerdeOptions, iterable?: Iterable<any>) {
|
|
||||||
super(iterable)
|
|
||||||
this.options = options
|
|
||||||
this.buildAliasMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
private setAliasesFromOptions(key: string, options: PropertyOptions) {
|
|
||||||
this.aliases.set(key, key)
|
|
||||||
|
|
||||||
if (options.alias != null) {
|
|
||||||
this.addAlias(options.alias, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isString(options.rename)) {
|
|
||||||
this.aliases.set(options.rename, key)
|
|
||||||
} else if (isNumber(this.options.container.renameAll)) {
|
|
||||||
this.aliases.set(this.options.applyCaseConvention(Stage.Deserialize, key), key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set(key: string, value: PropertyOptions): this {
|
|
||||||
super.set(key, value)
|
|
||||||
this.setAliasesFromOptions(key, value)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
addAlias(alias: string | Iterable<string>, key: string) {
|
|
||||||
if (isString(alias)) {
|
|
||||||
this.aliases.set(alias, key)
|
|
||||||
} else if (isIterable(alias)) {
|
|
||||||
for (const a of alias) {
|
|
||||||
this.addAlias(a, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getFieldFromAlias(alias: string) {
|
|
||||||
return this.aliases.get(alias)
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildAliasMap() {
|
|
||||||
for (const [key, value] of this.entries()) {
|
|
||||||
if (value.alias != null) {
|
|
||||||
this.addAlias(value.alias, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Stage = typeof Stage[keyof typeof Stage]
|
|
||||||
|
|
||||||
export class SerdeOptions {
|
|
||||||
readonly target: Nullable<any>
|
|
||||||
readonly container: ContainerOptions
|
|
||||||
readonly properties: PropertyMap
|
|
||||||
|
|
||||||
constructor(target: any, container: ContainerOptions = {}, properties: PropertyMap = new PropertyMap(this)) {
|
|
||||||
this.target = target
|
|
||||||
this.container = container
|
|
||||||
this.properties = properties
|
|
||||||
}
|
|
||||||
|
|
||||||
private getClassName(stage: Stage, defaultName: string = this.target?.constructor?.name) {
|
|
||||||
if (isString(this.container.rename)) {
|
|
||||||
return this.container.rename
|
|
||||||
} else if (stage === Stage.Serialize && isString(this.container.rename?.serialize)) {
|
|
||||||
return this.container.rename.serialize
|
|
||||||
} else if (stage === Stage.Deserialize && isString(this.container.rename?.deserialize)) {
|
|
||||||
return this.container.rename.serialize
|
|
||||||
} else {
|
|
||||||
return defaultName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSerializedClassName(defaultName: string = this.target?.constructor?.name) {
|
|
||||||
return this.getClassName(Stage.Serialize, defaultName)
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeserializedClassName(defaultName: string = this.target?.constructor?.name) {
|
|
||||||
return this.getClassName(Stage.Deserialize, defaultName)
|
|
||||||
}
|
|
||||||
|
|
||||||
getCaseConvention(stage: Stage) {
|
|
||||||
if (isNumber(this.container.renameAll)) {
|
|
||||||
if (stage === Stage.Serialize) {
|
|
||||||
return this.container.renameAll
|
|
||||||
} else {
|
|
||||||
return CaseConvention.CamelCase
|
|
||||||
}
|
|
||||||
} else if (stage === Stage.Serialize && isNumber(this.container.renameAll?.serialize)) {
|
|
||||||
return this.container.renameAll.serialize
|
|
||||||
} else if (stage === Stage.Deserialize && isNumber(this.container.renameAll?.deserialize)) {
|
|
||||||
return this.container.renameAll.deserialize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
applyCaseConvention(stage: Stage, property: string) {
|
|
||||||
const convention = this.getCaseConvention(stage)
|
|
||||||
return convention != null ? convertCase(property, convention) : property
|
|
||||||
}
|
|
||||||
|
|
||||||
getPropertyRename(stage: Stage, property: string) {
|
|
||||||
const options = this.properties.get(property)
|
|
||||||
|
|
||||||
if (options == null) {
|
|
||||||
return
|
|
||||||
} else 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getSerializationPropertyName(property: string) {
|
|
||||||
return this.getPropertyRename(Stage.Serialize, property) ||
|
|
||||||
this.applyCaseConvention(Stage.Serialize, property)
|
|
||||||
}
|
|
||||||
|
|
||||||
getNameFromAlias(alias: string) {
|
|
||||||
return this.properties.getFieldFromAlias(alias)
|
|
||||||
}
|
|
||||||
|
|
||||||
getDeserializationPropertyName(property: string) {
|
|
||||||
return this.getNameFromAlias(property) ||
|
|
||||||
this.applyCaseConvention(Stage.Deserialize, property)
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldSkip(stage: Stage, property: string, value: any) {
|
|
||||||
const options = this.properties.get(property)
|
|
||||||
if (options == null) {
|
|
||||||
return false
|
|
||||||
} else if (typeof options.skip === 'boolean') {
|
|
||||||
return options.skip
|
|
||||||
} else if (isFunction(options.skip)) {
|
|
||||||
return options.skip(value)
|
|
||||||
} else if (stage === Stage.Serialize && typeof options.skip?.serialize === 'boolean') {
|
|
||||||
return options.skip.serialize
|
|
||||||
} else if (stage === Stage.Serialize && isFunction(options.skip?.serialize)) {
|
|
||||||
return options.skip.serialize(value)
|
|
||||||
} else if (stage === Stage.Deserialize && typeof options.skip?.deserialize === 'boolean') {
|
|
||||||
return options.skip.deserialize
|
|
||||||
} else if (stage === Stage.Deserialize && isFunction(options.skip?.deserialize)) {
|
|
||||||
return options.skip.deserialize(value)
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldSkipSerialization(property: string, value: any) {
|
|
||||||
return this.shouldSkip(Stage.Serialize, property, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldSkipDeserialization(property: string, value: any) {
|
|
||||||
return this.shouldSkip(Stage.Deserialize, property, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultFor(property: string) {
|
|
||||||
const options = this.properties.get(property)
|
|
||||||
if (options != null && isFunction(options.default)) {
|
|
||||||
return options.default()
|
|
||||||
} else if (isFunction(this.container.default)) {
|
|
||||||
return this.container.default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { ISerializeObject, ISerializer } from './interface'
|
import { ISerializeObject, ISerializer } from './interface'
|
||||||
import { isPlainObject, Nullable } from '../utils'
|
import { isPlainObject } from '../utils'
|
||||||
import { SerdeOptions } from '../option'
|
|
||||||
|
|
||||||
class UnhandledTypeError extends TypeError {
|
class UnhandledTypeError extends TypeError {
|
||||||
constructor(serializer: ISerializer<unknown>, value: any) {
|
constructor(serializer: ISerializer<unknown>, value: any) {
|
||||||
|
@ -8,29 +7,22 @@ class UnhandledTypeError extends TypeError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeObject<T, V extends object, S extends ISerializeObject<T>>(serializer: S, obj: V, options?: SerdeOptions): T {
|
function serializeObject<T, V extends object, S extends ISerializeObject<T>>(serializer: S, obj: V): T {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
const value = obj[key]
|
const value = obj[key]
|
||||||
if (!options?.shouldSkipSerialization(key, value)) {
|
serializer.serializeEntry(key, value)
|
||||||
const name = options?.getSerializationPropertyName(key) || key
|
|
||||||
serializer.serializeEntry(name, value)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return serializer.end()
|
return serializer.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeClass<T, V extends object, S extends ISerializer<T>>(serializer: S, value: V, options?: SerdeOptions): T {
|
function serializeClass<T, V extends object, S extends ISerializer<T>>(serializer: S, value: V): T {
|
||||||
const name = value.constructor.name
|
const name = value.constructor.name
|
||||||
const ser = serializer.serializeClass(name)
|
const ser = serializer.serializeClass(name)
|
||||||
return serializeObject(ser, value, options)
|
return serializeObject(ser, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultGetter = (value: any): Nullable<SerdeOptions> => {
|
export function serialize<T, V, S extends ISerializer<T>>(serializer: S, value: V): T {
|
||||||
return value.constructor[Symbol.metadata]?.serde
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serialize<T, V, S extends ISerializer<T>>(serializer: S, value: V, optionsGetter: (value: V) => Nullable<SerdeOptions> = defaultGetter): T {
|
|
||||||
switch (typeof value) {
|
switch (typeof value) {
|
||||||
case 'string': return serializer.serializeString(value)
|
case 'string': return serializer.serializeString(value)
|
||||||
case 'number': return serializer.serializeNumber(value)
|
case 'number': return serializer.serializeNumber(value)
|
||||||
|
@ -39,11 +31,10 @@ export function serialize<T, V, S extends ISerializer<T>>(serializer: S, value:
|
||||||
case 'symbol': return serializer.serializeSymbol(value)
|
case 'symbol': return serializer.serializeSymbol(value)
|
||||||
case 'undefined': return serializer.serializeNull()
|
case 'undefined': return serializer.serializeNull()
|
||||||
case 'object':
|
case 'object':
|
||||||
const options = optionsGetter(value)
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case value == null: return serializer.serializeNull()
|
case value == null: return serializer.serializeNull()
|
||||||
case !isPlainObject(value): return serializeClass(serializer, value, options)
|
case !isPlainObject(value): return serializeClass(serializer, value)
|
||||||
default: return serializeObject(serializer.serializeObject(), value, options)
|
default: return serializeObject(serializer.serializeObject(), value)
|
||||||
}
|
}
|
||||||
default: throw new UnhandledTypeError(serializer, value)
|
default: throw new UnhandledTypeError(serializer, value)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue