prepare to add options

This commit is contained in:
Rowan 2025-05-22 03:35:53 -05:00
parent a163468b7f
commit da1fad1cd0
36 changed files with 1262 additions and 805 deletions

34
dist/de/forward.d.ts vendored Normal file
View file

@ -0,0 +1,34 @@
import { Deserialize, IDeserializer, IterableAccess, IVisitor, MapAccess } from './interface';
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;
}

134
dist/de/forward.js vendored Normal file
View file

@ -0,0 +1,134 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Forward = exports.ForwardIterableAccess = exports.ForwardMapAccess = void 0;
const utils_1 = require("../utils");
const interface_1 = require("./interface");
class ForwardMapAccess extends interface_1.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 utils_1.IterResult.Next(this.keys[this.kindex++]);
}
else {
return utils_1.IterResult.Done();
}
}
nextValue() {
if (this.vindex < this.values.length) {
return utils_1.IterResult.Next(this.values[this.vindex++]);
}
else {
return utils_1.IterResult.Done();
}
}
}
exports.ForwardMapAccess = ForwardMapAccess;
class ForwardIterableAccess extends interface_1.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 utils_1.IterResult.Next(this.items[this.index++]);
}
else {
return utils_1.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;

17
dist/de/generic.d.ts vendored Normal file
View file

@ -0,0 +1,17 @@
import { IDeserializer, IIterableAccess, IMapAccess, IVisitor } from './interface';
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;
}

58
dist/de/generic.js vendored Normal file
View file

@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenericVisitor = exports.GenericSeed = void 0;
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;

105
dist/de/impl.d.ts vendored
View file

@ -1,107 +1,4 @@
import { Registry } from '../registry';
import { Constructor } from '../utils';
type Nullable<T> = T | undefined;
export declare class IterResult {
static Next<T>(value: T): IteratorResult<T>;
static Done<T>(): IteratorResult<T>;
}
export interface IMapAccess {
nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>;
nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>;
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]>;
nextKey<T>(): IteratorResult<T>;
nextValue<V>(): IteratorResult<V>;
nextEntry<K, V>(): IteratorResult<[K, V]>;
sizeHint?(): Nullable<number>;
}
export declare abstract class MapAccess {
abstract nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>;
abstract nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>;
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]>;
abstract nextKey<T>(): IteratorResult<T>;
abstract nextValue<V>(): IteratorResult<V>;
nextEntry<K, V>(): IteratorResult<[K, V]>;
}
export interface IIterableAccess {
nextElement<T>(): IteratorResult<T>;
sizeHint?(): Nullable<number>;
}
export declare abstract class IterableAccess implements IIterableAccess {
abstract nextElement<T>(): IteratorResult<T>;
}
export interface IVisitor<T> {
visitBoolean(value: boolean): T;
visitNumber(value: number): T;
visitBigInt(value: bigint): T;
visitString(value: string): T;
visitSymbol(value: symbol): T;
visitNull(): T;
visitObject(access: IMapAccess): T;
visitIterable(access: IIterableAccess): T;
}
export declare class GenericSeed<T> {
readonly visitor: IVisitor<T>;
constructor(visitor?: IVisitor<T>);
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor?: IVisitor<T>): T;
deserialize<D extends IDeserializer>(deserializer: D): T;
}
export declare class GenericVisitor<T> implements IVisitor<T> {
visitBoolean(value: boolean): T;
visitNumber(value: number): T;
visitBigInt(value: bigint): T;
visitString(value: string): T;
visitSymbol(value: symbol): T;
visitNull(): T;
visitObject(access: IMapAccess): T;
visitIterable(access: IIterableAccess): T;
}
export interface IDeserializer {
deserializeAny<T, V extends IVisitor<T>>(visitor: V): T;
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T;
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T;
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T;
deserializeString<T, V extends IVisitor<T>>(visitor: V): T;
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T;
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T;
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T;
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T;
deserializeFunction<T, V extends IVisitor<T>>(visitor: V): T;
}
export declare class ForwardMapAccess extends MapAccess {
private readonly keys;
private readonly values;
private kindex;
private vindex;
constructor(keys: string[], values: any[]);
static fromObject(obj: object): ForwardMapAccess;
nextKeySeed<T, K extends Deserialize>(_seed: K): IteratorResult<T>;
nextValueSeed<T, V extends Deserialize>(_seed: V): IteratorResult<T>;
nextKey<T>(): IteratorResult<T>;
nextValue<V>(): IteratorResult<V>;
}
export declare class ForwardIterableAccess extends IterableAccess {
private readonly items;
private index;
constructor(items: any[]);
nextElement<T>(): IteratorResult<T>;
}
export declare class Forward implements IDeserializer {
private readonly value;
constructor(value: any);
static with(value: any): Forward;
deserializeAny<T, V extends IVisitor<T>>(_visitor: V): T;
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T;
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T;
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T;
deserializeString<T, V extends IVisitor<T>>(visitor: V): T;
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T;
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T;
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T;
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T;
deserializeFunction<T, V extends IVisitor<T>>(_visitor: V): T;
}
export interface Deserialize {
<T>(deserializer: IDeserializer): T;
}
import { IDeserializer } from './interface';
export declare function deserialize<T, D extends IDeserializer>(deserializer: D, into: Constructor<T>, registry?: Registry): T;
export {};

220
dist/de/impl.js vendored
View file

@ -1,227 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Forward = exports.ForwardIterableAccess = exports.ForwardMapAccess = exports.GenericVisitor = exports.GenericSeed = exports.IterableAccess = exports.MapAccess = exports.IterResult = void 0;
exports.deserialize = deserialize;
const registry_1 = require("../registry");
class IterResult {
static Next(value) {
return { done: false, value };
}
static Done() {
return { done: true, value: undefined };
}
}
exports.IterResult = IterResult;
class MapAccess {
nextEntrySeed(kseed, vseed) {
const key = this.nextKeySeed(kseed);
if (!key.done) {
const value = this.nextValueSeed(vseed);
if (!value.done) {
return IterResult.Next([key.value, value.value]);
}
}
return IterResult.Done();
}
nextEntry() {
const key = this.nextKey();
if (!key.done) {
const value = this.nextValue();
if (!value.done) {
return IterResult.Next([key.value, value.value]);
}
}
return IterResult.Done();
}
}
exports.MapAccess = MapAccess;
class IterableAccess {
}
exports.IterableAccess = IterableAccess;
class GenericSeed {
constructor(visitor = new GenericVisitor()) {
Object.defineProperty(this, "visitor", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.visitor = visitor;
}
static deserialize(deserializer, visitor = new GenericVisitor()) {
return deserializer.deserializeAny(visitor);
}
deserialize(deserializer) {
return GenericSeed.deserialize(deserializer, this.visitor);
}
}
exports.GenericSeed = GenericSeed;
class GenericVisitor {
visitBoolean(value) {
return value;
}
visitNumber(value) {
return value;
}
visitBigInt(value) {
return value;
}
visitString(value) {
return value;
}
visitSymbol(value) {
return value;
}
visitNull() {
return null;
}
visitObject(access) {
const result = [];
let entry;
while ((entry = access.nextEntry()) && !entry.done) {
result.push(entry.value);
}
return Object.fromEntries(result);
}
visitIterable(access) {
const result = [];
let element;
while ((element = access.nextElement())) {
result.push(element);
}
return result;
}
}
exports.GenericVisitor = GenericVisitor;
class ForwardMapAccess extends MapAccess {
constructor(keys, values) {
super();
Object.defineProperty(this, "keys", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "values", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "kindex", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
Object.defineProperty(this, "vindex", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
this.keys = keys;
this.values = values;
}
static fromObject(obj) {
return new ForwardMapAccess(Object.keys(obj), Object.values(obj));
}
nextKeySeed(_seed) {
return this.nextKey();
}
nextValueSeed(_seed) {
return this.nextValue();
}
nextKey() {
if (this.kindex < this.keys.length) {
return IterResult.Next(this.keys[this.kindex++]);
}
else {
return IterResult.Done();
}
}
nextValue() {
if (this.vindex < this.values.length) {
return IterResult.Next(this.values[this.vindex++]);
}
else {
return IterResult.Done();
}
}
}
exports.ForwardMapAccess = ForwardMapAccess;
class ForwardIterableAccess extends IterableAccess {
constructor(items) {
super();
Object.defineProperty(this, "items", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "index", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
this.items = items;
}
nextElement() {
if (this.index < this.items.length) {
return IterResult.Next(this.items[this.index++]);
}
else {
return IterResult.Done();
}
}
}
exports.ForwardIterableAccess = ForwardIterableAccess;
class Forward {
constructor(value) {
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.value = value;
}
static with(value) {
return new this(value);
}
deserializeAny(_visitor) {
throw new Error("Can't forward to deserializeAny");
}
deserializeBoolean(visitor) {
return visitor.visitBoolean(this.value);
}
deserializeNumber(visitor) {
return visitor.visitNumber(this.value);
}
deserializeBigInt(visitor) {
return visitor.visitBigInt(this.value);
}
deserializeString(visitor) {
return visitor.visitString(this.value);
}
deserializeSymbol(visitor) {
return visitor.visitSymbol(this.value);
}
deserializeNull(visitor) {
return visitor.visitNull();
}
deserializeObject(visitor) {
return visitor.visitObject(ForwardMapAccess.fromObject(this.value));
}
deserializeIterable(visitor) {
return visitor.visitIterable(new ForwardIterableAccess(this.value));
}
deserializeFunction(_visitor) {
throw new Error('Method not implemented.');
}
}
exports.Forward = Forward;
function deserialize(deserializer, into, registry = registry_1.GlobalRegistry) {
const de = registry.deserializers.get(into);
if (de == null) {

4
dist/de/index.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
export * from './forward';
export * from './generic';
export * from './impl';
export * from './interface';

20
dist/de/index.js vendored Normal file
View file

@ -0,0 +1,20 @@
"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("./forward"), exports);
__exportStar(require("./generic"), exports);
__exportStar(require("./impl"), exports);
__exportStar(require("./interface"), exports);

50
dist/de/interface.d.ts vendored Normal file
View file

@ -0,0 +1,50 @@
import { Nullable } from "../utils";
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 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 interface Deserialize {
<T>(deserializer: IDeserializer): T;
}

30
dist/de/interface.js vendored Normal file
View file

@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IterableAccess = exports.MapAccess = void 0;
const utils_1 = require("../utils");
class MapAccess {
nextEntrySeed(kseed, vseed) {
const key = this.nextKeySeed(kseed);
if (!key.done) {
const value = this.nextValueSeed(vseed);
if (!value.done) {
return utils_1.IterResult.Next([key.value, value.value]);
}
}
return utils_1.IterResult.Done();
}
nextEntry() {
const key = this.nextKey();
if (!key.done) {
const value = this.nextValue();
if (!value.done) {
return utils_1.IterResult.Next([key.value, value.value]);
}
}
return utils_1.IterResult.Done();
}
}
exports.MapAccess = MapAccess;
class IterableAccess {
}
exports.IterableAccess = IterableAccess;

1
dist/index.d.ts vendored
View file

@ -1,3 +1,4 @@
import '@tsmetadata/polyfill';
export * as ser from './ser/impl';
export * as de from './de/impl';
export * from './case';

1
dist/index.js vendored
View file

@ -37,6 +37,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.de = exports.ser = void 0;
require("@tsmetadata/polyfill");
exports.ser = __importStar(require("./ser/impl"));
exports.de = __importStar(require("./de/impl"));
__exportStar(require("./case"), exports);

4
dist/registry.d.ts vendored
View file

@ -1,5 +1,5 @@
import { Deserialize } from './de/impl';
import { Serialize } from './ser/impl';
import { Deserialize } from './de';
import { Serialize } from './ser';
export declare class Registry {
serializers: Map<Function, Serialize<any>>;
deserializers: Map<Function, Deserialize>;

59
dist/ser/decorator.d.ts vendored Normal file
View file

@ -0,0 +1,59 @@
import { CaseConvention } from '../case';
import { Deserialize } from '../de';
import { Nullable } from '../utils';
import { Serialize } from './interface';
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;
}
export declare const Stage: Readonly<{
readonly Serialize: 0;
readonly Deserialize: 1;
}>;
export type Stage = typeof Stage[keyof typeof Stage];
export declare class SerdeOptions {
readonly target: Nullable<any>;
readonly container: ContainerOptions;
readonly properties: Map<string, PropertyOptions>;
constructor(target: any, container?: ContainerOptions, properties?: Map<string, PropertyOptions>);
private getClassName;
getSerializedClassName(defaultName?: string): string | undefined;
getDeserializedClassName(defaultName?: string): string | undefined;
private applyPropertyCase;
private getPropertyName;
getSerializationPropertyName(property: string): string;
getDeserializationPropertyName(property: string): string;
private shouldSkip;
shouldSerialize(property: string, value: any): boolean;
shouldDeserialize(property: string, value: any): boolean;
defaultFor(property: string): any;
}
export {};

146
dist/ser/decorator.js vendored Normal file
View file

@ -0,0 +1,146 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SerdeOptions = exports.Stage = void 0;
const case_1 = require("../case");
const utils_1 = require("../utils");
exports.Stage = Object.freeze({
Serialize: 0,
Deserialize: 1
});
class SerdeOptions {
constructor(target, container = {}, properties = new Map()) {
Object.defineProperty(this, "target", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "container", {
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.container = container;
this.properties = properties;
}
getClassName(stage, defaultName) {
var _a, _b, _c, _d;
if (defaultName === void 0) { defaultName = (_b = (_a = this.target) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name; }
if ((0, utils_1.isString)(this.container.rename)) {
return this.container.rename;
}
else if (stage === exports.Stage.Serialize && (0, utils_1.isString)((_c = this.container.rename) === null || _c === void 0 ? void 0 : _c.serialize)) {
return this.container.rename.serialize;
}
else if (stage === exports.Stage.Deserialize && (0, utils_1.isString)((_d = this.container.rename) === null || _d === void 0 ? void 0 : _d.deserialize)) {
return this.container.rename.serialize;
}
else {
return defaultName;
}
}
getSerializedClassName(defaultName) {
var _a, _b;
if (defaultName === void 0) { defaultName = (_b = (_a = this.target) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name; }
return this.getClassName(exports.Stage.Serialize, defaultName);
}
getDeserializedClassName(defaultName) {
var _a, _b;
if (defaultName === void 0) { defaultName = (_b = (_a = this.target) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name; }
return this.getClassName(exports.Stage.Deserialize, defaultName);
}
applyPropertyCase(stage, property) {
var _a, _b;
if ((0, utils_1.isNumber)(this.container.renameAll)) {
return (0, case_1.convertCase)(property, this.container.renameAll);
}
else if (stage === exports.Stage.Serialize && (0, utils_1.isNumber)((_a = this.container.renameAll) === null || _a === void 0 ? void 0 : _a.serialize)) {
return (0, case_1.convertCase)(property, this.container.renameAll.serialize);
}
else if (stage === exports.Stage.Deserialize && (0, utils_1.isNumber)((_b = this.container.renameAll) === null || _b === void 0 ? void 0 : _b.deserialize)) {
return (0, case_1.convertCase)(property, this.container.renameAll.deserialize);
}
else {
return property;
}
}
getPropertyName(stage, property) {
var _a, _b;
const options = this.properties.get(property);
if (options == null) {
return property;
}
else 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;
}
else {
return property;
}
}
getSerializationPropertyName(property) {
const name = this.getPropertyName(exports.Stage.Serialize, property);
return this.applyPropertyCase(exports.Stage.Serialize, name);
}
getDeserializationPropertyName(property) {
const name = this.getPropertyName(exports.Stage.Deserialize, property);
return this.applyPropertyCase(exports.Stage.Deserialize, name);
}
shouldSkip(stage, property, value) {
var _a, _b, _c, _d;
const options = this.properties.get(property);
if (options == null) {
return false;
}
else if (typeof options.skip === 'boolean') {
return options.skip;
}
else if ((0, utils_1.isFunction)(options.skip)) {
return options.skip(value);
}
else if (stage === exports.Stage.Serialize && typeof ((_a = options.skip) === null || _a === void 0 ? void 0 : _a.serialize) === 'boolean') {
return options.skip.serialize;
}
else if (stage === exports.Stage.Serialize && (0, utils_1.isFunction)((_b = options.skip) === null || _b === void 0 ? void 0 : _b.serialize)) {
return options.skip.serialize(value);
}
else if (stage === exports.Stage.Deserialize && typeof ((_c = options.skip) === null || _c === void 0 ? void 0 : _c.deserialize) === 'boolean') {
return options.skip.deserialize;
}
else if (stage === exports.Stage.Deserialize && (0, utils_1.isFunction)((_d = options.skip) === null || _d === void 0 ? void 0 : _d.deserialize)) {
return options.skip.deserialize(value);
}
else {
return false;
}
}
shouldSerialize(property, value) {
return !this.shouldSkip(exports.Stage.Serialize, property, value);
}
shouldDeserialize(property, value) {
return !this.shouldSkip(exports.Stage.Deserialize, property, value);
}
defaultFor(property) {
const options = this.properties.get(property);
if (options != null && (0, utils_1.isFunction)(options.default)) {
return options.default();
}
else if ((0, utils_1.isFunction)(this.container.default)) {
return this.container.default();
}
}
}
exports.SerdeOptions = SerdeOptions;

50
dist/ser/impl.d.ts vendored
View file

@ -1,46 +1,4 @@
export interface ISerializeObject<T> {
serializeKey(key: string): void;
serializeValue<U>(value: U): void;
serializeEntry<U>(key: string, value: U): void;
end(): T;
}
export declare abstract class SerializeObject<T> implements ISerializeObject<T> {
abstract serializeKey(key: string): void;
abstract serializeValue<U>(value: U): void;
abstract end(): T;
serializeEntry<U>(key: string, value: U): void;
}
export interface ISerializeIterable<T> {
serializeElement<U>(value: U): void;
end(): T;
}
export declare abstract class SerializeIterable<T> implements ISerializeIterable<T> {
abstract serializeElement<U>(value: U): void;
abstract end(): T;
}
export interface ISerializer<T> {
serializeAny(value: any): T;
serializeBoolean(value: boolean): T;
serializeNumber(value: number): T;
serializeBigInt(value: bigint): T;
serializeString(value: string): T;
serializeSymbol(value: symbol): T;
serializeNull(): T;
serializeObject(): ISerializeObject<T>;
serializeClass(name: string): ISerializeObject<T>;
}
export declare class Serializer<T> implements ISerializer<T> {
serializeAny(_value: any): T;
serializeBoolean(_value: boolean): T;
serializeNumber(_value: number): T;
serializeBigInt(_value: bigint): T;
serializeString(_value: string): T;
serializeSymbol(_value: symbol): T;
serializeNull(): T;
serializeObject(): ISerializeObject<T>;
serializeClass(_name: string): ISerializeObject<T>;
}
export interface Serialize<T> {
<U, S extends Serializer<U>>(serializer: S, value: T): U;
}
export declare function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V): T;
import { SerdeOptions } from './decorator';
import { Serializer } from './interface';
import { Nullable } from '../utils';
export declare function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V, optionsGetter?: (value: V) => Nullable<SerdeOptions>): T;

76
dist/ser/impl.js vendored
View file

@ -1,77 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Serializer = exports.SerializeIterable = exports.SerializeObject = void 0;
exports.serialize = serialize;
class SerializeObject {
serializeEntry(key, value) {
this.serializeKey(key);
this.serializeValue(value);
}
}
exports.SerializeObject = SerializeObject;
class SerializeIterable {
}
exports.SerializeIterable = SerializeIterable;
class Serializer {
serializeAny(_value) {
throw new Error("Method not implemented.");
}
serializeBoolean(_value) {
throw new Error('Method not implemented.');
}
serializeNumber(_value) {
throw new Error('Method not implemented.');
}
serializeBigInt(_value) {
throw new Error('Method not implemented.');
}
serializeString(_value) {
throw new Error('Method not implemented.');
}
serializeSymbol(_value) {
throw new Error('Method not implemented.');
}
serializeNull() {
throw new Error('Method not implemented.');
}
serializeObject() {
throw new Error('Method not implemented.');
}
serializeClass(_name) {
throw new Error('Method not implemented.');
}
}
exports.Serializer = Serializer;
const isPlainObject = (value) => (value === null || value === void 0 ? void 0 : value.constructor) === Object;
const utils_1 = require("../utils");
class UnhandledTypeError extends TypeError {
constructor(serializer, value) {
super(`unhandled type: "${typeof value}" for serializer ${serializer.constructor.name}`);
super(`unhandled type: '${typeof value}' for serializer ${serializer.constructor.name}`);
}
}
function serializeObject(serializer, obj) {
function serializeObject(serializer, obj, options) {
for (const key in obj) {
serializer.serializeEntry(key, obj[key]);
}
return serializer.end();
}
function serializeClass(serializer, value) {
function serializeClass(serializer, value, options) {
const name = value.constructor.name;
const ser = serializer.serializeClass(name);
return serializeObject(ser, value);
return serializeObject(ser, value, options);
}
function serialize(serializer, value) {
const defaultGetter = (value) => {
var _a;
return (_a = value[Symbol.metadata]) === null || _a === void 0 ? void 0 : _a.serde;
};
function serialize(serializer, value, optionsGetter = defaultGetter) {
switch (typeof value) {
case "string": return serializer.serializeString(value);
case "number": return serializer.serializeNumber(value);
case "bigint": return serializer.serializeBigInt(value);
case "boolean": return serializer.serializeBoolean(value);
case "symbol": return serializer.serializeSymbol(value);
case "undefined": return serializer.serializeNull();
case "object":
case 'string': return serializer.serializeString(value);
case 'number': return serializer.serializeNumber(value);
case 'bigint': return serializer.serializeBigInt(value);
case 'boolean': return serializer.serializeBoolean(value);
case 'symbol': return serializer.serializeSymbol(value);
case 'undefined': return serializer.serializeNull();
case 'object':
const options = optionsGetter(value);
switch (true) {
case value == null: return serializer.serializeNull();
case !isPlainObject(value): return serializeClass(serializer, value);
default: return serializeObject(serializer.serializeObject(), value);
case !(0, utils_1.isPlainObject)(value): return serializeClass(serializer, value, options);
default: return serializeObject(serializer.serializeObject(), value, options);
}
default: throw new UnhandledTypeError(serializer, value);
}

2
dist/ser/index.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
export * from './impl';
export * from './interface';

18
dist/ser/index.js vendored Normal file
View file

@ -0,0 +1,18 @@
"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("./impl"), exports);
__exportStar(require("./interface"), exports);

45
dist/ser/interface.d.ts vendored Normal file
View file

@ -0,0 +1,45 @@
export interface ISerializeObject<T> {
serializeKey(key: string): void;
serializeValue<U>(value: U): void;
serializeEntry<U>(key: string, value: U): void;
end(): T;
}
export declare abstract class SerializeObject<T> implements ISerializeObject<T> {
abstract serializeKey(key: string): void;
abstract serializeValue<U>(value: U): void;
abstract end(): T;
serializeEntry<U>(key: string, value: U): void;
}
export interface ISerializeIterable<T> {
serializeElement<U>(value: U): void;
end(): T;
}
export declare abstract class SerializeIterable<T> implements ISerializeIterable<T> {
abstract serializeElement<U>(value: U): void;
abstract end(): T;
}
export interface ISerializer<T> {
serializeAny(value: any): T;
serializeBoolean(value: boolean): T;
serializeNumber(value: number): T;
serializeBigInt(value: bigint): T;
serializeString(value: string): T;
serializeSymbol(value: symbol): T;
serializeNull(): T;
serializeObject(): ISerializeObject<T>;
serializeClass(name: string): ISerializeObject<T>;
}
export declare class Serializer<T> implements ISerializer<T> {
serializeAny(_value: any): T;
serializeBoolean(_value: boolean): T;
serializeNumber(_value: number): T;
serializeBigInt(_value: bigint): T;
serializeString(_value: string): T;
serializeSymbol(_value: symbol): T;
serializeNull(): T;
serializeObject(): ISerializeObject<T>;
serializeClass(_name: string): ISerializeObject<T>;
}
export interface Serialize<T> {
<U, S extends Serializer<U>>(serializer: S, value: T): U;
}

43
dist/ser/interface.js vendored Normal file
View file

@ -0,0 +1,43 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Serializer = exports.SerializeIterable = exports.SerializeObject = void 0;
class SerializeObject {
serializeEntry(key, value) {
this.serializeKey(key);
this.serializeValue(value);
}
}
exports.SerializeObject = SerializeObject;
class SerializeIterable {
}
exports.SerializeIterable = SerializeIterable;
class Serializer {
serializeAny(_value) {
throw new Error("Method not implemented.");
}
serializeBoolean(_value) {
throw new Error('Method not implemented.');
}
serializeNumber(_value) {
throw new Error('Method not implemented.');
}
serializeBigInt(_value) {
throw new Error('Method not implemented.');
}
serializeString(_value) {
throw new Error('Method not implemented.');
}
serializeSymbol(_value) {
throw new Error('Method not implemented.');
}
serializeNull() {
throw new Error('Method not implemented.');
}
serializeObject() {
throw new Error('Method not implemented.');
}
serializeClass(_name) {
throw new Error('Method not implemented.');
}
}
exports.Serializer = Serializer;

4
dist/utils.d.ts vendored
View file

@ -13,3 +13,7 @@ export declare function isIterable(value: any): value is Iterable<any>;
export declare function isString(value: any): value is string;
export declare function isNumber(value: any): value is number;
export type Constructor<T = any> = new (...args: any[]) => T;
export declare class IterResult {
static Next<T>(value: T): IteratorResult<T>;
static Done<T>(): IteratorResult<T>;
}

10
dist/utils.js vendored
View file

@ -1,5 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IterResult = void 0;
exports.staticImplements = staticImplements;
exports.isPlainObject = isPlainObject;
exports.isFunction = isFunction;
@ -24,3 +25,12 @@ function isString(value) {
function isNumber(value) {
return !isNaN(value);
}
class IterResult {
static Next(value) {
return { done: false, value };
}
static Done() {
return { done: true, value: undefined };
}
}
exports.IterResult = IterResult;

View file

@ -9,12 +9,12 @@
"default": "./dist/index.js"
},
"./ser": {
"types": "./dist/ser/impl.d.ts",
"default": "./dist/ser/impl.js"
"types": "./dist/ser/index.d.ts",
"default": "./dist/ser/index.js"
},
"./de": {
"types": "./dist/de/impl.d.ts",
"default": "./dist/de/impl.js"
"types": "./dist/de/index.d.ts",
"default": "./dist/de/index.js"
},
"./utils": {
"types": "./dist/utils.d.ts",

118
src/de/forward.ts Normal file
View file

@ -0,0 +1,118 @@
import { IterResult } from '../utils'
import { Deserialize, IDeserializer, IterableAccess, IVisitor, MapAccess } from './interface'
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.')
}
}

68
src/de/generic.ts Normal file
View file

@ -0,0 +1,68 @@
import { IDeserializer, IIterableAccess, IMapAccess, IVisitor } from './interface'
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
}
}

View file

@ -1,279 +1,6 @@
import { GlobalRegistry, Registry } from '../registry'
import { Constructor } from '../utils'
type Nullable<T> = T | undefined
export class IterResult {
static Next<T>(value: T): IteratorResult<T> {
return { done: false, value }
}
static Done<T>(): IteratorResult<T> {
return { done: true, value: undefined }
}
}
export interface IMapAccess {
nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>
nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]>
nextKey<T>(): IteratorResult<T>
nextValue<V>(): IteratorResult<V>
nextEntry<K, V>(): IteratorResult<[K, V]>
sizeHint?(): Nullable<number>
}
export abstract class MapAccess {
abstract nextKeySeed<T, K extends Deserialize>(seed: K): IteratorResult<T>
abstract nextValueSeed<T, V extends Deserialize>(seed: V): IteratorResult<T>
nextEntrySeed<TK, TV, K extends Deserialize, V extends Deserialize>(kseed: K, vseed: V): IteratorResult<[TK, TV]> {
const key = this.nextKeySeed(kseed) as IteratorResult<TK>
if (!key.done) {
const value = this.nextValueSeed(vseed) as IteratorResult<TV>
if (!value.done) {
return IterResult.Next([key.value, value.value])
}
}
return IterResult.Done()
}
abstract nextKey<T>(): IteratorResult<T>
abstract nextValue<V>(): IteratorResult<V>
nextEntry<K, V>(): IteratorResult<[K, V]> {
const key = this.nextKey() as IteratorResult<K>
if (!key.done) {
const value = this.nextValue() as IteratorResult<V>
if (!value.done) {
return IterResult.Next([key.value, value.value])
}
}
return IterResult.Done()
}
}
export interface IIterableAccess {
nextElement<T>(): IteratorResult<T>
sizeHint?(): Nullable<number>
}
export abstract class IterableAccess implements IIterableAccess {
abstract nextElement<T>(): IteratorResult<T>
}
export interface IVisitor<T> {
visitBoolean(value: boolean): T
visitNumber(value: number): T
visitBigInt(value: bigint): T
visitString(value: string): T
visitSymbol(value: symbol): T
visitNull(): T
visitObject(access: IMapAccess): T
visitIterable(access: IIterableAccess): T
}
export class GenericSeed<T> {
readonly visitor: IVisitor<T>
constructor(visitor: IVisitor<T> = new GenericVisitor()) {
this.visitor = visitor
}
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor: IVisitor<T> = new GenericVisitor()): T {
return deserializer.deserializeAny(visitor)
}
deserialize<D extends IDeserializer>(deserializer: D): T {
return GenericSeed.deserialize(deserializer, this.visitor)
}
}
export class GenericVisitor<T> implements IVisitor<T> {
visitBoolean(value: boolean): T {
return value as T
}
visitNumber(value: number): T {
return value as T
}
visitBigInt(value: bigint): T {
return value as T
}
visitString(value: string): T {
return value as T
}
visitSymbol(value: symbol): T {
return value as T
}
visitNull(): T {
return null as T
}
visitObject(access: IMapAccess): T {
const result = []
let entry
while ((entry = access.nextEntry()) && !entry.done) {
result.push(entry.value)
}
return Object.fromEntries(result)
}
visitIterable(access: IIterableAccess): T {
const result = []
let element
while ((element = access.nextElement())) {
result.push(element)
}
return result as T
}
}
export interface IDeserializer {
deserializeAny<T, V extends IVisitor<T>>(visitor: V): T
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T
deserializeString<T, V extends IVisitor<T>>(visitor: V): T
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T
deserializeFunction<T, V extends IVisitor<T>>(visitor: V): T
}
export class ForwardMapAccess extends MapAccess {
private readonly keys: string[]
private readonly values: any[]
private kindex: number = 0
private vindex: number = 0
constructor(keys: string[], values: any[]) {
super()
this.keys = keys
this.values = values
}
static fromObject(obj: object): ForwardMapAccess {
return new ForwardMapAccess(
Object.keys(obj),
Object.values(obj)
)
}
nextKeySeed<T, K extends Deserialize>(_seed: K): IteratorResult<T> {
return this.nextKey()
}
nextValueSeed<T, V extends Deserialize>(_seed: V): IteratorResult<T> {
return this.nextValue()
}
nextKey<T>(): IteratorResult<T> {
if (this.kindex < this.keys.length) {
return IterResult.Next(this.keys[this.kindex++]) as IteratorResult<T>
} else {
return IterResult.Done()
}
}
nextValue<V>(): IteratorResult<V> {
if (this.vindex < this.values.length) {
return IterResult.Next(this.values[this.vindex++]) as IteratorResult<V>
} else {
return IterResult.Done()
}
}
}
export class ForwardIterableAccess extends IterableAccess {
private readonly items: any[]
private index: number = 0
constructor(items: any[]) {
super()
this.items = items
}
nextElement<T>(): IteratorResult<T> {
if (this.index < this.items.length) {
return IterResult.Next(this.items[this.index++]) as IteratorResult<T>
} else {
return IterResult.Done()
}
}
}
export class Forward implements IDeserializer {
private readonly value: any
constructor(value: any) {
this.value = value
}
static with(value: any): Forward {
return new this(value)
}
deserializeAny<T, V extends IVisitor<T>>(_visitor: V): T {
throw new Error("Can't forward to deserializeAny")
}
deserializeBoolean<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitBoolean(this.value)
}
deserializeNumber<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitNumber(this.value)
}
deserializeBigInt<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitBigInt(this.value)
}
deserializeString<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitString(this.value)
}
deserializeSymbol<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitSymbol(this.value)
}
deserializeNull<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitNull()
}
deserializeObject<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitObject(ForwardMapAccess.fromObject(this.value))
}
deserializeIterable<T, V extends IVisitor<T>>(visitor: V): T {
return visitor.visitIterable(new ForwardIterableAccess(this.value))
}
deserializeFunction<T, V extends IVisitor<T>>(_visitor: V): T {
throw new Error('Method not implemented.')
}
}
export interface Deserialize {
<T>(deserializer: IDeserializer): T
}
import { IDeserializer } from './interface'
export function deserialize<T, D extends IDeserializer>(deserializer: D, into: Constructor<T>, registry: Registry = GlobalRegistry): T {
const de = registry.deserializers.get(into)

5
src/de/index.ts Normal file
View file

@ -0,0 +1,5 @@
export * from './forward'
export * from './generic'
export * from './impl'
export * from './interface'

85
src/de/interface.ts Normal file
View file

@ -0,0 +1,85 @@
import { IterResult, Nullable } from "../utils"
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 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 interface Deserialize {
<T>(deserializer: IDeserializer): T
}

View file

@ -1,3 +1,5 @@
import '@tsmetadata/polyfill'
export * as ser from './ser/impl'
export * as de from './de/impl'
export * from './case'

View file

@ -1,5 +1,5 @@
import { Deserialize } from './de/impl'
import { Serialize } from './ser/impl'
import { Deserialize } from './de'
import { Serialize } from './ser'
export class Registry {
serializers: Map<Function, Serialize<any>> = new Map()

157
src/ser/decorator.ts Normal file
View file

@ -0,0 +1,157 @@
import { CaseConvention, convertCase } from '../case'
import { Deserialize } from '../de'
import { isFunction, isNumber, isString, Nullable } from '../utils'
import { Serialize } from './interface'
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
}
export const Stage = Object.freeze({
Serialize: 0,
Deserialize: 1
} as const)
export type Stage = typeof Stage[keyof typeof Stage]
export class SerdeOptions {
readonly target: Nullable<any>
readonly container: ContainerOptions
readonly properties: Map<string, PropertyOptions>
constructor(target: any, container: ContainerOptions = {}, properties: Map<string, PropertyOptions> = new Map()) {
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)
}
private applyPropertyCase(stage: Stage, property: string) {
if (isNumber(this.container.renameAll)) {
return convertCase(property, this.container.renameAll)
} else if (stage === Stage.Serialize && isNumber(this.container.renameAll?.serialize)) {
return convertCase(property, this.container.renameAll.serialize)
} else if (stage === Stage.Deserialize && isNumber(this.container.renameAll?.deserialize)) {
return convertCase(property, this.container.renameAll.deserialize)
} else {
return property
}
}
private getPropertyName(stage: Stage, property: string) {
const options = this.properties.get(property)
if (options == null) {
return property
} 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
} else {
return property
}
}
getSerializationPropertyName(property: string) {
const name = this.getPropertyName(Stage.Serialize, property)
return this.applyPropertyCase(Stage.Serialize, name)
}
getDeserializationPropertyName(property: string) {
const name = this.getPropertyName(Stage.Deserialize, property)
return this.applyPropertyCase(Stage.Deserialize, name)
}
private 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
}
}
shouldSerialize(property: string, value: any) {
return !this.shouldSkip(Stage.Serialize, property, value)
}
shouldDeserialize(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()
}
}
}

View file

@ -1,94 +1,14 @@
export interface ISerializeObject<T> {
serializeKey(key: string): void
serializeValue<U>(value: U): void
serializeEntry<U>(key: string, value: U): void
end(): T
}
export 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 {
this.serializeKey(key)
this.serializeValue(value)
}
}
export interface ISerializeIterable<T> {
serializeElement<U>(value: U): void
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.")
}
serializeBoolean(_value: boolean): T {
throw new Error('Method not implemented.')
}
serializeNumber(_value: number): T {
throw new Error('Method not implemented.')
}
serializeBigInt(_value: bigint): T {
throw new Error('Method not implemented.')
}
serializeString(_value: string): T {
throw new Error('Method not implemented.')
}
serializeSymbol(_value: symbol): T {
throw new Error('Method not implemented.')
}
serializeNull(): T {
throw new Error('Method not implemented.')
}
serializeObject(): ISerializeObject<T> {
throw new Error('Method not implemented.')
}
serializeClass(_name: string): ISerializeObject<T> {
throw new Error('Method not implemented.')
}
}
export interface Serialize<T> {
<U, S extends Serializer<U>>(serializer: S, value: T): U
}
const isPlainObject = (value: any): value is object => value?.constructor === Object
import { SerdeOptions } from './decorator'
import { ISerializeObject, Serializer } from './interface'
import { isPlainObject, Nullable } from '../utils'
class UnhandledTypeError extends TypeError {
constructor(serializer: Serializer<unknown>, value: any) {
super(`unhandled type: "${typeof value}" for serializer ${serializer.constructor.name}`)
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 {
function serializeObject<T, V extends object, S extends ISerializeObject<T>>(serializer: S, obj: V, options?: SerdeOptions): T {
for (const key in obj) {
serializer.serializeEntry(key, obj[key])
}
@ -96,25 +16,30 @@ function serializeObject<T, V extends object, S extends ISerializeObject<T>>(ser
return serializer.end()
}
function serializeClass<T, V extends object, S extends Serializer<T>>(serializer: S, value: V): T {
function serializeClass<T, V extends object, S extends Serializer<T>>(serializer: S, value: V, options?: SerdeOptions): T {
const name = value.constructor.name
const ser = serializer.serializeClass(name)
return serializeObject(ser, value)
return serializeObject(ser, value, options)
}
export function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V): T {
const defaultGetter = (value: any): Nullable<SerdeOptions> => {
return value[Symbol.metadata]?.serde
}
export function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V, optionsGetter: (value: V) => Nullable<SerdeOptions> = defaultGetter): T {
switch (typeof value) {
case "string": return serializer.serializeString(value)
case "number": return serializer.serializeNumber(value)
case "bigint": return serializer.serializeBigInt(value)
case "boolean": return serializer.serializeBoolean(value)
case "symbol": return serializer.serializeSymbol(value)
case "undefined": return serializer.serializeNull()
case "object":
case 'string': return serializer.serializeString(value)
case 'number': return serializer.serializeNumber(value)
case 'bigint': return serializer.serializeBigInt(value)
case 'boolean': return serializer.serializeBoolean(value)
case 'symbol': return serializer.serializeSymbol(value)
case 'undefined': return serializer.serializeNull()
case 'object':
const options = optionsGetter(value)
switch (true) {
case value == null: return serializer.serializeNull()
case !isPlainObject(value): return serializeClass(serializer, value)
default: return serializeObject(serializer.serializeObject(), value)
case !isPlainObject(value): return serializeClass(serializer, value, options)
default: return serializeObject(serializer.serializeObject(), value, options)
}
default: throw new UnhandledTypeError(serializer, value)
}

3
src/ser/index.ts Normal file
View file

@ -0,0 +1,3 @@
export * from './impl'
export * from './interface'

82
src/ser/interface.ts Normal file
View file

@ -0,0 +1,82 @@
export interface ISerializeObject<T> {
serializeKey(key: string): void
serializeValue<U>(value: U): void
serializeEntry<U>(key: string, value: U): void
end(): T
}
export 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 {
this.serializeKey(key)
this.serializeValue(value)
}
}
export interface ISerializeIterable<T> {
serializeElement<U>(value: U): void
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.")
}
serializeBoolean(_value: boolean): T {
throw new Error('Method not implemented.')
}
serializeNumber(_value: number): T {
throw new Error('Method not implemented.')
}
serializeBigInt(_value: bigint): T {
throw new Error('Method not implemented.')
}
serializeString(_value: string): T {
throw new Error('Method not implemented.')
}
serializeSymbol(_value: symbol): T {
throw new Error('Method not implemented.')
}
serializeNull(): T {
throw new Error('Method not implemented.')
}
serializeObject(): ISerializeObject<T> {
throw new Error('Method not implemented.')
}
serializeClass(_name: string): ISerializeObject<T> {
throw new Error('Method not implemented.')
}
}
export interface Serialize<T> {
<U, S extends Serializer<U>>(serializer: S, value: T): U
}

View file

@ -36,3 +36,13 @@ export function isNumber(value: any): value is number {
export type Constructor<T = any> = new (...args: any[]) => T
export class IterResult {
static Next<T>(value: T): IteratorResult<T> {
return { done: false, value }
}
static Done<T>(): IteratorResult<T> {
return { done: true, value: undefined }
}
}