@apollo/client-react-streaming
Version:
This package provides building blocks to create framework-level integration of Apollo Client with React's streaming SSR. See the [@apollo/client-integration-nextjs](https://github.com/apollographql/apollo-client-integrations/tree/main/packages/nextjs) pac
99 lines (96 loc) • 3.04 kB
JavaScript
import { renderToString } from 'react-dom/server';
import * as React from 'react';
// src/stream-utils/JSONTransformStreams.tsx
var JSONEncodeStream = class extends TransformStream {
constructor() {
super({
transform(chunk, controller) {
controller.enqueue(JSON.stringify(chunk));
}
});
}
};
var JSONDecodeStream = class extends TransformStream {
constructor() {
super({
transform(chunk, controller) {
if (typeof chunk !== "string") {
chunk = new TextDecoder().decode(chunk);
}
controller.enqueue(JSON.parse(chunk));
}
});
}
};
function createInjectionTransformStream() {
let queuedInjections = [];
async function renderInjectedHtml() {
const injections = [...queuedInjections];
queuedInjections = [];
return renderToString(
/* @__PURE__ */ React.createElement(React.Fragment, null, injections.map((callback, i) => /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, callback())))
);
}
let headInserted = false;
let currentlyStreaming = false;
let tailOfLastChunk = "";
const textDecoder = new TextDecoder();
const textEncoder = new TextEncoder();
const HEAD_END = "</head>";
const KEEP_BYTES = HEAD_END.length;
const transformStream = new TransformStream({
async transform(chunk, controller) {
if (currentlyStreaming) {
controller.enqueue(chunk);
return;
}
if (!headInserted) {
const content = tailOfLastChunk + textDecoder.decode(chunk, { stream: true });
const index = content.indexOf(HEAD_END);
if (index !== -1) {
const insertedHeadContent = content.slice(0, index) + await renderInjectedHtml() + content.slice(index);
controller.enqueue(textEncoder.encode(insertedHeadContent));
currentlyStreaming = true;
setImmediate(() => {
currentlyStreaming = false;
});
headInserted = true;
} else {
tailOfLastChunk = content.slice(-KEEP_BYTES);
controller.enqueue(textEncoder.encode(content.slice(0, -KEEP_BYTES)));
}
} else {
controller.enqueue(textEncoder.encode(await renderInjectedHtml()));
controller.enqueue(chunk);
currentlyStreaming = true;
setImmediate(() => {
currentlyStreaming = false;
});
}
}
});
return {
transformStream,
injectIntoStream: (callback) => queuedInjections.push(callback)
};
}
// src/stream-utils/pipeReaderToResponse.ts
async function pipeReaderToResponse(reader, res) {
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
res.end();
return;
} else {
res.write(value);
}
}
} catch (e) {
res.destroy(e);
}
}
const built_for_ssr = true;
export { JSONDecodeStream, JSONEncodeStream, built_for_ssr, createInjectionTransformStream, pipeReaderToResponse };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=stream-utils.ssr.js.map