jasone
Version:
A lightweight, extensible JSON encoder and decoder that supports custom types.
262 lines • 9.21 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 Context = Record<string, unknown>;
type EncodeFilter<TType, TContext extends Context = Context> = {
class?: ClassLike<TType>;
any?: (options: EncodeOptions<unknown, TContext>) => boolean;
} & { [Key in keyof NonJsonType]?: ((options: EncodeOptions<NonJsonType[Key], TContext>) => boolean) | boolean };
type EncodeHandler<TType, TJson extends JsonValue, TContext extends Context = Context> = (ctx: EncodeOptions<TType, TContext>) => EncodeResult<TJson>;
type EncodeOptions<TType, TContext extends Context = Context> = {
value: TType;
jasone: Jasone;
context: TContext;
};
type EncodeResult<TJson extends JsonValue> = TJson extends Record<string, JsonValue> ? [TypeId, TJson] | [null, JsonValue] : [null, JsonValue];
type Encoder<TType = unknown, TJson extends JsonValue = JsonValue, TContext extends Context = Context> = {
filter?: EncodeFilter<TType, TContext> | EncodeFilter<TType, TContext>[];
handler: EncodeHandler<TType, TJson, TContext>;
};
type DecodeFilter<TContext extends Context = Context> = TypeId | ((options: DecodeOptions<Record<string, JsonValue>, TContext>) => boolean);
type DecodeOptions<TJson extends Record<string, JsonValue>, TContext extends Context = Context> = {
value: TJson;
typeId: TypeId;
jasone: Jasone;
context: TContext;
};
type DecodeHandler<TType, TJson extends Record<string, JsonValue>, TContext extends Context = Context> = (ctx: DecodeOptions<TJson, TContext>) => TType;
type Decoder<TType = unknown, TJson extends Record<string, JsonValue> = Record<string, JsonValue>, TContext extends Context = Context> = {
filter?: DecodeFilter<TContext> | DecodeFilter<TContext>[];
handler: DecodeHandler<TType, TJson, TContext>;
};
type Transformer<TType = unknown, TJson extends JsonValue = JsonValue, TContext extends Context = Context> = {
encoder?: Encoder<TType, TJson, TContext>;
decoder?: TJson extends Record<string, JsonValue> ? Decoder<TType, TJson, TContext> : 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, 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, TContext extends Context = Context>(transformer: Transformer<TType, TJson, TContext>): 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, context?: Context): 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, context?: Context): T;
/**
* Alias for `encode`.
*/
serialize(value: unknown, context?: Context): JsonValue;
/**
* Alias for `decode`.
*/
deserialize<T = unknown>(value: JsonValue, context?: Context): 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, context?: Context): 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, context?: Context): 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, context?: Context) => JsonValue;
/**
* Alias for `encode`.
*/
static serialize: (value: unknown, context?: Context) => 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, context?: Context) => T;
/**
* Alias for `decode`.
*/
static deserialize: <T = unknown>(value: JsonValue, context?: Context) => 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, context?: Context) => 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, context?: Context) => T;
/**
* Register a new type to this Jasone instance.
*
* @param type The type to register.
*/
static register: <TType, TJson extends JsonValue, TContext extends Context = Context>(transformer: Transformer<TType, TJson, TContext>) => 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, Context, DecodeFilter, DecodeHandler, DecodeOptions, Decoder, EncodeFilter, EncodeHandler, EncodeOptions, EncodeResult, Encoder, Jasone, JasoneOptions, JsonValue, NonJsonType, Transformer, TypeId, type bigIntTransformer, type dateTransformer, defaultTransformers, type mapTransformer, nonJsonTypes, type regExpTransformer, type setTransformer, type undefinedTransformer, type urlTransformer };