type Result = { errors: Map; successes: Map; }; type BulkMapFn = (request: In[], ...args: Args) => Promise>; type IsBulkFn = IsBulkMapFn extends true ? true : IsBulkArrayFn; type IsBulkMapFn = Fn extends (r: Array, ...args: infer _Args) => Promise> ? In extends unknown[] ? false : true : false; type IsBulkArrayFn = Fn extends (r: Array, ...args: infer _Args) => Promise> ? In extends unknown[] ? false : true : false; type AssertBulkRecord> = { [K in keyof R]: IsBulkFn extends true ? R[K] : never; }; type ScalarFn = (request: In, ...args: Args) => Promise; type BalarFn = BulkMapFn & ScalarFn; /** * Options for controlling the execution behavior of Balar. * * @property [concurrency] - The maximum number of concurrent executions for the processor function given to `balar.run()`. Defaults to unlimited if not specified. * @property [logger] - An optional function to handle logging messages (for debugging executions only). */ type ExecutionOptions = { concurrency?: number; logger?: (...args: any[]) => void; }; /** * Takes a bulk map/array function and converts its signature to a hybrid scalar/bulk(map) function. */ type BalarizeFn = F extends (input: Array, ...args: infer Args) => Promise> ? BalarFn : F extends (input: Array, ...args: infer Args) => Promise> ? BalarFn : never; type BulkMethods> = ValueTypes<{ [K in keyof O as IsBulkFn extends true ? K : never]: K; }>; /** * Takes a class object containing bulk methods and creates a facade only * containing scalar versions of these bulk methods. Exposes pick and exclude method filters. */ type ObjectFacade, P extends keyof O & string = BulkMethods & string, E extends keyof O & string = never> = { [K in keyof O as IsBulkFn extends true ? K extends UnionPickAndExclude ? K : never : never]: BalarizeFn; }; /** * Takes a registry and converts it to a record of hybrid scalar/bulk functions. */ type Facade> = { [K in keyof R as IsBulkFn extends true ? K : never]: BalarizeFn; }; type UnionPickAndExclude = Extract, P>; type ValueTypes = T extends { [key: string]: infer V; } ? V : never; /** * Processes a batch of inputs using the provided processor function and returns the results keyed by input. When a wrapped bulk function (obtained via `balar.wrap.fns()` or `balar.wrap.object()`) is called inside `balar.run()`, inputs are collected across all executions of the processor function so that only a single call to the underlying bulk function is performed. * * @param inputs - The array of inputs to process. * @param processor - An asynchronous function that processes each individual input. * @param opts - Optional execution options (e.g., concurrency control, logger). * @returns A `Promise` resolving to a `Map` that associates each input request with its corresponding output. * * @example * * ```ts * import { balar } from 'balar'; * * // Suppose we have a remote API for managing a greenhouse that we interact with * // through this service * class GreenhouseService { * async getPlants(plantIds: number[]): Promise> { ... } * async waterPlants(plants: Plant[]): Promise> { ... } * } * * // Wrap the service object with Balar * const wrapper = balar.wrap.object(new GreenhouseService()); * * // You can also wrap standalone functions like this * // const wrapper = balar.wrap.fns({ getPlants, waterPlants }); * * // Let's water multiple plants at once * const plantIds = [1, 2, 3]; // 🌿 🌵 🌱 * * // This code reads like plants are being watered in sequence, but... * const results = await balar.run(plantIds, async function waterPlant(plantId) { * // Balar queues all calls to `wrapper.getPlants(plantId)` and invokes * // the real `getPlants([1, 2, 3])` exactly once under the hood * const plant = await wrapper.getPlants(plantId); * * // ... Do other sync/async operations, return error, anything goes ... * * // Similarly, the real `waterPlants([plant, ...])` is called exactly once * const wateredAt = await wrapper.waterPlants(plant!); * * return { name: plant!.name, wateredAt }; * }); * * // Total number of requests to our remote API: 2! ✔️ * * // Map { 1 => { name: "Fern", wateredAt: ... }, 2 => { name: "Cactus", wateredAt: ... }, ... } * console.log(results); * ``` */ declare function run(inputs: In[], processor: (request: In) => Promise, opts?: ExecutionOptions): Promise>; /** * Creates a wrapper for a set of bulk functions. The wrapper can be used in balar execution contexts (e.g. inside the `processorFn` provided to `balar.execute(inputs, processorFn)`). When a wrapped function is called inside `balar.run()`, inputs are collected across all executions of the processor function so that a single call to the underlying bulk method is performed. * * @param bulkFunctions A record of bulk functions or bulk function configurations (defined with `balar.def()`). * @returns An object containing balar functions ready for use in balar execution contexts (`balar.run()`). * * @example * * ```ts * // Define a function with the required bulk signature `(inputs: I[]) => Promise>` * async function getBooks(bookIds: number[]): Promise> { ... } * * // Wrap it with `balar.wrap.fns()` * const booksRepository = balar.wrap.fns({ getBooks }); * * // You can now use these 2 overloads inside `balar.run()` to queue inputs * // for a call to the underlying function and get back the result once executed * booksRepository.getBooks(1); // Returns a Promise * booksRepository.getBooks([1, 2]); // Returns a Promise> * ``` */ declare function fns>(bulkFunctions: AssertBulkRecord): Facade; /** * Creates a wrapper for an object containing bulk methods. The wrapper can be used in balar execution contexts (e.g. inside the `processorFn` provided to `balar.execute(inputs, processorFn)`). When a wrapper method is called inside `balar.run()`, inputs are collected across all executions of the processor function so that a single call to the underlying bulk method is performed. * * @param object An object containing bulk methods. * @param opts An options object containing `pick` and `exclude` properties to control which bulk methods of the input object should be exposed in the output object. * @param {string[]} opts.pick The names of bulk methods to include. * @param {string[]} opts.exclude The names of bulk methods to exclude. * @returns An object containing balar functions ready for use in balar execution contexts. * * @example * * ```ts * // Define an object containing methods with the required bulk signature `(inputs: I[]) => Promise>` * class BooksRepository { * async getBooks(bookIds: number[]): Promise> { ... } * async createBooks(books: Book[]): Promise> { ... } * } * * // Wrap it with `balar.wrap.object()` * const wrapper = balar.wrap.object(new BooksRepository()); * * // You can also specify which methods to expose with `pick` and `exclude` * const wrapperWithConfig = balar.wrap.object(new BooksRepository(), { * pick: ['getBooks'], * exclude: ['createBooks'], * }); * * // For each wrapped bulk method, you can now use 2 overloads inside `balar.run()` * // to queue inputs for a call and get back the result once it's executed * wrapper.getBooks(1); // Returns a Promise * wrapper.getBooks([1, 2]); // Returns a Promise> * ``` * */ declare function object, P extends BulkMethods & string = BulkMethods & string, E extends BulkMethods & string = never>(object: O, opts?: { pick?: P[]; exclude?: E[]; }): ObjectFacade; declare function _if(condition: boolean, thenFn: () => Promise, elseFn?: () => Promise): Promise; type Case = [T, () => Promise]; type DefaultHandler = () => Promise; declare function _switch(val: T extends readonly unknown[] ? never : T, cases: [...{ [K in keyof R]: Case; }, DefaultHandler]): Promise; declare function _switch(val: T extends readonly unknown[] ? never : T, cases: { [K in keyof R]: Case; }): Promise; declare function _switch(...cases: [...{ [K in keyof R]: Case; }, DefaultHandler]): Promise; declare function _switch(...cases: { [K in keyof R]: Case; }): Promise; declare const balar: { wrap: { fns: typeof fns; object: typeof object; }; if: typeof _if; switch: typeof _switch; run: typeof run; }; export { type BalarFn, type ExecutionOptions, balar };