@beignet/core
Version:
Core framework primitives for Beignet
69 lines (63 loc) • 2.07 kB
text/typescript
import {
compareQueryTransportFields,
formatQueryTransportMismatch,
getObjectSchemaShape,
} from "./schema-shape.js";
import type { HttpContractConfig } from "./types.js";
/** Assert that a plain contract config declares one transport per query. */
export function assertContractQueryTransport(
contract: HttpContractConfig,
): void {
if (contract.query && !contract.queryTransport) {
throw new Error(
`Contract "${contract.name}" declares a query schema without a query transport. Pass defineQueryTransport(...) as the second argument to .query(...).`,
);
}
if (!contract.query && contract.queryTransport) {
throw new Error(
`Contract "${contract.name}" declares a query transport without a query schema.`,
);
}
if (contract.query && contract.queryTransport) {
const shape = getObjectSchemaShape(contract.query);
if (!shape) return;
const schemaKeys = Object.keys(shape);
const transportKeys = Object.keys(contract.queryTransport.fields);
if (!compareQueryTransportFields({ schemaKeys, transportKeys })) {
throw new Error(
formatQueryTransportMismatch({
contractName: contract.name,
schemaKeys,
transportKeys,
}),
);
}
}
}
/**
* Contract input accepted by APIs that can unwrap builders.
*/
export type ContractLike = HttpContractConfig | { config: HttpContractConfig };
/**
* Resolve a contract-like type to its plain `HttpContractConfig`.
*/
export type ResolveContract<T> = T extends { config: infer C }
? C extends HttpContractConfig
? C
: never
: T extends HttpContractConfig
? T
: never;
/**
* Resolve a contract builder or plain config to the underlying contract config.
*
* @param c - The contract-like object to resolve
* @returns The underlying HttpContractConfig
*/
export function resolveContract<T extends ContractLike>(
c: T,
): ResolveContract<T> {
const contract = ("config" in c ? c.config : c) as ResolveContract<T>;
assertContractQueryTransport(contract);
return contract;
}