Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- summary: |
Gzip-compress v2 docs publish request bodies (register, finish, API
registration, translation registration) to reduce transfer time for
large deployments.
type: feat
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Returns a `fetch` function that gzip-compresses request bodies using the
* Web Streams `CompressionStream` API.
*
* Designed to be captured by oRPC's `LinkFetchClient` at construction time
* so all subsequent requests through the client are transparently compressed.
*
* The FDR server registers `@fastify/compress` which transparently
* decompresses incoming `Content-Encoding: gzip` request bodies.
*/
export function createGzipFetch(): typeof globalThis.fetch {
return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {
if (input instanceof Request && input.body != null) {
const headers = new Headers(input.headers);
headers.set("Content-Encoding", "gzip");
headers.delete("Content-Length");

const compressedBody = input.body.pipeThrough(new CompressionStream("gzip"));

const compressedRequest = new Request(input.url, {
method: input.method,
headers,
body: compressedBody,
// @ts-expect-error duplex required for streaming request bodies in Node.js
duplex: "half"
});
return globalThis.fetch(compressedRequest, init);
}

return globalThis.fetch(input, init);
};
}

/**
* Gzip-compresses a JSON body and returns a `RequestInit` suitable for
* `fetch()`, with the correct `Content-Encoding` and `Content-Type` headers.
*/
export async function gzipJsonBody(body: unknown): Promise<{ body: ReadableStream; headers: Record<string, string> }> {
const json = JSON.stringify(body);
const stream = new Blob([json]).stream().pipeThrough(new CompressionStream("gzip"));
return {
body: stream,
headers: {
"Content-Encoding": "gzip",
"Content-Type": "application/json"
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { readFile } from "fs/promises";
import { chunk } from "lodash-es";
import * as mime from "mime-types";
import terminalLink from "terminal-link";
import { createGzipFetch, gzipJsonBody } from "./compressedFetch.js";
import { getDynamicGeneratorConfig } from "./getDynamicGeneratorConfig.js";
import { measureImageSizes } from "./measureImageSizes.js";
import { asyncPool } from "./utils/asyncPool.js";
Expand Down Expand Up @@ -205,10 +206,17 @@ export async function publishDocs({
if (deployerAuthor?.email != null) {
headers["X-Deployer-Author-Email"] = deployerAuthor.email;
}
// Capture a gzip-compressing fetch into the oRPC client so all FDR
// requests with a body are transparently compressed. LinkFetchClient
// snapshots globalThis.fetch at construction time, so we swap it in
// before building the client and restore immediately after.
const savedFetch = globalThis.fetch;
globalThis.fetch = createGzipFetch();
const fdr = createFdrService({
token: token.value,
...(Object.keys(headers).length > 0 && { headers })
});
globalThis.fetch = savedFetch;
const authConfig = { type: "public" as const };

if (excludeApis) {
Expand Down Expand Up @@ -821,24 +829,28 @@ export async function publishDocs({
);
// Use a raw fetch instead of the oRPC client to send `docsDefinition`
// (the live server expects that field; the published fdr-sdk still uses `content`).
const translationPayload = {
domain: translationDomain,
// Send customDomains in production so FDR fans the translation
// S3 write out across every URL the docs are published to.
// Skipped in preview because preview deploys to a single
// ephemeral URL with no custom-domain mirrors.
customDomains: preview ? [] : customDomains,
orgId: organization,
locale,
docsDefinition: translatedDefinition
};
const compressed = await gzipJsonBody(translationPayload);
const translationResponse = await fetch(`${fdrOrigin}/v2/registry/docs/translations/register`, {
method: "POST",
headers: {
"Content-Type": "application/json",
...compressed.headers,
Authorization: `Bearer ${token.value}`,
...headers // Include telemetry headers (X-CLI-Version, X-CI-Source, etc.)
},
body: JSON.stringify({
domain: translationDomain,
// Send customDomains in production so FDR fans the translation
// S3 write out across every URL the docs are published to.
// Skipped in preview because preview deploys to a single
// ephemeral URL with no custom-domain mirrors.
customDomains: preview ? [] : customDomains,
orgId: organization,
locale,
docsDefinition: translatedDefinition
})
body: compressed.body,
// @ts-expect-error duplex required for streaming request bodies in Node.js
duplex: "half"
});
if (!translationResponse.ok) {
const body = await translationResponse.text();
Expand Down