Compare commits

...

3 commits

Author SHA1 Message Date
a163468b7f refactor for new architecture 2025-05-21 19:33:10 -05:00
5ef148193e wip classes 2025-05-20 02:25:09 -05:00
69e56d2d74 whatever 2025-05-19 13:30:11 -05:00
43 changed files with 880 additions and 1445 deletions

15
dist/de/generic.d.ts vendored
View file

@ -1,15 +0,0 @@
import { Deserialize, Deserializer, IterableAccess, MapAccess, Visitor } from './interface';
export declare class GenericSeed<T> implements Deserialize<T> {
static deserialize<T, D extends Deserializer>(deserializer: D): T;
deserialize<D extends Deserializer>(deserializer: D): T;
}
export declare class GenericVisitor<T> implements Visitor<T> {
visitString(value: string): T;
visitNumber(value: number): T;
visitBigInt(value: bigint): T;
visitBoolean(value: boolean): T;
visitSymbol(value: symbol): T;
visitNull(): T;
visitObject(access: MapAccess): T;
visitIterable(access: IterableAccess): T;
}

103
dist/de/generic.js vendored
View file

@ -1,103 +0,0 @@
"use strict";
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenericVisitor = exports.GenericSeed = void 0;
const utils_1 = require("../utils");
let GenericSeed = (() => {
let _classDecorators = [(0, utils_1.staticImplements)()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
var GenericSeed = _classThis = class {
static deserialize(deserializer) {
return deserializer.deserializeAny(new GenericVisitor());
}
deserialize(deserializer) {
return GenericSeed.deserialize(deserializer);
}
};
__setFunctionName(_classThis, "GenericSeed");
(() => {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
GenericSeed = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
__runInitializers(_classThis, _classExtraInitializers);
})();
return GenericSeed = _classThis;
})();
exports.GenericSeed = GenericSeed;
class GenericVisitor {
visitString(value) {
return value;
}
visitNumber(value) {
return value;
}
visitBigInt(value) {
return value;
}
visitBoolean(value) {
return value;
}
visitSymbol(value) {
return value;
}
visitNull() {
return null;
}
visitObject(access) {
const result = {};
let entry;
while ((entry = access.nextEntry())) {
result[entry[0]] = entry[1];
}
return result;
}
visitIterable(access) {
const result = new Array(access.sizeHint());
let element;
while ((element = access.nextElement())) {
result.push(element);
}
return result;
}
}
exports.GenericVisitor = GenericVisitor;

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

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

244
dist/de/impl.js vendored
View file

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

3
dist/de/index.d.ts vendored
View file

@ -1,3 +0,0 @@
export * from './interface';
export * from './generic';
export * from './mixin';

19
dist/de/index.js vendored
View file

@ -1,19 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./interface"), exports);
__exportStar(require("./generic"), exports);
__exportStar(require("./mixin"), exports);

View file

@ -1,52 +0,0 @@
import { Nullable } from '../utils';
export interface MapAccess {
nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>;
nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>;
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]>;
nextKey<T>(): Nullable<T>;
nextValue<T>(): Nullable<T>;
nextEntry<K, V>(): Nullable<[K, V]>;
}
export declare abstract class DefaultMapAccessImpl implements MapAccess {
abstract nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>;
abstract nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>;
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]>;
nextKey<T>(): Nullable<T>;
nextValue<T>(): Nullable<T>;
nextEntry<K, V>(): Nullable<[K, V]>;
}
export interface IterableAccess {
nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>;
nextElement<T>(): Nullable<T>;
sizeHint(): number;
}
export declare abstract class DefaultIterableAccessImpl implements IterableAccess {
abstract nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>;
nextElement<T>(): Nullable<T>;
sizeHint(): number;
}
export interface Visitor<T> {
visitString(value: string): T;
visitNumber(value: number): T;
visitBigInt(value: bigint): T;
visitBoolean(value: boolean): T;
visitSymbol(value: symbol): T;
visitNull(): T;
visitObject(value: MapAccess): T;
visitIterable?(value: IterableAccess): T;
}
export interface Deserializer {
deserializeAny<T, V extends Visitor<T>>(visitor: V): T;
deserializeString<T, V extends Visitor<T>>(visitor: V): T;
deserializeNumber<T, V extends Visitor<T>>(visitor: V): T;
deserializeBigInt<T, V extends Visitor<T>>(visitor: V): T;
deserializeBoolean<T, V extends Visitor<T>>(visitor: V): T;
deserializeSymbol<T, V extends Visitor<T>>(visitor: V): T;
deserializeNull<T, V extends Visitor<T>>(visitor: V): T;
deserializeObject<T, V extends Visitor<T>>(visitor: V): T;
deserializeClass<T, V extends Visitor<T>>(name: string, fields: string[], visitor: V): T;
deserializeIterable<T, V extends Visitor<T>>(visitor: V): T;
}
export interface Deserialize<T> {
deserialize<D extends Deserializer>(deserializer: D): T;
}

32
dist/de/interface.js vendored
View file

@ -1,32 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultIterableAccessImpl = exports.DefaultMapAccessImpl = void 0;
const generic_1 = require("./generic");
class DefaultMapAccessImpl {
nextEntrySeed(kseed, vseed) {
const key = this.nextKeySeed(kseed);
if (key !== undefined) {
const value = this.nextValueSeed(vseed);
if (value !== undefined) {
return [key, value];
}
}
}
nextKey() {
return this.nextValueSeed((generic_1.GenericSeed));
}
nextValue() {
return this.nextValueSeed((generic_1.GenericSeed));
}
nextEntry() {
return this.nextEntrySeed((generic_1.GenericSeed), (generic_1.GenericSeed));
}
}
exports.DefaultMapAccessImpl = DefaultMapAccessImpl;
class DefaultIterableAccessImpl {
nextElement() {
return this.nextElementSeed((generic_1.GenericSeed));
}
sizeHint() { return 0; }
}
exports.DefaultIterableAccessImpl = DefaultIterableAccessImpl;

9
dist/de/mixin.d.ts vendored
View file

@ -1,9 +0,0 @@
import { Constructor } from '../utils';
import { Deserializer } from './interface';
export declare function deserialize<T, C extends Constructor>(constructor: C): {
new (...args: any[]): {
[x: string]: any;
};
name: string;
deserialize<D extends Deserializer>(deserializer: D): T;
} & C;

76
dist/de/mixin.js vendored
View file

@ -1,76 +0,0 @@
"use strict";
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.deserialize = deserialize;
const utils_1 = require("../utils");
const impl_1 = require("./impl");
function deserialize(constructor) {
let Deserializable = (() => {
let _classDecorators = [(0, utils_1.staticImplements)()];
let _classDescriptor;
let _classExtraInitializers = [];
let _classThis;
let _classSuper = constructor;
var Deserializable = _classThis = class extends _classSuper {
static deserialize(deserializer) {
return (0, impl_1.deserializeWith)(deserializer, this, constructor[Symbol.metadata]);
}
};
__setFunctionName(_classThis, "Deserializable");
(() => {
var _a;
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
Deserializable = _classThis = _classDescriptor.value;
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
})();
Object.defineProperty(_classThis, "name", {
enumerable: true,
configurable: true,
writable: true,
value: constructor.name
});
(() => {
__runInitializers(_classThis, _classExtraInitializers);
})();
return Deserializable = _classThis;
})();
return Deserializable;
}

4
dist/decorator.d.ts vendored
View file

@ -1,4 +0,0 @@
import { ContainerOptions, PropertyOptions } from './options';
import { Registry } from './registry';
export declare function serde(options: ContainerOptions | PropertyOptions): (target: any, context: any) => void;
export declare function register(registry?: Registry): (target: any, _context: any) => void;

33
dist/decorator.js vendored
View file

@ -1,33 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serde = serde;
exports.register = register;
const options_1 = require("./options");
const registry_1 = require("./registry");
function decorateContainer(target, context, options) {
const meta = context.metadata;
const serde = (meta.serde || new options_1.SerdeOptions(target));
serde.options = options;
meta.serde = serde;
}
function decorateProperty(target, context, options) {
const meta = context.metadata;
const serde = (meta.serde || new options_1.SerdeOptions(target));
serde.properties.set(context, options);
meta.serde = serde;
}
function serde(options) {
return function (target, context) {
if (context != null) {
decorateProperty(target, context, options);
}
else {
decorateContainer(target, context, options);
}
};
}
function register(registry = registry_1.GlobalRegistry) {
return function (target, _context) {
registry.add(target);
};
}

7
dist/index.d.ts vendored
View file

@ -1,6 +1,5 @@
export * as ser from './ser/index';
export * as de from './de/index';
export * as ser from './ser/impl';
export * as de from './de/impl';
export * from './case';
export * from './registry';
export * from './decorator';
export * from './options';
export * from './utils';

7
dist/index.js vendored
View file

@ -37,9 +37,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.de = exports.ser = void 0;
exports.ser = __importStar(require("./ser/index"));
exports.de = __importStar(require("./de/index"));
exports.ser = __importStar(require("./ser/impl"));
exports.de = __importStar(require("./de/impl"));
__exportStar(require("./case"), exports);
__exportStar(require("./registry"), exports);
__exportStar(require("./decorator"), exports);
__exportStar(require("./options"), exports);
__exportStar(require("./utils"), exports);

63
dist/options.d.ts vendored
View file

@ -1,63 +0,0 @@
import { CaseConvention } from './case';
import { Deserializer } from './de';
import { Serializer } from './ser';
import { Registry } from './registry';
export interface RenameOptions {
serialize?: string;
deserialize?: string;
}
export interface RenameAllOptions {
serialize?: CaseConvention;
deserialize?: CaseConvention;
}
export interface ContainerOptions {
default?: () => any;
denyUnknownFields?: boolean;
expecting?: string;
rename?: RenameOptions | string;
renameAll?: RenameAllOptions | CaseConvention;
registry?: Registry;
tag?: string;
content?: string;
untagged?: boolean;
}
export interface ConditionalSkipOptions {
if: (value: any) => boolean;
}
export interface SkipOptions {
serializing?: ConditionalSkipOptions | boolean;
deserializing?: ConditionalSkipOptions | boolean;
}
export type CustomSerializer = <T, V, S extends Serializer<T>>(value: V, serializer: S) => T;
export type CustomDeserializer = <T, D extends Deserializer>(deserializer: D) => T;
export interface PropertyOptions {
alias?: string;
default?: () => any;
flatten?: boolean;
rename?: RenameOptions | string;
skip?: SkipOptions | ConditionalSkipOptions | boolean;
}
export declare const Stage: Readonly<{
readonly Serialize: 0;
readonly Deserialize: 1;
}>;
export type Stage = typeof Stage[keyof typeof Stage];
export declare class SerdeOptions {
private readonly target;
readonly options: ContainerOptions;
readonly properties: Map<string, PropertyOptions>;
get registry(): Registry;
constructor(target: any, options?: ContainerOptions, properties?: Map<string, PropertyOptions>);
static from(target: any): SerdeOptions;
getClassName(stage: Stage): any;
private getPropertyRename;
private getPropertyCase;
getPropertyName(property: string, stage: Stage): string;
getSerializationName(property: string): string;
getDeserializationName(property: string): string;
getDefault(property: string): any;
private isConditionalSkip;
shouldSkip(property: string, value: any, stage: Stage): boolean;
shouldSkipSerializing(property: string, value: any): boolean;
shouldSkipDeserializing(property: string, value: any): boolean;
}

156
dist/options.js vendored
View file

@ -1,156 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SerdeOptions = exports.Stage = void 0;
const case_1 = require("./case");
const utils_1 = require("./utils");
const registry_1 = require("./registry");
exports.Stage = Object.freeze({
Serialize: 0,
Deserialize: 1
});
class SerdeOptions {
get registry() {
return this.options.registry || registry_1.GlobalRegistry;
}
constructor(target, options = {}, properties = new Map()) {
Object.defineProperty(this, "target", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "options", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "properties", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.target = target;
this.options = options;
this.properties = properties;
}
static from(target) {
return new this(target);
}
getClassName(stage) {
var _a, _b;
if ((0, utils_1.isString)(this.options.rename)) {
return this.options.rename;
}
else if (stage === exports.Stage.Serialize && (0, utils_1.isString)((_a = this.options.rename) === null || _a === void 0 ? void 0 : _a.serialize)) {
return this.options.rename.serialize;
}
else if (stage === exports.Stage.Deserialize && (0, utils_1.isString)((_b = this.options.rename) === null || _b === void 0 ? void 0 : _b.deserialize)) {
return this.options.rename.deserialize;
}
else {
return this.target.constructor.name;
}
}
getPropertyRename(property, stage, options) {
var _a, _b;
if (options != null) {
if ((0, utils_1.isString)(options.rename)) {
return options.rename;
}
else if (stage === exports.Stage.Serialize && (0, utils_1.isString)((_a = options.rename) === null || _a === void 0 ? void 0 : _a.serialize)) {
return options.rename.serialize;
}
else if (stage === exports.Stage.Deserialize && (0, utils_1.isString)((_b = options.rename) === null || _b === void 0 ? void 0 : _b.deserialize)) {
return options.rename.deserialize;
}
}
return property;
}
getPropertyCase(name, stage) {
var _a, _b;
if ((0, utils_1.isNumber)(this.options.renameAll)) {
return (0, case_1.convertCase)(name, this.options.renameAll);
}
else if (stage === exports.Stage.Serialize && (0, utils_1.isNumber)((_a = this.options.renameAll) === null || _a === void 0 ? void 0 : _a.serialize)) {
return (0, case_1.convertCase)(name, this.options.renameAll.serialize);
}
else if (stage === exports.Stage.Deserialize && (0, utils_1.isNumber)((_b = this.options.renameAll) === null || _b === void 0 ? void 0 : _b.deserialize)) {
return (0, case_1.convertCase)(name, this.options.renameAll.deserialize);
}
else {
return name;
}
}
getPropertyName(property, stage) {
const options = this.properties.get(property);
const name = options != null ? this.getPropertyRename(property, stage, options) : property;
return this.getPropertyCase(name, stage);
}
getSerializationName(property) {
return this.getPropertyName(property, exports.Stage.Serialize);
}
getDeserializationName(property) {
return this.getPropertyName(property, exports.Stage.Deserialize);
}
getDefault(property) {
const options = this.properties.get(property);
if (options && (0, utils_1.isFunction)(options.default)) {
return options.default();
}
else if ((0, utils_1.isFunction)(this.options.default)) {
return this.options.default();
}
}
//getCustomImpl(property: string, stage: Stage) {
// const options = this.properties.get(property)
// if (options != null) {
// if (stage === Stage.Serialize && isFunction(options.serializeWith)) {
// return options.serializeWith
// } else if (stage === Stage.Deserialize && isFunction(options.deserializeWith)) {
// return options.deserializeWith
// }
// }
//}
//getSerializer(property: string): Nullable<CustomSerializer> {
// return this.getCustomImpl(property, Stage.Serialize) as CustomSerializer
//}
//getDeserializer(property: string): Nullable<CustomDeserializer> {
// return this.getCustomImpl(property, Stage.Deserialize) as CustomDeserializer
//}
isConditionalSkip(skip) {
return 'if' in skip && (0, utils_1.isFunction)(skip.if);
}
shouldSkip(property, value, stage) {
const options = this.properties.get(property);
if (options != null && options.skip != null) {
if (typeof options.skip === 'boolean') {
return options.skip;
}
else if (this.isConditionalSkip(options.skip)) {
return options.skip.if(value);
}
else if (stage === exports.Stage.Serialize && typeof options.skip.serializing === 'boolean') {
return options.skip.serializing;
}
else if (stage === exports.Stage.Serialize && this.isConditionalSkip(options.skip.serializing)) {
return options.skip.serializing.if(value);
}
else if (stage === exports.Stage.Deserialize && typeof options.skip.deserializing === 'boolean') {
return options.skip.deserializing;
}
else if (stage === exports.Stage.Deserialize && this.isConditionalSkip(options.skip.deserializing)) {
return options.skip.deserializing.if(value);
}
}
return false;
}
shouldSkipSerializing(property, value) {
return this.shouldSkip(property, value, exports.Stage.Serialize);
}
shouldSkipDeserializing(property, value) {
return this.shouldSkip(property, value, exports.Stage.Deserialize);
}
}
exports.SerdeOptions = SerdeOptions;

12
dist/registry.d.ts vendored
View file

@ -1,7 +1,11 @@
import { Constructor } from './utils';
import { Deserialize } from './de/impl';
import { Serialize } from './ser/impl';
export declare class Registry {
registeredClasses: Map<string, Constructor>;
add(ctor: Constructor, key?: string): void;
get(name: string): Constructor | undefined;
serializers: Map<Function, Serialize<any>>;
deserializers: Map<Function, Deserialize>;
registerSerializer<T>(ctor: Function, serialize: Serialize<T>): void;
registerDeserializer(ctor: Function, deserialize: Deserialize): void;
}
export declare const GlobalRegistry: Registry;
export declare const registerSerializer: <T>(ctor: Function, serialize: Serialize<T>) => void;
export declare const registerDeserializer: (ctor: Function, deserialize: Deserialize) => void;

20
dist/registry.js vendored
View file

@ -1,21 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GlobalRegistry = exports.Registry = void 0;
exports.registerDeserializer = exports.registerSerializer = exports.GlobalRegistry = exports.Registry = void 0;
class Registry {
constructor() {
Object.defineProperty(this, "registeredClasses", {
Object.defineProperty(this, "serializers", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
Object.defineProperty(this, "deserializers", {
enumerable: true,
configurable: true,
writable: true,
value: new Map()
});
}
add(ctor, key = ctor.name) {
this.registeredClasses.set(key, ctor);
registerSerializer(ctor, serialize) {
this.serializers.set(ctor, serialize);
}
get(name) {
return this.registeredClasses.get(name);
registerDeserializer(ctor, deserialize) {
this.deserializers.set(ctor, deserialize);
}
}
exports.Registry = Registry;
exports.GlobalRegistry = new Registry();
exports.registerSerializer = exports.GlobalRegistry.registerSerializer.bind(exports.GlobalRegistry);
exports.registerDeserializer = exports.GlobalRegistry.registerDeserializer.bind(exports.GlobalRegistry);

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

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

120
dist/ser/impl.js vendored
View file

@ -1,62 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeWith = serializeWith;
const options_1 = require("../options");
const utils_1 = require("../utils");
const unhandledType = (serializer, value) => new TypeError(`'${serializer.constructor.name}' has no method for value type '${typeof value}'`);
function serializeEntries(serializer, value, options) {
var _a;
let state;
for (const [key, val] of value) {
if (options === null || options === void 0 ? void 0 : options.shouldSkipSerializing(key, val)) {
continue;
}
const name = (_a = options === null || options === void 0 ? void 0 : options.getPropertyName(key, options_1.Stage.Serialize)) !== null && _a !== void 0 ? _a : key;
state = serializer.serializeKey(name);
state = serializer.serializeValue(val);
exports.Serializer = exports.SerializeIterable = exports.SerializeObject = void 0;
exports.serialize = serialize;
class SerializeObject {
serializeEntry(key, value) {
this.serializeKey(key);
this.serializeValue(value);
}
}
exports.SerializeObject = SerializeObject;
class SerializeIterable {
}
exports.SerializeIterable = SerializeIterable;
class Serializer {
serializeAny(_value) {
throw new Error("Method not implemented.");
}
serializeBoolean(_value) {
throw new Error('Method not implemented.');
}
serializeNumber(_value) {
throw new Error('Method not implemented.');
}
serializeBigInt(_value) {
throw new Error('Method not implemented.');
}
serializeString(_value) {
throw new Error('Method not implemented.');
}
serializeSymbol(_value) {
throw new Error('Method not implemented.');
}
serializeNull() {
throw new Error('Method not implemented.');
}
serializeObject() {
throw new Error('Method not implemented.');
}
serializeClass(_name) {
throw new Error('Method not implemented.');
}
}
exports.Serializer = Serializer;
const isPlainObject = (value) => (value === null || value === void 0 ? void 0 : value.constructor) === Object;
class UnhandledTypeError extends TypeError {
constructor(serializer, value) {
super(`unhandled type: "${typeof value}" for serializer ${serializer.constructor.name}`);
}
}
function serializeObject(serializer, obj) {
for (const key in obj) {
serializer.serializeEntry(key, obj[key]);
}
return serializer.end();
}
function serializeClass(serializer, value, options) {
const classSerializer = serializer.serializeClass(value.constructor.name);
return serializeEntries(classSerializer, Object.entries(value), options);
function serializeClass(serializer, value) {
const name = value.constructor.name;
const ser = serializer.serializeClass(name);
return serializeObject(ser, value);
}
function serializeObject(serializer, value, options) {
return serializeEntries(serializer.serializeObject(), Object.entries(value), options);
}
function serializeIter(serializer, value, options) {
let state;
for (const val of value) {
state = serializer.serializeElement(val);
}
return serializer.end();
}
function defaultOptions(value) {
return value.constructor[Symbol.metadata];
}
// dispatches in the order of serializeType -> serializeAny -> throw TypeError
function serializeWith(serializer, value, optionsGetter = defaultOptions) {
// prepare fallback methods
const serializeAny = (0, utils_1.orElse)(serializer, serializer.serializeAny, (value) => unhandledType(serializer, value));
const serialize = (0, utils_1.ifNull)(serializer, serializeAny, value);
function serialize(serializer, value) {
switch (typeof value) {
case 'string': return serialize(serializer.serializeString);
case 'number': return serialize(serializer.serializeNumber);
case 'bigint': return serialize(serializer.serializeBigInt);
case 'boolean': return serialize(serializer.serializeBoolean);
case 'symbol': return serialize(serializer.serializeSymbol);
case 'undefined': return serialize(serializer.serializeNull);
case 'object':
const options = optionsGetter(value);
if ((0, utils_1.isIterable)(value) && (0, utils_1.isFunction)(serializer.serializeIterable)) {
return serializeIter(serializer.serializeIterable(), value, options);
case "string": return serializer.serializeString(value);
case "number": return serializer.serializeNumber(value);
case "bigint": return serializer.serializeBigInt(value);
case "boolean": return serializer.serializeBoolean(value);
case "symbol": return serializer.serializeSymbol(value);
case "undefined": return serializer.serializeNull();
case "object":
switch (true) {
case value == null: return serializer.serializeNull();
case !isPlainObject(value): return serializeClass(serializer, value);
default: return serializeObject(serializer.serializeObject(), value);
}
if (!(0, utils_1.isPlainObject)(value)) {
return serializeClass(serializer, value, options);
}
else if ((0, utils_1.isFunction)(serializer.serializeObject)) {
return serializeObject(serializer, value, options);
} // deliberate fallthrough when the above fail
default: return serializeAny(value);
default: throw new UnhandledTypeError(serializer, value);
}
}

4
dist/ser/index.d.ts vendored
View file

@ -1,4 +0,0 @@
import '@tsmetadata/polyfill';
export * from './interface';
export * from './mixin';
export * from './impl';

20
dist/ser/index.js vendored
View file

@ -1,20 +0,0 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
require("@tsmetadata/polyfill");
__exportStar(require("./interface"), exports);
__exportStar(require("./mixin"), exports);
__exportStar(require("./impl"), exports);

View file

@ -1,31 +0,0 @@
import { Primitive, ToString } from "../utils";
export interface ObjectSerializer<T = void> {
serializeKey<U extends Serializable>(key: U): T;
serializeValue<U extends Serializable>(value: U): T;
end(): T;
}
export interface IterableSerializer<T = void> {
serializeElement<U extends Serializable>(element: U): T;
end(): T;
}
interface TypeSerializer<T> {
serializeString(value: string): T;
serializeNumber(value: number): T;
serializeBigInt(value: bigint): T;
serializeBoolean(value: boolean): T;
serializeSymbol(value: Symbol): T;
serializeNull(): T;
serializeObject(): ObjectSerializer<T>;
serializeIterable?(): IterableSerializer<T>;
serializeClass(name: PropertyKey): ObjectSerializer<T>;
}
interface AnySerializer<T> {
serializeAny?(value?: any): T;
}
export declare function isGenericSerializer(value: any): boolean;
export type Serializer<T> = Partial<TypeSerializer<T>> & Partial<AnySerializer<T>>;
export type Serializable = Primitive | ToString | Serialize;
export interface Serialize {
serialize<T, S extends Serializer<T>>(serializer: S): T;
}
export {};

21
dist/ser/interface.js vendored
View file

@ -1,21 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isGenericSerializer = isGenericSerializer;
const utils_1 = require("../utils");
const TypeSerializerMethods = [
'serializeString',
'serializeNumber',
'serializeBigInt',
'serializeBoolean',
'serializeSymbol',
//'serializeMap',
'serializeIterable',
'serializeNull',
'serializeObject',
'serializeClass',
];
const AnySerializerMethods = ['serializeAny'];
function isGenericSerializer(value) {
return AnySerializerMethods.every(k => (0, utils_1.isFunction)(value[k])) &&
TypeSerializerMethods.every(k => !(0, utils_1.isFunction)(value[k]));
}

9
dist/ser/mixin.d.ts vendored
View file

@ -1,9 +0,0 @@
import { Constructor } from '../utils';
import { Serializer } from './interface';
export declare function serialize<T extends Constructor>(constructor: T): {
new (...args: any[]): {
[x: string]: any;
serialize<U>(serializer: Serializer<U>): U;
};
name: string;
} & T;

25
dist/ser/mixin.js vendored
View file

@ -1,25 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.serialize = serialize;
const impl_1 = require("./impl");
const interface_1 = require("./interface");
function serialize(constructor) {
class Serializable extends constructor {
serialize(serializer) {
// shortcut for serializers with only the serializeAny method
if ((0, interface_1.isGenericSerializer)(serializer)) {
return serializer.serializeAny(this);
}
else {
return (0, impl_1.serializeWith)(serializer, this);
}
}
}
Object.defineProperty(Serializable, "name", {
enumerable: true,
configurable: true,
writable: true,
value: constructor.name
});
return Serializable;
}

13
dist/utils.d.ts vendored
View file

@ -13,16 +13,3 @@ export declare function isIterable(value: any): value is Iterable<any>;
export declare function isString(value: any): value is string;
export declare function isNumber(value: any): value is number;
export type Constructor<T = any> = new (...args: any[]) => T;
export declare function orElse(thisArg: any, a: Nullable<Function>, b: Function): (...args: any) => any;
export declare function ifNull(thisArg: any, b: Function, ...args: any): (a: Nullable<Function>) => any;
export declare function mixin<U = any>(impl: Function): <TBase extends Constructor<U>>(constructor: TBase) => TBase;
type AnyFunc = (...arg: any) => any;
type PipeArgs<F extends AnyFunc[], Acc extends AnyFunc[] = []> = F extends [
(...args: infer A) => infer B
] ? [...Acc, (...args: A) => B] : F extends [(...args: infer A) => any, ...infer Tail] ? Tail extends [(arg: infer B) => any, ...any[]] ? PipeArgs<Tail, [...Acc, (...args: A) => B]> : Acc : Acc;
type LastFnReturnType<F extends Array<AnyFunc>, Else = never> = F extends [
...any[],
(...arg: any) => infer R
] ? R : Else;
export declare function pipe<FirstFn extends AnyFunc, F extends AnyFunc[]>(arg: Parameters<FirstFn>[0], firstFn: FirstFn, ...fns: PipeArgs<F> extends F ? F : PipeArgs<F>): LastFnReturnType<F, ReturnType<FirstFn>>;
export {};

32
dist/utils.js vendored
View file

@ -6,10 +6,6 @@ exports.isFunction = isFunction;
exports.isIterable = isIterable;
exports.isString = isString;
exports.isNumber = isNumber;
exports.orElse = orElse;
exports.ifNull = ifNull;
exports.mixin = mixin;
exports.pipe = pipe;
function staticImplements() {
return (constructor) => { constructor; };
}
@ -28,31 +24,3 @@ function isString(value) {
function isNumber(value) {
return !isNaN(value);
}
function orElse(thisArg, a, b) {
return function (...args) {
const fn = a != null ? a : b;
return fn.apply(thisArg, args);
};
}
function ifNull(thisArg, b, ...args) {
return function (a) {
return orElse(thisArg, a, b).apply(thisArg, args);
};
}
function applyMixins(derivedCtor, constructors) {
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null));
});
});
}
function mixin(impl) {
return function (constructor) {
applyMixins(constructor, [impl]);
return constructor;
};
}
function pipe(arg, firstFn, ...fns) {
return fns.reduce((acc, fn) => fn(acc), firstFn(arg));
}

View file

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

View file

@ -1,62 +0,0 @@
import { staticImplements } from '../utils'
import { Deserialize, Deserializer, IterableAccess, MapAccess, Visitor } from './interface'
@staticImplements<Deserialize<any>>()
export class GenericSeed<T> implements Deserialize<T> {
static deserialize<T, D extends Deserializer>(deserializer: D): T {
return deserializer.deserializeAny(new GenericVisitor<T>())
}
deserialize<D extends Deserializer>(deserializer: D): T {
return GenericSeed.deserialize(deserializer)
}
}
export class GenericVisitor<T> implements Visitor<T> {
visitString(value: string): T {
return value as T
}
visitNumber(value: number): T {
return value as T
}
visitBigInt(value: bigint): T {
return value as T
}
visitBoolean(value: boolean): T {
return value as T
}
visitSymbol(value: symbol): T {
return value as T
}
visitNull(): T {
return null as T
}
visitObject(access: MapAccess): T {
const result: Record<PropertyKey, any> = {}
let entry
while ((entry = access.nextEntry<string, any>())) {
result[entry[0]] = entry[1]
}
return result
}
visitIterable(access: IterableAccess): T {
const result = new Array(access.sizeHint())
let element
while ((element = access.nextElement())) {
result.push(element)
}
return result as T
}
}

View file

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

View file

@ -1,4 +0,0 @@
export * from './interface'
export * from './generic'
export * from './mixin'

View file

@ -1,83 +0,0 @@
import { Nullable } from '../utils'
import { GenericSeed } from './generic'
export interface MapAccess {
nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>
nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]>
nextKey<T>(): Nullable<T>
nextValue<T>(): Nullable<T>
nextEntry<K, V>(): Nullable<[K, V]>
}
export abstract class DefaultMapAccessImpl implements MapAccess {
abstract nextKeySeed<T, K extends Deserialize<T>>(seed: K): Nullable<T>
abstract nextValueSeed<T, V extends Deserialize<T>>(seed: V): Nullable<T>
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Nullable<[TK, TV]> {
const key = this.nextKeySeed(kseed) as Nullable<TK>
if (key !== undefined) {
const value = this.nextValueSeed(vseed) as Nullable<TV>
if (value !== undefined) {
return [key, value]
}
}
}
nextKey<T>(): Nullable<T> {
return this.nextValueSeed(GenericSeed<T>)
}
nextValue<T>(): Nullable<T> {
return this.nextValueSeed(GenericSeed<T>)
}
nextEntry<K, V>(): Nullable<[K, V]> {
return this.nextEntrySeed(GenericSeed<K>, GenericSeed<V>)
}
}
export interface IterableAccess {
nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>
nextElement<T>(): Nullable<T>
sizeHint(): number
}
export abstract class DefaultIterableAccessImpl implements IterableAccess {
abstract nextElementSeed<T, I extends Deserialize<T>>(seed: I): Nullable<T>
nextElement<T>(): Nullable<T> {
return this.nextElementSeed(GenericSeed<T>)
}
sizeHint(): number { return 0 }
}
export interface Visitor<T> {
visitString(value: string): T
visitNumber(value: number): T
visitBigInt(value: bigint): T
visitBoolean(value: boolean): T
visitSymbol(value: symbol): T
visitNull(): T
visitObject(value: MapAccess): T
visitIterable?(value: IterableAccess): T
}
export interface Deserializer {
deserializeAny<T, V extends Visitor<T>>(visitor: V): T
deserializeString<T, V extends Visitor<T>>(visitor: V): T
deserializeNumber<T, V extends Visitor<T>>(visitor: V): T
deserializeBigInt<T, V extends Visitor<T>>(visitor: V): T
deserializeBoolean<T, V extends Visitor<T>>(visitor: V): T
deserializeSymbol<T, V extends Visitor<T>>(visitor: V): T
deserializeNull<T, V extends Visitor<T>>(visitor: V): T
deserializeObject<T, V extends Visitor<T>>(visitor: V): T
deserializeClass<T, V extends Visitor<T>>(name: string, fields: string[], visitor: V): T
deserializeIterable<T, V extends Visitor<T>>(visitor: V): T
}
export interface Deserialize<T> {
deserialize<D extends Deserializer>(deserializer: D): T
}

View file

@ -1,17 +0,0 @@
import { Constructor, staticImplements } from '../utils'
import { deserializeWith } from './impl'
import { Deserialize, Deserializer } from './interface'
export function deserialize<T, C extends Constructor>(constructor: C) {
@staticImplements<Deserialize<T>>()
class Deserializable extends constructor {
static name = constructor.name
static deserialize<D extends Deserializer>(deserializer: D): T {
return deserializeWith(deserializer, this, (constructor as any)[Symbol.metadata])
}
}
return Deserializable
}

View file

@ -1,33 +0,0 @@
import { ContainerOptions, PropertyOptions, SerdeOptions } from './options'
import { GlobalRegistry, Registry } from './registry'
function decorateContainer(target: any, context: any, options: ContainerOptions) {
const meta = context.metadata
const serde = (meta.serde || new SerdeOptions(target))
serde.options = options
meta.serde = serde
}
function decorateProperty(target: any, context: any, options: PropertyOptions) {
const meta = context.metadata
const serde = (meta.serde || new SerdeOptions(target))
serde.properties.set(context, options)
meta.serde = serde
}
export function serde(options: ContainerOptions | PropertyOptions) {
return function(target: any, context: any) {
if (context != null) {
decorateProperty(target, context, options as PropertyOptions)
} else {
decorateContainer(target, context, options as ContainerOptions)
}
}
}
export function register(registry: Registry = GlobalRegistry) {
return function(target: any, _context: any) {
registry.add(target)
}
}

View file

@ -1,7 +1,6 @@
export * as ser from './ser/index'
export * as de from './de/index'
export * as ser from './ser/impl'
export * as de from './de/impl'
export * from './case'
export * from './registry'
export * from './decorator'
export * from './options'
export * from './utils'

View file

@ -1,193 +0,0 @@
import { CaseConvention, convertCase } from './case'
import { Deserializer } from './de'
import { Serializer } from './ser'
import { isFunction, isNumber, isString } from './utils'
import { GlobalRegistry, Registry } from './registry'
export interface RenameOptions {
serialize?: string
deserialize?: string
}
export interface RenameAllOptions {
serialize?: CaseConvention
deserialize?: CaseConvention
}
export interface ContainerOptions {
// deserialization only
default?: () => any
// deserialization only
denyUnknownFields?: boolean
expecting?: string
rename?: RenameOptions | string
renameAll?: RenameAllOptions | CaseConvention
registry?: Registry
tag?: string
content?: string
untagged?: boolean
}
export interface ConditionalSkipOptions {
if: (value: any) => boolean
}
export interface SkipOptions {
serializing?: ConditionalSkipOptions | boolean
deserializing?: ConditionalSkipOptions | boolean
}
export type CustomSerializer = <T, V, S extends Serializer<T>>(value: V, serializer: S) => T
export type CustomDeserializer = <T, D extends Deserializer>(deserializer: D) => T
export interface PropertyOptions {
alias?: string
// deserialization only
default?: () => any
flatten?: boolean
rename?: RenameOptions | string
//serializeWith?: CustomSerializer
//deserializeWith: CustomDeserializer
skip?: SkipOptions | ConditionalSkipOptions | boolean
}
export const Stage = Object.freeze({
Serialize: 0,
Deserialize: 1
} as const)
export type Stage = typeof Stage[keyof typeof Stage]
export class SerdeOptions {
private readonly target: any
readonly options: ContainerOptions
readonly properties: Map<string, PropertyOptions>
get registry() {
return this.options.registry || GlobalRegistry
}
constructor(target: any, options: ContainerOptions = {}, properties: Map<string, PropertyOptions> = new Map()) {
this.target = target
this.options = options
this.properties = properties
}
static from(target: any) {
return new this(target)
}
getClassName(stage: Stage) {
if (isString(this.options.rename)) {
return this.options.rename
} else if (stage === Stage.Serialize && isString(this.options.rename?.serialize)) {
return this.options.rename.serialize
} else if (stage === Stage.Deserialize && isString(this.options.rename?.deserialize)) {
return this.options.rename.deserialize
} else {
return this.target.constructor.name
}
}
private getPropertyRename(property: string, stage: Stage, options: PropertyOptions) {
if (options != null) {
if (isString(options.rename)) {
return options.rename
} else if (stage === Stage.Serialize && isString(options.rename?.serialize)) {
return options.rename.serialize
} else if (stage === Stage.Deserialize && isString(options.rename?.deserialize)) {
return options.rename.deserialize
}
}
return property
}
private getPropertyCase(name: string, stage: Stage) {
if (isNumber(this.options.renameAll)) {
return convertCase(name, this.options.renameAll)
} else if (stage === Stage.Serialize && isNumber(this.options.renameAll?.serialize)) {
return convertCase(name, this.options.renameAll.serialize)
} else if (stage === Stage.Deserialize && isNumber(this.options.renameAll?.deserialize)) {
return convertCase(name, this.options.renameAll.deserialize)
} else {
return name
}
}
getPropertyName(property: string, stage: Stage) {
const options = this.properties.get(property)
const name = options != null ? this.getPropertyRename(property, stage, options) : property
return this.getPropertyCase(name, stage)
}
getSerializationName(property: string) {
return this.getPropertyName(property, Stage.Serialize)
}
getDeserializationName(property: string) {
return this.getPropertyName(property, Stage.Deserialize)
}
getDefault(property: string) {
const options = this.properties.get(property)
if (options && isFunction(options.default)) {
return options.default()
} else if (isFunction(this.options.default)) {
return this.options.default()
}
}
//getCustomImpl(property: string, stage: Stage) {
// const options = this.properties.get(property)
// if (options != null) {
// if (stage === Stage.Serialize && isFunction(options.serializeWith)) {
// return options.serializeWith
// } else if (stage === Stage.Deserialize && isFunction(options.deserializeWith)) {
// return options.deserializeWith
// }
// }
//}
//getSerializer(property: string): Nullable<CustomSerializer> {
// return this.getCustomImpl(property, Stage.Serialize) as CustomSerializer
//}
//getDeserializer(property: string): Nullable<CustomDeserializer> {
// return this.getCustomImpl(property, Stage.Deserialize) as CustomDeserializer
//}
private isConditionalSkip(skip: any): skip is ConditionalSkipOptions {
return 'if' in skip && isFunction(skip.if)
}
shouldSkip(property: string, value: any, stage: Stage): boolean {
const options = this.properties.get(property)
if (options != null && options.skip != null) {
if (typeof options.skip === 'boolean') {
return options.skip
} else if (this.isConditionalSkip(options.skip)) {
return options.skip.if(value)
} else if (stage === Stage.Serialize && typeof options.skip.serializing === 'boolean') {
return options.skip.serializing
} else if (stage === Stage.Serialize && this.isConditionalSkip(options.skip.serializing)) {
return options.skip.serializing.if(value)
} else if (stage === Stage.Deserialize && typeof options.skip.deserializing === 'boolean') {
return options.skip.deserializing
} else if (stage === Stage.Deserialize && this.isConditionalSkip(options.skip.deserializing)) {
return options.skip.deserializing.if(value)
}
}
return false
}
shouldSkipSerializing(property: string, value: any): boolean {
return this.shouldSkip(property, value, Stage.Serialize)
}
shouldSkipDeserializing(property: string, value: any): boolean {
return this.shouldSkip(property, value, Stage.Deserialize)
}
}

View file

@ -1,16 +1,20 @@
import { Constructor } from './utils'
import { Deserialize } from './de/impl'
import { Serialize } from './ser/impl'
export class Registry {
registeredClasses: Map<string, Constructor> = new Map()
serializers: Map<Function, Serialize<any>> = new Map()
deserializers: Map<Function, Deserialize> = new Map()
add(ctor: Constructor, key: string = ctor.name) {
this.registeredClasses.set(key, ctor)
registerSerializer<T>(ctor: Function, serialize: Serialize<T>) {
this.serializers.set(ctor, serialize)
}
get(name: string) {
return this.registeredClasses.get(name)
registerDeserializer(ctor: Function, deserialize: Deserialize) {
this.deserializers.set(ctor, deserialize)
}
}
export const GlobalRegistry = new Registry()
export const registerSerializer = GlobalRegistry.registerSerializer.bind(GlobalRegistry)
export const registerDeserializer = GlobalRegistry.registerDeserializer.bind(GlobalRegistry)

View file

@ -1,78 +1,122 @@
import { IterableSerializer, ObjectSerializer, Serializable, Serializer } from './interface'
import { SerdeOptions, Stage } from '../options'
import { ifNull, isFunction, isIterable, isPlainObject, Nullable, orElse } from '../utils'
export interface ISerializeObject<T> {
serializeKey(key: string): void
serializeValue<U>(value: U): void
serializeEntry<U>(key: string, value: U): void
end(): T
}
const unhandledType = (serializer: any, value: any) => new TypeError(`'${serializer.constructor.name}' has no method for value type '${typeof value}'`)
export abstract class SerializeObject<T> implements ISerializeObject<T> {
abstract serializeKey(key: string): void
abstract serializeValue<U>(value: U): void
abstract end(): T
function serializeEntries<T, K extends string, V extends Serializable, E extends Iterable<[K, V]>>(serializer: ObjectSerializer<T>, value: E, options?: SerdeOptions) {
let state
serializeEntry<U>(key: string, value: U): void {
this.serializeKey(key)
this.serializeValue(value)
}
}
for (const [key, val] of value) {
if (options?.shouldSkipSerializing(key, val)) {
continue
}
export interface ISerializeIterable<T> {
serializeElement<U>(value: U): void
end(): T
}
const name = options?.getPropertyName(key as string, Stage.Serialize) ?? key
state = serializer.serializeKey(name)
state = serializer.serializeValue(val)
export abstract class SerializeIterable<T> implements ISerializeIterable<T> {
abstract serializeElement<U>(value: U): void
abstract end(): T
}
export interface ISerializer<T> {
serializeAny(value: any): T
serializeBoolean(value: boolean): T
serializeNumber(value: number): T
serializeBigInt(value: bigint): T
serializeString(value: string): T
serializeSymbol(value: symbol): T
serializeNull(): T
serializeObject(): ISerializeObject<T>
serializeClass(name: string): ISerializeObject<T>
}
export class Serializer<T> implements ISerializer<T> {
serializeAny(_value: any): T {
throw new Error("Method not implemented.")
}
serializeBoolean(_value: boolean): T {
throw new Error('Method not implemented.')
}
serializeNumber(_value: number): T {
throw new Error('Method not implemented.')
}
serializeBigInt(_value: bigint): T {
throw new Error('Method not implemented.')
}
serializeString(_value: string): T {
throw new Error('Method not implemented.')
}
serializeSymbol(_value: symbol): T {
throw new Error('Method not implemented.')
}
serializeNull(): T {
throw new Error('Method not implemented.')
}
serializeObject(): ISerializeObject<T> {
throw new Error('Method not implemented.')
}
serializeClass(_name: string): ISerializeObject<T> {
throw new Error('Method not implemented.')
}
}
export interface Serialize<T> {
<U, S extends Serializer<U>>(serializer: S, value: T): U
}
const isPlainObject = (value: any): value is object => value?.constructor === Object
class UnhandledTypeError extends TypeError {
constructor(serializer: Serializer<unknown>, value: any) {
super(`unhandled type: "${typeof value}" for serializer ${serializer.constructor.name}`)
}
}
function serializeObject<T, V extends object, S extends ISerializeObject<T>>(serializer: S, obj: V): T {
for (const key in obj) {
serializer.serializeEntry(key, obj[key])
}
return serializer.end()
}
function serializeClass<T, K extends string, V extends Serializable, R extends Record<K, V>>(serializer: Serializer<T>, value: R, options?: SerdeOptions) {
const classSerializer = serializer.serializeClass!(value.constructor.name)
return serializeEntries(classSerializer, Object.entries(value) as Iterable<[K, V]>, options)
function serializeClass<T, V extends object, S extends Serializer<T>>(serializer: S, value: V): T {
const name = value.constructor.name
const ser = serializer.serializeClass(name)
return serializeObject(ser, value)
}
function serializeObject<T, K extends string, V extends Serializable, R extends Record<K, V>>(serializer: Serializer<T>, value: R, options?: SerdeOptions) {
return serializeEntries(serializer.serializeObject!(), Object.entries(value) as Iterable<[K, V]>, options)
}
function serializeIter<T, V extends Iterable<any>>(serializer: IterableSerializer<T>, value: V, options?: SerdeOptions) {
let state
for (const val of value) {
state = serializer.serializeElement(val)
}
return serializer.end()
}
function defaultOptions(value: any) {
return value.constructor[Symbol.metadata]
}
// dispatches in the order of serializeType -> serializeAny -> throw TypeError
export function serializeWith<T>(serializer: Serializer<T>, value: Serializable, optionsGetter: (value: any) => Nullable<SerdeOptions> = defaultOptions): Nullable<T> {
// prepare fallback methods
const serializeAny = orElse(
serializer,
serializer.serializeAny,
(value: Serializable) => unhandledType(serializer, value)
)
const serialize = ifNull(serializer, serializeAny, value)
export function serialize<T, V, S extends Serializer<T>>(serializer: S, value: V): T {
switch (typeof value) {
case 'string': return serialize(serializer.serializeString)
case 'number': return serialize(serializer.serializeNumber)
case 'bigint': return serialize(serializer.serializeBigInt)
case 'boolean': return serialize(serializer.serializeBoolean)
case 'symbol': return serialize(serializer.serializeSymbol)
case 'undefined': return serialize(serializer.serializeNull)
case 'object':
const options = optionsGetter(value)
if (isIterable(value) && isFunction(serializer.serializeIterable)) {
return serializeIter(serializer.serializeIterable(), value, options)
} if (!isPlainObject(value)) {
return serializeClass(serializer, value as Record<PropertyKey, any>, options)
} else if (isFunction(serializer.serializeObject)) {
return serializeObject(serializer, value as Record<PropertyKey, any>, options)
} // deliberate fallthrough when the above fail
default: return serializeAny(value)
case "string": return serializer.serializeString(value)
case "number": return serializer.serializeNumber(value)
case "bigint": return serializer.serializeBigInt(value)
case "boolean": return serializer.serializeBoolean(value)
case "symbol": return serializer.serializeSymbol(value)
case "undefined": return serializer.serializeNull()
case "object":
switch (true) {
case value == null: return serializer.serializeNull()
case !isPlainObject(value): return serializeClass(serializer, value)
default: return serializeObject(serializer.serializeObject(), value)
}
default: throw new UnhandledTypeError(serializer, value)
}
}

View file

@ -1,6 +0,0 @@
import '@tsmetadata/polyfill'
export * from './interface'
export * from './mixin'
export * from './impl'

View file

@ -1,58 +0,0 @@
import { isFunction, Primitive, ToString } from "../utils"
export interface ObjectSerializer<T = void> {
serializeKey<U extends Serializable>(key: U): T
serializeValue<U extends Serializable>(value: U): T
end(): T
}
export interface IterableSerializer<T = void> {
serializeElement<U extends Serializable>(element: U): T
end(): T
}
const TypeSerializerMethods = [
'serializeString',
'serializeNumber',
'serializeBigInt',
'serializeBoolean',
'serializeSymbol',
//'serializeMap',
'serializeIterable',
'serializeNull',
'serializeObject',
'serializeClass',
] as const
interface TypeSerializer<T> {
serializeString(value: string): T
serializeNumber(value: number): T
serializeBigInt(value: bigint): T
serializeBoolean(value: boolean): T
serializeSymbol(value: Symbol): T
serializeNull(): T
serializeObject(): ObjectSerializer<T>
// serializeMap?(): ObjectSerializer<T>
serializeIterable?(): IterableSerializer<T>
serializeClass(name: PropertyKey): ObjectSerializer<T>
}
const AnySerializerMethods = ['serializeAny']
interface AnySerializer<T> {
serializeAny?(value?: any): T
}
export function isGenericSerializer(value: any): boolean {
return AnySerializerMethods.every(k => isFunction(value[k])) &&
TypeSerializerMethods.every(k => !isFunction(value[k]))
}
export type Serializer<T> = Partial<TypeSerializer<T>> & Partial<AnySerializer<T>>
export type Serializable = Primitive | ToString | Serialize
export interface Serialize {
serialize<T, S extends Serializer<T>>(serializer: S): T
}

View file

@ -1,19 +0,0 @@
import { Constructor } from '../utils'
import { serializeWith } from './impl'
import { isGenericSerializer, Serializer } from './interface'
export function serialize<T extends Constructor>(constructor: T) {
class Serializable extends constructor implements Serializable {
static name = constructor.name
serialize<U>(serializer: Serializer<U>): U {
// shortcut for serializers with only the serializeAny method
if (isGenericSerializer(serializer)) {
return serializer.serializeAny!(this) as U
} else {
return serializeWith(serializer, this) as U
}
}
}
return Serializable
}

View file

@ -36,61 +36,3 @@ export function isNumber(value: any): value is number {
export type Constructor<T = any> = new (...args: any[]) => T
export function orElse(thisArg: any, a: Nullable<Function>, b: Function) {
return function(...args: any) {
const fn = a != null ? a : b
return fn.apply(thisArg, args)
}
}
export function ifNull(thisArg: any, b: Function, ...args: any) {
return function(a: Nullable<Function>) {
return orElse(thisArg, a, b).apply(thisArg, args)
}
}
function applyMixins(derivedCtor: any, constructors: any[]) {
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null)
)
})
})
}
export function mixin<U = any>(impl: Function) {
return function <TBase extends Constructor<U>>(constructor: TBase) {
applyMixins(constructor, [impl])
return constructor
}
}
type AnyFunc = (...arg: any) => any
type PipeArgs<F extends AnyFunc[], Acc extends AnyFunc[] = []> = F extends [
(...args: infer A) => infer B
]
? [...Acc, (...args: A) => B]
: F extends [(...args: infer A) => any, ...infer Tail]
? Tail extends [(arg: infer B) => any, ...any[]]
? PipeArgs<Tail, [...Acc, (...args: A) => B]>
: Acc
: Acc
type LastFnReturnType<F extends Array<AnyFunc>, Else = never> = F extends [
...any[],
(...arg: any) => infer R
] ? R : Else
export function pipe<FirstFn extends AnyFunc, F extends AnyFunc[]>(
arg: Parameters<FirstFn>[0],
firstFn: FirstFn,
...fns: PipeArgs<F> extends F ? F : PipeArgs<F>
): LastFnReturnType<F, ReturnType<FirstFn>> {
return (fns as AnyFunc[]).reduce((acc, fn) => fn(acc), firstFn(arg))
}