@replanejs/next 0.7.3 → 0.7.5
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/README.md +160 -296
- package/dist/index.cjs +100 -174
- package/dist/index.d.cts +23 -183
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +24 -184
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -171
- package/dist/index.js.map +1 -1
- package/package.json +7 -15
- package/dist/chunk-CUT6urMc.cjs +0 -30
- package/dist/server.cjs +0 -95
- package/dist/server.d.cts +0 -114
- package/dist/server.d.cts.map +0 -1
- package/dist/server.d.ts +0 -114
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -94
- package/dist/server.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,216 +1,56 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { ReplaneClient, ReplaneContext, ReplaneContext as ReplaneContext$1, ReplaneError, ReplaneSnapshot, ReplaneSnapshot as ReplaneSnapshot$1, restoreReplaneClient } from "@replanejs/sdk";
|
|
4
|
-
import * as react_jsx_runtime0$1 from "react/jsx-runtime";
|
|
1
|
+
import { GetReplaneSnapshotOptions, ReplaneProvider, ReplaneProviderProps, ReplaneProviderWithClientProps, ReplaneProviderWithOptionsProps, clearSnapshotCache, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane } from "@replanejs/react";
|
|
2
|
+
import { GetConfigOptions, ReplaneClient, ReplaneClientOptions, ReplaneClientOptions as ReplaneClientOptions$1, ReplaneContext, ReplaneError, ReplaneErrorCode, ReplaneLogger, ReplaneSnapshot, RestoreReplaneClientOptions, createInMemoryReplaneClient, createReplaneClient, restoreReplaneClient } from "@replanejs/sdk";
|
|
5
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
|
+
import { ReactNode } from "react";
|
|
5
|
+
|
|
6
|
+
//#region src/root.d.ts
|
|
6
7
|
|
|
7
|
-
//#region src/types.d.ts
|
|
8
|
-
/**
|
|
9
|
-
* Connection options for real-time updates.
|
|
10
|
-
*/
|
|
11
|
-
interface ReplaneConnectionOptions {
|
|
12
|
-
/**
|
|
13
|
-
* Base URL of the Replane instance (no trailing slash).
|
|
14
|
-
* Use a NEXT_PUBLIC_ prefixed env var for client-side access.
|
|
15
|
-
*/
|
|
16
|
-
baseUrl: string;
|
|
17
|
-
/**
|
|
18
|
-
* Project SDK key for authorization.
|
|
19
|
-
* Use a NEXT_PUBLIC_ prefixed env var for client-side access.
|
|
20
|
-
*/
|
|
21
|
-
sdkKey: string;
|
|
22
|
-
/**
|
|
23
|
-
* Optional timeout in ms for requests.
|
|
24
|
-
* @default 2000
|
|
25
|
-
*/
|
|
26
|
-
requestTimeoutMs?: number;
|
|
27
|
-
/**
|
|
28
|
-
* Delay between retries in ms.
|
|
29
|
-
* @default 200
|
|
30
|
-
*/
|
|
31
|
-
retryDelayMs?: number;
|
|
32
|
-
/**
|
|
33
|
-
* Timeout in ms for SSE connection inactivity.
|
|
34
|
-
* @default 30000
|
|
35
|
-
*/
|
|
36
|
-
inactivityTimeoutMs?: number;
|
|
37
|
-
}
|
|
38
8
|
/**
|
|
39
|
-
* Props for
|
|
9
|
+
* Props for ReplaneRoot server component
|
|
40
10
|
*/
|
|
41
|
-
interface
|
|
11
|
+
interface ReplaneRootProps<T extends object> {
|
|
42
12
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
13
|
+
* Options for Replane client.
|
|
14
|
+
* Used for both server-side fetching and client-side live updates.
|
|
45
15
|
*/
|
|
46
|
-
|
|
16
|
+
options: ReplaneClientOptions$1<T>;
|
|
47
17
|
/**
|
|
48
|
-
*
|
|
49
|
-
* If not provided, the client will only use the snapshot data (no live updates).
|
|
50
|
-
*
|
|
51
|
-
* For SSR apps that need real-time updates, provide connection options:
|
|
52
|
-
* ```tsx
|
|
53
|
-
* <ReplaneNextProvider
|
|
54
|
-
* snapshot={snapshot}
|
|
55
|
-
* connection={{
|
|
56
|
-
* baseUrl: process.env.NEXT_PUBLIC_REPLANE_BASE_URL!,
|
|
57
|
-
* sdkKey: process.env.NEXT_PUBLIC_REPLANE_SDK_KEY!,
|
|
58
|
-
* }}
|
|
59
|
-
* >
|
|
60
|
-
* {children}
|
|
61
|
-
* </ReplaneNextProvider>
|
|
62
|
-
* ```
|
|
18
|
+
* React children to render inside the provider
|
|
63
19
|
*/
|
|
64
|
-
connection?: ReplaneConnectionOptions;
|
|
65
|
-
/**
|
|
66
|
-
* Override the context from the snapshot on the client.
|
|
67
|
-
* Useful for client-specific context like browser info.
|
|
68
|
-
*/
|
|
69
|
-
context?: ReplaneContext$1;
|
|
70
20
|
children: ReactNode;
|
|
71
21
|
}
|
|
72
|
-
//# sourceMappingURL=types.d.ts.map
|
|
73
|
-
//#endregion
|
|
74
|
-
//#region src/provider.d.ts
|
|
75
22
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* This component:
|
|
79
|
-
* 1. Restores the Replane client from a server-side snapshot instantly (no loading state)
|
|
80
|
-
* 2. Optionally connects to Replane for real-time updates
|
|
81
|
-
* 3. Preserves the client across re-renders for minimal latency
|
|
23
|
+
* Server component that fetches Replane configs and provides them to the app.
|
|
24
|
+
* This is the simplest way to set up Replane in Next.js App Router.
|
|
82
25
|
*
|
|
83
|
-
* @example
|
|
26
|
+
* @example Basic usage in layout.tsx
|
|
84
27
|
* ```tsx
|
|
85
28
|
* // app/layout.tsx
|
|
86
|
-
* import {
|
|
87
|
-
* import { ReplaneNextProvider } from "@replanejs/next";
|
|
88
|
-
*
|
|
89
|
-
* export default async function RootLayout({ children }) {
|
|
90
|
-
* const snapshot = await getReplaneSnapshot({
|
|
91
|
-
* baseUrl: process.env.REPLANE_BASE_URL!,
|
|
92
|
-
* sdkKey: process.env.REPLANE_SDK_KEY!,
|
|
93
|
-
* });
|
|
29
|
+
* import { ReplaneRoot } from "@replanejs/next";
|
|
94
30
|
*
|
|
31
|
+
* export default function RootLayout({ children }) {
|
|
95
32
|
* return (
|
|
96
33
|
* <html>
|
|
97
34
|
* <body>
|
|
98
|
-
* <
|
|
99
|
-
*
|
|
100
|
-
* connection={{
|
|
35
|
+
* <ReplaneRoot
|
|
36
|
+
* options={{
|
|
101
37
|
* baseUrl: process.env.NEXT_PUBLIC_REPLANE_BASE_URL!,
|
|
102
38
|
* sdkKey: process.env.NEXT_PUBLIC_REPLANE_SDK_KEY!,
|
|
103
39
|
* }}
|
|
104
40
|
* >
|
|
105
41
|
* {children}
|
|
106
|
-
* </
|
|
42
|
+
* </ReplaneRoot>
|
|
107
43
|
* </body>
|
|
108
44
|
* </html>
|
|
109
45
|
* );
|
|
110
46
|
* }
|
|
111
47
|
* ```
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```tsx
|
|
115
|
-
* // Without real-time updates (static snapshot only)
|
|
116
|
-
* <ReplaneNextProvider snapshot={snapshot}>
|
|
117
|
-
* {children}
|
|
118
|
-
* </ReplaneNextProvider>
|
|
119
|
-
* ```
|
|
120
|
-
*/
|
|
121
|
-
declare function ReplaneNextProvider<T extends object = Record<string, unknown>>({
|
|
122
|
-
snapshot,
|
|
123
|
-
connection,
|
|
124
|
-
context,
|
|
125
|
-
children
|
|
126
|
-
}: ReplaneNextProviderProps<T>): react_jsx_runtime0$1.JSX.Element;
|
|
127
|
-
//# sourceMappingURL=provider.d.ts.map
|
|
128
|
-
//#endregion
|
|
129
|
-
//#region src/script.d.ts
|
|
130
|
-
declare const REPLANE_SNAPSHOT_KEY = "__REPLANE_SNAPSHOT__";
|
|
131
|
-
type AnyConfig = Record<string, unknown>;
|
|
132
|
-
declare global {
|
|
133
|
-
interface Window {
|
|
134
|
-
[REPLANE_SNAPSHOT_KEY]?: ReplaneSnapshot$1<AnyConfig>;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Generate the script content for embedding the snapshot.
|
|
139
|
-
*
|
|
140
|
-
* Use this in a Server Component to embed the snapshot in the page:
|
|
141
|
-
*
|
|
142
|
-
* @example
|
|
143
|
-
* ```tsx
|
|
144
|
-
* // app/layout.tsx
|
|
145
|
-
* import { getReplaneSnapshot } from "@replanejs/next/server";
|
|
146
|
-
* import { getReplaneSnapshotScript, ReplaneScriptProvider } from "@replanejs/next";
|
|
147
|
-
*
|
|
148
|
-
* export default async function RootLayout({ children }) {
|
|
149
|
-
* const snapshot = await getReplaneSnapshot({ ... });
|
|
150
|
-
*
|
|
151
|
-
* return (
|
|
152
|
-
* <html>
|
|
153
|
-
* <head>
|
|
154
|
-
* <script
|
|
155
|
-
* dangerouslySetInnerHTML={{
|
|
156
|
-
* __html: getReplaneSnapshotScript(snapshot),
|
|
157
|
-
* }}
|
|
158
|
-
* />
|
|
159
|
-
* </head>
|
|
160
|
-
* <body>
|
|
161
|
-
* <ReplaneScriptProvider connection={{ ... }}>
|
|
162
|
-
* {children}
|
|
163
|
-
* </ReplaneScriptProvider>
|
|
164
|
-
* </body>
|
|
165
|
-
* </html>
|
|
166
|
-
* );
|
|
167
|
-
* }
|
|
168
|
-
* ```
|
|
169
|
-
*/
|
|
170
|
-
declare function getReplaneSnapshotScript<T extends object = AnyConfig>(snapshot: ReplaneSnapshot$1<T>): string;
|
|
171
|
-
/**
|
|
172
|
-
* Props for ReplaneScriptProvider.
|
|
173
|
-
*/
|
|
174
|
-
interface ReplaneScriptProviderProps {
|
|
175
|
-
/**
|
|
176
|
-
* Connection options for real-time updates.
|
|
177
|
-
* If not provided, the client will only use the snapshot data (no live updates).
|
|
178
|
-
*/
|
|
179
|
-
connection?: ReplaneConnectionOptions;
|
|
180
|
-
/**
|
|
181
|
-
* Fallback to render while waiting for the snapshot.
|
|
182
|
-
* This should rarely be needed since the script is in the head.
|
|
183
|
-
*/
|
|
184
|
-
fallback?: ReactNode;
|
|
185
|
-
children: ReactNode;
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Provider that reads the snapshot from a script tag.
|
|
189
|
-
*
|
|
190
|
-
* Use this with `getReplaneSnapshotScript()` for an alternative hydration pattern
|
|
191
|
-
* where the snapshot is embedded in a script tag instead of passed as a prop.
|
|
192
|
-
*
|
|
193
|
-
* This pattern can be useful for:
|
|
194
|
-
* - Pages with heavy component trees where prop drilling is inconvenient
|
|
195
|
-
* - Partial hydration scenarios
|
|
196
|
-
* - When you want the snapshot to be available before React hydrates
|
|
197
|
-
*
|
|
198
|
-
* @example
|
|
199
|
-
* ```tsx
|
|
200
|
-
* // In app/layout.tsx (Server Component)
|
|
201
|
-
* <script dangerouslySetInnerHTML={{ __html: getReplaneSnapshotScript(snapshot) }} />
|
|
202
|
-
*
|
|
203
|
-
* // In a client component
|
|
204
|
-
* <ReplaneScriptProvider connection={{ baseUrl, sdkKey }}>
|
|
205
|
-
* <App />
|
|
206
|
-
* </ReplaneScriptProvider>
|
|
207
|
-
* ```
|
|
208
48
|
*/
|
|
209
|
-
declare function
|
|
210
|
-
|
|
211
|
-
fallback,
|
|
49
|
+
declare function ReplaneRoot<T extends object>({
|
|
50
|
+
options,
|
|
212
51
|
children
|
|
213
|
-
}:
|
|
52
|
+
}: ReplaneRootProps<T>): Promise<react_jsx_runtime0.JSX.Element>;
|
|
53
|
+
//# sourceMappingURL=root.d.ts.map
|
|
214
54
|
//#endregion
|
|
215
|
-
export { type ReplaneClient, type
|
|
55
|
+
export { type GetConfigOptions, type GetReplaneSnapshotOptions, type ReplaneClient, type ReplaneClientOptions, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, ReplaneRoot, type ReplaneRootProps, type ReplaneSnapshot, type RestoreReplaneClientOptions, clearSnapshotCache, clearSuspenseCache, createConfigHook, createInMemoryReplaneClient, createReplaneClient, createReplaneHook, getReplaneSnapshot, restoreReplaneClient, useConfig, useReplane };
|
|
216
56
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/root.tsx"],"sourcesContent":[],"mappings":";;;;;;;AAaA;;;AAKW,UALM,gBAKN,CAAA,UAAA,MAAA,CAAA,CAAA;EAAoB;AAIV;AA8BrB;;EAAiC,OAAqB,EAlC3C,sBAkC2C,CAlCtB,CAkCsB,CAAA;EAAO;;;EAA8B,QAAG,EA9BlF,SA8BkF;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAxE;;;GAAqD,iBAAiB,KAAE,QAAA,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,198 +1,44 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { Fragment, jsx } from "react/jsx-runtime";
|
|
1
|
+
import { ReplaneProvider, ReplaneProvider as ReplaneProvider$1, clearSnapshotCache, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, getReplaneSnapshot as getReplaneSnapshot$1, useConfig, useReplane } from "@replanejs/react";
|
|
2
|
+
import { ReplaneError, ReplaneErrorCode, createInMemoryReplaneClient, createReplaneClient, restoreReplaneClient } from "@replanejs/sdk";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
4
|
|
|
6
|
-
//#region src/
|
|
5
|
+
//#region src/root.tsx
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
7
|
+
* Server component that fetches Replane configs and provides them to the app.
|
|
8
|
+
* This is the simplest way to set up Replane in Next.js App Router.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
* 1. Restores the Replane client from a server-side snapshot instantly (no loading state)
|
|
12
|
-
* 2. Optionally connects to Replane for real-time updates
|
|
13
|
-
* 3. Preserves the client across re-renders for minimal latency
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
10
|
+
* @example Basic usage in layout.tsx
|
|
16
11
|
* ```tsx
|
|
17
12
|
* // app/layout.tsx
|
|
18
|
-
* import {
|
|
19
|
-
* import { ReplaneNextProvider } from "@replanejs/next";
|
|
20
|
-
*
|
|
21
|
-
* export default async function RootLayout({ children }) {
|
|
22
|
-
* const snapshot = await getReplaneSnapshot({
|
|
23
|
-
* baseUrl: process.env.REPLANE_BASE_URL!,
|
|
24
|
-
* sdkKey: process.env.REPLANE_SDK_KEY!,
|
|
25
|
-
* });
|
|
13
|
+
* import { ReplaneRoot } from "@replanejs/next";
|
|
26
14
|
*
|
|
15
|
+
* export default function RootLayout({ children }) {
|
|
27
16
|
* return (
|
|
28
17
|
* <html>
|
|
29
18
|
* <body>
|
|
30
|
-
* <
|
|
31
|
-
*
|
|
32
|
-
* connection={{
|
|
19
|
+
* <ReplaneRoot
|
|
20
|
+
* options={{
|
|
33
21
|
* baseUrl: process.env.NEXT_PUBLIC_REPLANE_BASE_URL!,
|
|
34
22
|
* sdkKey: process.env.NEXT_PUBLIC_REPLANE_SDK_KEY!,
|
|
35
23
|
* }}
|
|
36
24
|
* >
|
|
37
25
|
* {children}
|
|
38
|
-
* </
|
|
26
|
+
* </ReplaneRoot>
|
|
39
27
|
* </body>
|
|
40
28
|
* </html>
|
|
41
29
|
* );
|
|
42
30
|
* }
|
|
43
31
|
* ```
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```tsx
|
|
47
|
-
* // Without real-time updates (static snapshot only)
|
|
48
|
-
* <ReplaneNextProvider snapshot={snapshot}>
|
|
49
|
-
* {children}
|
|
50
|
-
* </ReplaneNextProvider>
|
|
51
|
-
* ```
|
|
52
32
|
*/
|
|
53
|
-
function
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const connectionKey = connection ? `${connection.baseUrl}:${connection.sdkKey}` : "no-connection";
|
|
58
|
-
const contextKey = context ? JSON.stringify(context) : "no-context";
|
|
59
|
-
return `${snapshotKey}:${connectionKey}:${contextKey}`;
|
|
60
|
-
}, [
|
|
33
|
+
async function ReplaneRoot({ options, children }) {
|
|
34
|
+
const snapshot = await getReplaneSnapshot$1(options);
|
|
35
|
+
return /* @__PURE__ */ jsx(ReplaneProvider$1, {
|
|
36
|
+
options,
|
|
61
37
|
snapshot,
|
|
62
|
-
connection,
|
|
63
|
-
context
|
|
64
|
-
]);
|
|
65
|
-
const client = useMemo(() => {
|
|
66
|
-
if (clientRef.current) {}
|
|
67
|
-
const newClient = restoreReplaneClient$1({
|
|
68
|
-
snapshot,
|
|
69
|
-
connection: connection ? {
|
|
70
|
-
baseUrl: connection.baseUrl,
|
|
71
|
-
sdkKey: connection.sdkKey,
|
|
72
|
-
requestTimeoutMs: connection.requestTimeoutMs,
|
|
73
|
-
retryDelayMs: connection.retryDelayMs,
|
|
74
|
-
inactivityTimeoutMs: connection.inactivityTimeoutMs
|
|
75
|
-
} : void 0,
|
|
76
|
-
context
|
|
77
|
-
});
|
|
78
|
-
clientRef.current = newClient;
|
|
79
|
-
return newClient;
|
|
80
|
-
}, [clientKey]);
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
return () => {
|
|
83
|
-
if (clientRef.current) {
|
|
84
|
-
clientRef.current.close();
|
|
85
|
-
clientRef.current = null;
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
}, []);
|
|
89
|
-
return /* @__PURE__ */ jsx(ReplaneProvider, {
|
|
90
|
-
client,
|
|
91
|
-
children
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
//#endregion
|
|
96
|
-
//#region src/script.tsx
|
|
97
|
-
const REPLANE_SNAPSHOT_KEY = "__REPLANE_SNAPSHOT__";
|
|
98
|
-
/**
|
|
99
|
-
* Generate the script content for embedding the snapshot.
|
|
100
|
-
*
|
|
101
|
-
* Use this in a Server Component to embed the snapshot in the page:
|
|
102
|
-
*
|
|
103
|
-
* @example
|
|
104
|
-
* ```tsx
|
|
105
|
-
* // app/layout.tsx
|
|
106
|
-
* import { getReplaneSnapshot } from "@replanejs/next/server";
|
|
107
|
-
* import { getReplaneSnapshotScript, ReplaneScriptProvider } from "@replanejs/next";
|
|
108
|
-
*
|
|
109
|
-
* export default async function RootLayout({ children }) {
|
|
110
|
-
* const snapshot = await getReplaneSnapshot({ ... });
|
|
111
|
-
*
|
|
112
|
-
* return (
|
|
113
|
-
* <html>
|
|
114
|
-
* <head>
|
|
115
|
-
* <script
|
|
116
|
-
* dangerouslySetInnerHTML={{
|
|
117
|
-
* __html: getReplaneSnapshotScript(snapshot),
|
|
118
|
-
* }}
|
|
119
|
-
* />
|
|
120
|
-
* </head>
|
|
121
|
-
* <body>
|
|
122
|
-
* <ReplaneScriptProvider connection={{ ... }}>
|
|
123
|
-
* {children}
|
|
124
|
-
* </ReplaneScriptProvider>
|
|
125
|
-
* </body>
|
|
126
|
-
* </html>
|
|
127
|
-
* );
|
|
128
|
-
* }
|
|
129
|
-
* ```
|
|
130
|
-
*/
|
|
131
|
-
function getReplaneSnapshotScript(snapshot) {
|
|
132
|
-
const json = JSON.stringify(snapshot).replace(/<\/script>/gi, "<\\/script>");
|
|
133
|
-
return `window.${REPLANE_SNAPSHOT_KEY}=${json};`;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Provider that reads the snapshot from a script tag.
|
|
137
|
-
*
|
|
138
|
-
* Use this with `getReplaneSnapshotScript()` for an alternative hydration pattern
|
|
139
|
-
* where the snapshot is embedded in a script tag instead of passed as a prop.
|
|
140
|
-
*
|
|
141
|
-
* This pattern can be useful for:
|
|
142
|
-
* - Pages with heavy component trees where prop drilling is inconvenient
|
|
143
|
-
* - Partial hydration scenarios
|
|
144
|
-
* - When you want the snapshot to be available before React hydrates
|
|
145
|
-
*
|
|
146
|
-
* @example
|
|
147
|
-
* ```tsx
|
|
148
|
-
* // In app/layout.tsx (Server Component)
|
|
149
|
-
* <script dangerouslySetInnerHTML={{ __html: getReplaneSnapshotScript(snapshot) }} />
|
|
150
|
-
*
|
|
151
|
-
* // In a client component
|
|
152
|
-
* <ReplaneScriptProvider connection={{ baseUrl, sdkKey }}>
|
|
153
|
-
* <App />
|
|
154
|
-
* </ReplaneScriptProvider>
|
|
155
|
-
* ```
|
|
156
|
-
*/
|
|
157
|
-
function ReplaneScriptProvider({ connection, fallback, children }) {
|
|
158
|
-
const [snapshot, setSnapshot] = useState(() => {
|
|
159
|
-
if (typeof window !== "undefined" && window[REPLANE_SNAPSHOT_KEY]) return window[REPLANE_SNAPSHOT_KEY];
|
|
160
|
-
return null;
|
|
161
|
-
});
|
|
162
|
-
const clientRef = useRef(null);
|
|
163
|
-
useEffect(() => {
|
|
164
|
-
if (!snapshot && typeof window !== "undefined" && window[REPLANE_SNAPSHOT_KEY]) setSnapshot(window[REPLANE_SNAPSHOT_KEY]);
|
|
165
|
-
}, [snapshot]);
|
|
166
|
-
const client = useMemo(() => {
|
|
167
|
-
if (!snapshot) return null;
|
|
168
|
-
const newClient = restoreReplaneClient$1({
|
|
169
|
-
snapshot,
|
|
170
|
-
connection: connection ? {
|
|
171
|
-
baseUrl: connection.baseUrl,
|
|
172
|
-
sdkKey: connection.sdkKey,
|
|
173
|
-
requestTimeoutMs: connection.requestTimeoutMs,
|
|
174
|
-
retryDelayMs: connection.retryDelayMs,
|
|
175
|
-
inactivityTimeoutMs: connection.inactivityTimeoutMs
|
|
176
|
-
} : void 0
|
|
177
|
-
});
|
|
178
|
-
clientRef.current = newClient;
|
|
179
|
-
return newClient;
|
|
180
|
-
}, [snapshot, connection]);
|
|
181
|
-
useEffect(() => {
|
|
182
|
-
return () => {
|
|
183
|
-
if (clientRef.current) {
|
|
184
|
-
clientRef.current.close();
|
|
185
|
-
clientRef.current = null;
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
}, []);
|
|
189
|
-
if (!client) return /* @__PURE__ */ jsx(Fragment, { children: fallback ?? null });
|
|
190
|
-
return /* @__PURE__ */ jsx(ReplaneProvider, {
|
|
191
|
-
client,
|
|
192
38
|
children
|
|
193
39
|
});
|
|
194
40
|
}
|
|
195
41
|
|
|
196
42
|
//#endregion
|
|
197
|
-
export {
|
|
43
|
+
export { ReplaneError, ReplaneErrorCode, ReplaneProvider, ReplaneRoot, clearSnapshotCache, clearSuspenseCache, createConfigHook, createInMemoryReplaneClient, createReplaneClient, createReplaneHook, getReplaneSnapshot, restoreReplaneClient, useConfig, useReplane };
|
|
198
44
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["snapshot: ReplaneSnapshot<T>"],"sources":["../src/provider.tsx","../src/script.tsx"],"sourcesContent":["\"use client\";\n\nimport { useMemo, useRef, useEffect } from \"react\";\nimport { ReplaneProvider } from \"@replanejs/react\";\nimport { restoreReplaneClient, type ReplaneClient } from \"@replanejs/sdk\";\nimport type { ReplaneNextProviderProps } from \"./types\";\n\n/**\n * Next.js-optimized Replane provider with SSR hydration support.\n *\n * This component:\n * 1. Restores the Replane client from a server-side snapshot instantly (no loading state)\n * 2. Optionally connects to Replane for real-time updates\n * 3. Preserves the client across re-renders for minimal latency\n *\n * @example\n * ```tsx\n * // app/layout.tsx\n * import { getReplaneSnapshot } from \"@replanejs/next/server\";\n * import { ReplaneNextProvider } from \"@replanejs/next\";\n *\n * export default async function RootLayout({ children }) {\n * const snapshot = await getReplaneSnapshot({\n * baseUrl: process.env.REPLANE_BASE_URL!,\n * sdkKey: process.env.REPLANE_SDK_KEY!,\n * });\n *\n * return (\n * <html>\n * <body>\n * <ReplaneNextProvider\n * snapshot={snapshot}\n * connection={{\n * baseUrl: process.env.NEXT_PUBLIC_REPLANE_BASE_URL!,\n * sdkKey: process.env.NEXT_PUBLIC_REPLANE_SDK_KEY!,\n * }}\n * >\n * {children}\n * </ReplaneNextProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Without real-time updates (static snapshot only)\n * <ReplaneNextProvider snapshot={snapshot}>\n * {children}\n * </ReplaneNextProvider>\n * ```\n */\nexport function ReplaneNextProvider<T extends object = Record<string, unknown>>({\n snapshot,\n connection,\n context,\n children,\n}: ReplaneNextProviderProps<T>) {\n // Use a ref to store the client to preserve it across re-renders\n // This is important for minimizing latency - we don't want to recreate\n // the client on every render\n const clientRef = useRef<ReplaneClient<T> | null>(null);\n\n // Create a stable key for the client based on snapshot and connection\n // We only recreate the client if these change\n const clientKey = useMemo(() => {\n const snapshotKey = JSON.stringify(snapshot.configs.map((c) => c.name).sort());\n const connectionKey = connection ? `${connection.baseUrl}:${connection.sdkKey}` : \"no-connection\";\n const contextKey = context ? JSON.stringify(context) : \"no-context\";\n return `${snapshotKey}:${connectionKey}:${contextKey}`;\n }, [snapshot, connection, context]);\n\n // Memoize client creation\n const client = useMemo(() => {\n // If we have a cached client with the same key, reuse it\n if (clientRef.current) {\n // Check if we need to create a new client\n // For simplicity, we always create a new client if the key changes\n // This happens when snapshot or connection changes\n }\n\n const newClient = restoreReplaneClient<T>({\n snapshot,\n connection: connection\n ? {\n baseUrl: connection.baseUrl,\n sdkKey: connection.sdkKey,\n requestTimeoutMs: connection.requestTimeoutMs,\n retryDelayMs: connection.retryDelayMs,\n inactivityTimeoutMs: connection.inactivityTimeoutMs,\n }\n : undefined,\n context,\n });\n\n clientRef.current = newClient;\n return newClient;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientKey]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (clientRef.current) {\n clientRef.current.close();\n clientRef.current = null;\n }\n };\n }, []);\n\n return <ReplaneProvider client={client}>{children}</ReplaneProvider>;\n}\n","\"use client\";\n\nimport { useEffect, useState, useMemo, useRef, type ReactNode } from \"react\";\nimport { ReplaneProvider } from \"@replanejs/react\";\nimport { restoreReplaneClient, type ReplaneSnapshot, type ReplaneClient } from \"@replanejs/sdk\";\nimport type { ReplaneConnectionOptions } from \"./types\";\n\n// Global variable name for the snapshot\nconst REPLANE_SNAPSHOT_KEY = \"__REPLANE_SNAPSHOT__\";\n\ntype AnyConfig = Record<string, unknown>;\n\ndeclare global {\n interface Window {\n [REPLANE_SNAPSHOT_KEY]?: ReplaneSnapshot<AnyConfig>;\n }\n}\n\n/**\n * Generate the script content for embedding the snapshot.\n *\n * Use this in a Server Component to embed the snapshot in the page:\n *\n * @example\n * ```tsx\n * // app/layout.tsx\n * import { getReplaneSnapshot } from \"@replanejs/next/server\";\n * import { getReplaneSnapshotScript, ReplaneScriptProvider } from \"@replanejs/next\";\n *\n * export default async function RootLayout({ children }) {\n * const snapshot = await getReplaneSnapshot({ ... });\n *\n * return (\n * <html>\n * <head>\n * <script\n * dangerouslySetInnerHTML={{\n * __html: getReplaneSnapshotScript(snapshot),\n * }}\n * />\n * </head>\n * <body>\n * <ReplaneScriptProvider connection={{ ... }}>\n * {children}\n * </ReplaneScriptProvider>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport function getReplaneSnapshotScript<T extends object = AnyConfig>(\n snapshot: ReplaneSnapshot<T>\n): string {\n // Escape script closing tags in JSON to prevent XSS\n const json = JSON.stringify(snapshot).replace(/<\\/script>/gi, \"<\\\\/script>\");\n return `window.${REPLANE_SNAPSHOT_KEY}=${json};`;\n}\n\n/**\n * Props for ReplaneScriptProvider.\n */\nexport interface ReplaneScriptProviderProps {\n /**\n * Connection options for real-time updates.\n * If not provided, the client will only use the snapshot data (no live updates).\n */\n connection?: ReplaneConnectionOptions;\n\n /**\n * Fallback to render while waiting for the snapshot.\n * This should rarely be needed since the script is in the head.\n */\n fallback?: ReactNode;\n\n children: ReactNode;\n}\n\n/**\n * Provider that reads the snapshot from a script tag.\n *\n * Use this with `getReplaneSnapshotScript()` for an alternative hydration pattern\n * where the snapshot is embedded in a script tag instead of passed as a prop.\n *\n * This pattern can be useful for:\n * - Pages with heavy component trees where prop drilling is inconvenient\n * - Partial hydration scenarios\n * - When you want the snapshot to be available before React hydrates\n *\n * @example\n * ```tsx\n * // In app/layout.tsx (Server Component)\n * <script dangerouslySetInnerHTML={{ __html: getReplaneSnapshotScript(snapshot) }} />\n *\n * // In a client component\n * <ReplaneScriptProvider connection={{ baseUrl, sdkKey }}>\n * <App />\n * </ReplaneScriptProvider>\n * ```\n */\nexport function ReplaneScriptProvider({\n connection,\n fallback,\n children,\n}: ReplaneScriptProviderProps) {\n const [snapshot, setSnapshot] = useState<ReplaneSnapshot<AnyConfig> | null>(() => {\n // Try to get snapshot from window on initial render (SSR-safe)\n if (typeof window !== \"undefined\" && window[REPLANE_SNAPSHOT_KEY]) {\n return window[REPLANE_SNAPSHOT_KEY];\n }\n return null;\n });\n\n const clientRef = useRef<ReplaneClient<AnyConfig> | null>(null);\n\n // Check for snapshot on mount (in case the script runs after initial render)\n useEffect(() => {\n if (!snapshot && typeof window !== \"undefined\" && window[REPLANE_SNAPSHOT_KEY]) {\n setSnapshot(window[REPLANE_SNAPSHOT_KEY]);\n }\n }, [snapshot]);\n\n // Create client from snapshot\n const client = useMemo(() => {\n if (!snapshot) return null;\n\n const newClient = restoreReplaneClient({\n snapshot,\n connection: connection\n ? {\n baseUrl: connection.baseUrl,\n sdkKey: connection.sdkKey,\n requestTimeoutMs: connection.requestTimeoutMs,\n retryDelayMs: connection.retryDelayMs,\n inactivityTimeoutMs: connection.inactivityTimeoutMs,\n }\n : undefined,\n });\n\n clientRef.current = newClient;\n return newClient;\n }, [snapshot, connection]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n if (clientRef.current) {\n clientRef.current.close();\n clientRef.current = null;\n }\n };\n }, []);\n\n if (!client) {\n return <>{fallback ?? null}</>;\n }\n\n return <ReplaneProvider client={client}>{children}</ReplaneProvider>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA,SAAgB,oBAAgE,EAC9E,UACA,YACA,SACA,UAC4B,EAAE;CAI9B,MAAM,YAAY,OAAgC,KAAK;CAIvD,MAAM,YAAY,QAAQ,MAAM;EAC9B,MAAM,cAAc,KAAK,UAAU,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;EAC9E,MAAM,gBAAgB,cAAc,EAAE,WAAW,QAAQ,GAAG,WAAW,OAAO,IAAI;EAClF,MAAM,aAAa,UAAU,KAAK,UAAU,QAAQ,GAAG;AACvD,UAAQ,EAAE,YAAY,GAAG,cAAc,GAAG,WAAW;CACtD,GAAE;EAAC;EAAU;EAAY;CAAQ,EAAC;CAGnC,MAAM,SAAS,QAAQ,MAAM;AAE3B,MAAI,UAAU,SAAS,CAItB;EAED,MAAM,YAAY,uBAAwB;GACxC;GACA,YAAY,aACR;IACE,SAAS,WAAW;IACpB,QAAQ,WAAW;IACnB,kBAAkB,WAAW;IAC7B,cAAc,WAAW;IACzB,qBAAqB,WAAW;GACjC;GAEL;EACD,EAAC;AAEF,YAAU,UAAU;AACpB,SAAO;CAER,GAAE,CAAC,SAAU,EAAC;AAGf,WAAU,MAAM;AACd,SAAO,MAAM;AACX,OAAI,UAAU,SAAS;AACrB,cAAU,QAAQ,OAAO;AACzB,cAAU,UAAU;GACrB;EACF;CACF,GAAE,CAAE,EAAC;AAEN,wBAAO,IAAC;EAAwB;EAAS;GAA2B;AACrE;;;;ACxGD,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2C7B,SAAgB,yBACdA,UACQ;CAER,MAAM,OAAO,KAAK,UAAU,SAAS,CAAC,QAAQ,gBAAgB,cAAc;AAC5E,SAAQ,SAAS,qBAAqB,GAAG,KAAK;AAC/C;;;;;;;;;;;;;;;;;;;;;;;AA2CD,SAAgB,sBAAsB,EACpC,YACA,UACA,UAC2B,EAAE;CAC7B,MAAM,CAAC,UAAU,YAAY,GAAG,SAA4C,MAAM;AAEhF,aAAW,WAAW,eAAe,OAAO,sBAC1C,QAAO,OAAO;AAEhB,SAAO;CACR,EAAC;CAEF,MAAM,YAAY,OAAwC,KAAK;AAG/D,WAAU,MAAM;AACd,OAAK,mBAAmB,WAAW,eAAe,OAAO,sBACvD,aAAY,OAAO,sBAAsB;CAE5C,GAAE,CAAC,QAAS,EAAC;CAGd,MAAM,SAAS,QAAQ,MAAM;AAC3B,OAAK,SAAU,QAAO;EAEtB,MAAM,YAAY,uBAAqB;GACrC;GACA,YAAY,aACR;IACE,SAAS,WAAW;IACpB,QAAQ,WAAW;IACnB,kBAAkB,WAAW;IAC7B,cAAc,WAAW;IACzB,qBAAqB,WAAW;GACjC;EAEN,EAAC;AAEF,YAAU,UAAU;AACpB,SAAO;CACR,GAAE,CAAC,UAAU,UAAW,EAAC;AAG1B,WAAU,MAAM;AACd,SAAO,MAAM;AACX,OAAI,UAAU,SAAS;AACrB,cAAU,QAAQ,OAAO;AACzB,cAAU,UAAU;GACrB;EACF;CACF,GAAE,CAAE,EAAC;AAEN,MAAK,OACH,wBAAO,0BAAG,YAAY,OAAQ;AAGhC,wBAAO,IAAC;EAAwB;EAAS;GAA2B;AACrE"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["ReplaneProvider"],"sources":["../src/root.tsx"],"sourcesContent":["/**\n * Server-side utilities for Next.js\n * Use this module in Server Components, getServerSideProps, or getStaticProps\n */\n\nimport type { ReactNode } from \"react\";\nimport { getReplaneSnapshot } from \"@replanejs/react\";\nimport { type ReplaneClientOptions } from \"@replanejs/sdk\";\nimport { ReplaneProvider } from \"@replanejs/react\";\n\n/**\n * Props for ReplaneRoot server component\n */\nexport interface ReplaneRootProps<T extends object> {\n /**\n * Options for Replane client.\n * Used for both server-side fetching and client-side live updates.\n */\n options: ReplaneClientOptions<T>;\n /**\n * React children to render inside the provider\n */\n children: ReactNode;\n}\n\n/**\n * Server component that fetches Replane configs and provides them to the app.\n * This is the simplest way to set up Replane in Next.js App Router.\n *\n * @example Basic usage in layout.tsx\n * ```tsx\n * // app/layout.tsx\n * import { ReplaneRoot } from \"@replanejs/next\";\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <ReplaneRoot\n * options={{\n * baseUrl: process.env.NEXT_PUBLIC_REPLANE_BASE_URL!,\n * sdkKey: process.env.NEXT_PUBLIC_REPLANE_SDK_KEY!,\n * }}\n * >\n * {children}\n * </ReplaneRoot>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nexport async function ReplaneRoot<T extends object>({ options, children }: ReplaneRootProps<T>) {\n const snapshot = await getReplaneSnapshot(options);\n\n return (\n <ReplaneProvider options={options} snapshot={snapshot}>\n {children}\n </ReplaneProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,eAAsB,YAA8B,EAAE,SAAS,UAA+B,EAAE;CAC9F,MAAM,WAAW,MAAM,qBAAmB,QAAQ;AAElD,wBACE,IAACA;EAAyB;EAAmB;EAC1C;GACe;AAErB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replanejs/next",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Next.js SDK for Replane - feature flags and remote configuration with SSR support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -11,11 +11,6 @@
|
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js",
|
|
13
13
|
"require": "./dist/index.cjs"
|
|
14
|
-
},
|
|
15
|
-
"./server": {
|
|
16
|
-
"types": "./dist/server.d.ts",
|
|
17
|
-
"import": "./dist/server.js",
|
|
18
|
-
"require": "./dist/server.cjs"
|
|
19
14
|
}
|
|
20
15
|
},
|
|
21
16
|
"files": [
|
|
@@ -26,13 +21,13 @@
|
|
|
26
21
|
"replane",
|
|
27
22
|
"next",
|
|
28
23
|
"nextjs",
|
|
29
|
-
"react",
|
|
30
24
|
"feature-flags",
|
|
31
25
|
"remote-config",
|
|
32
26
|
"configuration",
|
|
33
27
|
"sdk",
|
|
34
28
|
"ssr",
|
|
35
|
-
"
|
|
29
|
+
"app-router",
|
|
30
|
+
"pages-router"
|
|
36
31
|
],
|
|
37
32
|
"author": "",
|
|
38
33
|
"license": "MIT",
|
|
@@ -50,16 +45,13 @@
|
|
|
50
45
|
"react": ">=18.0.0"
|
|
51
46
|
},
|
|
52
47
|
"dependencies": {
|
|
53
|
-
"@replanejs/react": "^0.7.
|
|
54
|
-
"@replanejs/sdk": "^0.7.
|
|
48
|
+
"@replanejs/react": "^0.7.5",
|
|
49
|
+
"@replanejs/sdk": "^0.7.5"
|
|
55
50
|
},
|
|
56
51
|
"devDependencies": {
|
|
57
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
58
|
-
"@testing-library/react": "^16.3.1",
|
|
59
52
|
"@types/node": "^22.19.3",
|
|
60
53
|
"@types/react": "^18.2.0",
|
|
61
54
|
"@types/react-dom": "^18.3.7",
|
|
62
|
-
"jsdom": "^27.3.0",
|
|
63
55
|
"next": "^15.0.0",
|
|
64
56
|
"react": "^18.2.0",
|
|
65
57
|
"react-dom": "^18.3.1",
|
|
@@ -77,7 +69,7 @@
|
|
|
77
69
|
"build": "tsdown",
|
|
78
70
|
"dev": "tsdown --watch",
|
|
79
71
|
"typecheck": "tsc --noEmit",
|
|
80
|
-
"test": "
|
|
81
|
-
"test:watch": "
|
|
72
|
+
"test": "echo 'No tests yet'",
|
|
73
|
+
"test:watch": "echo 'No tests yet'"
|
|
82
74
|
}
|
|
83
75
|
}
|
package/dist/chunk-CUT6urMc.cjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
//#region rolldown:runtime
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
-
key = keys[i];
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
-
get: ((k) => from[k]).bind(null, key),
|
|
13
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
-
value: mod,
|
|
20
|
-
enumerable: true
|
|
21
|
-
}) : target, mod));
|
|
22
|
-
|
|
23
|
-
//#endregion
|
|
24
|
-
|
|
25
|
-
Object.defineProperty(exports, '__toESM', {
|
|
26
|
-
enumerable: true,
|
|
27
|
-
get: function () {
|
|
28
|
-
return __toESM;
|
|
29
|
-
}
|
|
30
|
-
});
|