@tanstack/react-query-next-experimental 5.0.0-alpha.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/build/lib/HydrationStreamProvider.cjs +120 -0
- package/build/lib/HydrationStreamProvider.cjs.map +1 -0
- package/build/lib/HydrationStreamProvider.d.ts +57 -0
- package/build/lib/HydrationStreamProvider.d.ts.map +1 -0
- package/build/lib/HydrationStreamProvider.js +99 -0
- package/build/lib/HydrationStreamProvider.js.map +1 -0
- package/build/lib/HydrationStreamProvider.legacy.cjs +125 -0
- package/build/lib/HydrationStreamProvider.legacy.cjs.map +1 -0
- package/build/lib/HydrationStreamProvider.legacy.js +104 -0
- package/build/lib/HydrationStreamProvider.legacy.js.map +1 -0
- package/build/lib/ReactQueryStreamedHydration.cjs +89 -0
- package/build/lib/ReactQueryStreamedHydration.cjs.map +1 -0
- package/build/lib/ReactQueryStreamedHydration.d.ts +18 -0
- package/build/lib/ReactQueryStreamedHydration.d.ts.map +1 -0
- package/build/lib/ReactQueryStreamedHydration.js +68 -0
- package/build/lib/ReactQueryStreamedHydration.js.map +1 -0
- package/build/lib/ReactQueryStreamedHydration.legacy.cjs +91 -0
- package/build/lib/ReactQueryStreamedHydration.legacy.cjs.map +1 -0
- package/build/lib/ReactQueryStreamedHydration.legacy.js +70 -0
- package/build/lib/ReactQueryStreamedHydration.legacy.js.map +1 -0
- package/build/lib/index.cjs +8 -0
- package/build/lib/index.cjs.map +1 -0
- package/build/lib/index.d.ts +2 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +2 -0
- package/build/lib/index.js.map +1 -0
- package/build/lib/index.legacy.cjs +8 -0
- package/build/lib/index.legacy.cjs.map +1 -0
- package/build/lib/index.legacy.js +2 -0
- package/build/lib/index.legacy.js.map +1 -0
- package/package.json +57 -0
- package/src/HydrationStreamProvider.tsx +184 -0
- package/src/ReactQueryStreamedHydration.tsx +93 -0
- package/src/index.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-present Tanner Linsley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var navigation = require('next/navigation');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
25
|
+
|
|
26
|
+
function createHydrationStreamProvider() {
|
|
27
|
+
const context = /*#__PURE__*/React__namespace.createContext(null);
|
|
28
|
+
/**
|
|
29
|
+
* 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes
|
|
30
|
+
* - This means that we might have some new entries in the cache that needs to be flushed
|
|
31
|
+
* - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`
|
|
32
|
+
* 2. (Happens in browser) In `useEffect()`:
|
|
33
|
+
* - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries
|
|
34
|
+
* - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received
|
|
35
|
+
**/
|
|
36
|
+
function UseClientHydrationStreamProvider(props) {
|
|
37
|
+
// unique id for the cache provider
|
|
38
|
+
const id = `__RQ${React__namespace.useId()}`;
|
|
39
|
+
const idJSON = JSON.stringify(id);
|
|
40
|
+
const [transformer] = React__namespace.useState(() => props.transformer ?? {
|
|
41
|
+
// noop
|
|
42
|
+
serialize: obj => obj,
|
|
43
|
+
deserialize: obj => obj
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// <server stuff>
|
|
47
|
+
const [stream] = React__namespace.useState(() => {
|
|
48
|
+
if (typeof window !== 'undefined') {
|
|
49
|
+
return {
|
|
50
|
+
push() {
|
|
51
|
+
// no-op on the client
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return [];
|
|
56
|
+
});
|
|
57
|
+
const count = React__namespace.useRef(0);
|
|
58
|
+
navigation.useServerInsertedHTML(() => {
|
|
59
|
+
// This only happens on the server
|
|
60
|
+
stream.push(...(props.onFlush?.() ?? []));
|
|
61
|
+
if (!stream.length) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
// console.log(`pushing ${stream.length} entries`)
|
|
65
|
+
const serializedCacheArgs = stream.map(entry => transformer.serialize(entry)).map(entry => JSON.stringify(entry)).join(',');
|
|
66
|
+
|
|
67
|
+
// Flush stream
|
|
68
|
+
stream.length = 0;
|
|
69
|
+
const html = [`window[${idJSON}] = window[${idJSON}] || [];`, `window[${idJSON}].push(${serializedCacheArgs});`];
|
|
70
|
+
return /*#__PURE__*/React__namespace.createElement("script", {
|
|
71
|
+
key: count.current++,
|
|
72
|
+
dangerouslySetInnerHTML: {
|
|
73
|
+
__html: html.join('')
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
// </server stuff>
|
|
78
|
+
|
|
79
|
+
// <client stuff>
|
|
80
|
+
const onEntriesRef = React__namespace.useRef(props.onEntries);
|
|
81
|
+
React__namespace.useEffect(() => {
|
|
82
|
+
onEntriesRef.current = props.onEntries;
|
|
83
|
+
});
|
|
84
|
+
React__namespace.useEffect(() => {
|
|
85
|
+
// Client: consume cache:
|
|
86
|
+
const onEntries = (...serializedEntries) => {
|
|
87
|
+
const entries = serializedEntries.map(serialized => transformer.deserialize(serialized));
|
|
88
|
+
onEntriesRef.current(entries);
|
|
89
|
+
};
|
|
90
|
+
const win = window;
|
|
91
|
+
// Register cache consumer
|
|
92
|
+
const winStream = win[id] ?? [];
|
|
93
|
+
onEntries(...winStream);
|
|
94
|
+
|
|
95
|
+
// Register our own consumer
|
|
96
|
+
win[id] = {
|
|
97
|
+
push: onEntries
|
|
98
|
+
};
|
|
99
|
+
return () => {
|
|
100
|
+
// Cleanup after unmount
|
|
101
|
+
win[id] = [];
|
|
102
|
+
};
|
|
103
|
+
}, [id, transformer]);
|
|
104
|
+
// </client stuff>
|
|
105
|
+
|
|
106
|
+
return /*#__PURE__*/React__namespace.createElement(context.Provider, {
|
|
107
|
+
value: {
|
|
108
|
+
stream,
|
|
109
|
+
id
|
|
110
|
+
}
|
|
111
|
+
}, props.children);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
Provider: UseClientHydrationStreamProvider,
|
|
115
|
+
context
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
exports.createHydrationStreamProvider = createHydrationStreamProvider;
|
|
120
|
+
//# sourceMappingURL=HydrationStreamProvider.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HydrationStreamProvider.cjs","sources":["../../src/HydrationStreamProvider.tsx"],"sourcesContent":["'use client'\n\nimport { useServerInsertedHTML } from 'next/navigation'\nimport * as React from 'react'\n\nconst serializedSymbol = Symbol('serialized')\n\ninterface DataTransformer {\n serialize(object: any): any\n deserialize(object: any): any\n}\n\ntype Serialized<TData> = unknown & {\n [serializedSymbol]: TData\n}\n\ninterface TypedDataTransformer<TData> {\n serialize: (obj: TData) => Serialized<TData>\n deserialize: (obj: Serialized<TData>) => TData\n}\n\ninterface HydrationStreamContext<TShape> {\n id: string\n stream: {\n /**\n * **Server method**\n * Push a new entry to the stream\n * Will be ignored on the client\n */\n push: (...shape: TShape[]) => void\n }\n}\n\nexport interface HydrationStreamProviderProps<TShape> {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n}\n\nexport function createHydrationStreamProvider<TShape>() {\n const context = React.createContext<HydrationStreamContext<TShape>>(\n null as any,\n )\n /**\n\n * 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes\n * - This means that we might have some new entries in the cache that needs to be flushed\n * - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`\n * 2. (Happens in browser) In `useEffect()`:\n * - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries\n * - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received\n **/\n function UseClientHydrationStreamProvider(props: {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n }) {\n // unique id for the cache provider\n const id = `__RQ${React.useId()}`\n const idJSON = JSON.stringify(id)\n\n const [transformer] = React.useState(\n () =>\n (props.transformer ?? {\n // noop\n serialize: (obj: any) => obj,\n deserialize: (obj: any) => obj,\n }) as unknown as TypedDataTransformer<TShape>,\n )\n\n // <server stuff>\n const [stream] = React.useState<TShape[]>(() => {\n if (typeof window !== 'undefined') {\n return {\n push() {\n // no-op on the client\n },\n } as unknown as TShape[]\n }\n return []\n })\n const count = React.useRef(0)\n useServerInsertedHTML(() => {\n // This only happens on the server\n stream.push(...(props.onFlush?.() ?? []))\n\n if (!stream.length) {\n return null\n }\n // console.log(`pushing ${stream.length} entries`)\n const serializedCacheArgs = stream\n .map((entry) => transformer.serialize(entry))\n .map((entry) => JSON.stringify(entry))\n .join(',')\n\n // Flush stream\n stream.length = 0\n\n const html: string[] = [\n `window[${idJSON}] = window[${idJSON}] || [];`,\n `window[${idJSON}].push(${serializedCacheArgs});`,\n ]\n return (\n <script\n key={count.current++}\n dangerouslySetInnerHTML={{\n __html: html.join(''),\n }}\n />\n )\n })\n // </server stuff>\n\n // <client stuff>\n const onEntriesRef = React.useRef(props.onEntries)\n React.useEffect(() => {\n onEntriesRef.current = props.onEntries\n })\n\n React.useEffect(() => {\n // Client: consume cache:\n const onEntries = (...serializedEntries: Serialized<TShape>[]) => {\n const entries = serializedEntries.map((serialized) =>\n transformer.deserialize(serialized),\n )\n onEntriesRef.current(entries)\n }\n\n const win = window as any\n // Register cache consumer\n const winStream: Array<Serialized<TShape>> = win[id] ?? []\n\n onEntries(...winStream)\n\n // Register our own consumer\n win[id] = {\n push: onEntries,\n }\n\n return () => {\n // Cleanup after unmount\n win[id] = []\n }\n }, [id, transformer])\n // </client stuff>\n\n return (\n <context.Provider value={{ stream, id }}>\n {props.children}\n </context.Provider>\n )\n }\n\n return {\n Provider: UseClientHydrationStreamProvider,\n context,\n }\n}\n"],"names":["push","useServerInsertedHTML","stream","key","dangerouslySetInnerHTML","__html","onEntriesRef","win","value","id","Provider","context"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAoDO;AACL;AAGA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;;AAoBI;;AAEA;AAEA;AAGM;;;AAGF;;AAGJ;;AAEE;;AAEIA;AACE;AAAA;;AAGN;AACA;AACF;AACA;AACAC;AACE;AACAC;AAEA;AACE;AACF;AACA;AACA;;AAKA;;AAGA;;AAMIC;AACAC;AACEC;AACF;AAAE;AAGR;AACA;;AAEA;;;AAGEC;AACF;;AAGE;AACA;AACE;AAGAA;;;AAIF;AACA;;;AAIA;;AAEEN;;AAGF;AACE;AACAO;;AAEJ;AACA;;AAEA;AACoBC;;AAAiBC;AAAG;;AAI1C;;AAGEC;AACAC;;AAEJ;;"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
interface DataTransformer {
|
|
3
|
+
serialize(object: any): any;
|
|
4
|
+
deserialize(object: any): any;
|
|
5
|
+
}
|
|
6
|
+
interface HydrationStreamContext<TShape> {
|
|
7
|
+
id: string;
|
|
8
|
+
stream: {
|
|
9
|
+
/**
|
|
10
|
+
* **Server method**
|
|
11
|
+
* Push a new entry to the stream
|
|
12
|
+
* Will be ignored on the client
|
|
13
|
+
*/
|
|
14
|
+
push: (...shape: TShape[]) => void;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface HydrationStreamProviderProps<TShape> {
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Optional transformer to serialize/deserialize the data
|
|
21
|
+
* Example devalue, superjson et al
|
|
22
|
+
*/
|
|
23
|
+
transformer?: DataTransformer;
|
|
24
|
+
/**
|
|
25
|
+
* **Client method**
|
|
26
|
+
* Called in the browser when new entries are received
|
|
27
|
+
*/
|
|
28
|
+
onEntries: (entries: TShape[]) => void;
|
|
29
|
+
/**
|
|
30
|
+
* **Server method**
|
|
31
|
+
* onFlush is called on the server when the cache is flushed
|
|
32
|
+
*/
|
|
33
|
+
onFlush?: () => TShape[];
|
|
34
|
+
}
|
|
35
|
+
export declare function createHydrationStreamProvider<TShape>(): {
|
|
36
|
+
Provider: (props: {
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
/**
|
|
39
|
+
* Optional transformer to serialize/deserialize the data
|
|
40
|
+
* Example devalue, superjson et al
|
|
41
|
+
*/
|
|
42
|
+
transformer?: DataTransformer | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* **Client method**
|
|
45
|
+
* Called in the browser when new entries are received
|
|
46
|
+
*/
|
|
47
|
+
onEntries: (entries: TShape[]) => void;
|
|
48
|
+
/**
|
|
49
|
+
* **Server method**
|
|
50
|
+
* onFlush is called on the server when the cache is flushed
|
|
51
|
+
*/
|
|
52
|
+
onFlush?: (() => TShape[]) | undefined;
|
|
53
|
+
}) => React.JSX.Element;
|
|
54
|
+
context: React.Context<HydrationStreamContext<TShape>>;
|
|
55
|
+
};
|
|
56
|
+
export {};
|
|
57
|
+
//# sourceMappingURL=HydrationStreamProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HydrationStreamProvider.d.ts","sourceRoot":"","sources":["../../src/HydrationStreamProvider.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,UAAU,eAAe;IACvB,SAAS,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAA;IAC3B,WAAW,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAA;CAC9B;AAWD,UAAU,sBAAsB,CAAC,MAAM;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE;QACN;;;;WAIG;QACH,IAAI,EAAE,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;KACnC,CAAA;CACF;AAED,MAAM,WAAW,4BAA4B,CAAC,MAAM;IAClD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,eAAe,CAAA;IAC7B;;;OAGG;IACH,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,MAAM,EAAE,CAAA;CACzB;AAED,wBAAgB,6BAA6B,CAAC,MAAM;;kBActC,MAAM,SAAS;QACzB;;;WAGG;;QAEH;;;WAGG;6BACkB,MAAM,EAAE,KAAK,IAAI;QACtC;;;WAGG;yBACa,MAAM,EAAE;;;EAsG3B"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useServerInsertedHTML } from 'next/navigation';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
function createHydrationStreamProvider() {
|
|
6
|
+
const context = /*#__PURE__*/React.createContext(null);
|
|
7
|
+
/**
|
|
8
|
+
* 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes
|
|
9
|
+
* - This means that we might have some new entries in the cache that needs to be flushed
|
|
10
|
+
* - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`
|
|
11
|
+
* 2. (Happens in browser) In `useEffect()`:
|
|
12
|
+
* - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries
|
|
13
|
+
* - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received
|
|
14
|
+
**/
|
|
15
|
+
function UseClientHydrationStreamProvider(props) {
|
|
16
|
+
// unique id for the cache provider
|
|
17
|
+
const id = `__RQ${React.useId()}`;
|
|
18
|
+
const idJSON = JSON.stringify(id);
|
|
19
|
+
const [transformer] = React.useState(() => props.transformer ?? {
|
|
20
|
+
// noop
|
|
21
|
+
serialize: obj => obj,
|
|
22
|
+
deserialize: obj => obj
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// <server stuff>
|
|
26
|
+
const [stream] = React.useState(() => {
|
|
27
|
+
if (typeof window !== 'undefined') {
|
|
28
|
+
return {
|
|
29
|
+
push() {
|
|
30
|
+
// no-op on the client
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return [];
|
|
35
|
+
});
|
|
36
|
+
const count = React.useRef(0);
|
|
37
|
+
useServerInsertedHTML(() => {
|
|
38
|
+
// This only happens on the server
|
|
39
|
+
stream.push(...(props.onFlush?.() ?? []));
|
|
40
|
+
if (!stream.length) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
// console.log(`pushing ${stream.length} entries`)
|
|
44
|
+
const serializedCacheArgs = stream.map(entry => transformer.serialize(entry)).map(entry => JSON.stringify(entry)).join(',');
|
|
45
|
+
|
|
46
|
+
// Flush stream
|
|
47
|
+
stream.length = 0;
|
|
48
|
+
const html = [`window[${idJSON}] = window[${idJSON}] || [];`, `window[${idJSON}].push(${serializedCacheArgs});`];
|
|
49
|
+
return /*#__PURE__*/React.createElement("script", {
|
|
50
|
+
key: count.current++,
|
|
51
|
+
dangerouslySetInnerHTML: {
|
|
52
|
+
__html: html.join('')
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
// </server stuff>
|
|
57
|
+
|
|
58
|
+
// <client stuff>
|
|
59
|
+
const onEntriesRef = React.useRef(props.onEntries);
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
onEntriesRef.current = props.onEntries;
|
|
62
|
+
});
|
|
63
|
+
React.useEffect(() => {
|
|
64
|
+
// Client: consume cache:
|
|
65
|
+
const onEntries = (...serializedEntries) => {
|
|
66
|
+
const entries = serializedEntries.map(serialized => transformer.deserialize(serialized));
|
|
67
|
+
onEntriesRef.current(entries);
|
|
68
|
+
};
|
|
69
|
+
const win = window;
|
|
70
|
+
// Register cache consumer
|
|
71
|
+
const winStream = win[id] ?? [];
|
|
72
|
+
onEntries(...winStream);
|
|
73
|
+
|
|
74
|
+
// Register our own consumer
|
|
75
|
+
win[id] = {
|
|
76
|
+
push: onEntries
|
|
77
|
+
};
|
|
78
|
+
return () => {
|
|
79
|
+
// Cleanup after unmount
|
|
80
|
+
win[id] = [];
|
|
81
|
+
};
|
|
82
|
+
}, [id, transformer]);
|
|
83
|
+
// </client stuff>
|
|
84
|
+
|
|
85
|
+
return /*#__PURE__*/React.createElement(context.Provider, {
|
|
86
|
+
value: {
|
|
87
|
+
stream,
|
|
88
|
+
id
|
|
89
|
+
}
|
|
90
|
+
}, props.children);
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
Provider: UseClientHydrationStreamProvider,
|
|
94
|
+
context
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { createHydrationStreamProvider };
|
|
99
|
+
//# sourceMappingURL=HydrationStreamProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HydrationStreamProvider.js","sources":["../../src/HydrationStreamProvider.tsx"],"sourcesContent":["'use client'\n\nimport { useServerInsertedHTML } from 'next/navigation'\nimport * as React from 'react'\n\nconst serializedSymbol = Symbol('serialized')\n\ninterface DataTransformer {\n serialize(object: any): any\n deserialize(object: any): any\n}\n\ntype Serialized<TData> = unknown & {\n [serializedSymbol]: TData\n}\n\ninterface TypedDataTransformer<TData> {\n serialize: (obj: TData) => Serialized<TData>\n deserialize: (obj: Serialized<TData>) => TData\n}\n\ninterface HydrationStreamContext<TShape> {\n id: string\n stream: {\n /**\n * **Server method**\n * Push a new entry to the stream\n * Will be ignored on the client\n */\n push: (...shape: TShape[]) => void\n }\n}\n\nexport interface HydrationStreamProviderProps<TShape> {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n}\n\nexport function createHydrationStreamProvider<TShape>() {\n const context = React.createContext<HydrationStreamContext<TShape>>(\n null as any,\n )\n /**\n\n * 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes\n * - This means that we might have some new entries in the cache that needs to be flushed\n * - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`\n * 2. (Happens in browser) In `useEffect()`:\n * - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries\n * - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received\n **/\n function UseClientHydrationStreamProvider(props: {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n }) {\n // unique id for the cache provider\n const id = `__RQ${React.useId()}`\n const idJSON = JSON.stringify(id)\n\n const [transformer] = React.useState(\n () =>\n (props.transformer ?? {\n // noop\n serialize: (obj: any) => obj,\n deserialize: (obj: any) => obj,\n }) as unknown as TypedDataTransformer<TShape>,\n )\n\n // <server stuff>\n const [stream] = React.useState<TShape[]>(() => {\n if (typeof window !== 'undefined') {\n return {\n push() {\n // no-op on the client\n },\n } as unknown as TShape[]\n }\n return []\n })\n const count = React.useRef(0)\n useServerInsertedHTML(() => {\n // This only happens on the server\n stream.push(...(props.onFlush?.() ?? []))\n\n if (!stream.length) {\n return null\n }\n // console.log(`pushing ${stream.length} entries`)\n const serializedCacheArgs = stream\n .map((entry) => transformer.serialize(entry))\n .map((entry) => JSON.stringify(entry))\n .join(',')\n\n // Flush stream\n stream.length = 0\n\n const html: string[] = [\n `window[${idJSON}] = window[${idJSON}] || [];`,\n `window[${idJSON}].push(${serializedCacheArgs});`,\n ]\n return (\n <script\n key={count.current++}\n dangerouslySetInnerHTML={{\n __html: html.join(''),\n }}\n />\n )\n })\n // </server stuff>\n\n // <client stuff>\n const onEntriesRef = React.useRef(props.onEntries)\n React.useEffect(() => {\n onEntriesRef.current = props.onEntries\n })\n\n React.useEffect(() => {\n // Client: consume cache:\n const onEntries = (...serializedEntries: Serialized<TShape>[]) => {\n const entries = serializedEntries.map((serialized) =>\n transformer.deserialize(serialized),\n )\n onEntriesRef.current(entries)\n }\n\n const win = window as any\n // Register cache consumer\n const winStream: Array<Serialized<TShape>> = win[id] ?? []\n\n onEntries(...winStream)\n\n // Register our own consumer\n win[id] = {\n push: onEntries,\n }\n\n return () => {\n // Cleanup after unmount\n win[id] = []\n }\n }, [id, transformer])\n // </client stuff>\n\n return (\n <context.Provider value={{ stream, id }}>\n {props.children}\n </context.Provider>\n )\n }\n\n return {\n Provider: UseClientHydrationStreamProvider,\n context,\n }\n}\n"],"names":["push","useServerInsertedHTML","stream","key","dangerouslySetInnerHTML","__html","onEntriesRef","win","value","id","Provider","context"],"mappings":";;;;AAoDO;AACL;AAGA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;;AAoBI;;AAEA;AAEA;AAGM;;;AAGF;;AAGJ;;AAEE;;AAEIA;AACE;AAAA;;AAGN;AACA;AACF;AACA;AACAC;AACE;AACAC;AAEA;AACE;AACF;AACA;AACA;;AAKA;;AAGA;;AAMIC;AACAC;AACEC;AACF;AAAE;AAGR;AACA;;AAEA;;;AAGEC;AACF;;AAGE;AACA;AACE;AAGAA;;;AAIF;AACA;;;AAIA;;AAEEN;;AAGF;AACE;AACAO;;AAEJ;AACA;;AAEA;AACoBC;;AAAiBC;AAAG;;AAI1C;;AAGEC;AACAC;;AAEJ;;"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var navigation = require('next/navigation');
|
|
5
|
+
var React = require('react');
|
|
6
|
+
|
|
7
|
+
function _interopNamespaceDefault(e) {
|
|
8
|
+
var n = Object.create(null);
|
|
9
|
+
if (e) {
|
|
10
|
+
Object.keys(e).forEach(function (k) {
|
|
11
|
+
if (k !== 'default') {
|
|
12
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
13
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
14
|
+
enumerable: true,
|
|
15
|
+
get: function () { return e[k]; }
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
n.default = e;
|
|
21
|
+
return Object.freeze(n);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
|
|
25
|
+
|
|
26
|
+
function createHydrationStreamProvider() {
|
|
27
|
+
const context = /*#__PURE__*/React__namespace.createContext(null);
|
|
28
|
+
/**
|
|
29
|
+
* 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes
|
|
30
|
+
* - This means that we might have some new entries in the cache that needs to be flushed
|
|
31
|
+
* - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`
|
|
32
|
+
* 2. (Happens in browser) In `useEffect()`:
|
|
33
|
+
* - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries
|
|
34
|
+
* - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received
|
|
35
|
+
**/
|
|
36
|
+
function UseClientHydrationStreamProvider(props) {
|
|
37
|
+
// unique id for the cache provider
|
|
38
|
+
const id = `__RQ${React__namespace.useId()}`;
|
|
39
|
+
const idJSON = JSON.stringify(id);
|
|
40
|
+
const [transformer] = React__namespace.useState(() => {
|
|
41
|
+
var _props$transformer;
|
|
42
|
+
return (_props$transformer = props.transformer) != null ? _props$transformer : {
|
|
43
|
+
// noop
|
|
44
|
+
serialize: obj => obj,
|
|
45
|
+
deserialize: obj => obj
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// <server stuff>
|
|
50
|
+
const [stream] = React__namespace.useState(() => {
|
|
51
|
+
if (typeof window !== 'undefined') {
|
|
52
|
+
return {
|
|
53
|
+
push() {
|
|
54
|
+
// no-op on the client
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return [];
|
|
59
|
+
});
|
|
60
|
+
const count = React__namespace.useRef(0);
|
|
61
|
+
navigation.useServerInsertedHTML(() => {
|
|
62
|
+
var _props$onFlush;
|
|
63
|
+
// This only happens on the server
|
|
64
|
+
stream.push(...((_props$onFlush = props.onFlush == null ? void 0 : props.onFlush()) != null ? _props$onFlush : []));
|
|
65
|
+
if (!stream.length) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
// console.log(`pushing ${stream.length} entries`)
|
|
69
|
+
const serializedCacheArgs = stream.map(entry => transformer.serialize(entry)).map(entry => JSON.stringify(entry)).join(',');
|
|
70
|
+
|
|
71
|
+
// Flush stream
|
|
72
|
+
stream.length = 0;
|
|
73
|
+
const html = [`window[${idJSON}] = window[${idJSON}] || [];`, `window[${idJSON}].push(${serializedCacheArgs});`];
|
|
74
|
+
return /*#__PURE__*/React__namespace.createElement("script", {
|
|
75
|
+
key: count.current++,
|
|
76
|
+
dangerouslySetInnerHTML: {
|
|
77
|
+
__html: html.join('')
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
// </server stuff>
|
|
82
|
+
|
|
83
|
+
// <client stuff>
|
|
84
|
+
const onEntriesRef = React__namespace.useRef(props.onEntries);
|
|
85
|
+
React__namespace.useEffect(() => {
|
|
86
|
+
onEntriesRef.current = props.onEntries;
|
|
87
|
+
});
|
|
88
|
+
React__namespace.useEffect(() => {
|
|
89
|
+
var _win$id;
|
|
90
|
+
// Client: consume cache:
|
|
91
|
+
const onEntries = (...serializedEntries) => {
|
|
92
|
+
const entries = serializedEntries.map(serialized => transformer.deserialize(serialized));
|
|
93
|
+
onEntriesRef.current(entries);
|
|
94
|
+
};
|
|
95
|
+
const win = window;
|
|
96
|
+
// Register cache consumer
|
|
97
|
+
const winStream = (_win$id = win[id]) != null ? _win$id : [];
|
|
98
|
+
onEntries(...winStream);
|
|
99
|
+
|
|
100
|
+
// Register our own consumer
|
|
101
|
+
win[id] = {
|
|
102
|
+
push: onEntries
|
|
103
|
+
};
|
|
104
|
+
return () => {
|
|
105
|
+
// Cleanup after unmount
|
|
106
|
+
win[id] = [];
|
|
107
|
+
};
|
|
108
|
+
}, [id, transformer]);
|
|
109
|
+
// </client stuff>
|
|
110
|
+
|
|
111
|
+
return /*#__PURE__*/React__namespace.createElement(context.Provider, {
|
|
112
|
+
value: {
|
|
113
|
+
stream,
|
|
114
|
+
id
|
|
115
|
+
}
|
|
116
|
+
}, props.children);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
Provider: UseClientHydrationStreamProvider,
|
|
120
|
+
context
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
exports.createHydrationStreamProvider = createHydrationStreamProvider;
|
|
125
|
+
//# sourceMappingURL=HydrationStreamProvider.legacy.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HydrationStreamProvider.legacy.cjs","sources":["../../src/HydrationStreamProvider.tsx"],"sourcesContent":["'use client'\n\nimport { useServerInsertedHTML } from 'next/navigation'\nimport * as React from 'react'\n\nconst serializedSymbol = Symbol('serialized')\n\ninterface DataTransformer {\n serialize(object: any): any\n deserialize(object: any): any\n}\n\ntype Serialized<TData> = unknown & {\n [serializedSymbol]: TData\n}\n\ninterface TypedDataTransformer<TData> {\n serialize: (obj: TData) => Serialized<TData>\n deserialize: (obj: Serialized<TData>) => TData\n}\n\ninterface HydrationStreamContext<TShape> {\n id: string\n stream: {\n /**\n * **Server method**\n * Push a new entry to the stream\n * Will be ignored on the client\n */\n push: (...shape: TShape[]) => void\n }\n}\n\nexport interface HydrationStreamProviderProps<TShape> {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n}\n\nexport function createHydrationStreamProvider<TShape>() {\n const context = React.createContext<HydrationStreamContext<TShape>>(\n null as any,\n )\n /**\n\n * 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes\n * - This means that we might have some new entries in the cache that needs to be flushed\n * - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`\n * 2. (Happens in browser) In `useEffect()`:\n * - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries\n * - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received\n **/\n function UseClientHydrationStreamProvider(props: {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n }) {\n // unique id for the cache provider\n const id = `__RQ${React.useId()}`\n const idJSON = JSON.stringify(id)\n\n const [transformer] = React.useState(\n () =>\n (props.transformer ?? {\n // noop\n serialize: (obj: any) => obj,\n deserialize: (obj: any) => obj,\n }) as unknown as TypedDataTransformer<TShape>,\n )\n\n // <server stuff>\n const [stream] = React.useState<TShape[]>(() => {\n if (typeof window !== 'undefined') {\n return {\n push() {\n // no-op on the client\n },\n } as unknown as TShape[]\n }\n return []\n })\n const count = React.useRef(0)\n useServerInsertedHTML(() => {\n // This only happens on the server\n stream.push(...(props.onFlush?.() ?? []))\n\n if (!stream.length) {\n return null\n }\n // console.log(`pushing ${stream.length} entries`)\n const serializedCacheArgs = stream\n .map((entry) => transformer.serialize(entry))\n .map((entry) => JSON.stringify(entry))\n .join(',')\n\n // Flush stream\n stream.length = 0\n\n const html: string[] = [\n `window[${idJSON}] = window[${idJSON}] || [];`,\n `window[${idJSON}].push(${serializedCacheArgs});`,\n ]\n return (\n <script\n key={count.current++}\n dangerouslySetInnerHTML={{\n __html: html.join(''),\n }}\n />\n )\n })\n // </server stuff>\n\n // <client stuff>\n const onEntriesRef = React.useRef(props.onEntries)\n React.useEffect(() => {\n onEntriesRef.current = props.onEntries\n })\n\n React.useEffect(() => {\n // Client: consume cache:\n const onEntries = (...serializedEntries: Serialized<TShape>[]) => {\n const entries = serializedEntries.map((serialized) =>\n transformer.deserialize(serialized),\n )\n onEntriesRef.current(entries)\n }\n\n const win = window as any\n // Register cache consumer\n const winStream: Array<Serialized<TShape>> = win[id] ?? []\n\n onEntries(...winStream)\n\n // Register our own consumer\n win[id] = {\n push: onEntries,\n }\n\n return () => {\n // Cleanup after unmount\n win[id] = []\n }\n }, [id, transformer])\n // </client stuff>\n\n return (\n <context.Provider value={{ stream, id }}>\n {props.children}\n </context.Provider>\n )\n }\n\n return {\n Provider: UseClientHydrationStreamProvider,\n context,\n }\n}\n"],"names":["push","useServerInsertedHTML","key","dangerouslySetInnerHTML","__html","onEntriesRef","win","value","id","Provider","context"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAoDO;AACL;AAGA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;;AAoBI;;AAEA;AAEA;AACE;AAAA;AAEI;;;;AAGD;;AAGL;;AAEE;;AAEIA;AACE;AAAA;;AAGN;AACA;AACF;AACA;AACAC;AAA4B;AAC1B;;AAGA;AACE;AACF;AACA;AACA;;AAKA;;AAGA;;AAMIC;AACAC;AACEC;AACF;AAAE;AAGR;AACA;;AAEA;;;AAGEC;AACF;;AAEsB;AACpB;AACA;AACE;AAGAA;;;AAIF;;;;AAKA;;AAEEL;;AAGF;AACE;AACAM;;AAEJ;AACA;;AAEA;AACoBC;;AAAiBC;AAAG;;AAI1C;;AAGEC;AACAC;;AAEJ;;"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useServerInsertedHTML } from 'next/navigation';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
function createHydrationStreamProvider() {
|
|
6
|
+
const context = /*#__PURE__*/React.createContext(null);
|
|
7
|
+
/**
|
|
8
|
+
* 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes
|
|
9
|
+
* - This means that we might have some new entries in the cache that needs to be flushed
|
|
10
|
+
* - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`
|
|
11
|
+
* 2. (Happens in browser) In `useEffect()`:
|
|
12
|
+
* - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries
|
|
13
|
+
* - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received
|
|
14
|
+
**/
|
|
15
|
+
function UseClientHydrationStreamProvider(props) {
|
|
16
|
+
// unique id for the cache provider
|
|
17
|
+
const id = `__RQ${React.useId()}`;
|
|
18
|
+
const idJSON = JSON.stringify(id);
|
|
19
|
+
const [transformer] = React.useState(() => {
|
|
20
|
+
var _props$transformer;
|
|
21
|
+
return (_props$transformer = props.transformer) != null ? _props$transformer : {
|
|
22
|
+
// noop
|
|
23
|
+
serialize: obj => obj,
|
|
24
|
+
deserialize: obj => obj
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// <server stuff>
|
|
29
|
+
const [stream] = React.useState(() => {
|
|
30
|
+
if (typeof window !== 'undefined') {
|
|
31
|
+
return {
|
|
32
|
+
push() {
|
|
33
|
+
// no-op on the client
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return [];
|
|
38
|
+
});
|
|
39
|
+
const count = React.useRef(0);
|
|
40
|
+
useServerInsertedHTML(() => {
|
|
41
|
+
var _props$onFlush;
|
|
42
|
+
// This only happens on the server
|
|
43
|
+
stream.push(...((_props$onFlush = props.onFlush == null ? void 0 : props.onFlush()) != null ? _props$onFlush : []));
|
|
44
|
+
if (!stream.length) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
// console.log(`pushing ${stream.length} entries`)
|
|
48
|
+
const serializedCacheArgs = stream.map(entry => transformer.serialize(entry)).map(entry => JSON.stringify(entry)).join(',');
|
|
49
|
+
|
|
50
|
+
// Flush stream
|
|
51
|
+
stream.length = 0;
|
|
52
|
+
const html = [`window[${idJSON}] = window[${idJSON}] || [];`, `window[${idJSON}].push(${serializedCacheArgs});`];
|
|
53
|
+
return /*#__PURE__*/React.createElement("script", {
|
|
54
|
+
key: count.current++,
|
|
55
|
+
dangerouslySetInnerHTML: {
|
|
56
|
+
__html: html.join('')
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
// </server stuff>
|
|
61
|
+
|
|
62
|
+
// <client stuff>
|
|
63
|
+
const onEntriesRef = React.useRef(props.onEntries);
|
|
64
|
+
React.useEffect(() => {
|
|
65
|
+
onEntriesRef.current = props.onEntries;
|
|
66
|
+
});
|
|
67
|
+
React.useEffect(() => {
|
|
68
|
+
var _win$id;
|
|
69
|
+
// Client: consume cache:
|
|
70
|
+
const onEntries = (...serializedEntries) => {
|
|
71
|
+
const entries = serializedEntries.map(serialized => transformer.deserialize(serialized));
|
|
72
|
+
onEntriesRef.current(entries);
|
|
73
|
+
};
|
|
74
|
+
const win = window;
|
|
75
|
+
// Register cache consumer
|
|
76
|
+
const winStream = (_win$id = win[id]) != null ? _win$id : [];
|
|
77
|
+
onEntries(...winStream);
|
|
78
|
+
|
|
79
|
+
// Register our own consumer
|
|
80
|
+
win[id] = {
|
|
81
|
+
push: onEntries
|
|
82
|
+
};
|
|
83
|
+
return () => {
|
|
84
|
+
// Cleanup after unmount
|
|
85
|
+
win[id] = [];
|
|
86
|
+
};
|
|
87
|
+
}, [id, transformer]);
|
|
88
|
+
// </client stuff>
|
|
89
|
+
|
|
90
|
+
return /*#__PURE__*/React.createElement(context.Provider, {
|
|
91
|
+
value: {
|
|
92
|
+
stream,
|
|
93
|
+
id
|
|
94
|
+
}
|
|
95
|
+
}, props.children);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
Provider: UseClientHydrationStreamProvider,
|
|
99
|
+
context
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export { createHydrationStreamProvider };
|
|
104
|
+
//# sourceMappingURL=HydrationStreamProvider.legacy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HydrationStreamProvider.legacy.js","sources":["../../src/HydrationStreamProvider.tsx"],"sourcesContent":["'use client'\n\nimport { useServerInsertedHTML } from 'next/navigation'\nimport * as React from 'react'\n\nconst serializedSymbol = Symbol('serialized')\n\ninterface DataTransformer {\n serialize(object: any): any\n deserialize(object: any): any\n}\n\ntype Serialized<TData> = unknown & {\n [serializedSymbol]: TData\n}\n\ninterface TypedDataTransformer<TData> {\n serialize: (obj: TData) => Serialized<TData>\n deserialize: (obj: Serialized<TData>) => TData\n}\n\ninterface HydrationStreamContext<TShape> {\n id: string\n stream: {\n /**\n * **Server method**\n * Push a new entry to the stream\n * Will be ignored on the client\n */\n push: (...shape: TShape[]) => void\n }\n}\n\nexport interface HydrationStreamProviderProps<TShape> {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n}\n\nexport function createHydrationStreamProvider<TShape>() {\n const context = React.createContext<HydrationStreamContext<TShape>>(\n null as any,\n )\n /**\n\n * 1. (Happens on server): `useServerInsertedHTML()` is called **on the server** whenever a `Suspense`-boundary completes\n * - This means that we might have some new entries in the cache that needs to be flushed\n * - We pass these to the client by inserting a `<script>`-tag where we do `window[id].push(serializedVersionOfCache)`\n * 2. (Happens in browser) In `useEffect()`:\n * - We check if `window[id]` is set to an array and call `push()` on all the entries which will call `onEntries()` with the new entries\n * - We replace `window[id]` with a `push()`-method that will be called whenever new entries are received\n **/\n function UseClientHydrationStreamProvider(props: {\n children: React.ReactNode\n /**\n * Optional transformer to serialize/deserialize the data\n * Example devalue, superjson et al\n */\n transformer?: DataTransformer\n /**\n * **Client method**\n * Called in the browser when new entries are received\n */\n onEntries: (entries: TShape[]) => void\n /**\n * **Server method**\n * onFlush is called on the server when the cache is flushed\n */\n onFlush?: () => TShape[]\n }) {\n // unique id for the cache provider\n const id = `__RQ${React.useId()}`\n const idJSON = JSON.stringify(id)\n\n const [transformer] = React.useState(\n () =>\n (props.transformer ?? {\n // noop\n serialize: (obj: any) => obj,\n deserialize: (obj: any) => obj,\n }) as unknown as TypedDataTransformer<TShape>,\n )\n\n // <server stuff>\n const [stream] = React.useState<TShape[]>(() => {\n if (typeof window !== 'undefined') {\n return {\n push() {\n // no-op on the client\n },\n } as unknown as TShape[]\n }\n return []\n })\n const count = React.useRef(0)\n useServerInsertedHTML(() => {\n // This only happens on the server\n stream.push(...(props.onFlush?.() ?? []))\n\n if (!stream.length) {\n return null\n }\n // console.log(`pushing ${stream.length} entries`)\n const serializedCacheArgs = stream\n .map((entry) => transformer.serialize(entry))\n .map((entry) => JSON.stringify(entry))\n .join(',')\n\n // Flush stream\n stream.length = 0\n\n const html: string[] = [\n `window[${idJSON}] = window[${idJSON}] || [];`,\n `window[${idJSON}].push(${serializedCacheArgs});`,\n ]\n return (\n <script\n key={count.current++}\n dangerouslySetInnerHTML={{\n __html: html.join(''),\n }}\n />\n )\n })\n // </server stuff>\n\n // <client stuff>\n const onEntriesRef = React.useRef(props.onEntries)\n React.useEffect(() => {\n onEntriesRef.current = props.onEntries\n })\n\n React.useEffect(() => {\n // Client: consume cache:\n const onEntries = (...serializedEntries: Serialized<TShape>[]) => {\n const entries = serializedEntries.map((serialized) =>\n transformer.deserialize(serialized),\n )\n onEntriesRef.current(entries)\n }\n\n const win = window as any\n // Register cache consumer\n const winStream: Array<Serialized<TShape>> = win[id] ?? []\n\n onEntries(...winStream)\n\n // Register our own consumer\n win[id] = {\n push: onEntries,\n }\n\n return () => {\n // Cleanup after unmount\n win[id] = []\n }\n }, [id, transformer])\n // </client stuff>\n\n return (\n <context.Provider value={{ stream, id }}>\n {props.children}\n </context.Provider>\n )\n }\n\n return {\n Provider: UseClientHydrationStreamProvider,\n context,\n }\n}\n"],"names":["push","useServerInsertedHTML","key","dangerouslySetInnerHTML","__html","onEntriesRef","win","value","id","Provider","context"],"mappings":";;;;AAoDO;AACL;AAGA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;;AAoBI;;AAEA;AAEA;AACE;AAAA;AAEI;;;;AAGD;;AAGL;;AAEE;;AAEIA;AACE;AAAA;;AAGN;AACA;AACF;AACA;AACAC;AAA4B;AAC1B;;AAGA;AACE;AACF;AACA;AACA;;AAKA;;AAGA;;AAMIC;AACAC;AACEC;AACF;AAAE;AAGR;AACA;;AAEA;;;AAGEC;AACF;;AAEsB;AACpB;AACA;AACE;AAGAA;;;AAIF;;;;AAKA;;AAEEL;;AAGF;AACE;AACAM;;AAEJ;AACA;;AAEA;AACoBC;;AAAiBC;AAAG;;AAI1C;;AAGEC;AACAC;;AAEJ;;"}
|