import {z, ZodObject} from "zod"; import {ZodRawShape} from "zod/lib/types"; import {Identifiable} from "../identifiable"; export const UnsafeEntitySymbol: symbol = Symbol("MyType"); export const EntitySymbol: symbol = Symbol("SafeEntity"); export type EntityCore = Identifiable & { createdAt: Date, updatedAt: Date, } export const EntityCoreShape = { id: z.string(), createdAt: z.coerce.date(), updatedAt: z.coerce.date(), }; export type EntityCoreShapeType = typeof EntityCoreShape type EntityFn
= { copy: (fields: Partial
) => Zentity
, toPlain: () => P & EntityCore } type NonBrandedEntity
= EntityCore & P & EntityFn
& E export type UnsafeZentity
= NonBrandedEntity
& { _brand: typeof UnsafeEntitySymbol, validate: () => Zentity
} export type Zentity
= UnsafeZentity
& {
_safe: typeof EntitySymbol
}
export function isZentity(data: any): data is Zentity => {
const data = schema.parse(params) as any
return {
_brand: UnsafeEntitySymbol,
_safe: EntitySymbol,
...data,
copy: copyFn(data),
...extend(data),
validate: toSafeFn(data),
toPlain: () => data
}
}
const toSafeFn = (params: P & EntityCore) => () => {
return create(params)
}
const copyFn = (params: P & EntityCore) => (fields: Partial ) => {
return create({
...params,
...fields,
})
}
return {
name: options.name,
create,
new: (params: P & { id: string }): Zentity => {
const createdAt = new Date()
const updatedAt = new Date()
return create({
...params,
createdAt,
updatedAt,
})
},
unsafe: (params: P & EntityCore): UnsafeZentity => {
return {
_brand: UnsafeEntitySymbol,
...params,
copy: copyFn(params),
...extend(params),
validate: toSafeFn(params),
toPlain: () => {
return params
}
}
},
schema,
shape,
extend,
}
} = {
name: string,
create: (params: ZodShapeType & EntityCore) => Zentity & { id: string }) => Zentity & EntityCore) => UnsafeZentity,
shape: S & EntityCoreShapeType,
extend: (e: EntityCore & ZodShapeType) => R,
}
export type inferPlainTypeFromClass & EntityCore : never
export type inferZentityTypeFromClass & R : never
type inferUnsafeEntity = z.infer = (e: EntityCore & ZodShapeType) => R
export function entity(options: {
name: string,
shape: S,
extend?: Extender,
}): EntityClass {
const shape = {
...EntityCoreShape,
...options.shape,
}
const schema = z.object(shape)
const extend = options.extend ?? ((_: EntityCore & ZodShapeType) => ({} as R))
type P = ZodShapeType
const create = (params: P & EntityCore): Zentity