You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
63 lines
3.2 KiB
63 lines
3.2 KiB
|
|
import AJV, {ValidateFunction, JTDSchemaType} from "ajv/dist/jtd.js";
|
|
|
|
const ajv = new AJV();
|
|
|
|
interface BaseSchemaData<RepresentedT, ReferenceT extends string> {
|
|
value?: RepresentedT,
|
|
schema: unknown,
|
|
key: ReferenceT,
|
|
definition: { [key in ReferenceT]: unknown }
|
|
reference: { "ref": ReferenceT }
|
|
validate: ValidateFunction<RepresentedT>
|
|
requiredReferences: AnyReferenceList
|
|
}
|
|
|
|
export interface SchemaData<RepresentedT, ReferenceT extends string, ReferencesT extends AnyReferenceList, DefinitionsT extends ReferencedTypes<ReferencesT>> extends BaseSchemaData<RepresentedT, ReferenceT> {
|
|
value?: RepresentedT,
|
|
schema: JTDSchemaType<RepresentedT, DefinitionsT>,
|
|
key: ReferenceT,
|
|
definition: { [key in ReferenceT]: JTDSchemaType<RepresentedT, DefinitionsT> }
|
|
reference: { "ref": ReferenceT },
|
|
validate: ValidateFunction<RepresentedT>
|
|
requiredReferences: ReferencesT
|
|
}
|
|
|
|
export type AnySchemaDataFor<RepresentedT> = BaseSchemaData<RepresentedT, string>
|
|
export type AnySchemaData = AnySchemaDataFor<unknown>
|
|
export type AnyReferenceList = AnySchemaData[]
|
|
export type AnyDefinitions = Record<string, unknown>
|
|
|
|
export type Schema<DataT extends AnySchemaData> = Exclude<DataT["schema"], undefined>
|
|
export type Value<DataT extends AnySchemaData> = Exclude<DataT["value"], undefined>
|
|
export type Definition<DataT extends AnySchemaData> = Exclude<DataT["definition"], undefined>
|
|
export type Reference<DataT extends AnySchemaData> = Exclude<DataT["reference"], undefined>
|
|
export type ReferenceKey<DataT extends AnySchemaData> = Exclude<DataT["key"], undefined>
|
|
|
|
export type ReferencedSchemaMap<ReferencesT extends AnyReferenceList> = {
|
|
[Property in keyof ReferencesT as ReferencesT[Property] extends AnySchemaData ? ReferenceKey<ReferencesT[Property]> : never]: ReferencesT[Property] extends AnySchemaData ? ReferencesT[Property] : never
|
|
}
|
|
export type ReferencedSchema<ReferencesT extends AnyReferenceList> = ReferencedSchemaMap<ReferencesT>[keyof ReferencedSchemaMap<ReferencesT>]
|
|
export type ReferencedTypes<ReferencesT extends AnyReferenceList> = {
|
|
[Property in keyof ReferencedSchemaMap<ReferencesT>]?: Value<ReferencedSchemaMap<ReferencesT>[Property]>
|
|
}
|
|
|
|
// TODO: Add a test for a circular definition (i.e., an object that contains a reference to itself, for recursive objects like trees) in both one- and two-object loops
|
|
export function schema<
|
|
RepresentedT,
|
|
KeyT extends string,
|
|
ReferencesT extends AnySchemaData[],
|
|
>({schema, key, references}: { schema: JTDSchemaType<RepresentedT, ReferencedTypes<ReferencesT>>, key: KeyT, references: ReferencesT, typeHint?: RepresentedT|null }): SchemaData<RepresentedT, KeyT, ReferencesT, ReferencedTypes<ReferencesT>> {
|
|
const definition = {[key]: schema} as Definition<SchemaData<RepresentedT, KeyT, ReferencesT, ReferencedTypes<ReferencesT>>>
|
|
const reference = {ref: key}
|
|
const definitions = references.reduce<AnyDefinitions>((definitions, reference) => ({...reference.definition, ...definitions}), definition)
|
|
const validate = ajv.compile<RepresentedT>({ ...reference, definitions })
|
|
return {
|
|
schema,
|
|
key,
|
|
definition,
|
|
reference,
|
|
validate,
|
|
requiredReferences: references
|
|
}
|
|
} |