/** * Class-level method aggregation. * * Where `RemoteHandler` describes one method, the types here describe a * whole class's worth of methods. `ClassMethodsImpl` enforces * that every implementable method on class `K` is provided. The interface * variant handles abstract definitions; the schema variant indexes by class * name. * * `remoteClassMethods` is the identity helper an author calls when supplying * a class's full method record — it gives full inference for the class * methods at once, complementing `remoteMethod` (one method at a time). */ import type { DefForInterface, InterfaceMethodDefs, InterfaceMethodKeys, MethodClassKeys, NonSealedMethodKeys, OwnMethodKeys, ResolveParams, ResolveReturn, SealedKeys, } from '@astrale-os/kernel-core/domain' import type { ImplementableOwnKeys, Schema } from '@astrale-os/kernel-dsl' import type { TypedSelf } from '../dispatch/self.js' import type { DomainKernel } from './context.js' import type { MethodImpl, RemoteHandler } from './single.js' /** Per-class method implementations for a remote domain. */ export type ClassMethodsImpl< S extends Schema, K extends MethodClassKeys & string, TDeps = unknown, > = { [M in OwnMethodKeys]: MethodImpl } & { [M in NonSealedMethodKeys]: MethodImpl } & { [M in SealedKeys]?: never } /** Resolve interface method handler directly (InterfaceMethodKeys ≠ MethodKeys). */ type InterfaceMethodHandler< S extends Schema, K extends string, M extends string, TDeps, > = M extends keyof InterfaceMethodDefs ? InterfaceMethodDefs[M] extends { readonly config: infer MC } ? MC extends { readonly static: true } ? RemoteHandler< ResolveParams, ResolveReturn, undefined, TDeps, 'required', DomainKernel > : // Non-static interface methods receive the schema-typed `self` keyed by // THIS interface's def — `self.node()` binds to a `BoundNode` with // short-keyed, def-typed `props`, `self.call` is typed, exactly as the // class-method path (`MethodImpl`) does. RemoteHandler< ResolveParams, ResolveReturn, TypedSelf, S>, TDeps, 'required', DomainKernel > : never : never /** Per-interface method implementations. */ export type InterfaceMethodsImpl< S extends Schema, K extends InterfaceMethodKeys & string, TDeps = unknown, > = { [M in ImplementableOwnKeys> & string]: InterfaceMethodHandler< S, K, M, TDeps > } /** * Namespace-keyed full schema methods map, parameterized by deps. * Handlers for classes and interfaces live under distinct `class` / `interface` * slots, preventing name collisions between a class and an interface with the * same name. * * `interface` is optional — schemas without non-abstract interface methods * (or with no interfaces at all) do not need to provide it. */ export type SchemaMethodsImpl = { class: { [K in MethodClassKeys & string]: ClassMethodsImpl } interface?: { [K in InterfaceMethodKeys & string]: InterfaceMethodsImpl } } type RejectSealedKeys = keyof I & SealedKeys extends never ? I : `Error: sealed methods must not be implemented by class '${K}': ${keyof I & SealedKeys & string}` /** Identity helper for authoring one class's remote methods with full schema typing. */ export function remoteClassMethods(): < S extends Schema, K extends MethodClassKeys & string, I extends ClassMethodsImpl, >( schema: S, className: K, impl: I & RejectSealedKeys, ) => I export function remoteClassMethods< S extends Schema, K extends MethodClassKeys & string, I extends ClassMethodsImpl, >(schema: S, className: K, impl: I & RejectSealedKeys): I export function remoteClassMethods(...args: unknown[]) { if (args.length === 0) { return (...innerArgs: unknown[]) => innerArgs[2] } return args[2] } /** * Identity helper for authoring one interface's remote methods with full * schema typing — the interface-hosted counterpart of `remoteClassMethods`. * * Methods declared on an interface (e.g. a static `createNote` on `NoteOps`) * live under the `interface:` slot of `SchemaMethodsImpl`. Without this helper * an author has to fall back to `any` (there is no other way to name the * per-method handler type — `InterfaceMethodHandler` is internal). Returns the * per-interface impl object the `interface: { : … }` slot expects. */ export function remoteInterfaceMethods(): < S extends Schema, K extends InterfaceMethodKeys & string, I extends InterfaceMethodsImpl, >( schema: S, interfaceName: K, impl: I, ) => I export function remoteInterfaceMethods< S extends Schema, K extends InterfaceMethodKeys & string, I extends InterfaceMethodsImpl, >(schema: S, interfaceName: K, impl: I): I export function remoteInterfaceMethods(...args: unknown[]) { if (args.length === 0) { return (...innerArgs: unknown[]) => innerArgs[2] } return args[2] }