counterfact
Version:
Generate a TypeScript-based mock server from an OpenAPI spec in seconds — with stateful routes, hot reload, and REPL support.
83 lines (82 loc) • 3.46 kB
JavaScript
import { format } from "prettier";
/**
* Builds the `VersionsGTE` map: for each version V at index i,
* maps V to all versions at indices >= i (i.e. V and all later-declared
* versions, where "later" means "newer").
*/
function buildVersionsGTE(versions) {
const result = new Map();
versions.forEach((version, index) => {
result.set(version, versions.slice(index));
});
return result;
}
/**
* Generates the TypeScript source text for `types/versions.ts`.
*
* Returns an empty string when `versions` is empty.
* The returned string is formatted with Prettier.
*
* @param versions - Ordered list of unique, non-empty version strings.
* The first entry is the oldest version.
*/
export async function generateVersionsTsContent(versions) {
if (versions.length === 0) {
return "";
}
const versionsUnion = versions.map((v) => `"${v}"`).join(" | ");
const versionsGTE = buildVersionsGTE(versions);
const versionsGTEBody = Array.from(versionsGTE, ([v, gte]) => ` "${v}": ${gte.map((g) => `"${g}"`).join(" | ")};`).join("\n");
const source = [
"// This file is auto-generated by Counterfact. Do not edit.",
"",
'/** Union of all version strings declared for this API group (e.g. `"v1" | "v2" | "v3"`). */',
`export type Versions = ${versionsUnion};`,
"",
"/**",
" * Maps each version to the set of versions that are greater than or equal to it.",
" * Used by `Versioned.minVersion()` to narrow which versions a handler must support.",
" */",
"export type VersionsGTE = {",
versionsGTEBody,
"};",
"",
"type VersionMap = Partial<Record<Versions, object>>;",
"",
"/**",
" * The type of the `$` argument in a versioned route handler.",
" *",
" * @typeParam T - A map from version string to the `$`-arg type for that version",
" * (e.g. `{ v1: $v1Type; v2: $v2Type }`). Generated by Counterfact from the spec.",
" * @typeParam V - The union of currently active version keys; defaults to all keys of `T`.",
" *",
" * An instance of `Versioned<T, V>` exposes:",
" * - All properties of the intersection `T[V]` (request data, response builders, etc.)",
' * - `version` — the version string for the current request (e.g. `"v2"`).',
" * - `minVersion(min)` — type predicate that returns `true` when the current version",
" * is at or after `min` in the declared version order and narrows `$` accordingly.",
" *",
" * @example",
" * ```ts",
" * export const GET: HTTP_GET = ($) => {",
' * if ($.minVersion("v2")) {',
" * // $ is now typed as the v2+ arg — v2-only fields are available",
" * return $.response[200].json({ id: $.path.id, extra: $.body.extra });",
" * }",
" * return $.response[200].json({ id: $.path.id });",
" * };",
" * ```",
" */",
"export type Versioned<",
" T extends VersionMap,",
" V extends keyof T & Versions = keyof T & Versions,",
"> = T[V] & {",
" version: V;",
" minVersion<M extends keyof T & Versions>(",
" min: M,",
" ): this is Versioned<T, Extract<V, VersionsGTE[M]>>;",
"};",
"",
].join("\n");
return format(source, { parser: "typescript" });
}