/** * Provides the `definePlugin()` utility function. * * @module @common-grants/sdk/extensions */ import { z } from "zod"; import type { ExtensibleSchemaName, CustomFieldSpec, SchemaInput, SchemaMappings, PluginMeta, PluginRoutes, TransformResult } from "./types"; import { EXTENSIBLE_SCHEMA_MAP } from "./types"; import { type WithCustomFieldsResult } from "./with-custom-fields"; import type { BuiltClient } from "../client/resources/builder"; import type { ClientConfig } from "../client/config"; import type { AuthMethod } from "../client/auth"; /** * Per-object schemas input keyed by extensible model name. * * Plugin authors populate this with hand-written or `buildTransforms()`-generated * `toCommon` / `fromCommon` callables, an optional `sourceSchema`, and optional * `customFields` specs. Passed as `DefinePluginOptions.schemas`. */ export type PluginSchemasInput = Partial>; /** * Options for `definePlugin()`. * * `schemas` is the single surface for all per-object declarations: custom * fields, source schema, declarative `mappings`, and explicit transform * callables. Inputs are declarative wherever possible; explicit callables are * available when custom code-driven logic is needed. * * Structured as an options object for forward-compatibility with future * properties like `namespace`. */ export interface DefinePluginOptions { /** Optional plugin identity and capability declaration. */ meta?: PluginMeta; /** * Per-object input — `sourceSchema`, `customFields` specs, declarative * `mappings`, and `toCommon` / `fromCommon` callables — for each extensible model. * * `definePlugin()` compiles this into runtime schemas: `commonSchema` is built via * `withCustomFields()` when `customFields` are declared; `toCommon` / `fromCommon` * are auto-wired from `schemas[Name].mappings` when `mappings` is used. Providing * both `mappings` and explicit callables is a runtime error. */ schemas?: T; /** * Route-keyed custom filter declarations. * * Passed through unchanged to `Plugin.routes`. Filters attach to resource * methods (e.g. `opportunities.search.filters`), not to a schema key — because * filters vary asymmetrically across methods. * * Registration-time validation (`validateRoutes`) and call-time classification * (`classifyFilters`) consume these declarations. * * @example * ```typescript * definePlugin({ * routes: { * opportunities: { * search: { * filters: { * agency: { filterType: "stringArray" }, * }, * }, * }, * }, * } as const) * ``` */ routes?: PluginRoutes; } /** * Configuration object returned by `definePlugin()`. * * - `schemas` — per-object compiled output: `commonSchema` (extended Zod schema), * `sourceSchema`, `toCommon`, and `fromCommon` for each extensible model * - `meta` — plugin identity passed through from options * - `routes` — route-keyed custom filter declarations; when defined `as const`, the * literal `filterType` values are preserved so that `TypedConsumerFilters` * can narrow call-site filter keys, operators, and value shapes. * * The second generic parameter `TRoutes` captures the literal routes type when the * caller uses `as const`. Callers that do not care about typed narrowing can ignore it * (the default is `PluginRoutes`). */ export interface Plugin { schemas: PluginSchemas; meta?: PluginMeta; /** Route-keyed custom filter declarations, passed through unchanged from `DefinePluginOptions.routes`. */ routes?: TRoutes; /** * Builds a client pre-bound to this plugin: responses parse with the plugin's * compiled schemas by default, and `search({ filters })` types the registered * filter names — no constructor `routes` or per-call `schema` needed. */ getClient(config?: ClientConfig & { auth?: AuthMethod; }): BuiltClient; } /** * Creates a `Plugin` from the given options. * * Iterates over extensible schemas. For each model, looks up `customFields` * specs from `schemas[name].customFields`. When specs are present, applies * `withCustomFields()` to produce a typed `commonSchema`; otherwise the base * schema passes through unchanged. The per-object result is wrapped under * `.commonSchema` alongside any `sourceSchema`, `toCommon`, and `fromCommon` provided. * * @param options - Options containing `schemas` (per-object input) and optional `meta` * @returns A `Plugin` with `.schemas` and optional `.meta` * * @example * ```typescript * const plugin = definePlugin({ * schemas: { * Opportunity: { * customFields: { * legacyId: { fieldType: "string" }, * category: { fieldType: "string", description: "Grant category" }, * }, * toCommon, * fromCommon, * }, * }, * } as const); * * // Access the extended Zod schema: * const opp = plugin.schemas.Opportunity.commonSchema.parse(rawData); * // Access the transform callables: * const result = plugin.schemas.Opportunity.toCommon?.(sourceData); * ``` */ export declare function definePlugin(options: DefinePluginOptions & { routes?: TRoutes; }): Plugin; /** Looks up the base Zod schema for an extensible model name. */ type BaseZodSchema = (typeof EXTENSIBLE_SCHEMA_MAP)[K]; /** * Extracts the `customFields` record from `T[K]`, or `never` if absent. * * Used to feed the custom-fields spec into `WithCustomFieldsResult` while * keeping the base schema as the fallback when no specs are declared. */ type ExtractCustomFields = K extends keyof T ? NonNullable extends { customFields?: infer CF; } ? CF extends Record ? CF : never : never : never; /** Resolves the `commonSchema` Zod schema for a single model. */ type ResolveCommonSchema = [ ExtractCustomFields ] extends [never] ? BaseZodSchema : WithCustomFieldsResult, ExtractCustomFields>; /** * Returns `true` when `T[K]` has transforms: either a `mappings` entry or an * explicit `toCommon` callable. Used to produce the right callable type on the * compiled output — the input's `toCommon` is `never` in the mappings branch * of the `SchemaInput` XOR union, so we cannot just read `T[K]["toCommon"]` * directly for mappings-based entries. */ type EntryHasTransforms = K extends keyof T ? NonNullable extends { mappings: SchemaMappings; } ? true : NonNullable extends { toCommon: (...args: any[]) => any; } ? true : false : false; /** * Extracts the source `TSource` from the entry's `sourceSchema`, or `unknown`. */ type ExtractSourceType = K extends keyof T ? NonNullable["sourceSchema"] extends z.ZodType ? S : unknown : unknown; /** * Maps each extensible model to its compiled per-object output. * * When the entry has transforms (mappings or explicit callables), both * `toCommon` and `fromCommon` are present and callable. When it is schema-only * (no transforms configured) they are absent. `customFields` and `mappings` * are kept for consumer inspection regardless of which path was used. */ type PluginSchemas = { [K in ExtensibleSchemaName]: EntryHasTransforms extends true ? { commonSchema: ResolveCommonSchema; sourceSchema: K extends keyof T ? NonNullable["sourceSchema"] : undefined; customFields: K extends keyof T ? NonNullable["customFields"] : undefined; mappings: K extends keyof T ? NonNullable["mappings"] : undefined; toCommon: (source: ExtractSourceType) => TransformResult>>; fromCommon: (common: z.infer>) => TransformResult>; } : { commonSchema: ResolveCommonSchema; sourceSchema?: undefined; customFields: K extends keyof T ? NonNullable["customFields"] : undefined; mappings?: undefined; toCommon?: undefined; fromCommon?: undefined; }; }; export {}; //# sourceMappingURL=define-plugin.d.ts.map