import * as Context from "effect/Context"; import type { Yieldable } from "effect/Effect"; import * as Effect from "effect/Effect"; import * as S from "effect/Schema"; import type { Class, IsAny } from "../Util/index.ts"; import { ContextPlugin } from "./ContextPlugin.ts"; import { createPluginBuilder, type Plugins } from "./Plugin.ts"; import { type Parameters } from "./tool/parameter.ts"; import type { Result } from "./tool/result.ts"; import type { ToolHandler } from "./tool/tool.ts"; export const isAspect = (a: any): a is Aspect => { return ( typeof a === "object" && a !== null && "type" in a && "id" in a && "template" in a && "references" in a && "fields" in a && "schema" in a ); }; export class AspectConfig extends Context.Service< AspectConfig, { cwd: string; } >()("AspectConfig") {} export type AspectClass> = Fn & { kind: "aspect"; type: string; schema: GetAspectType["schema"]; plugin: Plugins>; with(context: T): AspectClass & T; }; export type AspectLike = { kind: "aspect"; id: string; type: string; references: any[]; }; export interface Aspect< Self = any, Type extends string = string, Name extends string = string, References extends any[] = any[], Props = any, Handler extends ToolHandler = ToolHandler, > { kind: "aspect"; class: Class; type: Type; id: Name; props: Props; template: TemplateStringsArray; references: References; schema: IsAny extends true ? any : [Props] extends [S.Top] ? S.Schema : [Props] extends [object] ? Class & S.Schema : S.Schema; handle: Handler; new (): Aspect; } export const defineAspect: >( type: GetAspectType["type"], ...props: GetAspectType["schema"] extends infer Schema ? Schema extends Class ? IsAny extends true ? [] : [C] extends [S.Top] ? [schema: S.Schema] : [C] extends [object] ? [cls: Class] : [schema: S.Schema] : IsAny extends true ? [] : [Schema] : [] ) => AspectClass = ((type: string, schema: any) => Object.assign( (name: string, data: any) => (template: TemplateStringsArray, ...references: any[]) => { const aspect = (handler: any) => ({ kind: "aspect", type, id: name, template, references, schema, data, handler, plugin: { context: createPluginBuilder(ContextPlugin(type) as any), }, }); // function declaration so that a class can extends this return Object.assign(function (handler: any) { return Object.assign(function () {}, aspect(handler)); }, aspect(undefined)); }, { kind: "aspect", type, schema, plugin: { context: createPluginBuilder(ContextPlugin(type) as any), }, }, )) as AspectClass; type AspectType = AspectObject | AspectFunction; type AspectObject = ( name: Name, props?: Props, ) => ( template: TemplateStringsArray, ...references: References ) => Aspect; type AspectFunction = ( name: Name, props?: Props, ) => ( template: TemplateStringsArray, ...references: References ) => | (>( handler: ( input: Parameters.Of, ) => Generator>, never>, ) => Aspect< Aspect, string, Name, References, Props, ( input: Parameters.Of, ) => Effect.Effect< NoInfer>, [Eff] extends [never] ? never : [Eff] extends [Yieldable] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Yieldable] ? R : never > >) | (>( handler: Handler, ) => Aspect); type GetAspectType = Fn extends (name: string, props?: any) => infer Return ? Return extends (...args: any[]) => infer Return2 ? Return2 extends (...args: any[]) => infer Return3 ? Return3 : Return2 : never : never;