Document rate limit headers in OpenAPI spec
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled

- Add reusable header components (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After)
- Reference headers in 200 responses on all conversion and demo endpoints
- Add Retry-After header to 429 responses
- Update Rate Limits section in API description to mention response headers
- Add comprehensive tests for header documentation (21 new tests)
- All 809 tests passing
This commit is contained in:
OpenClaw Subagent 2026-03-18 11:06:22 +01:00
parent a3bba8f0d5
commit 70eb6908e3
18 changed files with 801 additions and 821 deletions

View file

@ -196,6 +196,32 @@ export async function closeBrowser() {
}
instances.length = 0;
}
/** Build a Puppeteer-compatible PDFOptions object from user-supplied render options. */
export function buildPdfOptions(options) {
const result = {
format: options.format || "A4",
landscape: options.landscape || false,
printBackground: options.printBackground !== false,
margin: options.margin || { top: "0", right: "0", bottom: "0", left: "0" },
};
if (options.headerTemplate !== undefined)
result.headerTemplate = options.headerTemplate;
if (options.footerTemplate !== undefined)
result.footerTemplate = options.footerTemplate;
if (options.displayHeaderFooter !== undefined)
result.displayHeaderFooter = options.displayHeaderFooter;
if (options.scale !== undefined)
result.scale = options.scale;
if (options.pageRanges)
result.pageRanges = options.pageRanges;
if (options.preferCSSPageSize !== undefined)
result.preferCSSPageSize = options.preferCSSPageSize;
if (options.width)
result.width = options.width;
if (options.height)
result.height = options.height;
return result;
}
export async function renderPdf(html, options = {}) {
const { page, instance } = await acquirePage();
try {
@ -206,20 +232,7 @@ export async function renderPdf(html, options = {}) {
(async () => {
await page.setContent(html, { waitUntil: "domcontentloaded", timeout: 15_000 });
await page.addStyleTag({ content: "* { margin: 0; padding: 0; } body { margin: 0; }" });
const pdf = await page.pdf({
format: options.format || "A4",
landscape: options.landscape || false,
printBackground: options.printBackground !== false,
margin: options.margin || { top: "0", right: "0", bottom: "0", left: "0" },
headerTemplate: options.headerTemplate,
footerTemplate: options.footerTemplate,
displayHeaderFooter: options.displayHeaderFooter || false,
...(options.scale !== undefined && { scale: options.scale }),
...(options.pageRanges && { pageRanges: options.pageRanges }),
...(options.preferCSSPageSize !== undefined && { preferCSSPageSize: options.preferCSSPageSize }),
...(options.width && { width: options.width }),
...(options.height && { height: options.height }),
});
const pdf = await page.pdf(buildPdfOptions(options));
return Buffer.from(pdf);
})(),
new Promise((_, reject) => {
@ -281,20 +294,7 @@ export async function renderUrlPdf(url, options = {}) {
waitUntil: options.waitUntil || "domcontentloaded",
timeout: 30_000,
});
const pdf = await page.pdf({
format: options.format || "A4",
landscape: options.landscape || false,
printBackground: options.printBackground !== false,
margin: options.margin || { top: "0", right: "0", bottom: "0", left: "0" },
...(options.headerTemplate && { headerTemplate: options.headerTemplate }),
...(options.footerTemplate && { footerTemplate: options.footerTemplate }),
...(options.displayHeaderFooter !== undefined && { displayHeaderFooter: options.displayHeaderFooter }),
...(options.scale !== undefined && { scale: options.scale }),
...(options.pageRanges && { pageRanges: options.pageRanges }),
...(options.preferCSSPageSize !== undefined && { preferCSSPageSize: options.preferCSSPageSize }),
...(options.width && { width: options.width }),
...(options.height && { height: options.height }),
});
const pdf = await page.pdf(buildPdfOptions(options));
return Buffer.from(pdf);
})(),
new Promise((_, reject) => {