@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
194 lines (186 loc) • 6.03 kB
JavaScript
import React, { useRef, useMemo, useId } from 'react';
import { resetApolloSingletons, DataTransportContext } from '@apollo/client-react-streaming';
import { invariant } from '@apollo/client/utilities/invariant';
// src/ManualDataTransport/ManualDataTransport.tsx
// src/ManualDataTransport/ApolloRehydrateSymbols.tsx
var ApolloSSRDataTransport = /* @__PURE__ */ Symbol.for(
"ApolloSSRDataTransport"
);
var ApolloHookRehydrationCache = /* @__PURE__ */ Symbol.for(
"apollo.hookRehydrationCache"
);
// src/ManualDataTransport/lateInitializingQueue.ts
function registerLateInitializingQueue(key, callback) {
const previousData = window[key] || [];
if (Array.isArray(previousData)) {
window[key] = {
push: (...data) => {
for (const value of data) {
callback(value);
}
}
};
window[key].push(...previousData);
}
}
// src/ManualDataTransport/htmlescape.ts
var ESCAPE_LOOKUP = {
"&": "\\u0026",
">": "\\u003e",
"<": "\\u003c",
"\u2028": "\\u2028",
"\u2029": "\\u2029"
};
var ESCAPE_REGEX = /[&><\u2028\u2029]/g;
function htmlEscapeJsonString(str) {
return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
}
// src/ManualDataTransport/dataTransport.ts
function transportDataToJS(data, stringify2) {
const key = Symbol.keyFor(ApolloSSRDataTransport);
return `(window[Symbol.for("${key}")] ??= []).push(${htmlEscapeJsonString(
stringify2(data)
)})`;
}
function buildApolloRehydrationContext({
insertHtml,
stringify: stringify2,
extraScriptProps
}) {
function ensureInserted() {
if (!rehydrationContext.currentlyInjected) {
rehydrationContext.currentlyInjected = true;
insertHtml(() => /* @__PURE__ */ React.createElement(rehydrationContext.RehydrateOnClient, null));
}
}
const rehydrationContext = {
currentlyInjected: false,
transportValueData: getTransportObject(ensureInserted),
transportedValues: {},
incomingEvents: getTransportArray(ensureInserted),
RehydrateOnClient() {
rehydrationContext.currentlyInjected = false;
if (!Object.keys(rehydrationContext.transportValueData).length && !Object.keys(rehydrationContext.incomingEvents).length)
return /* @__PURE__ */ React.createElement(React.Fragment, null);
invariant.debug(
"transporting data",
rehydrationContext.transportValueData
);
invariant.debug("transporting events", rehydrationContext.incomingEvents);
const __html = transportDataToJS(
{
rehydrate: Object.fromEntries(
Object.entries(rehydrationContext.transportValueData).filter(
([key, value]) => rehydrationContext.transportedValues[key] !== value
)
),
events: rehydrationContext.incomingEvents
},
stringify2
);
Object.assign(
rehydrationContext.transportedValues,
rehydrationContext.transportValueData
);
rehydrationContext.transportValueData = getTransportObject(ensureInserted);
rehydrationContext.incomingEvents = getTransportArray(ensureInserted);
return /* @__PURE__ */ React.createElement(
"script",
{
...extraScriptProps,
dangerouslySetInnerHTML: {
__html
}
}
);
}
};
return rehydrationContext;
}
function getTransportObject(ensureInserted) {
return new Proxy(
{},
{
set(...args) {
ensureInserted();
return Reflect.set(...args);
}
}
);
}
function getTransportArray(ensureInserted) {
return new Proxy([], {
get(...args) {
if (args[1] === "push") {
return (...values) => {
ensureInserted();
return args[0].push(...values);
};
}
return Reflect.get(...args);
}
});
}
// src/ManualDataTransport/serialization.ts
function stringify(value) {
let undefinedPlaceholder = "$apollo.undefined$";
const stringified = JSON.stringify(value);
while (stringified.includes(JSON.stringify(undefinedPlaceholder))) {
undefinedPlaceholder = "$" + undefinedPlaceholder;
}
return JSON.stringify(
value,
(_, v) => v === undefined ? undefinedPlaceholder : v
).replaceAll(JSON.stringify(undefinedPlaceholder), "undefined");
}
// src/ManualDataTransport/ManualDataTransport.tsx
var buildManualDataTransportSSRImpl = ({
useInsertHtml,
stringifyForStream = stringify,
dangerous_disableHookValueTransportation: disableHookValueTransportation
}) => function ManualDataTransportSSRImpl({
extraScriptProps,
children,
registerDispatchRequestStarted
}) {
const insertHtml = useInsertHtml();
const rehydrationContext = useRef(undefined);
if (!rehydrationContext.current) {
rehydrationContext.current = buildApolloRehydrationContext({
insertHtml,
extraScriptProps,
stringify: stringifyForStream
});
}
registerDispatchRequestStarted(({ event, observable }) => {
rehydrationContext.current.incomingEvents.push(event);
observable.subscribe({
next(event2) {
rehydrationContext.current.incomingEvents.push(event2);
}
});
});
const contextValue = useMemo(
() => ({
useStaticValueRef: function useStaticValueRef(value) {
const id = useId();
if (!disableHookValueTransportation) {
rehydrationContext.current.transportValueData[id] = value;
}
return { current: value };
}
}),
[]
);
return /* @__PURE__ */ React.createElement(DataTransportContext.Provider, { value: contextValue }, children);
};
var buildManualDataTransport = buildManualDataTransportSSRImpl ;
function resetManualSSRApolloSingletons() {
resetApolloSingletons();
delete window[ApolloHookRehydrationCache];
delete window[ApolloSSRDataTransport];
}
const built_for_ssr = true;
export { buildManualDataTransport, built_for_ssr, registerLateInitializingQueue, resetManualSSRApolloSingletons };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=manual-transport.ssr.js.map