Comparing version 0.1.0-next.12 to 0.1.0-next.13
@@ -35,5 +35,2 @@ import { Initial } from './Builder'; | ||
/** | ||
* Helper for creating ADT `is` functions. | ||
*/ | ||
/** | ||
* Define an algebraic data type. There must be at least two members. If all members have a parse function then an ADT level parse function will automatically be derived. | ||
@@ -40,0 +37,0 @@ */ |
@@ -22,88 +22,3 @@ "use strict"; | ||
exports.createCreate = createCreate; | ||
// export const createSchema = < | ||
// Member1 extends ADTMemberNamespaceBase, | ||
// Member2 extends ADTMemberNamespaceBase, | ||
// MembersRest extends ADTMemberNamespaceBase[], | ||
// >( | ||
// member1: Member1, | ||
// member2: Member2, | ||
// ...members: [...MembersRest] | ||
// ) => { | ||
// return z.union([member1.schema, member2.schema, ...members.map((_) => _.schema)]) | ||
// } | ||
/** | ||
* Helper for creating ADT `is` functions. | ||
*/ | ||
// export const createIs = <ADTMember extends VariantBase>( | ||
// memberTag: ADTMember[`_tag`] | ||
// ): ((x: unknown) => x is ADTMember) => { | ||
// return (x: unknown): x is ADTMember => { | ||
// return is$(x, memberTag) | ||
// } | ||
// } | ||
// /** | ||
// * Helper for implementing parseOrThrow. | ||
// * | ||
// * @param parser - The parse function. This wraps it to automate an implementation of parseOrThrow. | ||
// * @param dataTypeMemberName - The name of the data type trying to be parsed. Used for nicer error messages. | ||
// * @returns The data type. | ||
// * @throws An error if parsing fails. | ||
// */ | ||
// export const createParseOrThrow = <ADTMember extends VariantBase>( | ||
// parser: Parse<ADTMember>, | ||
// dataTypeMemberName: ADTMember[`_tag`] | ADTMember[`_tag`][] | ||
// ): ((x: string) => ADTMember) => { | ||
// return (x) => { | ||
// const result = parser(x) | ||
// if (result === null) { | ||
// // prettier-ignore | ||
// const message = | ||
// Array.isArray(dataTypeMemberName) | ||
// ? endent` | ||
// Could not parse the given string into any of the data types: ${dataTypeMemberName.map(_=>`"${_}"`).join(`, `)}. | ||
// ` | ||
// : endent` | ||
// Could not parse the given string into the data type "${dataTypeMemberName}". | ||
// ` | ||
// throw new Error(endent` | ||
// ${message} | ||
// The given string was: | ||
// ${x} | ||
// `) | ||
// } | ||
// return result | ||
// } | ||
// } | ||
// export const deriveEnum = <S extends z.ZodUnion<[z.ZodLiteral<string>, ...z.ZodLiteral<string>[]]>>( | ||
// schema: S | ||
// ): DeriveEnum<S[`_def`][`options`]> => | ||
// // eslint-disable-next-line | ||
// schema._def.options.reduce((_enum, literal) => { | ||
// return Object.assign(_enum, { [literal._def.value]: literal._def.value }) | ||
// // eslint-disable-next-line | ||
// }, {}) as any | ||
// type DeriveEnum<Literals extends [...z.ZodLiteral<string>[]]> = { | ||
// [k in Literals[number] as k[`_def`][`value`]]: k[`_def`][`value`] | ||
// } | ||
// export const deriveCreate = | ||
// <S extends z.ZodObject<{ _tag: z.ZodLiteral<string> }>>(schema: S) => | ||
// (input: z.TypeOf<z.Omit<S, { _tag: true }>>): z.TypeOf<S> => { | ||
// return { | ||
// ...input, | ||
// _tag: schema._def.shape()._tag._def.value, | ||
// } | ||
// } | ||
// export const deriveIs = | ||
// <S extends ADTMember>(schema: S) => | ||
// (value: unknown): value is z.TypeOf<S> => { | ||
// return ( | ||
// // TODO | ||
// // eslint-disable-next-line | ||
// typeof value === `object` && value !== null && (value as any)._tag === schema._def.shape()._tag.value | ||
// ) | ||
// } | ||
// type ADTMember = z.ZodObject<{ | ||
// _tag: z.ZodLiteral<string> | ||
// }> | ||
/** | ||
* Define an algebraic data type. There must be at least two members. If all members have a parse function then an ADT level parse function will automatically be derived. | ||
@@ -113,8 +28,21 @@ */ | ||
const create = (name) => { | ||
let currentVariant = null; | ||
const variants = []; | ||
const api = { | ||
variant: (name, schema) => { | ||
variants.push({ name, schema: z_1.z.object(schema) }); | ||
currentVariant = { | ||
name, | ||
schema: z_1.z.object(schema), | ||
}; | ||
variants.push(currentVariant); | ||
return api; | ||
}, | ||
codec: (codecDef) => { | ||
if (!currentVariant) | ||
throw new Error(`Define variant first.`); | ||
if (currentVariant.codec) | ||
throw new Error(`Codec already defined.`); | ||
currentVariant.codec = codecDef; | ||
return api; | ||
}, | ||
done: () => { | ||
@@ -138,2 +66,12 @@ if ((0, utils_1.isEmpty)(variants)) | ||
is: (x) => (0, helpers_1.is)(x, symbol), | ||
decode: (data) => { | ||
if (!v.codec) | ||
throw new Error(`Codec not implemented.`); | ||
return api.create(v.codec.decode(data)); | ||
}, | ||
encode: (data) => { | ||
if (!v.codec) | ||
throw new Error(`Codec not implemented.`); | ||
return v.codec.encode(data); | ||
}, | ||
}; | ||
@@ -140,0 +78,0 @@ return api; |
/** | ||
* This module is concerned with the static types for the API of building up an ADT. | ||
*/ | ||
import { GetADTMethods, GetVariantsNamespacedMethods } from './Controller'; | ||
import { Controller, GetConstructorInput } from './Controller'; | ||
import { z } from './lib/z'; | ||
@@ -12,3 +12,3 @@ export declare type SchemaBase = Record<string, z.ZodType<unknown>>; | ||
*/ | ||
export declare type Initial<ADT, StoredVariants extends StoredVariantsBase> = VariantRequired<ADT, StoredVariants>; | ||
export declare type Initial<ADT extends StoredADT, Vs extends StoredVariants> = VariantRequired<ADT, Vs>; | ||
/** | ||
@@ -19,3 +19,3 @@ * The builder API when it is in a state where a variant is required. | ||
*/ | ||
export interface VariantRequired<ADT, StoredVariants extends StoredVariantsBase> { | ||
export interface VariantRequired<ADT extends StoredADT, Vs extends StoredVariants> { | ||
/** | ||
@@ -28,7 +28,7 @@ * Create a variant of this ADT. | ||
extensions?: Extensions; | ||
}): PostVariantBuilder<ADT, [CreateStoredVariant<Name, Schema, Parse, Extensions>, ...StoredVariants]>; | ||
}): PostVariantBuilder<ADT, CreateStoredVariant<Name, Schema, false, Parse, Extensions>, Vs>; | ||
/** | ||
* Sugar for quickly defining ADT members that only have a schema. | ||
*/ | ||
variant<Name extends string, Schema extends SchemaBase>(name: Name, schema?: Schema): PostVariantBuilder<ADT, [CreateStoredVariant<Name, Schema, never, never>, ...StoredVariants]>; | ||
variant<Name extends string, Schema extends SchemaBase>(name: Name, schema?: Schema): PostVariantBuilder<ADT, CreateStoredVariant<Name, Schema, false, never, never>, Vs>; | ||
} | ||
@@ -39,29 +39,59 @@ /** | ||
*/ | ||
export interface PostVariantBuilder<ADT, Vs extends StoredVariantsBase> extends VariantRequired<ADT, Vs> { | ||
done(): ADT & GetADTMethods<Vs> & GetVariantsNamespacedMethods<Vs>; | ||
export interface PostVariantBuilder<ADT extends StoredADT, V extends StoredVariant, Vs extends StoredVariants> extends VariantRequired<ADT, [V, ...Vs]> { | ||
codec(params: CodecParams<V>): PostCodecBuilder<ADT, StoredVariant.EnableCodec<V>, Vs>; | ||
done(): Controller<ADT, [V, ...Vs]>; | ||
} | ||
declare type CreateStoredVariant<Name extends NameBase, Schema extends SchemaBase, Parse extends (expression: string, extensions: Extensions) => null | z.TypeOf<z.ZodObject<Schema>>, Extensions extends ExtensionsBase> = [ | ||
Name, | ||
{ | ||
schema: SchemaBase extends Schema ? { | ||
_tag: z.ZodLiteral<Name>; | ||
} : Schema & { | ||
_tag: z.ZodLiteral<Name>; | ||
}; | ||
parse: Parse; | ||
extensions: ExtensionsBase extends Extensions ? {} : Extensions; | ||
} | ||
]; | ||
export declare type GetName<V extends StoredVariantsBase> = V[0]; | ||
export declare type Parse2<T = unknown> = (string: string) => null | T; | ||
export declare type Parse2OrThrow<T = unknown> = (string: string) => T; | ||
export declare type StoredVariantsBase = [StoredVariantBase, ...StoredVariantBase[]]; | ||
declare type StoredVariantBase = [name: string, data: StoredVariantData]; | ||
export declare type StoredVariantRecordBase = Record<string, StoredVariantData>; | ||
export declare type StoredVariantData = { | ||
export interface CodecParams<V extends StoredVariant = StoredVariant> { | ||
encode: Encoder<V>; | ||
decode: Decoder<V>; | ||
} | ||
export declare type Encoder<V extends StoredVariant> = (decodedData: StoredVariant.GetType<V>) => string; | ||
export declare type Decoder<V extends StoredVariant> = (encodedData: string) => GetConstructorInput<V>; | ||
/** | ||
* The builder API when it is a state of having at least one variant defined. | ||
* At this point the ADT can be marked as done. | ||
*/ | ||
export interface PostCodecBuilder<ADT extends StoredADT, V extends StoredVariant, Vs extends StoredVariants> extends VariantRequired<ADT, [V, ...Vs]> { | ||
done(): Controller<ADT, [V, ...Vs]>; | ||
} | ||
declare type CreateStoredVariant<Name extends NameBase, Schema extends SchemaBase, Codec extends boolean, Parse extends (expression: string, extensions: Extensions) => null | z.TypeOf<z.ZodObject<Schema>>, Extensions extends ExtensionsBase> = { | ||
name: Name; | ||
schema: SchemaBase extends Schema ? { | ||
_tag: z.ZodLiteral<Name>; | ||
} : Schema & { | ||
_tag: z.ZodLiteral<Name>; | ||
}; | ||
codec: Codec; | ||
parse: Parse; | ||
extensions: ExtensionsBase extends Extensions ? {} : Extensions; | ||
}; | ||
export declare type StoredADT<Name extends string = string> = { | ||
name: Name; | ||
}; | ||
export declare type StoredVariant = { | ||
name: string; | ||
schema: z.ZodRawShape; | ||
codec: boolean; | ||
parse?: Parse2; | ||
extensions?: ExtensionsBase; | ||
}; | ||
export declare namespace StoredVariant { | ||
type EnableCodec<V extends StoredVariant> = Omit<V, `codec`> & { | ||
codec: true; | ||
}; | ||
type GetType<V extends StoredVariant> = z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
type GetZodSchema<V extends StoredVariant> = z.ZodObject<V[`schema`]>; | ||
} | ||
export declare type StoredVariants = [StoredVariant, ...StoredVariant[]]; | ||
export declare namespace StoredVariants { | ||
export type ZodUnion<Vs extends StoredVariants> = z.ZodUnion<ToZodObjects<Vs>>; | ||
export type Union<Vs extends StoredVariants> = z.TypeOf<ZodUnion<Vs>>; | ||
type ToZodObjects<Vs extends StoredVariants> = { | ||
[Index in keyof Vs]: z.ZodObject<Vs[Index][`schema`]>; | ||
}; | ||
export {}; | ||
} | ||
export declare type Parse2<T = unknown> = (string: string) => null | T; | ||
export declare type Parse2OrThrow<T = unknown> = (string: string) => T; | ||
export {}; | ||
//# sourceMappingURL=Builder.d.ts.map |
@@ -1,25 +0,37 @@ | ||
import { Parse2, Parse2OrThrow, StoredVariantData, StoredVariantRecordBase, StoredVariantsBase } from './Builder'; | ||
import { IsUnknown, TupleToObject } from './lib/utils'; | ||
import { Decoder, Encoder, Parse2, Parse2OrThrow, StoredADT, StoredVariant, StoredVariants } from './Builder'; | ||
import { IsUnknown, OmitRequired } from './lib/utils'; | ||
import { z } from './lib/z'; | ||
import { ZodRawShape } from 'zod'; | ||
export declare type GetADTMethods<Vs extends StoredVariantsBase> = { | ||
schema: GetADTSchema<Vs>; | ||
export declare type Controller<ADT extends StoredADT, Vs extends StoredVariants> = ADT & ADTMethods<Vs> & VariantsNamespacedMethods<Vs>; | ||
/** | ||
* Build up the API on the ADT itself: | ||
* | ||
* ```ts | ||
* const A = Alge.create('A')... | ||
* // A.<...> <-- Methods here | ||
* ``` | ||
*/ | ||
declare type ADTMethods<Vs extends StoredVariants> = { | ||
schema: StoredVariants.ZodUnion<Vs>; | ||
} & (IsAllMembersHaveParse<Vs> extends true ? { | ||
parse: Parse2<z.TypeOf<GetADTSchema<Vs>>>; | ||
parseOrThrow: Parse2OrThrow<z.TypeOf<GetADTSchema<Vs>>>; | ||
parse: Parse2<StoredVariants.Union<Vs>>; | ||
parseOrThrow: Parse2OrThrow<StoredVariants.Union<Vs>>; | ||
} : {}); | ||
declare type IsAllMembersHaveParse<Vs extends StoredVariantsBase> = { | ||
declare type IsAllMembersHaveParse<Vs extends StoredVariants> = { | ||
[K in keyof Vs]: IsUnknown<Vs[K][1][`parse`]> extends true ? `missing` : never; | ||
} extends [never, ...never[]] ? true : false; | ||
declare type GetADTSchema<Members extends StoredVariantsBase> = z.ZodUnion<{ | ||
[K in keyof Members]: z.ZodObject<Members[K][1][`schema`]>; | ||
}>; | ||
export declare type GetVariantsNamespacedMethods<Vs extends StoredVariantsBase> = GetVariantsNamespacedMethods_<Vs, TupleToObject<Vs[number]>>; | ||
export declare type GetVariantsNamespacedMethods_<Vs extends StoredVariantsBase, VsRec extends StoredVariantRecordBase> = { | ||
[Name in keyof VsRec]: VariantApi<Vs, Name, VsRec[Name]>; | ||
/** | ||
* build up the API for each variant defined in the ADT: | ||
* | ||
* ```ts | ||
* const A = Alge.create('A').variant('B',...)... | ||
* // A.B.<...> <-- Methods here | ||
* ``` | ||
*/ | ||
export declare type VariantsNamespacedMethods<Vs extends StoredVariants> = { | ||
[V in Vs[number] as V[`name`]]: VariantApi<Vs, V>; | ||
}; | ||
declare type VariantApi<Vs extends StoredVariantsBase, Name, V extends StoredVariantData> = { | ||
name: Name; | ||
declare type VariantApi<Vs extends StoredVariants, V extends StoredVariant> = { | ||
name: V[`name`]; | ||
symbol: symbol; | ||
schema: z.ZodObject<V[`schema`]>; | ||
schema: StoredVariant.GetZodSchema<V>; | ||
/** | ||
@@ -36,3 +48,3 @@ * Strict predicate/type guard for this variant. | ||
*/ | ||
is(value: z.TypeOf<GetADTSchema<Vs>>): value is z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
is(value: StoredVariants.Union<Vs>): value is StoredVariant.GetType<V>; | ||
/** | ||
@@ -48,20 +60,70 @@ * Loose predicate/type guard for this variant. | ||
* the error of that not being the case while this function would not. | ||
*/ | ||
is$(value: unknown): value is StoredVariant.GetType<V>; | ||
} & (keyof GetConstructorInput<V> extends never ? { | ||
/** | ||
* TODO | ||
*/ | ||
create(): StoredVariant.GetType<V>; | ||
} : keyof OmitRequired<GetConstructorInput<V>> extends never ? { | ||
/** | ||
* TODO | ||
*/ | ||
create(input?: GetConstructorInput<V>): StoredVariant.GetType<V>; | ||
} : { | ||
/** | ||
* TODO | ||
*/ | ||
create(input: GetConstructorInput<V>): StoredVariant.GetType<V>; | ||
}) & (V[`codec`] extends true ? { | ||
/** | ||
* Serialize this variant into a string representation. | ||
*/ | ||
encode: Encoder<V>; | ||
/** | ||
* Deserialize a string representation of this variant. | ||
*/ | ||
decode: Decoder<V>; | ||
} : { | ||
/** | ||
* This method is not available. You have not defined a codec on this variant. | ||
* | ||
* Define a codec on your variant like this: | ||
* | ||
* ```ts | ||
* Alge | ||
* .create('Foo') | ||
* .variant('Bar', { | ||
* qux: z.string(), | ||
* }) | ||
* .codec({ | ||
* encode: (data) => data.qux, | ||
* decode: (data) => ({ qux: data }), | ||
* }) | ||
* ``` | ||
*/ | ||
is$(value: unknown): value is z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
} & (keyof GetInput<V[`schema`]> extends never ? { | ||
create(): GetOutput<V[`schema`]>; | ||
} : keyof OmitRequired<GetInput<V[`schema`]>> extends never ? { | ||
create(input?: GetInput<V[`schema`]>): z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
} : { | ||
create(input: GetInput<V[`schema`]>): z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
encode: never; | ||
/** | ||
* This method is not available. You have not defined a codec on this variant. | ||
* | ||
* Define a codec on your variant like this: | ||
* | ||
* ```ts | ||
* Alge | ||
* .create('Foo') | ||
* .variant('Bar', { | ||
* qux: z.string(), | ||
* }) | ||
* .codec({ | ||
* encode: (data) => data.qux, | ||
* decode: (data) => ({ qux: data }), | ||
* }) | ||
* ``` | ||
*/ | ||
decode: never; | ||
}); | ||
declare type OmitRequired<T> = { | ||
[K in keyof T as undefined extends T[K] ? never : K]: T[K]; | ||
}; | ||
declare type GetInput<Schema extends ZodRawShape> = z.TypeOf<z.Omit<z.ZodObject<Schema>, { | ||
export declare type GetConstructorInput<V extends StoredVariant> = z.TypeOf<z.Omit<StoredVariant.GetZodSchema<V>, { | ||
_tag: true; | ||
}>>; | ||
declare type GetOutput<Schema extends ZodRawShape> = z.TypeOf<z.ZodObject<Schema>>; | ||
export {}; | ||
//# sourceMappingURL=Controller.d.ts.map |
@@ -0,1 +1,4 @@ | ||
export declare type OmitRequired<T> = { | ||
[K in keyof T as undefined extends T[K] ? never : K]: T[K]; | ||
}; | ||
export declare type IsUnknown<T> = IsEqual<T, unknown>; | ||
@@ -6,2 +9,5 @@ export declare type IsEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false; | ||
}; | ||
export declare type CreateObject<key extends string, Value> = { | ||
[k in key]: Value; | ||
}; | ||
export declare const isEmpty: (value: unknown[] | object) => boolean; | ||
@@ -8,0 +14,0 @@ export declare const ensurePeriod: (s: string) => string; |
@@ -35,5 +35,2 @@ import { Initial } from './Builder'; | ||
/** | ||
* Helper for creating ADT `is` functions. | ||
*/ | ||
/** | ||
* Define an algebraic data type. There must be at least two members. If all members have a parse function then an ADT level parse function will automatically be derived. | ||
@@ -40,0 +37,0 @@ */ |
@@ -18,88 +18,3 @@ import { Errors } from './Errors'; | ||
}; | ||
// export const createSchema = < | ||
// Member1 extends ADTMemberNamespaceBase, | ||
// Member2 extends ADTMemberNamespaceBase, | ||
// MembersRest extends ADTMemberNamespaceBase[], | ||
// >( | ||
// member1: Member1, | ||
// member2: Member2, | ||
// ...members: [...MembersRest] | ||
// ) => { | ||
// return z.union([member1.schema, member2.schema, ...members.map((_) => _.schema)]) | ||
// } | ||
/** | ||
* Helper for creating ADT `is` functions. | ||
*/ | ||
// export const createIs = <ADTMember extends VariantBase>( | ||
// memberTag: ADTMember[`_tag`] | ||
// ): ((x: unknown) => x is ADTMember) => { | ||
// return (x: unknown): x is ADTMember => { | ||
// return is$(x, memberTag) | ||
// } | ||
// } | ||
// /** | ||
// * Helper for implementing parseOrThrow. | ||
// * | ||
// * @param parser - The parse function. This wraps it to automate an implementation of parseOrThrow. | ||
// * @param dataTypeMemberName - The name of the data type trying to be parsed. Used for nicer error messages. | ||
// * @returns The data type. | ||
// * @throws An error if parsing fails. | ||
// */ | ||
// export const createParseOrThrow = <ADTMember extends VariantBase>( | ||
// parser: Parse<ADTMember>, | ||
// dataTypeMemberName: ADTMember[`_tag`] | ADTMember[`_tag`][] | ||
// ): ((x: string) => ADTMember) => { | ||
// return (x) => { | ||
// const result = parser(x) | ||
// if (result === null) { | ||
// // prettier-ignore | ||
// const message = | ||
// Array.isArray(dataTypeMemberName) | ||
// ? endent` | ||
// Could not parse the given string into any of the data types: ${dataTypeMemberName.map(_=>`"${_}"`).join(`, `)}. | ||
// ` | ||
// : endent` | ||
// Could not parse the given string into the data type "${dataTypeMemberName}". | ||
// ` | ||
// throw new Error(endent` | ||
// ${message} | ||
// The given string was: | ||
// ${x} | ||
// `) | ||
// } | ||
// return result | ||
// } | ||
// } | ||
// export const deriveEnum = <S extends z.ZodUnion<[z.ZodLiteral<string>, ...z.ZodLiteral<string>[]]>>( | ||
// schema: S | ||
// ): DeriveEnum<S[`_def`][`options`]> => | ||
// // eslint-disable-next-line | ||
// schema._def.options.reduce((_enum, literal) => { | ||
// return Object.assign(_enum, { [literal._def.value]: literal._def.value }) | ||
// // eslint-disable-next-line | ||
// }, {}) as any | ||
// type DeriveEnum<Literals extends [...z.ZodLiteral<string>[]]> = { | ||
// [k in Literals[number] as k[`_def`][`value`]]: k[`_def`][`value`] | ||
// } | ||
// export const deriveCreate = | ||
// <S extends z.ZodObject<{ _tag: z.ZodLiteral<string> }>>(schema: S) => | ||
// (input: z.TypeOf<z.Omit<S, { _tag: true }>>): z.TypeOf<S> => { | ||
// return { | ||
// ...input, | ||
// _tag: schema._def.shape()._tag._def.value, | ||
// } | ||
// } | ||
// export const deriveIs = | ||
// <S extends ADTMember>(schema: S) => | ||
// (value: unknown): value is z.TypeOf<S> => { | ||
// return ( | ||
// // TODO | ||
// // eslint-disable-next-line | ||
// typeof value === `object` && value !== null && (value as any)._tag === schema._def.shape()._tag.value | ||
// ) | ||
// } | ||
// type ADTMember = z.ZodObject<{ | ||
// _tag: z.ZodLiteral<string> | ||
// }> | ||
/** | ||
* Define an algebraic data type. There must be at least two members. If all members have a parse function then an ADT level parse function will automatically be derived. | ||
@@ -109,8 +24,21 @@ */ | ||
export const create = (name) => { | ||
let currentVariant = null; | ||
const variants = []; | ||
const api = { | ||
variant: (name, schema) => { | ||
variants.push({ name, schema: z.object(schema) }); | ||
currentVariant = { | ||
name, | ||
schema: z.object(schema), | ||
}; | ||
variants.push(currentVariant); | ||
return api; | ||
}, | ||
codec: (codecDef) => { | ||
if (!currentVariant) | ||
throw new Error(`Define variant first.`); | ||
if (currentVariant.codec) | ||
throw new Error(`Codec already defined.`); | ||
currentVariant.codec = codecDef; | ||
return api; | ||
}, | ||
done: () => { | ||
@@ -134,2 +62,12 @@ if (isEmpty(variants)) | ||
is: (x) => is(x, symbol), | ||
decode: (data) => { | ||
if (!v.codec) | ||
throw new Error(`Codec not implemented.`); | ||
return api.create(v.codec.decode(data)); | ||
}, | ||
encode: (data) => { | ||
if (!v.codec) | ||
throw new Error(`Codec not implemented.`); | ||
return v.codec.encode(data); | ||
}, | ||
}; | ||
@@ -136,0 +74,0 @@ return api; |
/** | ||
* This module is concerned with the static types for the API of building up an ADT. | ||
*/ | ||
import { GetADTMethods, GetVariantsNamespacedMethods } from './Controller'; | ||
import { Controller, GetConstructorInput } from './Controller'; | ||
import { z } from './lib/z'; | ||
@@ -12,3 +12,3 @@ export declare type SchemaBase = Record<string, z.ZodType<unknown>>; | ||
*/ | ||
export declare type Initial<ADT, StoredVariants extends StoredVariantsBase> = VariantRequired<ADT, StoredVariants>; | ||
export declare type Initial<ADT extends StoredADT, Vs extends StoredVariants> = VariantRequired<ADT, Vs>; | ||
/** | ||
@@ -19,3 +19,3 @@ * The builder API when it is in a state where a variant is required. | ||
*/ | ||
export interface VariantRequired<ADT, StoredVariants extends StoredVariantsBase> { | ||
export interface VariantRequired<ADT extends StoredADT, Vs extends StoredVariants> { | ||
/** | ||
@@ -28,7 +28,7 @@ * Create a variant of this ADT. | ||
extensions?: Extensions; | ||
}): PostVariantBuilder<ADT, [CreateStoredVariant<Name, Schema, Parse, Extensions>, ...StoredVariants]>; | ||
}): PostVariantBuilder<ADT, CreateStoredVariant<Name, Schema, false, Parse, Extensions>, Vs>; | ||
/** | ||
* Sugar for quickly defining ADT members that only have a schema. | ||
*/ | ||
variant<Name extends string, Schema extends SchemaBase>(name: Name, schema?: Schema): PostVariantBuilder<ADT, [CreateStoredVariant<Name, Schema, never, never>, ...StoredVariants]>; | ||
variant<Name extends string, Schema extends SchemaBase>(name: Name, schema?: Schema): PostVariantBuilder<ADT, CreateStoredVariant<Name, Schema, false, never, never>, Vs>; | ||
} | ||
@@ -39,29 +39,59 @@ /** | ||
*/ | ||
export interface PostVariantBuilder<ADT, Vs extends StoredVariantsBase> extends VariantRequired<ADT, Vs> { | ||
done(): ADT & GetADTMethods<Vs> & GetVariantsNamespacedMethods<Vs>; | ||
export interface PostVariantBuilder<ADT extends StoredADT, V extends StoredVariant, Vs extends StoredVariants> extends VariantRequired<ADT, [V, ...Vs]> { | ||
codec(params: CodecParams<V>): PostCodecBuilder<ADT, StoredVariant.EnableCodec<V>, Vs>; | ||
done(): Controller<ADT, [V, ...Vs]>; | ||
} | ||
declare type CreateStoredVariant<Name extends NameBase, Schema extends SchemaBase, Parse extends (expression: string, extensions: Extensions) => null | z.TypeOf<z.ZodObject<Schema>>, Extensions extends ExtensionsBase> = [ | ||
Name, | ||
{ | ||
schema: SchemaBase extends Schema ? { | ||
_tag: z.ZodLiteral<Name>; | ||
} : Schema & { | ||
_tag: z.ZodLiteral<Name>; | ||
}; | ||
parse: Parse; | ||
extensions: ExtensionsBase extends Extensions ? {} : Extensions; | ||
} | ||
]; | ||
export declare type GetName<V extends StoredVariantsBase> = V[0]; | ||
export declare type Parse2<T = unknown> = (string: string) => null | T; | ||
export declare type Parse2OrThrow<T = unknown> = (string: string) => T; | ||
export declare type StoredVariantsBase = [StoredVariantBase, ...StoredVariantBase[]]; | ||
declare type StoredVariantBase = [name: string, data: StoredVariantData]; | ||
export declare type StoredVariantRecordBase = Record<string, StoredVariantData>; | ||
export declare type StoredVariantData = { | ||
export interface CodecParams<V extends StoredVariant = StoredVariant> { | ||
encode: Encoder<V>; | ||
decode: Decoder<V>; | ||
} | ||
export declare type Encoder<V extends StoredVariant> = (decodedData: StoredVariant.GetType<V>) => string; | ||
export declare type Decoder<V extends StoredVariant> = (encodedData: string) => GetConstructorInput<V>; | ||
/** | ||
* The builder API when it is a state of having at least one variant defined. | ||
* At this point the ADT can be marked as done. | ||
*/ | ||
export interface PostCodecBuilder<ADT extends StoredADT, V extends StoredVariant, Vs extends StoredVariants> extends VariantRequired<ADT, [V, ...Vs]> { | ||
done(): Controller<ADT, [V, ...Vs]>; | ||
} | ||
declare type CreateStoredVariant<Name extends NameBase, Schema extends SchemaBase, Codec extends boolean, Parse extends (expression: string, extensions: Extensions) => null | z.TypeOf<z.ZodObject<Schema>>, Extensions extends ExtensionsBase> = { | ||
name: Name; | ||
schema: SchemaBase extends Schema ? { | ||
_tag: z.ZodLiteral<Name>; | ||
} : Schema & { | ||
_tag: z.ZodLiteral<Name>; | ||
}; | ||
codec: Codec; | ||
parse: Parse; | ||
extensions: ExtensionsBase extends Extensions ? {} : Extensions; | ||
}; | ||
export declare type StoredADT<Name extends string = string> = { | ||
name: Name; | ||
}; | ||
export declare type StoredVariant = { | ||
name: string; | ||
schema: z.ZodRawShape; | ||
codec: boolean; | ||
parse?: Parse2; | ||
extensions?: ExtensionsBase; | ||
}; | ||
export declare namespace StoredVariant { | ||
type EnableCodec<V extends StoredVariant> = Omit<V, `codec`> & { | ||
codec: true; | ||
}; | ||
type GetType<V extends StoredVariant> = z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
type GetZodSchema<V extends StoredVariant> = z.ZodObject<V[`schema`]>; | ||
} | ||
export declare type StoredVariants = [StoredVariant, ...StoredVariant[]]; | ||
export declare namespace StoredVariants { | ||
export type ZodUnion<Vs extends StoredVariants> = z.ZodUnion<ToZodObjects<Vs>>; | ||
export type Union<Vs extends StoredVariants> = z.TypeOf<ZodUnion<Vs>>; | ||
type ToZodObjects<Vs extends StoredVariants> = { | ||
[Index in keyof Vs]: z.ZodObject<Vs[Index][`schema`]>; | ||
}; | ||
export {}; | ||
} | ||
export declare type Parse2<T = unknown> = (string: string) => null | T; | ||
export declare type Parse2OrThrow<T = unknown> = (string: string) => T; | ||
export {}; | ||
//# sourceMappingURL=Builder.d.ts.map |
@@ -1,25 +0,37 @@ | ||
import { Parse2, Parse2OrThrow, StoredVariantData, StoredVariantRecordBase, StoredVariantsBase } from './Builder'; | ||
import { IsUnknown, TupleToObject } from './lib/utils'; | ||
import { Decoder, Encoder, Parse2, Parse2OrThrow, StoredADT, StoredVariant, StoredVariants } from './Builder'; | ||
import { IsUnknown, OmitRequired } from './lib/utils'; | ||
import { z } from './lib/z'; | ||
import { ZodRawShape } from 'zod'; | ||
export declare type GetADTMethods<Vs extends StoredVariantsBase> = { | ||
schema: GetADTSchema<Vs>; | ||
export declare type Controller<ADT extends StoredADT, Vs extends StoredVariants> = ADT & ADTMethods<Vs> & VariantsNamespacedMethods<Vs>; | ||
/** | ||
* Build up the API on the ADT itself: | ||
* | ||
* ```ts | ||
* const A = Alge.create('A')... | ||
* // A.<...> <-- Methods here | ||
* ``` | ||
*/ | ||
declare type ADTMethods<Vs extends StoredVariants> = { | ||
schema: StoredVariants.ZodUnion<Vs>; | ||
} & (IsAllMembersHaveParse<Vs> extends true ? { | ||
parse: Parse2<z.TypeOf<GetADTSchema<Vs>>>; | ||
parseOrThrow: Parse2OrThrow<z.TypeOf<GetADTSchema<Vs>>>; | ||
parse: Parse2<StoredVariants.Union<Vs>>; | ||
parseOrThrow: Parse2OrThrow<StoredVariants.Union<Vs>>; | ||
} : {}); | ||
declare type IsAllMembersHaveParse<Vs extends StoredVariantsBase> = { | ||
declare type IsAllMembersHaveParse<Vs extends StoredVariants> = { | ||
[K in keyof Vs]: IsUnknown<Vs[K][1][`parse`]> extends true ? `missing` : never; | ||
} extends [never, ...never[]] ? true : false; | ||
declare type GetADTSchema<Members extends StoredVariantsBase> = z.ZodUnion<{ | ||
[K in keyof Members]: z.ZodObject<Members[K][1][`schema`]>; | ||
}>; | ||
export declare type GetVariantsNamespacedMethods<Vs extends StoredVariantsBase> = GetVariantsNamespacedMethods_<Vs, TupleToObject<Vs[number]>>; | ||
export declare type GetVariantsNamespacedMethods_<Vs extends StoredVariantsBase, VsRec extends StoredVariantRecordBase> = { | ||
[Name in keyof VsRec]: VariantApi<Vs, Name, VsRec[Name]>; | ||
/** | ||
* build up the API for each variant defined in the ADT: | ||
* | ||
* ```ts | ||
* const A = Alge.create('A').variant('B',...)... | ||
* // A.B.<...> <-- Methods here | ||
* ``` | ||
*/ | ||
export declare type VariantsNamespacedMethods<Vs extends StoredVariants> = { | ||
[V in Vs[number] as V[`name`]]: VariantApi<Vs, V>; | ||
}; | ||
declare type VariantApi<Vs extends StoredVariantsBase, Name, V extends StoredVariantData> = { | ||
name: Name; | ||
declare type VariantApi<Vs extends StoredVariants, V extends StoredVariant> = { | ||
name: V[`name`]; | ||
symbol: symbol; | ||
schema: z.ZodObject<V[`schema`]>; | ||
schema: StoredVariant.GetZodSchema<V>; | ||
/** | ||
@@ -36,3 +48,3 @@ * Strict predicate/type guard for this variant. | ||
*/ | ||
is(value: z.TypeOf<GetADTSchema<Vs>>): value is z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
is(value: StoredVariants.Union<Vs>): value is StoredVariant.GetType<V>; | ||
/** | ||
@@ -48,20 +60,70 @@ * Loose predicate/type guard for this variant. | ||
* the error of that not being the case while this function would not. | ||
*/ | ||
is$(value: unknown): value is StoredVariant.GetType<V>; | ||
} & (keyof GetConstructorInput<V> extends never ? { | ||
/** | ||
* TODO | ||
*/ | ||
create(): StoredVariant.GetType<V>; | ||
} : keyof OmitRequired<GetConstructorInput<V>> extends never ? { | ||
/** | ||
* TODO | ||
*/ | ||
create(input?: GetConstructorInput<V>): StoredVariant.GetType<V>; | ||
} : { | ||
/** | ||
* TODO | ||
*/ | ||
create(input: GetConstructorInput<V>): StoredVariant.GetType<V>; | ||
}) & (V[`codec`] extends true ? { | ||
/** | ||
* Serialize this variant into a string representation. | ||
*/ | ||
encode: Encoder<V>; | ||
/** | ||
* Deserialize a string representation of this variant. | ||
*/ | ||
decode: Decoder<V>; | ||
} : { | ||
/** | ||
* This method is not available. You have not defined a codec on this variant. | ||
* | ||
* Define a codec on your variant like this: | ||
* | ||
* ```ts | ||
* Alge | ||
* .create('Foo') | ||
* .variant('Bar', { | ||
* qux: z.string(), | ||
* }) | ||
* .codec({ | ||
* encode: (data) => data.qux, | ||
* decode: (data) => ({ qux: data }), | ||
* }) | ||
* ``` | ||
*/ | ||
is$(value: unknown): value is z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
} & (keyof GetInput<V[`schema`]> extends never ? { | ||
create(): GetOutput<V[`schema`]>; | ||
} : keyof OmitRequired<GetInput<V[`schema`]>> extends never ? { | ||
create(input?: GetInput<V[`schema`]>): z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
} : { | ||
create(input: GetInput<V[`schema`]>): z.TypeOf<z.ZodObject<V[`schema`]>>; | ||
encode: never; | ||
/** | ||
* This method is not available. You have not defined a codec on this variant. | ||
* | ||
* Define a codec on your variant like this: | ||
* | ||
* ```ts | ||
* Alge | ||
* .create('Foo') | ||
* .variant('Bar', { | ||
* qux: z.string(), | ||
* }) | ||
* .codec({ | ||
* encode: (data) => data.qux, | ||
* decode: (data) => ({ qux: data }), | ||
* }) | ||
* ``` | ||
*/ | ||
decode: never; | ||
}); | ||
declare type OmitRequired<T> = { | ||
[K in keyof T as undefined extends T[K] ? never : K]: T[K]; | ||
}; | ||
declare type GetInput<Schema extends ZodRawShape> = z.TypeOf<z.Omit<z.ZodObject<Schema>, { | ||
export declare type GetConstructorInput<V extends StoredVariant> = z.TypeOf<z.Omit<StoredVariant.GetZodSchema<V>, { | ||
_tag: true; | ||
}>>; | ||
declare type GetOutput<Schema extends ZodRawShape> = z.TypeOf<z.ZodObject<Schema>>; | ||
export {}; | ||
//# sourceMappingURL=Controller.d.ts.map |
@@ -0,1 +1,4 @@ | ||
export declare type OmitRequired<T> = { | ||
[K in keyof T as undefined extends T[K] ? never : K]: T[K]; | ||
}; | ||
export declare type IsUnknown<T> = IsEqual<T, unknown>; | ||
@@ -6,2 +9,5 @@ export declare type IsEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false; | ||
}; | ||
export declare type CreateObject<key extends string, Value> = { | ||
[k in key]: Value; | ||
}; | ||
export declare const isEmpty: (value: unknown[] | object) => boolean; | ||
@@ -8,0 +14,0 @@ export declare const ensurePeriod: (s: string) => string; |
{ | ||
"name": "alge", | ||
"version": "0.1.0-next.12", | ||
"version": "0.1.0-next.13", | ||
"repository": "git@github.com:jasonkuhrt/alge.git", | ||
@@ -51,3 +51,3 @@ "author": "Jason Kuhrt", | ||
"@prisma-labs/prettier-config": "0.1.0", | ||
"@swc/core": "1.2.163", | ||
"@swc/core": "1.2.164", | ||
"@swc/helpers": "0.3.8", | ||
@@ -54,0 +54,0 @@ "@swc/jest": "0.2.20", |
@@ -1,2 +0,2 @@ | ||
# alge | ||
# alge 🌱 | ||
@@ -7,2 +7,12 @@ [![trunk](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml/badge.svg)](https://github.com/jasonkuhrt/alge/actions/workflows/trunk.yml) | ||
Pronounced "AL GEE" like [the plant](https://en.wikipedia.org/wiki/Algae). | ||
## Installation | ||
``` | ||
npm add alge | ||
``` | ||
## Guide | ||
This library is at alpha stage. For current examples of usage you can try the following: | ||
@@ -9,0 +19,0 @@ |
@@ -13,2 +13,4 @@ import { Alge } from '.' | ||
const A = Alge.create($A).variant($M, { m: z.string() }).variant($N, { n: z.number() }).done() | ||
describe(`.create()`, () => { | ||
@@ -34,5 +36,49 @@ describe(`errors`, () => { | ||
describe(`.codec()`, () => { | ||
it(`if not defined then variant API codec methods not available`, () => { | ||
expectType<never>(A.M.encode) | ||
expectType<never>(A.M.decode) | ||
//eslint-disable-next-line | ||
expect(() => (A.M as any).encode()).toThrowErrorMatchingInlineSnapshot(`"Codec not implemented."`) | ||
//eslint-disable-next-line | ||
expect(() => (A.M as any).decode()).toThrowErrorMatchingInlineSnapshot(`"Codec not implemented."`) | ||
}) | ||
it(`defines an encode and decode method`, () => { | ||
const A = Alge.create($A) | ||
.variant($M, { m: z.string() }) | ||
.codec({ | ||
encode: (data) => data.m, | ||
decode: (data) => ({ m: data }), | ||
}) | ||
.done() | ||
const m = A.M.create({ m: `m` }) | ||
expect(A.M.encode(m)).toEqual(`m`) | ||
expect(A.M.decode(`m`)).toEqual(m) | ||
}) | ||
it(`cannot define codec multiple times in the chain`, () => { | ||
// eslint-disable-next-line | ||
const _A = Alge.create($A) | ||
.variant($M, { m: z.string() }) | ||
.codec({ | ||
encode: (data) => data.m, | ||
decode: (data) => ({ m: data }), | ||
}) | ||
// @ts-expect-error: second codec method not present. | ||
_A.codec | ||
expect(() => | ||
//eslint-disable-next-line | ||
(_A as any).codec({ | ||
//eslint-disable-next-line | ||
encode: (data: any) => data.m, | ||
//eslint-disable-next-line | ||
decode: (data: any) => ({ m: data }), | ||
}) | ||
).toThrowErrorMatchingInlineSnapshot(`"Codec already defined."`) | ||
}) | ||
}) | ||
describe(`.variant()`, () => { | ||
it(`Can be given a name which becomes a static namespace on the ADT`, () => { | ||
const A = Alge.create($A).variant($M, { a: z.string() }).variant($N, { a: z.string() }).done() | ||
expect(A.M).toBeDefined() | ||
@@ -44,3 +90,2 @@ expect(A.N).toBeDefined() | ||
it(`.symbol contains the unique symbol for this variant`, () => { | ||
const A = Alge.create($A).variant($M, { a: z.string() }).variant($N, { a: z.string() }).done() | ||
expectType<symbol>(A.M.symbol) | ||
@@ -51,3 +96,2 @@ expect(typeof A.M.symbol).toBe(`symbol`) | ||
it(`.name contains the name of the variant`, () => { | ||
const A = Alge.create($A).variant($M, { a: z.string() }).variant($N, { a: z.string() }).done() | ||
expectType<$M>(A.M.name) | ||
@@ -60,3 +104,2 @@ expect(A.M.name).toBe($M) | ||
it(`.schema contains the zod schema for the variant`, () => { | ||
const A = Alge.create($A).variant($M, { a: z.string() }).variant($N, { a: z.string() }).done() | ||
expectType<z.ZodSchema>(A.M.schema) | ||
@@ -73,8 +116,8 @@ expect(A.M.schema).toBeDefined() | ||
const A = Alge.create($A).variant($N).variant($M).done() | ||
// @ts-expect-error: input not accepted | ||
// @ts-expect-error: empty object still not like empty variant | ||
A.N.create({}) | ||
expect(A.N.create()).toEqual({ _tag: $N, _: { symbol: A.N.symbol } }) | ||
expect(A.M.create()).toEqual({ _tag: $M, _: { symbol: A.M.symbol } }) | ||
// @ts-expect-error: empty object still not like empty variant | ||
expect(A.M.is({})).toEqual(false) | ||
// eslint-disable-next-line | ||
expect((A.M as any).is({})).toEqual(false) | ||
expect(A.M.is$({})).toEqual(false) | ||
@@ -91,4 +134,2 @@ }) | ||
it(`creates the variant`, () => { | ||
const A = Alge.create($A).variant($M, { m: z.string() }).variant($N, { n: z.number() }).done() | ||
// @ts-expect-error: Invalid input | ||
@@ -114,5 +155,4 @@ A.M.create({ x: 1 }) | ||
it(`.is() is a type guard / predicate function accepting only variants of the ADT`, () => { | ||
const A = Alge.create($A).variant($M, { a: z.string() }).variant($N, { x: z.number().int() }).done() | ||
const m = A.M.create({ a: `x` }) | ||
const n = A.N.create({ x: 1 }) | ||
const m = A.M.create({ m: `x` }) | ||
const n = A.N.create({ n: 1 }) | ||
const mn = Math.random() > 0.5 ? m : n | ||
@@ -126,5 +166,4 @@ | ||
if (A.M.is(mn)) { | ||
expectType<typeof m>(mn) | ||
} | ||
if (A.M.is(mn)) expectType<typeof m>(mn) | ||
if (!A.M.is(mn)) expectType<typeof n>(mn) | ||
@@ -138,4 +177,3 @@ expect(A.M.is(n)).toBe(false) | ||
it(`.is$() is a type guard / predicate function accepting any value`, () => { | ||
const A = Alge.create($A).variant($M, { a: z.string() }).variant($N, { a: z.string() }).done() | ||
const m = A.M.create({ a: `x` }) | ||
const m = A.M.create({ m: `x` }) | ||
const mMaybe = Math.random() > 0.5 ? m : false | ||
@@ -149,5 +187,4 @@ | ||
if (A.M.is$(mMaybe)) { | ||
expectType<typeof m>(mMaybe) | ||
} | ||
if (A.M.is$(mMaybe)) expectType<typeof m>(mMaybe) | ||
if (!A.M.is$(mMaybe)) expectType<false>(mMaybe) | ||
@@ -154,0 +191,0 @@ expect(A.M.is$({})).toBe(false) |
@@ -1,2 +0,2 @@ | ||
import { Initial } from './Builder' | ||
import { CodecParams, Initial, StoredVariant } from './Builder' | ||
import { Errors } from './Errors' | ||
@@ -154,2 +154,7 @@ import { is } from './helpers' | ||
type SomeVariantDef = Omit<StoredVariant, `codec` | `schema`> & { | ||
codec?: CodecParams | ||
schema?: z.SomeZodObject | ||
} | ||
/** | ||
@@ -160,12 +165,20 @@ * Define an algebraic data type. There must be at least two members. If all members have a parse function then an ADT level parse function will automatically be derived. | ||
export const create = <Name extends string>(name: Name): Initial<{ name: Name }, []> => { | ||
const variants: { | ||
name: string | ||
schema: z.SomeZodObject | ||
}[] = [] | ||
let currentVariant: null | SomeVariantDef = null | ||
const variants: SomeVariantDef[] = [] | ||
const api = { | ||
variant: (name: string, schema: Record<string, z.ZodType<unknown>>) => { | ||
variants.push({ name, schema: z.object(schema) }) | ||
currentVariant = { | ||
name, | ||
schema: z.object(schema), | ||
} | ||
variants.push(currentVariant) | ||
return api | ||
}, | ||
codec: (codecDef: CodecParams) => { | ||
if (!currentVariant) throw new Error(`Define variant first.`) | ||
if (currentVariant.codec) throw new Error(`Codec already defined.`) | ||
currentVariant.codec = codecDef | ||
return api | ||
}, | ||
done: () => { | ||
@@ -191,2 +204,10 @@ if (isEmpty(variants)) throw createEmptyVariantsError({ name }) | ||
is: (x: unknown) => is(x, symbol), | ||
decode: (data: string) => { | ||
if (!v.codec) throw new Error(`Codec not implemented.`) | ||
return api.create(v.codec.decode(data) as any) | ||
}, | ||
encode: (data: object) => { | ||
if (!v.codec) throw new Error(`Codec not implemented.`) | ||
return v.codec.encode(data) | ||
}, | ||
} | ||
@@ -193,0 +214,0 @@ return api |
@@ -5,3 +5,3 @@ /** | ||
import { GetADTMethods, GetVariantsNamespacedMethods } from './Controller' | ||
import { Controller, GetConstructorInput } from './Controller' | ||
import { z } from './lib/z' | ||
@@ -18,3 +18,3 @@ | ||
*/ | ||
export type Initial<ADT, StoredVariants extends StoredVariantsBase> = VariantRequired<ADT, StoredVariants> | ||
export type Initial<ADT extends StoredADT, Vs extends StoredVariants> = VariantRequired<ADT, Vs> | ||
@@ -26,3 +26,3 @@ /** | ||
*/ | ||
export interface VariantRequired<ADT, StoredVariants extends StoredVariantsBase> { | ||
export interface VariantRequired<ADT extends StoredADT, Vs extends StoredVariants> { | ||
/** | ||
@@ -40,3 +40,3 @@ * Create a variant of this ADT. | ||
// @ts-expect-error ... | ||
): PostVariantBuilder<ADT, [CreateStoredVariant<Name, Schema, Parse, Extensions>, ...StoredVariants]> | ||
): PostVariantBuilder<ADT, CreateStoredVariant<Name, Schema, false, Parse, Extensions>, Vs> | ||
@@ -49,3 +49,3 @@ /** | ||
schema?: Schema | ||
): PostVariantBuilder<ADT, [CreateStoredVariant<Name, Schema, never, never>, ...StoredVariants]> | ||
): PostVariantBuilder<ADT, CreateStoredVariant<Name, Schema, false, never, never>, Vs> | ||
} | ||
@@ -57,11 +57,26 @@ | ||
*/ | ||
export interface PostVariantBuilder<ADT, Vs extends StoredVariantsBase> extends VariantRequired<ADT, Vs> { | ||
// prettier-ignore | ||
done(): | ||
ADT | ||
& GetADTMethods<Vs> | ||
// & GetVariantsNamespacedMethods<TupleToObject<Vs[number]>> | ||
& GetVariantsNamespacedMethods<Vs> | ||
export interface PostVariantBuilder<ADT extends StoredADT, V extends StoredVariant, Vs extends StoredVariants> | ||
extends VariantRequired<ADT, [V, ...Vs]> { | ||
codec(params: CodecParams<V>): PostCodecBuilder<ADT, StoredVariant.EnableCodec<V>, Vs> | ||
done(): Controller<ADT, [V, ...Vs]> | ||
} | ||
export interface CodecParams<V extends StoredVariant = StoredVariant> { | ||
encode: Encoder<V> | ||
decode: Decoder<V> | ||
} | ||
export type Encoder<V extends StoredVariant> = (decodedData: StoredVariant.GetType<V>) => string | ||
export type Decoder<V extends StoredVariant> = (encodedData: string) => GetConstructorInput<V> | ||
/** | ||
* The builder API when it is a state of having at least one variant defined. | ||
* At this point the ADT can be marked as done. | ||
*/ | ||
export interface PostCodecBuilder<ADT extends StoredADT, V extends StoredVariant, Vs extends StoredVariants> | ||
extends VariantRequired<ADT, [V, ...Vs]> { | ||
done(): Controller<ADT, [V, ...Vs]> | ||
} | ||
// Helpers | ||
@@ -72,31 +87,50 @@ | ||
Schema extends SchemaBase, | ||
Codec extends boolean, | ||
Parse extends (expression: string, extensions: Extensions) => null | z.TypeOf<z.ZodObject<Schema>>, | ||
Extensions extends ExtensionsBase | ||
> = [ | ||
Name, | ||
{ | ||
schema: SchemaBase extends Schema ? { _tag: z.ZodLiteral<Name> } : Schema & { _tag: z.ZodLiteral<Name> } | ||
parse: Parse | ||
// TODO | ||
// eslint-disable-next-line | ||
extensions: ExtensionsBase extends Extensions ? {} : Extensions | ||
} | ||
] | ||
> = { | ||
name: Name | ||
schema: SchemaBase extends Schema ? { _tag: z.ZodLiteral<Name> } : Schema & { _tag: z.ZodLiteral<Name> } | ||
codec: Codec | ||
parse: Parse | ||
// TODO | ||
// eslint-disable-next-line | ||
extensions: ExtensionsBase extends Extensions ? {} : Extensions | ||
} | ||
export type GetName<V extends StoredVariantsBase> = V[0] | ||
export type StoredADT<Name extends string = string> = { | ||
name: Name | ||
} | ||
export type Parse2<T = unknown> = (string: string) => null | T | ||
export type StoredVariant = { | ||
name: string | ||
schema: z.ZodRawShape | ||
codec: boolean | ||
parse?: Parse2 | ||
extensions?: ExtensionsBase | ||
} | ||
export type Parse2OrThrow<T = unknown> = (string: string) => T | ||
// eslint-disable-next-line | ||
export namespace StoredVariant { | ||
export type EnableCodec<V extends StoredVariant> = Omit<V, `codec`> & { | ||
codec: true | ||
} | ||
export type GetType<V extends StoredVariant> = z.TypeOf<z.ZodObject<V[`schema`]>> | ||
export type GetZodSchema<V extends StoredVariant> = z.ZodObject<V[`schema`]> | ||
} | ||
export type StoredVariantsBase = [StoredVariantBase, ...StoredVariantBase[]] | ||
export type StoredVariants = [StoredVariant, ...StoredVariant[]] | ||
type StoredVariantBase = [name: string, data: StoredVariantData] | ||
// eslint-disable-next-line | ||
export namespace StoredVariants { | ||
export type ZodUnion<Vs extends StoredVariants> = z.ZodUnion<ToZodObjects<Vs>> | ||
export type Union<Vs extends StoredVariants> = z.TypeOf<ZodUnion<Vs>> | ||
type ToZodObjects<Vs extends StoredVariants> = { | ||
// @ts-expect-error todo | ||
[Index in keyof Vs]: z.ZodObject<Vs[Index][`schema`]> | ||
} | ||
} | ||
export type StoredVariantRecordBase = Record<string, StoredVariantData> | ||
export type Parse2<T = unknown> = (string: string) => null | T | ||
export type StoredVariantData = { | ||
schema: z.ZodRawShape | ||
parse?: Parse2 | ||
extensions?: ExtensionsBase | ||
} | ||
export type Parse2OrThrow<T = unknown> = (string: string) => T |
@@ -1,18 +0,23 @@ | ||
import { | ||
Parse2, | ||
Parse2OrThrow, | ||
StoredVariantData, | ||
StoredVariantRecordBase, | ||
StoredVariantsBase, | ||
} from './Builder' | ||
import { IsUnknown, TupleToObject } from './lib/utils' | ||
import { Decoder, Encoder, Parse2, Parse2OrThrow, StoredADT, StoredVariant, StoredVariants } from './Builder' | ||
import { IsUnknown, OmitRequired } from './lib/utils' | ||
import { z } from './lib/z' | ||
import { ZodRawShape, ZodSchema } from 'zod' | ||
export type GetADTMethods<Vs extends StoredVariantsBase> = { | ||
schema: GetADTSchema<Vs> | ||
export type Controller<ADT extends StoredADT, Vs extends StoredVariants> = ADT & | ||
ADTMethods<Vs> & | ||
VariantsNamespacedMethods<Vs> | ||
/** | ||
* Build up the API on the ADT itself: | ||
* | ||
* ```ts | ||
* const A = Alge.create('A')... | ||
* // A.<...> <-- Methods here | ||
* ``` | ||
*/ | ||
type ADTMethods<Vs extends StoredVariants> = { | ||
schema: StoredVariants.ZodUnion<Vs> | ||
} & (IsAllMembersHaveParse<Vs> extends true | ||
? { | ||
parse: Parse2<z.TypeOf<GetADTSchema<Vs>>> | ||
parseOrThrow: Parse2OrThrow<z.TypeOf<GetADTSchema<Vs>>> | ||
parse: Parse2<StoredVariants.Union<Vs>> | ||
parseOrThrow: Parse2OrThrow<StoredVariants.Union<Vs>> | ||
} | ||
@@ -23,3 +28,3 @@ : // TODO | ||
type IsAllMembersHaveParse<Vs extends StoredVariantsBase> = { | ||
type IsAllMembersHaveParse<Vs extends StoredVariants> = { | ||
// @ts-expect-error adf | ||
@@ -31,24 +36,19 @@ [K in keyof Vs]: IsUnknown<Vs[K][1][`parse`]> extends true ? `missing` : never | ||
type GetADTSchema<Members extends StoredVariantsBase> = z.ZodUnion<{ | ||
// @ts-expect-error adf | ||
[K in keyof Members]: z.ZodObject<Members[K][1][`schema`]> | ||
}> | ||
export type GetVariantsNamespacedMethods<Vs extends StoredVariantsBase> = GetVariantsNamespacedMethods_< | ||
Vs, | ||
TupleToObject<Vs[number]> | ||
> | ||
export type GetVariantsNamespacedMethods_< | ||
Vs extends StoredVariantsBase, | ||
VsRec extends StoredVariantRecordBase | ||
> = { | ||
[Name in keyof VsRec]: VariantApi<Vs, Name, VsRec[Name]> | ||
/** | ||
* build up the API for each variant defined in the ADT: | ||
* | ||
* ```ts | ||
* const A = Alge.create('A').variant('B',...)... | ||
* // A.B.<...> <-- Methods here | ||
* ``` | ||
*/ | ||
export type VariantsNamespacedMethods<Vs extends StoredVariants> = { | ||
[V in Vs[number] as V[`name`]]: VariantApi<Vs, V> | ||
} | ||
// prettier-ignore | ||
type VariantApi<Vs extends StoredVariantsBase, Name, V extends StoredVariantData> = { | ||
name: Name | ||
type VariantApi<Vs extends StoredVariants, V extends StoredVariant> = { | ||
name: V[`name`] | ||
symbol: symbol | ||
schema: z.ZodObject<V[`schema`]> | ||
schema: StoredVariant.GetZodSchema<V> | ||
/** | ||
@@ -66,3 +66,3 @@ * Strict predicate/type guard for this variant. | ||
// @ts-expect-error TODO | ||
is(value: z.TypeOf<GetADTSchema<Vs>>): value is z.TypeOf<z.ZodObject<V[`schema`]>> | ||
is(value: StoredVariants.Union<Vs>): value is StoredVariant.GetType<V> | ||
/** | ||
@@ -78,19 +78,74 @@ * Loose predicate/type guard for this variant. | ||
* the error of that not being the case while this function would not. | ||
* | ||
*/ | ||
is$(value: unknown): value is z.TypeOf<z.ZodObject<V[`schema`]>> | ||
} & | ||
( | ||
keyof GetInput<V[`schema`]> extends never | ||
is$(value: unknown): value is StoredVariant.GetType<V> | ||
} & (keyof GetConstructorInput<V> extends never | ||
? { | ||
create(): GetOutput<V[`schema`]> | ||
/** | ||
* TODO | ||
*/ | ||
create(): StoredVariant.GetType<V> | ||
} | ||
: keyof OmitRequired<GetInput<V[`schema`]>> extends never | ||
: keyof OmitRequired<GetConstructorInput<V>> extends never | ||
? { | ||
create(input?: GetInput<V[`schema`]>): z.TypeOf<z.ZodObject<V[`schema`]>> | ||
/** | ||
* TODO | ||
*/ | ||
create(input?: GetConstructorInput<V>): StoredVariant.GetType<V> | ||
} | ||
: { | ||
create(input: GetInput<V[`schema`]>): z.TypeOf<z.ZodObject<V[`schema`]>> | ||
} | ||
) | ||
/** | ||
* TODO | ||
*/ | ||
create(input: GetConstructorInput<V>): StoredVariant.GetType<V> | ||
}) & | ||
(V[`codec`] extends true | ||
? { | ||
/** | ||
* Serialize this variant into a string representation. | ||
*/ | ||
encode: Encoder<V> | ||
/** | ||
* Deserialize a string representation of this variant. | ||
*/ | ||
decode: Decoder<V> | ||
} | ||
: { | ||
/** | ||
* This method is not available. You have not defined a codec on this variant. | ||
* | ||
* Define a codec on your variant like this: | ||
* | ||
* ```ts | ||
* Alge | ||
* .create('Foo') | ||
* .variant('Bar', { | ||
* qux: z.string(), | ||
* }) | ||
* .codec({ | ||
* encode: (data) => data.qux, | ||
* decode: (data) => ({ qux: data }), | ||
* }) | ||
* ``` | ||
*/ | ||
encode: never | ||
/** | ||
* This method is not available. You have not defined a codec on this variant. | ||
* | ||
* Define a codec on your variant like this: | ||
* | ||
* ```ts | ||
* Alge | ||
* .create('Foo') | ||
* .variant('Bar', { | ||
* qux: z.string(), | ||
* }) | ||
* .codec({ | ||
* encode: (data) => data.qux, | ||
* decode: (data) => ({ qux: data }), | ||
* }) | ||
* ``` | ||
*/ | ||
decode: never | ||
}) | ||
// & ( | ||
@@ -109,15 +164,4 @@ // IsUnknown<Def[`parse`]> extends true | ||
type OmitRequired<T> = { | ||
[K in keyof T as undefined extends T[K] ? never : K]: T[K] | ||
} | ||
type y = OmitRequired<{ a: 1; b?: 2 }> | ||
type GetInput<Schema extends ZodRawShape> = z.TypeOf<z.Omit<z.ZodObject<Schema>, { _tag: true }>> | ||
type GetOutput<Schema extends ZodRawShape> = z.TypeOf<z.ZodObject<Schema>> | ||
type SomeRecord = Record<string, unknown> | ||
type HasInput = keyof {} | ||
type x = never extends never ? `a` : `b` | ||
export type GetConstructorInput<V extends StoredVariant> = z.TypeOf< | ||
z.Omit<StoredVariant.GetZodSchema<V>, { _tag: true }> | ||
> |
@@ -0,1 +1,5 @@ | ||
export type OmitRequired<T> = { | ||
[K in keyof T as undefined extends T[K] ? never : K]: T[K] | ||
} | ||
export type IsUnknown<T> = IsEqual<T, unknown> | ||
@@ -11,2 +15,6 @@ | ||
export type CreateObject<key extends string, Value> = { | ||
[k in key]: Value | ||
} | ||
export const isEmpty = (value: unknown[] | object) => { | ||
@@ -13,0 +21,0 @@ if (Array.isArray(value)) value.length === 0 |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
97206
1646
26