UNPKG

@on-the-ground/quackquack

Version:

Message-based duck typing system focused on behavioral roles instead of structural types.

135 lines (131 loc) 4.99 kB
type QuackDSL = string; /** * Wraps a function with runtime validation based on a given Quack signature. * * This allows you to enforce that the function matches a specific structure * (both syntactically and semantically) based on the Quack DSL. If the function * was already annotated via `quackable()`, the signature is checked for compatibility. * * - If the function is not annotated, the signature is enforced via `zod` wrapper. * - If the function is annotated and matches the expected signature, it's either: * - used as-is (`strict = false`) * - or rewrapped with validation (`strict = true`) * - If the function is annotated but does not match, an error is thrown. * * @param expected - A Quack DSL string describing the expected function signature. * @param strict - If `true`, wrap the function with runtime validation even if it already matches. * @returns A higher-order function that validates or wraps the target function. * * @example * ```ts * const greet = expectQuack("(name: string) => void")((name) => console.log(name)); * greet("Alice"); // valid * greet(123); // throws Zod validation error * ``` */ declare const expectQuack: (expected: QuackDSL, strict?: boolean) => <Q extends (...args: any[]) => any>(fn: Q) => Q; /** * Validates or wraps an entire object to ensure that each method matches a declared Quack signature. * * This is effectively a "duck typing contract enforcement" tool. Each method in the object * is validated against its corresponding expected Quack DSL. You can choose whether to rewrap * matched functions with runtime validation using the `strict` flag. * * @param expected - A mapping of method names to expected Quack DSL signatures. * @param strict - If `true`, wrap even matching methods with Zod validation. * @returns A higher-order function that returns a cloned object with validated/wrapped methods. * * @example * ```ts * const duck = expectDuck({ * greet: "(name: string) => void", * add: "(x: number, y: number) => number" * })({ * greet(name) { console.log(name); }, * add(x, y) { return x + y; } * }); * ``` */ declare const expectDuck: <D extends { [K in string]: QuackDSL; }>(expected: D, strict?: boolean) => <O extends { [K in keyof D]: (...args: any[]) => any; }>(obj: O) => { [K in keyof D]: O[K]; }; type ParsedType = "number" | "string" | "boolean" | "null" | "undefined" | "dontcare" | "void" | ArrayType | TupleType | PromiseType | FunctionType; type ArrayType = { type: "array"; element: ParsedType; }; type TupleType = { type: "tuple"; elements: ParsedType[]; }; type PromiseType = { type: "promise"; inner: ParsedType; }; type ParameterType = { optional: boolean; type: ParsedType; }; type FunctionType = { type: "function"; async: boolean; parameters: ParameterType[]; return: ParsedType; }; type QuackAst = FunctionType; /** * Parses a string representation of a function signature into a QuackAst (intermediate representation). * * @param fnSig - A string in the format of a function signature, e.g. "(x: number, y: string) => boolean" * @returns A QuackAst object representing the parsed function signature. */ declare function quackAstOf(fnSig: string): QuackAst; /** * Retrieves the QuackAst metadata from a previously marked function. * * @param fn - A function which may have been decorated or marked with `quackable`. * @returns The QuackAst if present, otherwise undefined. */ declare function quackAstFrom(fn: Function): QuackAst | null; /** * Checks if a function has been marked as quackable (i.e., has QuackAst metadata). * * @param fn - A function to check. * @returns True if the function has a QuackAst attached, false otherwise. */ declare function isQuackable(fn: Function): boolean; /** * Marks a function or class method with a quackable signature. * * This allows later validators (like `expectQuack`) to verify that * the function is intended to match a specific signature contract at runtime. * * ### Usage (Function) * ```ts * const add = quackable("(x: number, y: number) => number")( * (x, y) => x + y * ); * ``` * * ### Usage (Class Method - Legacy Decorator) * ```ts * class MyClass { * @quackable("(name: string) => void") * greet(name: string) { console.log(name); } * } * ``` * * ### Usage (Class Method - ECMA 2022 Decorator) * ```ts * class MyClass { * greet = quackable("(name: string) => void")(function(name: string) { * console.log(name); * }); * } * ``` * * @param sig - The expected function signature in string form. * @returns A decorator or a function wrapper that marks the target with QuackAst metadata. */ declare function quackable<I extends any[], O>(sig: string): (fn: (...args: I) => O) => (...args: I) => O; declare function quackable(sig: string): MethodDecorator; export { expectDuck, expectQuack, isQuackable, quackAstFrom, quackAstOf, quackable }; export type { QuackAst, QuackDSL };