clean up interfaces; merge nextSeed/next methods in accessors

This commit is contained in:
Rowan 2025-05-25 10:54:38 -05:00
parent f886c5c003
commit bbaea7cc9e
26 changed files with 650 additions and 293 deletions

45
dist/de/forward.d.ts vendored
View file

@ -1,34 +1,17 @@
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<T>>(_seed: K): IteratorResult<T>;
nextValueSeed<T, V extends Deserialize<T>>(_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 {
import { IDeserializer, IVisitor } from './interface';
import { Registry } from '../registry';
export declare function forward(value: any, into: any, registry?: Registry): unknown;
export declare class Forwarder 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;
deserializeAny<T>(visitor: IVisitor<T>): T;
deserializeBoolean<T>(visitor: IVisitor<T>): T;
deserializeNumber<T>(visitor: IVisitor<T>): T;
deserializeBigInt<T>(visitor: IVisitor<T>): T;
deserializeString<T>(visitor: IVisitor<T>): T;
deserializeSymbol<T>(visitor: IVisitor<T>): T;
deserializeNull<T>(visitor: IVisitor<T>): T;
deserializeObject<T>(visitor: IVisitor<T>): T;
deserializeIterable<T>(visitor: IVisitor<T>): T;
deserializeFunction<T>(_visitor: IVisitor<T>): T;
}

153
dist/de/forward.js vendored
View file

@ -1,69 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Forward = exports.ForwardIterableAccess = exports.ForwardMapAccess = void 0;
const utils_1 = require("../utils");
exports.Forwarder = void 0;
exports.forward = forward;
const impl_1 = require("./impl");
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();
}
}
const registry_1 = require("../registry");
const utils_1 = require("../utils");
function forward(value, into, registry = registry_1.GlobalRegistry) {
const forwarder = new Forwarder(value);
return (0, impl_1.deserialize)(forwarder, into, registry);
}
exports.ForwardMapAccess = ForwardMapAccess;
class ForwardIterableAccess extends interface_1.IterableAccess {
constructor(items) {
class ForwardMapAccess extends interface_1.MapAccess {
constructor(entries) {
super();
Object.defineProperty(this, "items", {
Object.defineProperty(this, "_entries", {
enumerable: true,
configurable: true,
writable: true,
@ -73,21 +23,65 @@ class ForwardIterableAccess extends interface_1.IterableAccess {
enumerable: true,
configurable: true,
writable: true,
value: 0
value: -1
});
this.items = items;
this._entries = entries;
}
nextElement() {
if (this.index < this.items.length) {
return utils_1.IterResult.Next(this.items[this.index++]);
static fromObject(value) {
return new this(Object.entries(value));
}
nextKey(seed) {
this.index += 1;
if (this.index >= this._entries.length) {
return utils_1.IterResult.Done();
}
else {
const key = this._entries[this.index][0];
const value = seed != null ? seed(new Forwarder(key)) : key;
return utils_1.IterResult.Next(value);
}
}
nextValue(seed) {
if (this.index >= this._entries.length) {
return utils_1.IterResult.Done();
}
else {
const value = this._entries[this.index][1];
const deser = seed != null ? seed(value) : value;
return utils_1.IterResult.Next(deser);
}
}
}
exports.ForwardIterableAccess = ForwardIterableAccess;
class Forward {
class ForwardIterableAccess extends interface_1.IterableAccess {
constructor(elements) {
super();
Object.defineProperty(this, "elements", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.defineProperty(this, "index", {
enumerable: true,
configurable: true,
writable: true,
value: -1
});
this.elements = elements;
}
nextElement(seed) {
this.index += 1;
if (this.index >= this.elements.length) {
return utils_1.IterResult.Done();
}
else {
const element = this.elements[this.index];
const deser = seed != null ? seed(new Forwarder(element)) : element;
return utils_1.IterResult.Next(deser);
}
}
}
class Forwarder {
constructor(value) {
Object.defineProperty(this, "value", {
enumerable: true,
@ -97,11 +91,22 @@ class Forward {
});
this.value = value;
}
static with(value) {
return new this(value);
}
deserializeAny(_visitor) {
throw new Error("Can't forward to deserializeAny");
deserializeAny(visitor) {
switch (typeof this.value) {
case 'string': return this.deserializeString(visitor);
case 'number': return this.deserializeNumber(visitor);
case 'bigint': return this.deserializeBigInt(visitor);
case 'boolean': return this.deserializeBoolean(visitor);
case 'symbol': return this.deserializeSymbol(visitor);
case 'undefined': return this.deserializeNull(visitor);
case 'object': {
switch (true) {
case Array.isArray(this.value): return this.deserializeIterable(visitor);
default: return this.deserializeObject(visitor);
}
}
case 'function': return this.deserializeFunction(visitor);
}
}
deserializeBoolean(visitor) {
return visitor.visitBoolean(this.value);
@ -131,4 +136,4 @@ class Forward {
throw new Error('Method not implemented.');
}
}
exports.Forward = Forward;
exports.Forwarder = Forwarder;

View file

@ -1,10 +1,4 @@
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;
}
import { IIterableAccess, IMapAccess, IVisitor } from './interface';
export declare class Visitor<T> implements IVisitor<T> {
private overrides?;
constructor(overrides?: Partial<IVisitor<T>>);

20
dist/de/generic.js vendored
View file

@ -1,26 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Visitor = exports.GenericSeed = void 0;
exports.Visitor = void 0;
const utils_1 = require("../utils");
const interface_1 = require("./interface");
class GenericSeed {
constructor(visitor = new Visitor()) {
Object.defineProperty(this, "visitor", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
this.visitor = visitor;
}
static deserialize(deserializer, visitor = new Visitor()) {
return deserializer.deserializeAny(visitor);
}
deserialize(deserializer) {
return GenericSeed.deserialize(deserializer, this.visitor);
}
}
exports.GenericSeed = GenericSeed;
class Visitor {
constructor(overrides) {
Object.defineProperty(this, "overrides", {

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

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

1
dist/de/index.js vendored
View file

@ -14,6 +14,7 @@ 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);

View file

@ -1,40 +1,31 @@
import { Nullable } from '../utils';
export interface IMapAccess {
nextKeySeed<T, K extends Deserialize<T>>(seed: K): IteratorResult<T>;
nextValueSeed<T, V extends Deserialize<T>>(seed: V): IteratorResult<T>;
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): IteratorResult<[TK, TV]>;
nextKey<T>(): IteratorResult<T>;
nextValue<V>(): IteratorResult<V>;
nextEntry<K, V>(): IteratorResult<[K, V]>;
nextKey<T>(seed?: Deserialize<T>): IteratorResult<T>;
nextValue<T>(seed?: Deserialize<T>): IteratorResult<T>;
nextEntry<K, V>(kseed?: Deserialize<K>, vseed?: Deserialize<V>): IteratorResult<[K, V]>;
sizeHint?(): Nullable<number>;
entries<T, K extends Deserialize<T>>(seed?: K): Iterator<T>;
values<T, V extends Deserialize<T>>(seed?: V): Iterator<T>;
entries<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Iterator<[TK, TV]>;
entries<T>(seed?: Deserialize<T>): Iterator<T>;
values<T>(seed?: Deserialize<T>): Iterator<T>;
entries<K, V>(kseed: Deserialize<K>, vseed: Deserialize<V>): Iterator<[K, V]>;
[Symbol.iterator]<K, V>(): Iterator<[K, V]>;
}
export declare abstract class MapAccess {
abstract nextKeySeed<T, K extends Deserialize<T>>(seed: K): IteratorResult<T>;
abstract nextValueSeed<T, V extends Deserialize<T>>(seed: V): IteratorResult<T>;
private orDefaultSeed;
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): IteratorResult<[TK, TV]>;
nextKey<T>(): IteratorResult<T>;
nextValue<V>(): IteratorResult<V>;
nextEntry<K, V>(): IteratorResult<[K, V]>;
abstract nextKey<T>(seed?: Deserialize<T>): IteratorResult<T>;
abstract nextValue<T>(seed?: Deserialize<T>): IteratorResult<T>;
nextEntry<K, V>(kseed?: Deserialize<K>, vseed?: Deserialize<V>): IteratorResult<[K, V]>;
private generate;
keys<T, K extends Deserialize<T>>(seed?: K): Iterator<T>;
values<T, V extends Deserialize<T>>(seed?: V): Iterator<T>;
entries<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed?: K, vseed?: V): Iterator<[TK, TV]>;
keys<T>(seed?: Deserialize<T>): Iterator<T>;
values<T>(seed?: Deserialize<T>): Iterator<T>;
entries<K, V>(kseed?: Deserialize<K>, vseed?: Deserialize<V>): Iterator<[K, V]>;
[Symbol.iterator]<K, V>(): Iterator<K, V>;
}
export interface IIterableAccess {
nextElementSeed<T, D extends Deserialize<T>>(seed: D): IteratorResult<T>;
nextElement<T>(): IteratorResult<T>;
nextElement<T>(seed: Deserialize<T>): IteratorResult<T>;
sizeHint?(): Nullable<number>;
[Symbol.iterator]<T>(): Iterator<T>;
}
export declare abstract class IterableAccess implements IIterableAccess {
abstract nextElementSeed<T, D extends Deserialize<T>>(seed: D): IteratorResult<T>;
nextElement<T>(): IteratorResult<T>;
abstract nextElement<T>(seed: Deserialize<T>): IteratorResult<T>;
[Symbol.iterator]<T>(): Iterator<T>;
}
export declare function isVisitor<T>(visitor: any): visitor is IVisitor<T>;

41
dist/de/interface.js vendored
View file

@ -3,31 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.IterableAccess = exports.MapAccess = void 0;
exports.isVisitor = isVisitor;
const utils_1 = require("../utils");
const generic_1 = require("./generic");
class MapAccess {
orDefaultSeed(seed) {
return seed || generic_1.GenericSeed.deserialize;
}
nextEntrySeed(kseed, vseed) {
const key = this.nextKeySeed(kseed);
nextEntry(kseed, vseed) {
const key = this.nextKey(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();
}
nextKey() {
return this.nextKeySeed(generic_1.GenericSeed.deserialize);
}
nextValue() {
return this.nextValueSeed(generic_1.GenericSeed.deserialize);
}
nextEntry() {
const key = this.nextKey();
if (!key.done) {
const value = this.nextValue();
const value = this.nextValue(vseed);
if (!value.done) {
return utils_1.IterResult.Next([key.value, value.value]);
}
@ -41,19 +21,13 @@ class MapAccess {
}
}
keys(seed) {
return this.generate(seed == null ?
this.nextKey.bind(this) :
this.nextKeySeed.bind(this, seed));
return this.generate(this.nextKey.bind(this, seed));
}
values(seed) {
return this.generate(seed == null ?
this.nextValue.bind(this) :
this.nextValueSeed.bind(this, seed));
return this.generate(this.nextValue.bind(this, seed));
}
entries(kseed, vseed) {
return this.generate(kseed == null && vseed == null ?
this.nextEntry.bind(this) :
this.nextEntrySeed.bind(this, this.orDefaultSeed(kseed), this.orDefaultSeed(vseed)));
return this.generate(this.nextEntry.bind(this, kseed, vseed));
}
[Symbol.iterator]() {
return this.entries();
@ -61,9 +35,6 @@ class MapAccess {
}
exports.MapAccess = MapAccess;
class IterableAccess {
nextElement() {
return this.nextElementSeed(generic_1.GenericSeed.deserialize);
}
[Symbol.iterator]() {
return {
next: this.nextElement.bind(this)

2
dist/registry.d.ts vendored
View file

@ -9,3 +9,5 @@ export declare class Registry {
export declare const GlobalRegistry: Registry;
export declare const registerSerialize: <T, U>(ctor: Function, serialize: Serialize<T, U>) => void;
export declare const registerDeserialize: <T>(ctor: Function, deserialize: Deserialize<T>) => void;
export declare function getSerialize<T, U>(value: U, fallback: Serialize<T, U>, registry: Registry): Serialize<T, U>;
export declare function getDeserialize<T, U>(value: U, fallback: Deserialize<T>, registry: Registry): Deserialize<T>;

73
dist/registry.js vendored
View file

@ -1,6 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.registerDeserialize = exports.registerSerialize = exports.GlobalRegistry = exports.Registry = void 0;
exports.getSerialize = getSerialize;
exports.getDeserialize = getDeserialize;
const utils_1 = require("./utils");
class Registry {
constructor() {
Object.defineProperty(this, "serializers", {
@ -27,3 +30,73 @@ exports.Registry = Registry;
exports.GlobalRegistry = new Registry();
exports.registerSerialize = exports.GlobalRegistry.registerSerialize.bind(exports.GlobalRegistry);
exports.registerDeserialize = exports.GlobalRegistry.registerDeserialize.bind(exports.GlobalRegistry);
function getFrom(map, value) {
return map.get((0, utils_1.type)(value));
}
function getSerialize(value, fallback, registry) {
return getFrom(registry.serializers, value) || fallback;
}
function getDeserialize(value, fallback, registry) {
return getFrom(registry.deserializers, value) || fallback;
}
(0, exports.registerSerialize)(Boolean, (ser, value) => ser.serializeBoolean(value));
(0, exports.registerSerialize)(String, (ser, value) => ser.serializeString(value));
(0, exports.registerSerialize)(Number, (ser, value) => ser.serializeNumber(value));
(0, exports.registerSerialize)(BigInt, (ser, value) => ser.serializeBigInt(value));
(0, exports.registerSerialize)(Symbol, (ser, value) => ser.serializeSymbol(value));
(0, exports.registerSerialize)(utils_1.Null, (ser, _value) => ser.serializeNull());
(0, exports.registerSerialize)(Object, (ser, value) => {
const obj = Object.entries(value);
const serObj = ser.serializeObject(obj.length);
obj.forEach(([key, value]) => serObj.serializeEntry(key, value));
return serObj.end();
});
(0, exports.registerSerialize)(Array, (ser, value) => {
const arr = value;
const iter = ser.serializeIterable(arr.length);
arr.forEach((value) => iter.serializeElement(value));
return iter.end();
});
(0, exports.registerDeserialize)(Boolean, (de) => de.deserializeBoolean({
visitBoolean(value) {
return value;
}
}));
(0, exports.registerDeserialize)(String, (de) => de.deserializeString({
visitString(value) {
return value;
}
}));
(0, exports.registerDeserialize)(Number, (de) => de.deserializeNumber({
visitNumber(value) {
return value;
}
}));
(0, exports.registerDeserialize)(BigInt, (de) => de.deserializeBigInt({
visitBigInt(value) {
return value;
}
}));
(0, exports.registerDeserialize)(Symbol, (de) => de.deserializeSymbol({
visitSymbol(value) {
return value;
}
}));
(0, exports.registerDeserialize)(Object, (de) => de.deserializeObject({
visitObject(access) {
let result = {};
for (const [key, value] of access) {
result[key] = value;
}
return result;
}
}));
(0, exports.registerDeserialize)(Array, (de) => de.deserializeIterable({
visitIterable(access) {
let result = [];
for (const value of access) {
result.push(value);
}
return result;
}
}));

13
dist/ser/identity.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
import { ISerializeIterable, ISerializeObject, ISerializer } from './interface';
export declare class IdentitySerializer<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;
serializeIterable(_len?: number): ISerializeIterable<T>;
serializeObject(_len?: number): ISerializeObject<T>;
serializeClass(_name: string, _len?: number): ISerializeObject<T>;
}

80
dist/ser/identity.js vendored Normal file
View file

@ -0,0 +1,80 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IdentitySerializer = void 0;
const interface_1 = require("./interface");
class IdentityMap extends interface_1.SerializeObject {
constructor() {
super(...arguments);
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: {}
});
Object.defineProperty(this, "currentKey", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
serializeKey(key) {
this.currentKey = key;
}
serializeValue(value) {
this.value[this.currentKey] = value;
}
end() {
return this.value;
}
}
class IdentityIterable extends interface_1.SerializeIterable {
constructor() {
super(...arguments);
Object.defineProperty(this, "value", {
enumerable: true,
configurable: true,
writable: true,
value: []
});
}
serializeElement(value) {
this.value.push(value);
}
end() {
return this.value;
}
}
class IdentitySerializer {
serializeAny(value) {
return value;
}
serializeBoolean(value) {
return value;
}
serializeNumber(value) {
return value;
}
serializeBigInt(value) {
return value;
}
serializeString(value) {
return value;
}
serializeSymbol(value) {
return value;
}
serializeNull() {
return null;
}
serializeIterable(_len) {
return new IdentityIterable();
}
serializeObject(_len) {
return new IdentityMap();
}
serializeClass(_name, _len) {
return new IdentityMap();
}
}
exports.IdentitySerializer = IdentitySerializer;

15
dist/ser/impl.js vendored
View file

@ -15,17 +15,17 @@ function serializeObject(serializer, obj) {
}
return serializer.end();
}
function serializeIterable(serializer, iter) {
for (const item of iter) {
serializer.serializeElement(item);
}
return serializer.end();
}
function serializeClass(serializer, value) {
const name = value.constructor.name;
const ser = serializer.serializeClass(name);
return serializeObject(ser, value);
}
function getSerialize(value, registry) {
if ((0, utils_1.isObject)(value)) {
return registry.serializers.get(value.constructor);
}
return registry.serializers.get(utils_1.PrimitivePrototype[typeof value]) || defaultSerialize;
}
function defaultSerialize(serializer, value) {
switch (typeof value) {
case 'string': return serializer.serializeString(value);
@ -37,6 +37,7 @@ function defaultSerialize(serializer, value) {
case 'object':
switch (true) {
case value == null: return serializer.serializeNull();
case Array.isArray(value): return serializeIterable(serializer.serializeIterable(value.length), value);
case !(0, utils_1.isPlainObject)(value): return serializeClass(serializer, value);
default: return serializeObject(serializer.serializeObject(), value);
}
@ -44,6 +45,6 @@ function defaultSerialize(serializer, value) {
}
}
function serialize(serializer, value, registry = registry_1.GlobalRegistry) {
const ser = getSerialize(value, registry);
const ser = (0, registry_1.getSerialize)(value, defaultSerialize, registry);
return ser(serializer, value);
}

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

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

1
dist/ser/index.js vendored
View file

@ -14,5 +14,6 @@ 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("./identity"), exports);
__exportStar(require("./impl"), exports);
__exportStar(require("./interface"), exports);

1
dist/utils.d.ts vendored
View file

@ -28,3 +28,4 @@ export declare const PrimitivePrototype: Readonly<{
readonly object: ObjectConstructor;
readonly function: FunctionConstructor;
}>;
export declare function type(value: any): Function | ObjectConstructor | SymbolConstructor | ArrayConstructor | BooleanConstructor | NumberConstructor | BigIntConstructor | StringConstructor | FunctionConstructor;

11
dist/utils.js vendored
View file

@ -8,8 +8,9 @@ exports.isIterable = isIterable;
exports.isString = isString;
exports.isNumber = isNumber;
exports.Null = Null;
exports.type = type;
function isObject(value) {
return typeof value === 'object';
return typeof value === 'object' && !Array.isArray(value);
}
function isPlainObject(value) {
return Object.getPrototypeOf(value) === Object.prototype;
@ -48,3 +49,11 @@ exports.PrimitivePrototype = Object.freeze({
object: Object,
function: Function
});
function type(value) {
switch (true) {
case Array.isArray(value): return Array;
case isPlainObject(value): return Object;
case isObject(value): return value.constructor;
default: return exports.PrimitivePrototype[typeof value];
}
}

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

@ -0,0 +1,132 @@
import { deserialize } from './impl'
import { Deserialize, IDeserializer, IMapAccess, IterableAccess, IVisitor, MapAccess } from './interface'
import { GlobalRegistry, Registry } from '../registry'
import { IterResult } from '../utils'
export function forward(value: any, into: any, registry: Registry = GlobalRegistry) {
const forwarder = new Forwarder(value)
return deserialize(forwarder, into, registry)
}
type Entry = [PropertyKey, any]
class ForwardMapAccess extends MapAccess {
private readonly _entries: Entry[]
private index: number = -1
constructor(entries: Entry[]) {
super()
this._entries = entries
}
static fromObject(value: Record<PropertyKey, any>) {
return new this(Object.entries(value))
}
nextKey<T, K extends Deserialize<T>>(seed?: K): IteratorResult<T> {
this.index += 1
if (this.index >= this._entries.length) {
return IterResult.Done()
} else {
const key = this._entries[this.index][0]
const value = seed != null ? seed(new Forwarder(key)) : key as T
return IterResult.Next(value)
}
}
nextValue<T, V extends Deserialize<T>>(seed?: V): IteratorResult<T> {
if (this.index >= this._entries.length) {
return IterResult.Done()
} else {
const value = this._entries[this.index][1]
const deser = seed != null ? seed(value) : value
return IterResult.Next(deser)
}
}
}
class ForwardIterableAccess<T> extends IterableAccess {
private readonly elements: T[]
private index: number = -1
constructor(elements: T[]) {
super()
this.elements = elements
}
nextElement<T, D extends Deserialize<T>>(seed?: D): IteratorResult<T> {
this.index += 1
if (this.index >= this.elements.length) {
return IterResult.Done()
} else {
const element = this.elements[this.index]
const deser = seed != null ? seed(new Forwarder(element)) : element
return IterResult.Next(deser as T)
}
}
}
export class Forwarder implements IDeserializer {
private readonly value: any
constructor(value: any) {
this.value = value
}
deserializeAny<T>(visitor: IVisitor<T>): T {
switch (typeof this.value) {
case 'string': return this.deserializeString(visitor)
case 'number': return this.deserializeNumber(visitor)
case 'bigint': return this.deserializeBigInt(visitor)
case 'boolean': return this.deserializeBoolean(visitor)
case 'symbol': return this.deserializeSymbol(visitor)
case 'undefined': return this.deserializeNull(visitor)
case 'object': {
switch (true) {
case Array.isArray(this.value): return this.deserializeIterable(visitor)
default: return this.deserializeObject(visitor)
}
}
case 'function': return this.deserializeFunction(visitor)
}
}
deserializeBoolean<T>(visitor: IVisitor<T>): T {
return visitor.visitBoolean(this.value)
}
deserializeNumber<T>(visitor: IVisitor<T>): T {
return visitor.visitNumber(this.value)
}
deserializeBigInt<T>(visitor: IVisitor<T>): T {
return visitor.visitBigInt(this.value)
}
deserializeString<T>(visitor: IVisitor<T>): T {
return visitor.visitString(this.value)
}
deserializeSymbol<T>(visitor: IVisitor<T>): T {
return visitor.visitSymbol(this.value)
}
deserializeNull<T>(visitor: IVisitor<T>): T {
return visitor.visitNull()
}
deserializeObject<T>(visitor: IVisitor<T>): T {
return visitor.visitObject(ForwardMapAccess.fromObject(this.value) as IMapAccess)
}
deserializeIterable<T>(visitor: IVisitor<T>): T {
return visitor.visitIterable(new ForwardIterableAccess(this.value))
}
deserializeFunction<T>(_visitor: IVisitor<T>): T {
throw new Error('Method not implemented.')
}
}

View file

@ -1,21 +1,5 @@
import { isFunction } from '../utils'
import { IDeserializer, IIterableAccess, IMapAccess, isVisitor, IVisitor } from './interface'
export class GenericSeed<T> {
readonly visitor: IVisitor<T>
constructor(visitor: IVisitor<T> = new Visitor()) {
this.visitor = visitor
}
static deserialize<T, D extends IDeserializer>(deserializer: D, visitor: IVisitor<T> = new Visitor()): T {
return deserializer.deserializeAny(visitor)
}
deserialize<D extends IDeserializer>(deserializer: D): T {
return GenericSeed.deserialize(deserializer, this.visitor)
}
}
import { IIterableAccess, IMapAccess, isVisitor, IVisitor } from './interface'
export class Visitor<T> implements IVisitor<T> {
private overrides?: Partial<IVisitor<T>>

View file

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

View file

@ -1,55 +1,25 @@
import { IterResult, Nullable } from '../utils'
import { GenericSeed } from './generic'
export interface IMapAccess {
nextKeySeed<T, K extends Deserialize<T>>(seed: K): IteratorResult<T>
nextValueSeed<T, V extends Deserialize<T>>(seed: V): IteratorResult<T>
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): IteratorResult<[TK, TV]>
nextKey<T>(): IteratorResult<T>
nextValue<V>(): IteratorResult<V>
nextEntry<K, V>(): IteratorResult<[K, V]>
nextKey<T>(seed?: Deserialize<T>): IteratorResult<T>
nextValue<T>(seed?: Deserialize<T>): IteratorResult<T>
nextEntry<K, V>(kseed?: Deserialize<K>, vseed?: Deserialize<V>): IteratorResult<[K, V]>
sizeHint?(): Nullable<number>
entries<T, K extends Deserialize<T>>(seed?: K): Iterator<T>
values<T, V extends Deserialize<T>>(seed?: V): Iterator<T>
entries<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): Iterator<[TK, TV]>
entries<T>(seed?: Deserialize<T>): Iterator<T>
values<T>(seed?: Deserialize<T>): Iterator<T>
entries<K, V>(kseed: Deserialize<K>, vseed: Deserialize<V>): Iterator<[K, V]>
[Symbol.iterator]<K, V>(): Iterator<[K, V]>
}
export abstract class MapAccess {
abstract nextKeySeed<T, K extends Deserialize<T>>(seed: K): IteratorResult<T>
abstract nextValueSeed<T, V extends Deserialize<T>>(seed: V): IteratorResult<T>
abstract nextKey<T>(seed?: Deserialize<T>): IteratorResult<T>
abstract nextValue<T>(seed?: Deserialize<T>): IteratorResult<T>
private orDefaultSeed<T>(seed?: Deserialize<T>): Deserialize<T> {
return seed || GenericSeed.deserialize
}
nextEntrySeed<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed: K, vseed: V): IteratorResult<[TK, TV]> {
const key = this.nextKeySeed(kseed) as IteratorResult<TK>
nextEntry<K, V>(kseed?: Deserialize<K>, vseed?: Deserialize<V>): IteratorResult<[K, V]> {
const key = this.nextKey(kseed) as IteratorResult<K>
if (!key.done) {
const value = this.nextValueSeed(vseed) as IteratorResult<TV>
if (!value.done) {
return IterResult.Next([key.value, value.value])
}
}
return IterResult.Done()
}
nextKey<T>(): IteratorResult<T> {
return this.nextKeySeed(GenericSeed.deserialize)
}
nextValue<V>(): IteratorResult<V> {
return this.nextValueSeed(GenericSeed.deserialize)
}
nextEntry<K, V>(): IteratorResult<[K, V]> {
const key = this.nextKey() as IteratorResult<K>
if (!key.done) {
const value = this.nextValue() as IteratorResult<V>
const value = this.nextValue(vseed) as IteratorResult<V>
if (!value.done) {
return IterResult.Next([key.value, value.value])
@ -67,28 +37,16 @@ export abstract class MapAccess {
}
}
keys<T, K extends Deserialize<T>>(seed?: K): Iterator<T> {
return this.generate(
seed == null ?
this.nextKey.bind(this) :
this.nextKeySeed.bind(this, seed) as any
)
keys<T>(seed?: Deserialize<T>): Iterator<T> {
return this.generate<T>(this.nextKey.bind(this, seed) as any)
}
values<T, V extends Deserialize<T>>(seed?: V): Iterator<T> {
return this.generate(
seed == null ?
this.nextValue.bind(this) :
this.nextValueSeed.bind(this, seed) as any
)
values<T>(seed?: Deserialize<T>): Iterator<T> {
return this.generate(this.nextValue.bind(this, seed) as any)
}
entries<TK, TV, K extends Deserialize<TK>, V extends Deserialize<TV>>(kseed?: K, vseed?: V): Iterator<[TK, TV]> {
return this.generate(
kseed == null && vseed == null ?
this.nextEntry.bind(this) :
this.nextEntrySeed.bind(this, this.orDefaultSeed(kseed), this.orDefaultSeed(vseed)) as any
)
entries<K, V>(kseed?: Deserialize<K>, vseed?: Deserialize<V>): Iterator<[K, V]> {
return this.generate(this.nextEntry.bind(this, kseed, vseed) as any)
}
[Symbol.iterator]<K, V>(): Iterator<K, V> {
@ -97,18 +55,13 @@ export abstract class MapAccess {
}
export interface IIterableAccess {
nextElementSeed<T, D extends Deserialize<T>>(seed: D): IteratorResult<T>
nextElement<T>(): IteratorResult<T>
nextElement<T>(seed: Deserialize<T>): IteratorResult<T>
sizeHint?(): Nullable<number>
[Symbol.iterator]<T>(): Iterator<T>
}
export abstract class IterableAccess implements IIterableAccess {
abstract nextElementSeed<T, D extends Deserialize<T>>(seed: D): IteratorResult<T>
nextElement<T>(): IteratorResult<T> {
return this.nextElementSeed(GenericSeed.deserialize)
}
abstract nextElement<T>(seed: Deserialize<T>): IteratorResult<T>
[Symbol.iterator]<T>(): Iterator<T> {
return {

View file

@ -1,5 +1,6 @@
import { Deserialize } from './de'
import { Serialize } from './ser'
import { Deserialize, IDeserializer, IIterableAccess, IMapAccess } from './de'
import { ISerializer, Serialize } from './ser'
import { isObject, Null, PrimitivePrototype, type } from './utils'
export class Registry {
serializers: Map<Function, Serialize<any, any>> = new Map()
@ -18,3 +19,95 @@ export const GlobalRegistry = new Registry()
export const registerSerialize = GlobalRegistry.registerSerialize.bind(GlobalRegistry)
export const registerDeserialize = GlobalRegistry.registerDeserialize.bind(GlobalRegistry)
function getFrom<T, V>(map: Map<Function, T>, value: V): T {
return map.get(type(value)) as T
}
export function getSerialize<T, U>(value: U, fallback: Serialize<T, U>, registry: Registry): Serialize<T, U> {
return getFrom(registry.serializers, value) || fallback
}
export function getDeserialize<T, U>(value: U, fallback: Deserialize<T>, registry: Registry): Deserialize<T> {
return getFrom(registry.deserializers, value) || fallback
}
registerSerialize(Boolean, <T, U>(ser: ISerializer<T>, value: U) => ser.serializeBoolean(value as boolean))
registerSerialize(String, <T, U>(ser: ISerializer<T>, value: U) => ser.serializeString(value as string))
registerSerialize(Number, <T, U>(ser: ISerializer<T>, value: U) => ser.serializeNumber(value as number))
registerSerialize(BigInt, <T, U>(ser: ISerializer<T>, value: U) => ser.serializeBigInt(value as bigint))
registerSerialize(Symbol, <T, U>(ser: ISerializer<T>, value: U) => ser.serializeSymbol(value as symbol))
registerSerialize(Null, <T, U>(ser: ISerializer<T>, _value: U) => ser.serializeNull())
registerSerialize(Object, <T, U>(ser: ISerializer<T>, value: U) => {
const obj = Object.entries(value as object)
const serObj = ser.serializeObject(obj.length)
obj.forEach(([key, value]) => serObj.serializeEntry(key, value))
return serObj.end()
})
registerSerialize(Array, <T, U>(ser: ISerializer<T>, value: U) => {
const arr = value as any[]
const iter = ser.serializeIterable(arr.length)
arr.forEach((value: any) => iter.serializeElement(value))
return iter.end()
})
registerDeserialize(Boolean, (de: IDeserializer) => de.deserializeBoolean({
visitBoolean(value: boolean) {
return value
}
}))
registerDeserialize(String, (de: IDeserializer) => de.deserializeString({
visitString(value: string) {
return value
}
}))
registerDeserialize(Number, (de: IDeserializer) => de.deserializeNumber({
visitNumber(value: number) {
return value
}
}))
registerDeserialize(BigInt, (de: IDeserializer) => de.deserializeBigInt({
visitBigInt(value: bigint) {
return value
}
}))
registerDeserialize(Symbol, (de: IDeserializer) => de.deserializeSymbol({
visitSymbol(value: symbol) {
return value
}
}))
registerDeserialize(Object, (de: IDeserializer) => de.deserializeObject({
visitObject(access: IMapAccess) {
let result = {} as any
for (const [key, value] of access) {
result[key] = value
}
return result
}
}))
registerDeserialize(Array, (de: IDeserializer) => de.deserializeIterable({
visitIterable(access: IIterableAccess) {
let result = []
for (const value of access) {
result.push(value)
}
return result
}
}))

73
src/ser/identity.ts Normal file
View file

@ -0,0 +1,73 @@
import { ISerializeIterable, ISerializeObject, ISerializer, SerializeIterable, SerializeObject } from './interface'
class IdentityMap<T> extends SerializeObject<T> {
private value: Record<any, any> = {}
private currentKey?: string
serializeKey(key: string): void {
this.currentKey = key
}
serializeValue<U>(value: U): void {
this.value[this.currentKey!] = value
}
end(): T {
return this.value
}
}
class IdentityIterable<T> extends SerializeIterable<T> {
private value: any[] = []
serializeElement<U>(value: U): void {
this.value.push(value)
}
end(): T {
return this.value as T
}
}
export class IdentitySerializer<T> implements ISerializer<T> {
serializeAny?(value: any): T {
return value as T
}
serializeBoolean(value: boolean): T {
return value as T
}
serializeNumber(value: number): T {
return value as T
}
serializeBigInt(value: bigint): T {
return value as T
}
serializeString(value: string): T {
return value as T
}
serializeSymbol(value: symbol): T {
return value as T
}
serializeNull(): T {
return null as T
}
serializeIterable(_len?: number): ISerializeIterable<T> {
return new IdentityIterable()
}
serializeObject(_len?: number): ISerializeObject<T> {
return new IdentityMap()
}
serializeClass(_name: string, _len?: number): ISerializeObject<T> {
return new IdentityMap()
}
}

View file

@ -1,6 +1,6 @@
import { ISerializeObject, ISerializer, Serialize } from './interface'
import { ISerializeIterable, ISerializeObject, ISerializer, Serialize } from './interface'
import { isObject, isPlainObject, PrimitivePrototype } from '../utils'
import { GlobalRegistry, Registry } from '../registry'
import { getSerialize, GlobalRegistry, Registry } from '../registry'
class UnhandledTypeError extends TypeError {
constructor(serializer: ISerializer<unknown>, value: any) {
@ -17,19 +17,20 @@ function serializeObject<T, U extends object, S extends ISerializeObject<T>>(ser
return serializer.end()
}
function serializeIterable<T, U extends Iterable<U>, S extends ISerializeIterable<T>>(serializer: S, iter: U): T {
for (const item of iter) {
serializer.serializeElement(item)
}
return serializer.end()
}
function serializeClass<T, U extends object, S extends ISerializer<T>>(serializer: S, value: U): T {
const name = value.constructor.name
const ser = serializer.serializeClass(name)
return serializeObject(ser, value)
}
function getSerialize<T, U>(value: U, registry: Registry): Serialize<T, U> {
if (isObject(value)) {
return registry.serializers.get(value.constructor) as Serialize<T, U>
}
return registry.serializers.get(PrimitivePrototype[typeof value]) || defaultSerialize as Serialize<T, U>
}
function defaultSerialize<T, U, S extends ISerializer<T>>(serializer: S, value: U): T {
switch (typeof value) {
case 'string': return serializer.serializeString(value)
@ -41,6 +42,7 @@ function defaultSerialize<T, U, S extends ISerializer<T>>(serializer: S, value:
case 'object':
switch (true) {
case value == null: return serializer.serializeNull()
case Array.isArray(value): return serializeIterable(serializer.serializeIterable(value.length), value)
case !isPlainObject(value): return serializeClass(serializer, value)
default: return serializeObject(serializer.serializeObject(), value)
}
@ -49,7 +51,7 @@ function defaultSerialize<T, U, S extends ISerializer<T>>(serializer: S, value:
}
export function serialize<T, U, S extends ISerializer<T>>(serializer: S, value: U, registry: Registry = GlobalRegistry): T {
const ser = getSerialize<T, U>(value, registry)
const ser = getSerialize<T, U>(value, defaultSerialize, registry)
return ser(serializer, value)
}

View file

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

View file

@ -11,7 +11,7 @@ export interface ToString {
}
export function isObject(value: any): value is object {
return typeof value === 'object'
return typeof value === 'object' && !Array.isArray(value)
}
export function isPlainObject(value: any): value is object {
@ -61,3 +61,12 @@ export const PrimitivePrototype = Object.freeze({
function: Function
} as const)
export function type(value: any) {
switch (true) {
case Array.isArray(value): return Array
case isPlainObject(value): return Object
case isObject(value): return value.constructor
default: return PrimitivePrototype[typeof value]
}
}