jasone
Version:
A lightweight, extensible JSON encoder and decoder that supports custom types.
259 lines • 8.31 kB
TypeScript
//#region src/types.d.ts
/**
* A JSON-compatible value.
*/
type JsonValue = string | number | boolean | null | JsonValue[] | {
[key: string]: JsonValue;
};
/**
* The id of an encoded type to identify it on decoding.
*/
type TypeId = string | number;
type ClassLike<TInstance> = new (...args: any[]) => TInstance;
declare const nonJsonTypes: ("bigint" | "symbol" | "undefined" | "object" | "function")[];
type NonJsonType = {
bigint: bigint;
function: Function;
object: object;
symbol: symbol;
undefined: undefined;
};
type EncodeFilter<TType> = {
class?: ClassLike<TType>;
any?: (value: unknown) => boolean;
} & { [Key in keyof NonJsonType]?: ((value: NonJsonType[Key]) => boolean) | boolean };
type EncodeHandler<TType, TJson extends JsonValue> = (ctx: EncodeContext<TType>) => EncodeResult<TJson>;
type EncodeContext<TType> = {
value: TType;
jasone: Jasone;
};
type EncodeResult<TJson extends JsonValue> = TJson extends Record<string, JsonValue> ? [TypeId, TJson] | [null, JsonValue] : [null, JsonValue];
type Encoder<TType = unknown, TJson extends JsonValue = JsonValue> = {
filter?: EncodeFilter<TType> | EncodeFilter<TType>[];
handler: EncodeHandler<TType, TJson>;
};
type DecodeFilter = TypeId | ((typeId: TypeId, value: Record<string, JsonValue>) => boolean);
type DecodeContext<TJson extends Record<string, JsonValue>> = {
value: TJson;
typeId: TypeId;
jasone: Jasone;
};
type DecodeHandler<TType, TJson extends Record<string, JsonValue>> = (ctx: DecodeContext<TJson>) => TType;
type Decoder<TType = unknown, TJson extends Record<string, JsonValue> = Record<string, JsonValue>> = {
filter?: DecodeFilter | DecodeFilter[];
handler: DecodeHandler<TType, TJson>;
};
type Transformer<TType = unknown, TJson extends JsonValue = JsonValue> = {
encoder?: Encoder<TType, TJson>;
decoder?: TJson extends Record<string, JsonValue> ? Decoder<TType, TJson> : never;
};
//#endregion
//#region src/jasone.d.ts
/**
* Jasone options that can be passed to the Jasone constructor.
*/
type JasoneOptions = {
/**
* The type identifier that is used to determine if an object is a typed object.
*
* @default "$"
*/
typeIdentifier?: string;
/**
* The type transformers that are used to transform types.
*
* If not provided, the default transformers are used. By providing your own transformers,
* the default transformers are not used anymore, so be sure to also include them if needed.
*
* @example
* ```ts
* // your custom transformer WITH default type transformers
* const jasone = new Jasone({ types: [myCustomTransformer, ...defaultTransformers] });
*
* // your custom transformer WITHOUT default type transformers
* const jasone = new Jasone({ types: [myCustomTransformer] });
* ```
*/
types?: Transformer<any, any>[];
};
/**
* Jasone is a JSON encoder and decoder that can handle custom types.
*
* It exposes the default Jasone instance as `Jasone.default` and also
* registers the methods as static methods on the class itself, so you
* can easily use them without having to create an instance.
*
* ```ts
* const encoded = Jasone.encode(value);
* const decoded = Jasone.decode(value);
* ```
*
* If you want to use your own types, you can either register them on any
* instance (even on `Jasone.default`) or instantiate your own Jasone instance.
*
* ```ts
* // use our own types without the default types (Date, BigInt, Map, etc.)
* const myInstance = new Jasone({
* types: [myCustomTransformer]
* });
*
* // or use our own types with the default types (recommend)
* const myInstance = new Jasone({
* types: [myCustomTransformer, ...defaultTransformers],
* });
*
* const encoded = myInstance.encode(value);
* const decoded = myInstance.decode(value);
* ```
*/
declare class Jasone {
#private;
constructor(options?: JasoneOptions);
/**
* Register a new type to this Jasone instance.
*
* @param type The type to register.
*/
register<TType, TJson extends JsonValue>(transformer: Transformer<TType, TJson>): void;
/**
* Encode an arbitrary value to a JSON-compatible Jasone encoded value.
*
* @param value The value to encode.
* @returns A JSON-compatible Jasone encoded value.
*/
encode(value: unknown): JsonValue;
/**
* Decode an Jasone encoded value to its decoded value.
*
* @param value The Jasone encoded value to decode.
* @returns The decoded value.
*/
decode<T = unknown>(value: JsonValue): T;
/**
* Alias for `encode`.
*/
serialize(value: unknown): JsonValue;
/**
* Alias for `decode`.
*/
deserialize<T = unknown>(value: JsonValue): T;
/**
* A wrapper around `JSON.stringify` that encodes a value and stringifies it.
*
* Under the hood, this functions looks like this:
*
* ```ts
* JSON.stringify(this.encode(value));
* ```
*/
stringify(value: unknown): string;
/**
* A wrapper around `JSON.parse` that parses a Jasone encoded value and decodes it.
*
* Under the hood, this functions looks like this:
*
* ```ts
* this.decode(JSON.parse(value));
* ```
*/
parse<T = unknown>(value: string): T;
/**
* The default Jasone instance with the default types already registered.
*/
static default: Jasone;
/**
* Register a new type to the default Jasone instance.
*
* @param type The type to register.
*/
/**
* Encode an arbitrary value to a JSON-compatible Jasone encoded value.
*
* @param value The value to encode.
* @returns A JSON-compatible Jasone encoded value.
*/
static encode: (value: unknown) => JsonValue;
/**
* Alias for `encode`.
*/
static serialize: (value: unknown) => JsonValue;
/**
* Decode an Jasone encoded value to its decoded value.
*
* @param value The Jasone encoded value to decode.
* @returns The decoded value.
*/
static decode: <T = unknown>(value: JsonValue) => T;
/**
* Alias for `decode`.
*/
static deserialize: <T = unknown>(value: JsonValue) => T;
/**
* A wrapper around `JSON.stringify` that encodes a value and stringifies it.
*
* Under the hood, this functions looks like this:
*
* ```ts
* JSON.stringify(Jasone.encode(value));
* ```
*/
static stringify: (value: unknown) => string;
/**
* A wrapper around `JSON.parse` that parses a Jasone encoded value and decodes it.
*
* Under the hood, this functions looks like this:
*
* ```ts
* Jasone.decode(JSON.parse(value));
* ```
*/
static parse: <T = unknown>(value: string) => T;
/**
* Register a new type to this Jasone instance.
*
* @param type The type to register.
*/
static register: <TType, TJson extends JsonValue>(transformer: Transformer<TType, TJson>) => void;
}
//#endregion
//#region src/transformers/bigint.d.ts
declare const bigIntTransformer: Transformer<bigint, {
bigint: string;
}>;
//#endregion
//#region src/transformers/date.d.ts
declare const dateTransformer: Transformer<Date, {
iso: string;
}>;
//#endregion
//#region src/transformers/map.d.ts
declare const mapTransformer: Transformer<Map<unknown, unknown>, {
entries: [key: JsonValue, value: JsonValue][];
}>;
//#endregion
//#region src/transformers/regexp.d.ts
declare const regExpTransformer: Transformer<RegExp, {
source: string;
flags: string;
}>;
//#endregion
//#region src/transformers/set.d.ts
declare const setTransformer: Transformer<Set<unknown>, {
values: JsonValue[];
}>;
//#endregion
//#region src/transformers/undefined.d.ts
declare const undefinedTransformer: Transformer<undefined, {}>;
//#endregion
//#region src/transformers/url.d.ts
declare const urlTransformer: Transformer<URL, {
url: string;
}>;
//#endregion
//#region src/transformers/index.d.ts
/**
* The default type transformers that are used by the default Jasone instance.
*/
declare const defaultTransformers: Transformer[];
//#endregion
export { ClassLike, DecodeContext, DecodeFilter, DecodeHandler, Decoder, EncodeContext, EncodeFilter, EncodeHandler, EncodeResult, Encoder, Jasone, JasoneOptions, JsonValue, NonJsonType, Transformer, TypeId, type bigIntTransformer, type dateTransformer, defaultTransformers, type mapTransformer, nonJsonTypes, type regExpTransformer, type setTransformer, type undefinedTransformer, type urlTransformer };