export * as Rpc from "./rpc.js" import type { StandardSchemaV1 } from "@standard-schema/spec" import type { JsonSchema, Schema } from "effect" import type { Event } from "./event.js" import type { Location } from "./location.js" import type { Tool } from "./tool.js" export type ErrorMap = Readonly> & { readonly [Name in `rpc.${string}`]?: never } export interface Method { readonly input: Tool.ValueSchema readonly output: Tool.ValueSchema readonly errors?: ErrorMap } export type PortableValueSchema = StandardSchemaV1 | JsonSchema.JsonSchema export interface PortableMethod extends Method { readonly input: PortableValueSchema readonly output: PortableValueSchema readonly errors?: Readonly> & { readonly [Name in `rpc.${string}`]?: never } } type EventDataObject = Readonly> type EventValueSchema = | Schema.Codec | StandardSchemaV1 | (JsonSchema.JsonSchema & { readonly type: "object" }) type PortableEventValueSchema = | StandardSchemaV1 | (JsonSchema.JsonSchema & { readonly type: "object" }) export interface EventDefinition { readonly schema: EventValueSchema } export type PortableEventDefinition = EventDefinition & { readonly schema: PortableEventValueSchema } export interface Definition { readonly id: string readonly methods: Readonly> & { readonly events?: never } readonly events: Readonly> } export interface PortableDefinition extends Definition { readonly methods: Readonly> & { readonly events?: never } readonly events: Readonly> } export function define(definition: D) { const reserved = Object.values(definition.methods) .flatMap((method) => Object.keys(method.errors ?? {})) .find((name) => name.startsWith("rpc.")) if (reserved) throw new Error(`RPC error names starting with "rpc." are reserved: ${reserved}`) return definition } export type Input = S extends Schema.Top ? S["Encoded"] : S extends StandardSchemaV1 ? StandardSchemaV1.InferInput : unknown export type Output = S extends Schema.Top ? S["Type"] : S extends StandardSchemaV1 ? StandardSchemaV1.InferOutput : unknown // Effect codecs encode handler results; Standard Schema parses them forward. export type HandlerOutput = S extends Schema.Top ? Output : Input type MethodErrors = M extends { readonly errors: infer Errors extends ErrorMap } ? Errors : never type ErrorSchema> = MethodErrors[Name] type ErrorData = unknown extends Data ? { readonly data: Data } : undefined extends Data ? { readonly data?: Data } : { readonly data: Data } type ErrorDataArguments = unknown extends Data ? [data: Data] : undefined extends Data ? [data?: Data] : [data: Data] type Simplify = { readonly [K in keyof A]: A[K] } declare const HandlerErrorTypeId: unique symbol export interface Failure { readonly type: Type readonly message: string readonly data?: Data } export type SystemError = Failure< | "rpc.unavailable" | "rpc.method_not_found" | "rpc.invalid_input" | "rpc.invalid_output" | "rpc.internal", never > export type ErrorName = M extends { readonly errors: infer Errors extends ErrorMap } ? Exclude : never export type HandlerErrorFor> = Simplify< { readonly type: Name readonly message: string readonly [HandlerErrorTypeId]: true } & ErrorData>> > export type HandlerError = { readonly [Name in ErrorName]: HandlerErrorFor }[ErrorName] export type MethodErrorFor> = Simplify< { readonly type: Name readonly message: string } & ErrorData>> > export type MethodError = { readonly [Name in ErrorName]: MethodErrorFor }[ErrorName] export type ErrorArguments> = [ type: Name, message: string, ...data: ErrorDataArguments>>, ] export type ErrorFactory = >( ...args: ErrorArguments ) => HandlerErrorFor export type EventInputData = S extends JsonSchema.JsonSchema ? EventDataObject : HandlerOutput export type EventData = S extends JsonSchema.JsonSchema ? EventDataObject : Output // Keep the event name correlated with its payload even when callers use unions. export type EventInput = { [Name in keyof D["events"] & string]: [name: Name, data: EventInputData] }[keyof D["events"] & string] type EventPayloadFor< D extends Definition, Name extends keyof D["events"] & string, > = Omit, "type" | "data" | "durable" | "location"> & { readonly type: `rpc.${D["id"]}.${Name}` readonly data: EventData readonly location: Location.Ref } export type EventPayload = { readonly [K in Name]: EventPayloadFor }[Name]