Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

react-streaming

Package Overview
Dependencies
Maintainers
1
Versions
100
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-streaming - npm Package Compare versions

Comparing version 0.1.4 to 0.1.5

dist/cjs/renderToStream/resolveSeoStrategy.d.ts

17

dist/cjs/renderToStream.d.ts

@@ -5,9 +5,4 @@ export { renderToStream };

import { Pipe } from './renderToStream/createPipeWrapper';
declare type Return = {
pipe: null | Pipe;
readable: null | ReadableStream;
injectToStream: (chunk: string) => void;
};
declare type SeoStrategy = 'conservative' | 'google-speed';
declare function renderToStream(element: React.ReactNode, options?: {
import { SeoStrategy } from './renderToStream/resolveSeoStrategy';
declare type Options = {
debug?: boolean;

@@ -19,2 +14,8 @@ webStream?: boolean;

renderToReadableStream?: typeof renderToReadableStream;
}): Promise<Return>;
};
declare type Result = {
pipe: null | Pipe;
readable: null | ReadableStream;
injectToStream: (chunk: string) => void;
};
declare function renderToStream(element: React.ReactNode, options?: Options): Promise<Result>;

@@ -11,86 +11,86 @@ "use strict";

const useStream_1 = require("./useStream");
const utils_1 = require("./utils");
const isbot_fast_1 = __importDefault(require("isbot-fast"));
const createPipeWrapper_1 = require("./renderToStream/createPipeWrapper");
const createReadableWrapper_1 = require("./renderToStream/createReadableWrapper");
const resolveSeoStrategy_1 = require("./renderToStream/resolveSeoStrategy");
async function renderToStream(element, options = {}) {
var _a, _b, _c;
let reject;
let resolve;
let resolved = false;
const promise = new Promise((resolve_, reject_) => {
resolve = () => {
if (resolved)
return;
resolved = true;
resolve_({ pipe, readable, injectToStream });
};
reject = (err) => {
if (resolved)
return;
resolved = true;
reject_(err);
};
});
const seoStrategy = options.seoStrategy || 'conservative';
const disabled = (_a = options.disabled) !== null && _a !== void 0 ? _a : (() => {
if (!options.userAgent) {
(0, utils_1.assertWarning)(false, 'Streaming disabled. Provide `options.userAgent` to enable streaming. (react-streaming needs the User Agent string in order to be able to disable streaming for bots, e.g. for Google Bot.) Or set `options.disabled` to `true` to get rid of this warning.');
return true;
}
// https://github.com/omrilotan/isbot
// https://github.com/mahovich/isbot-fast
// https://stackoverflow.com/questions/34647657/how-to-detect-web-crawlers-for-seo-using-express/68869738#68869738
if (!(0, isbot_fast_1.default)(options.userAgent)) {
return false;
}
const isGoogleBot = options.userAgent.toLowerCase().includes('googlebot');
if (seoStrategy === 'google-speed' && isGoogleBot) {
return false;
}
return true;
})();
// options.debug = true
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !(0, createPipeWrapper_1.nodeStreamModuleIsAvailable)();
const onError = (err) => {
reject(err);
};
const streamUtils = { injectToStream: (chunk) => injectToStream(chunk) };
element = react_1.default.createElement(useStream_1.StreamProvider, { value: streamUtils }, element);
var _a, _b;
element = react_1.default.createElement(useSsrData_1.SsrDataProvider, null, element);
let pipe = null;
let readable = null;
let injectToStream;
element = react_1.default.createElement(useStream_1.StreamProvider, { value: { injectToStream: (chunk) => injectToStream(chunk) } }, element);
const disabled = (_a = options.disabled) !== null && _a !== void 0 ? _a : (0, resolveSeoStrategy_1.resolveSeoStrategy)(options).disableStream;
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !(0, createPipeWrapper_1.nodeStreamModuleIsAvailable)();
if (!webStream) {
const { pipe: pipeOriginal } = (0, server_1.renderToPipeableStream)(element, {
onAllReady() {
resolve();
},
onShellReady() {
if (!disabled) {
resolve();
}
},
onShellError: onError,
onError
});
const { pipeWrapper, injectToStream: injectToStream_ } = (0, createPipeWrapper_1.createPipeWrapper)(pipeOriginal, options);
pipe = pipeWrapper;
injectToStream = injectToStream_;
pipe.injectToStream = injectToStream;
const result = await renderToNodeStream(element, disabled, options);
injectToStream = result.injectToStream;
return result;
}
else {
const readableOriginal = await ((_c = options.renderToReadableStream) !== null && _c !== void 0 ? _c : server_1.renderToReadableStream)(element, {
onError
});
if (disabled) {
await readableOriginal.allReady;
}
const { readableWrapper, injectToStream: injectToStream_ } = (0, createReadableWrapper_1.createReadableWrapper)(readableOriginal, options);
readable = readableWrapper;
injectToStream = injectToStream_;
const result = await renderToWebStream(element, disabled, options);
injectToStream = result.injectToStream;
return result;
}
}
exports.renderToStream = renderToStream;
async function renderToNodeStream(element, disabled, options) {
let resolve;
const promise = new Promise((r) => {
resolve = () => r();
});
let didError = false;
let firstErr = null;
const onError = (err) => {
didError = true;
firstErr = firstErr || err;
resolve();
};
const { pipe: pipeOriginal } = (0, server_1.renderToPipeableStream)(element, {
onAllReady() {
resolve();
},
onShellReady() {
if (!disabled) {
resolve();
}
},
onError,
onShellError: onError
});
const { pipeWrapper, injectToStream } = (0, createPipeWrapper_1.createPipeWrapper)(pipeOriginal, {
debug: options.debug,
onError
});
await promise;
if (didError) {
throw firstErr;
}
return promise;
return {
pipe: pipeWrapper,
readable: null,
injectToStream
};
}
exports.renderToStream = renderToStream;
async function renderToWebStream(element, disabled, options) {
var _a;
let didError = false;
let firstErr = null;
const onError = (err) => {
didError = true;
firstErr = firstErr || err;
};
const readableOriginal = await ((_a = options.renderToReadableStream) !== null && _a !== void 0 ? _a : server_1.renderToReadableStream)(element, { onError });
if (didError) {
throw firstErr;
}
if (disabled) {
await readableOriginal.allReady;
}
if (didError) {
throw firstErr;
}
const { readableWrapper, injectToStream } = (0, createReadableWrapper_1.createReadableWrapper)(readableOriginal, options);
return {
readable: readableWrapper,
pipe: null,
injectToStream
};
}
export { createBuffer };
declare function createBuffer(bufferParams: {
debug: boolean;
debug?: boolean;
writeChunk: null | ((_chunk: string) => void);

@@ -5,0 +5,0 @@ }): {

@@ -6,4 +6,5 @@ /// <reference types="node" />

import type { Writable as WritableType } from 'stream';
declare function createPipeWrapper(pipeOriginal: Pipe, options?: {
declare function createPipeWrapper(pipeOriginal: Pipe, { debug, onError }: {
debug?: boolean;
onError: (err: unknown) => void;
}): {

@@ -10,0 +11,0 @@ pipeWrapper: Pipe;

@@ -5,6 +5,6 @@ "use strict";

const createBuffer_1 = require("./createBuffer");
function createPipeWrapper(pipeOriginal, options = {}) {
function createPipeWrapper(pipeOriginal, { debug, onError }) {
const pipeWrapper = createPipeWrapper();
const bufferParams = {
debug: !!options.debug,
debug,
writeChunk: null

@@ -26,2 +26,6 @@ };

callback();
},
// If we don't define `destroy()`, then Node.js will `process.exit()`
destroy(err) {
onError(err);
}

@@ -28,0 +32,0 @@ });

@@ -5,9 +5,4 @@ export { renderToStream };

import { Pipe } from './renderToStream/createPipeWrapper';
declare type Return = {
pipe: null | Pipe;
readable: null | ReadableStream;
injectToStream: (chunk: string) => void;
};
declare type SeoStrategy = 'conservative' | 'google-speed';
declare function renderToStream(element: React.ReactNode, options?: {
import { SeoStrategy } from './renderToStream/resolveSeoStrategy';
declare type Options = {
debug?: boolean;

@@ -19,2 +14,8 @@ webStream?: boolean;

renderToReadableStream?: typeof renderToReadableStream;
}): Promise<Return>;
};
declare type Result = {
pipe: null | Pipe;
readable: null | ReadableStream;
injectToStream: (chunk: string) => void;
};
declare function renderToStream(element: React.ReactNode, options?: Options): Promise<Result>;

@@ -6,85 +6,85 @@ export { renderToStream };

import { StreamProvider } from './useStream';
import { assertWarning } from './utils';
import isBot from 'isbot-fast';
import { createPipeWrapper, nodeStreamModuleIsAvailable } from './renderToStream/createPipeWrapper';
import { createReadableWrapper } from './renderToStream/createReadableWrapper';
import { resolveSeoStrategy } from './renderToStream/resolveSeoStrategy';
async function renderToStream(element, options = {}) {
var _a, _b, _c;
let reject;
let resolve;
let resolved = false;
const promise = new Promise((resolve_, reject_) => {
resolve = () => {
if (resolved)
return;
resolved = true;
resolve_({ pipe, readable, injectToStream });
};
reject = (err) => {
if (resolved)
return;
resolved = true;
reject_(err);
};
});
const seoStrategy = options.seoStrategy || 'conservative';
const disabled = (_a = options.disabled) !== null && _a !== void 0 ? _a : (() => {
if (!options.userAgent) {
assertWarning(false, 'Streaming disabled. Provide `options.userAgent` to enable streaming. (react-streaming needs the User Agent string in order to be able to disable streaming for bots, e.g. for Google Bot.) Or set `options.disabled` to `true` to get rid of this warning.');
return true;
}
// https://github.com/omrilotan/isbot
// https://github.com/mahovich/isbot-fast
// https://stackoverflow.com/questions/34647657/how-to-detect-web-crawlers-for-seo-using-express/68869738#68869738
if (!isBot(options.userAgent)) {
return false;
}
const isGoogleBot = options.userAgent.toLowerCase().includes('googlebot');
if (seoStrategy === 'google-speed' && isGoogleBot) {
return false;
}
return true;
})();
// options.debug = true
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !nodeStreamModuleIsAvailable();
const onError = (err) => {
reject(err);
};
const streamUtils = { injectToStream: (chunk) => injectToStream(chunk) };
element = React.createElement(StreamProvider, { value: streamUtils }, element);
var _a, _b;
element = React.createElement(SsrDataProvider, null, element);
let pipe = null;
let readable = null;
let injectToStream;
element = React.createElement(StreamProvider, { value: { injectToStream: (chunk) => injectToStream(chunk) } }, element);
const disabled = (_a = options.disabled) !== null && _a !== void 0 ? _a : resolveSeoStrategy(options).disableStream;
const webStream = (_b = options.webStream) !== null && _b !== void 0 ? _b : !nodeStreamModuleIsAvailable();
if (!webStream) {
const { pipe: pipeOriginal } = renderToPipeableStream(element, {
onAllReady() {
resolve();
},
onShellReady() {
if (!disabled) {
resolve();
}
},
onShellError: onError,
onError
});
const { pipeWrapper, injectToStream: injectToStream_ } = createPipeWrapper(pipeOriginal, options);
pipe = pipeWrapper;
injectToStream = injectToStream_;
pipe.injectToStream = injectToStream;
const result = await renderToNodeStream(element, disabled, options);
injectToStream = result.injectToStream;
return result;
}
else {
const readableOriginal = await ((_c = options.renderToReadableStream) !== null && _c !== void 0 ? _c : renderToReadableStream)(element, {
onError
});
if (disabled) {
await readableOriginal.allReady;
}
const { readableWrapper, injectToStream: injectToStream_ } = createReadableWrapper(readableOriginal, options);
readable = readableWrapper;
injectToStream = injectToStream_;
const result = await renderToWebStream(element, disabled, options);
injectToStream = result.injectToStream;
return result;
}
}
async function renderToNodeStream(element, disabled, options) {
let resolve;
const promise = new Promise((r) => {
resolve = () => r();
});
let didError = false;
let firstErr = null;
const onError = (err) => {
didError = true;
firstErr = firstErr || err;
resolve();
};
const { pipe: pipeOriginal } = renderToPipeableStream(element, {
onAllReady() {
resolve();
},
onShellReady() {
if (!disabled) {
resolve();
}
},
onError,
onShellError: onError
});
const { pipeWrapper, injectToStream } = createPipeWrapper(pipeOriginal, {
debug: options.debug,
onError
});
await promise;
if (didError) {
throw firstErr;
}
return promise;
return {
pipe: pipeWrapper,
readable: null,
injectToStream
};
}
async function renderToWebStream(element, disabled, options) {
var _a;
let didError = false;
let firstErr = null;
const onError = (err) => {
didError = true;
firstErr = firstErr || err;
};
const readableOriginal = await ((_a = options.renderToReadableStream) !== null && _a !== void 0 ? _a : renderToReadableStream)(element, { onError });
if (didError) {
throw firstErr;
}
if (disabled) {
await readableOriginal.allReady;
}
if (didError) {
throw firstErr;
}
const { readableWrapper, injectToStream } = createReadableWrapper(readableOriginal, options);
return {
readable: readableWrapper,
pipe: null,
injectToStream
};
}
export { createBuffer };
declare function createBuffer(bufferParams: {
debug: boolean;
debug?: boolean;
writeChunk: null | ((_chunk: string) => void);

@@ -5,0 +5,0 @@ }): {

@@ -6,4 +6,5 @@ /// <reference types="node" />

import type { Writable as WritableType } from 'stream';
declare function createPipeWrapper(pipeOriginal: Pipe, options?: {
declare function createPipeWrapper(pipeOriginal: Pipe, { debug, onError }: {
debug?: boolean;
onError: (err: unknown) => void;
}): {

@@ -10,0 +11,0 @@ pipeWrapper: Pipe;

export { createPipeWrapper };
export { nodeStreamModuleIsAvailable };
import { createBuffer } from './createBuffer';
function createPipeWrapper(pipeOriginal, options = {}) {
function createPipeWrapper(pipeOriginal, { debug, onError }) {
const pipeWrapper = createPipeWrapper();
const bufferParams = {
debug: !!options.debug,
debug,
writeChunk: null

@@ -24,2 +24,6 @@ };

callback();
},
// If we don't define `destroy()`, then Node.js will `process.exit()`
destroy(err) {
onError(err);
}

@@ -26,0 +30,0 @@ });

{
"name": "react-streaming",
"description": "React 18 Streaming. Made fully-fledged & easy.",
"version": "0.1.4",
"version": "0.1.5",
"main": "./dist/cjs/index.js",
"dependencies": {
"@brillout/json-s": "^0.4.5",
"isbot-fast": "^1.2.0"
"isbot-fast": "1.2.0"
},

@@ -41,4 +41,4 @@ "scripts": {

"@types/react-dom": "^18.0.1",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react": "18.1.0",
"react-dom": "18.1.0",
"typescript": "^4.6.3",

@@ -45,0 +45,0 @@ "vitest": "^0.9.4"

@@ -0,4 +1,10 @@

<p align="center">
<a href="/../../#readme">
<img src="/images/logo.svg" height="145" alt="React Streaming"/>
</a>
</p>
# `react-streaming`
> React 18 Streaming. Made fully-fledged & easy.
> React 18 Streaming. Full-fledged & Easy.

@@ -18,4 +24,5 @@ <b>Contents</b>

- Unlocks React libraries of tomorrow. (Such as using [Telefunc](https://telefunc.com/) for data fetching.)
- Supports all platforms (Vercel, Cloudflare Workers, AWS, Netlify Edge, Deno, Deploy, ...).
- Supports all platforms (Vercel, Cloudflare Workers, AWS, Netlify Edge, Deno Deploy, ...).
- Two SEO strategies: `conservative` or `google-speed`.
- Easy error handling.
- **Bonus**: new `useAsync()` hook.

@@ -32,3 +39,2 @@

import { renderToStream } from 'react-streaming/server'
const {

@@ -45,3 +51,3 @@ pipe, // Node.js (Vercel, AWS)

- Data Fetching:
- Use RPC to fetch data in a seamless way, e.g. with [Telefunc](https://telefunc.com) or [tRPC](https://github.com/trpc/trpc). (Data fetching SSR hooks will be a thing of the past: no more Next.js's `getServerSideProps()` nor [`vite-plugin-ssr`](https://vite-plugin-ssr.com/)'s `onBeforeRender()`.)
- Use RPC to fetch data in a seamless way, e.g. with [Telefunc](https://telefunc.com). (Data fetching SSR hooks will be a thing of the past: no more Next.js's `getServerSideProps()` nor [`vite-plugin-ssr`](https://vite-plugin-ssr.com/)'s `onBeforeRender()`.)
- Expect your GraphQL tools to significantly improve, both performance and DX. (Also expect new tools such as [Vilay](https://github.com/XiNiHa/vilay).)

@@ -69,4 +75,2 @@ - Fundamentally improved mobile performance. (Mobile users can progressively load the page as data is fetched, before even a single line of JavaScript is loaded. Especially important for low-end and/or poorly-connected devices.)

import { renderToStream } from 'react-streaming/server'
// Cross-platform
const {

@@ -92,6 +96,6 @@ pipe, // Defined if running in Node.js, otherwise `null`

- `options.disable?: boolean`: Disable streaming.
> The component is still rendered to a stream, but the promise `const promise = await renderTo...` resolves only after the stream has finished. (This effectively disables streaming from a user perspective, while unlocking React 18 Streaming capabilities such as SSR `<Supsense>`.)
> `<Page>` is still rendered to a stream, but the promise `const promise = renderToStream()` resolves only after the stream has finished. (This effectively disables streaming from a user perspective, while unlocking React 18 Streaming capabilities such as SSR `<Supsense>`.)
- `options.seoStrategy?: 'conservative' | 'google-speed'`
- `conservative` (default): Disable streaming if the HTTP request originates from a bot. (Ensuring the bot always sees the whole HTML.)
- `conservative` (default): Disable streaming if the HTTP request originates from a bot. (Ensuring bots to always see the whole HTML.)
- `google-speed`: Don't disable streaming for the Google Bot.

@@ -111,3 +115,3 @@ - Pro: Google ranks your website higher because the initial HTTP response is faster. (To be researched.)

const stream = await renderToStream(<Page />, { disable })
await renderToStream(<Page />, { disable })
```

@@ -118,2 +122,19 @@

### Error Handling
If an error occurs at the beginning of the stream then `await renderToStream()` rejects with the error.
```js
try {
await renderToStream(<Page />)
// ✅ page shell succesfully rendered and is ready in the stream buffer.
} catch(err) {
// ❌ something went wrong while rendering the page shell.
}
```
Technically speaking, the page shell is composed of all components that are outside a `<Suspsense>` boundary.
If an error occurs later in the stream (i.e. a suspense boundary fails), then React swallows the error on the server-side and retries to resolve the suspense boundary on the client-side. If the `<Suspsense>` fails again on the client-side, then the client-side throws the error.
### Bonus: `useAsync()`

@@ -182,7 +203,7 @@

const someAsyncFunc = async function () {
return 'someData'
const value = 'someData'
return value
}
// `useSsrData()` suspends rendering until the promise returned by `someAsyncFunc()` resolves.
const value = useSsrData(key, someAsyncFunc)
// `value` is the value returned by `someAsyncFunc()`.
assert(value === 'someData')

@@ -189,0 +210,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc