Files
cfw-autumn/packages/openapi/utils/openapiTransform/applyPaginationExtensions.ts
2026-05-13 14:53:32 +01:00

54 lines
1.9 KiB
TypeScript

/**
* Stamps `x-speakeasy-pagination` on operations whose request body has
* a `cursor` string field plus a `limit` integer field. Detection is
* shape-based so new paginated routes get SDK pagination automatically.
*
* Locked field names: `cursor` in, `next_cursor` out.
*/
export function applyPaginationExtensions({
openApiDocument,
}: {
openApiDocument: Record<string, unknown>;
}): void {
const paths = openApiDocument.paths as Record<string, unknown> | undefined;
if (!paths || typeof paths !== "object") return;
for (const pathItem of Object.values(paths)) {
if (!pathItem || typeof pathItem !== "object") continue;
for (const op of Object.values(pathItem as Record<string, unknown>)) {
if (!op || typeof op !== "object") continue;
if (!hasCursorPaginationShape(op as Record<string, unknown>)) continue;
(op as Record<string, unknown>)["x-speakeasy-pagination"] = {
type: "cursor",
inputs: [
{ name: "cursor", in: "requestBody", type: "cursor" },
{ name: "limit", in: "requestBody", type: "limit" },
],
outputs: { nextCursor: "$.next_cursor" },
};
}
}
}
const hasCursorPaginationShape = (op: Record<string, unknown>): boolean => {
const requestBody = op.requestBody as Record<string, unknown> | undefined;
const content = requestBody?.content as Record<string, unknown> | undefined;
const json = content?.["application/json"] as
| Record<string, unknown>
| undefined;
const schema = json?.schema as Record<string, unknown> | undefined;
const props = schema?.properties as Record<string, unknown> | undefined;
if (!props) return false;
const cursorProp = props.cursor as Record<string, unknown> | undefined;
const limitProp = props.limit as Record<string, unknown> | undefined;
if (!cursorProp || !limitProp) return false;
if (cursorProp.type !== "string") return false;
if (limitProp.type !== "integer" && limitProp.type !== "number") return false;
return true;
};