import type { Effect } from "effect/Effect"; import * as S from "effect/Schema"; import * as AST from "effect/SchemaAST"; import type { Sink } from "effect/Sink"; import type { Stream } from "effect/Stream"; export * from "effect/Schema"; export const isNullishSchema = (schema: S.Schema) => isNullSchema(schema) || isUndefinedSchema(schema); export const isNullSchema = (schema: S.Schema) => AST.isNull(schema.ast); export const isUndefinedSchema = (schema: S.Schema) => AST.isUndefined(schema.ast); export const isBooleanSchema = (schema: S.Schema) => AST.isBoolean(schema.ast); export const isStringSchema = (schema: S.Schema) => AST.isString(schema.ast); export const isNumberSchema = (schema: S.Schema) => AST.isNumber(schema.ast); export const isRecordLikeSchema = (schema: S.Schema) => isMapSchema(schema) || isRecordSchema(schema) || isStructSchema(schema) || isClassSchema(schema) || false; export const isMapSchema = (schema: S.Schema): boolean => { const ast = schema.ast; if (AST.isDeclaration(ast)) { const typeConstructor = (ast.annotations as any)?.typeConstructor; return typeConstructor?._tag === "ReadonlyMap"; } return false; }; export const isClassSchema = (schema: S.Schema) => { const ast = schema.ast; if (AST.isDeclaration(ast)) { return AST.isObjects(ast.typeParameters[0]); } return false; }; export const isStructSchema = (schema: S.Schema) => { return AST.isObjects(schema.ast); }; export const isRecordSchema = (schema: S.Schema) => { const ast = schema.ast; return AST.isObjects(ast) && ast.indexSignatures?.length > 0; }; export const isListSchema = (schema: S.Schema) => { return AST.isArrays(schema.ast); }; export const isSetSchema = (schema: S.Schema): boolean => { const ast = schema.ast; if (AST.isDeclaration(ast)) { const typeConstructor = (ast.annotations as any)?.typeConstructor; return typeConstructor?._tag === "ReadonlySet"; } return false; }; export const getSetValueAST = (schema: S.Schema): AST.AST | undefined => { const ast = schema.ast; if (AST.isDeclaration(ast) && isSetSchema(schema)) { return ast.typeParameters[0]; } return undefined; }; /** A Schema representing a Schema */ export type Field = S.Top; export const Field = S.suspend( (): [Field] extends [any] ? S.Schema : never => S.Any, ); type FunctionType = (...args: any[]) => any; export type Function = S.Schema; export const Function: () => Function = S.suspend( (): S.Schema => S.Any, ) as any; export type CreatedAt = Date; export const CreatedAt = S.Date.annotate({ description: "The timestamp of when this record was created", }); export type UpdatedAt = Date; export const UpdatedAt = S.Date.annotate({ description: "The timestamp of when this record was last updated", }); export type AnyClassSchema< Self = any, Fields extends S.Top & { fields: S.Struct.Fields } = S.Top & { fields: S.Struct.Fields; }, > = S.Class; export type AnyClass = new (...args: any[]) => any; export type AnyErrorSchema = S.Class; export type SchemaWithTemplate< Schema extends S.Schema, References extends any[] = any[], > = Schema & { template: TemplateStringsArray; references: References; }; export type SchemaExt = | FunctionSchema | EffectSchema | StreamSchema | SinkSchema; export interface SchemaExtBase extends S.Schema { ( template: TemplateStringsArray, ...references: References ): SchemaWithTemplate; } export interface FunctionSchema< Input extends S.Top | undefined = S.Top | undefined, Output extends S.Top = S.Top, > extends SchemaExtBase< ( ...args: Input extends undefined ? [] : [input: S.Schema.Type>] ) => S.Schema.Type > { input: Input; output: Output; } export interface EffectSchema< A extends S.Top = S.Top, Err extends S.Top = S.Top, Req extends S.Top = S.Top, > extends SchemaExtBase< Effect, S.Schema.Type, S.Schema.Type> > { A: A; Err: Err; Req: Req; } export interface StreamSchema< A extends S.Top = S.Top, Err extends S.Top = S.Top, Req extends S.Top = S.Top, > extends SchemaExtBase< Stream, S.Schema.Type, S.Schema.Type> > { A: A; Err: Err; Req: Req; } export interface SinkSchema< A extends S.Top = S.Top, In extends S.Top = S.Top, L extends S.Top = S.Top, Err extends S.Top = S.Top, Req extends S.Top = S.Top, > extends SchemaExtBase< Sink< S.Schema.Type, S.Schema.Type, S.Schema.Type, S.Schema.Type, S.Schema.Type > > { A: A; In: In; L: L; Err: Err; Req: Req; } export const makeExtSchema = ( schema: Schema, ): SchemaExt => { const s = S.Any.annotate({ aspect: schema, }); return new Proxy(() => {}, { get: (_target, prop) => s[prop as keyof typeof s], apply: (_target, _thisArg, [template, ...references]) => { return S.annotate({ aspect: { ...schema, template, references, }, }); }, }) as any as SchemaExt; }; // export type def = typeof def; export interface func< Input extends undefined | S.Top | S.Top[], Output extends S.Top, > extends S.Schema< Input extends undefined ? () => S.Schema.Type : Input extends S.Top[] ? (...args: TypeArray) => S.Schema.Type : (input: S.Schema.Type>) => S.Schema.Type > {} type TypeArray = T extends [ infer Head, ...infer Tail extends S.Top[], ] ? Head extends S.Top ? [S.Schema.Type, ...TypeArray] : never : []; export const func: { >( output: Output, ): func & { ( template: TemplateStringsArray, ...references: R ): SchemaWithTemplate, R>; }; , Output extends S.Schema>( input: Input, output: Output, ): func & { ( template: TemplateStringsArray, ...references: R ): SchemaWithTemplate, R>; }; [], Output extends S.Schema>( args: Args, output: Output, ): func & { ( template: TemplateStringsArray, ...references: R ): SchemaWithTemplate, R>; }; } = ((input: any, output: any) => S.Any.annotate({ aspect: { type: "fn", input: output ? input : undefined, output: output ?? input, }, })) as any; export interface effect< A extends S.Top, Err extends S.Top, Req extends S.Top, > extends S.Schema< Effect, S.Schema.Type, S.Schema.Type> > {} export const effect = < A extends S.Schema, Err extends S.Schema | S.Never = S.Never, Req extends S.Schema | S.Any = S.Any, >( a: A, err: Err = S.Never as any, req: Req = S.Never as any, ): effect => S.Any.annotate({ aspect: { type: "effect", a: a, err: err, req: req, }, }); export const stream = < A extends S.Schema, Err extends S.Schema | S.Never = S.Never, Req extends S.Schema | S.Never = S.Never, >( a: A, err: Err = S.Never as any, req: Req = S.Never as any, ) => S.Any.annotate({ aspect: { type: "stream", a: a, err: err, req: req, }, }); export const sink = < A extends S.Schema, In extends S.Schema, L extends S.Schema, Err extends S.Schema | S.Never = S.Never, Req extends S.Schema | S.Never = S.Never, >( a: A, _in: In = S.Never as any, l: L = S.Never as any, err: Err = S.Never as any, req: Req = S.Never as any, ) => S.Any.annotate({ aspect: { type: "sink", a: a, in: _in, l: l, err: err, req: req, }, });