@replanejs/react 0.9.0 → 0.9.2
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 +41 -14
- package/dist/index.cjs +46 -63
- package/dist/index.d.cts +14 -69
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +14 -69
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -69
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ import { ReplaneProvider, useConfig } from "@replanejs/react";
|
|
|
29
29
|
function App() {
|
|
30
30
|
return (
|
|
31
31
|
<ReplaneProvider
|
|
32
|
-
|
|
32
|
+
connection={{
|
|
33
33
|
baseUrl: "https://your-replane-server.com",
|
|
34
34
|
sdkKey: "your-sdk-key",
|
|
35
35
|
}}
|
|
@@ -51,9 +51,9 @@ function MyComponent() {
|
|
|
51
51
|
|
|
52
52
|
### ReplaneProvider
|
|
53
53
|
|
|
54
|
-
Provider component that makes the Replane client available to your component tree. Supports
|
|
54
|
+
Provider component that makes the Replane client available to your component tree. Supports multiple usage patterns:
|
|
55
55
|
|
|
56
|
-
#### 1. With
|
|
56
|
+
#### 1. With connection (recommended)
|
|
57
57
|
|
|
58
58
|
The provider creates and manages the client internally. Use an Error Boundary to handle initialization errors:
|
|
59
59
|
|
|
@@ -62,7 +62,7 @@ import { ErrorBoundary } from "react-error-boundary";
|
|
|
62
62
|
|
|
63
63
|
<ErrorBoundary fallback={<div>Failed to load configuration</div>}>
|
|
64
64
|
<ReplaneProvider
|
|
65
|
-
|
|
65
|
+
connection={{
|
|
66
66
|
baseUrl: "https://your-replane-server.com",
|
|
67
67
|
sdkKey: "your-sdk-key",
|
|
68
68
|
}}
|
|
@@ -73,22 +73,32 @@ import { ErrorBoundary } from "react-error-boundary";
|
|
|
73
73
|
</ErrorBoundary>;
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
####
|
|
76
|
+
#### Provider Props
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
| Prop | Type | Required | Description |
|
|
79
|
+
| ------------ | --------------------------- | -------- | ------------------------------------------------------- |
|
|
80
|
+
| `connection` | `ConnectOptions \| null` | Yes | Connection options (see below), or `null` to skip connection |
|
|
81
|
+
| `defaults` | `Record<string, unknown>` | No | Default values if server is unavailable |
|
|
82
|
+
| `context` | `Record<string, unknown>` | No | Default context for override evaluations |
|
|
83
|
+
| `snapshot` | `ReplaneSnapshot` | No | Snapshot for SSR hydration |
|
|
84
|
+
| `logger` | `ReplaneLogger` | No | Custom logger (default: console) |
|
|
85
|
+
| `loader` | `ReactNode` | No | Component to show while loading |
|
|
86
|
+
| `suspense` | `boolean` | No | Use React Suspense for loading state |
|
|
87
|
+
| `async` | `boolean` | No | Connect asynchronously (renders immediately with defaults) |
|
|
88
|
+
|
|
89
|
+
#### Connection Options
|
|
90
|
+
|
|
91
|
+
The `connection` prop accepts the following options:
|
|
79
92
|
|
|
80
93
|
| Option | Type | Required | Description |
|
|
81
94
|
| -------------------- | --------------------- | -------- | -------------------------------------------- |
|
|
82
95
|
| `baseUrl` | `string` | Yes | Replane server URL |
|
|
83
96
|
| `sdkKey` | `string` | Yes | SDK key for authentication |
|
|
84
|
-
| `context` | `Record<string, any>` | No | Default context for override evaluations |
|
|
85
|
-
| `defaults` | `Record<string, any>` | No | Default values if server is unavailable |
|
|
86
97
|
| `connectTimeoutMs` | `number` | No | SDK connection timeout (default: 5000) |
|
|
87
98
|
| `requestTimeoutMs` | `number` | No | Timeout for SSE requests (default: 2000) |
|
|
88
99
|
| `retryDelayMs` | `number` | No | Base delay between retries (default: 200) |
|
|
89
100
|
| `inactivityTimeoutMs`| `number` | No | SSE inactivity timeout (default: 30000) |
|
|
90
101
|
| `fetchFn` | `typeof fetch` | No | Custom fetch implementation |
|
|
91
|
-
| `logger` | `ReplaneLogger` | No | Custom logger (default: console) |
|
|
92
102
|
|
|
93
103
|
See [`@replanejs/sdk` documentation](https://github.com/replane-dev/replane-javascript/tree/main/packages/sdk#api) for more details.
|
|
94
104
|
|
|
@@ -118,7 +128,7 @@ Integrates with React Suspense for loading states:
|
|
|
118
128
|
<ErrorBoundary fallback={<div>Failed to load configuration</div>}>
|
|
119
129
|
<Suspense fallback={<LoadingSpinner />}>
|
|
120
130
|
<ReplaneProvider
|
|
121
|
-
|
|
131
|
+
connection={{
|
|
122
132
|
baseUrl: "https://your-replane-server.com",
|
|
123
133
|
sdkKey: "your-sdk-key",
|
|
124
134
|
}}
|
|
@@ -130,7 +140,24 @@ Integrates with React Suspense for loading states:
|
|
|
130
140
|
</ErrorBoundary>
|
|
131
141
|
```
|
|
132
142
|
|
|
133
|
-
#### 4. With
|
|
143
|
+
#### 4. With async mode
|
|
144
|
+
|
|
145
|
+
Connect in the background while rendering immediately with defaults:
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<ReplaneProvider
|
|
149
|
+
connection={{
|
|
150
|
+
baseUrl: "https://your-replane-server.com",
|
|
151
|
+
sdkKey: "your-sdk-key",
|
|
152
|
+
}}
|
|
153
|
+
defaults={{ featureEnabled: false }}
|
|
154
|
+
async
|
|
155
|
+
>
|
|
156
|
+
<App />
|
|
157
|
+
</ReplaneProvider>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### 5. With snapshot (for SSR/hydration)
|
|
134
161
|
|
|
135
162
|
Restore a client from a snapshot obtained on the server. This is synchronous and useful for SSR scenarios:
|
|
136
163
|
|
|
@@ -143,7 +170,7 @@ const snapshot = serverClient.getSnapshot();
|
|
|
143
170
|
|
|
144
171
|
// On the client
|
|
145
172
|
<ReplaneProvider
|
|
146
|
-
|
|
173
|
+
connection={{
|
|
147
174
|
baseUrl: "https://your-replane-server.com",
|
|
148
175
|
sdkKey: "your-sdk-key",
|
|
149
176
|
}}
|
|
@@ -353,7 +380,7 @@ class ErrorBoundary extends Component<
|
|
|
353
380
|
|
|
354
381
|
// Usage
|
|
355
382
|
<ErrorBoundary fallback={<div>Configuration failed to load</div>}>
|
|
356
|
-
<ReplaneProvider
|
|
383
|
+
<ReplaneProvider connection={connection} loader={<Loading />}>
|
|
357
384
|
<App />
|
|
358
385
|
</ReplaneProvider>
|
|
359
386
|
</ErrorBoundary>;
|
|
@@ -373,7 +400,7 @@ import { ErrorBoundary } from "react-error-boundary";
|
|
|
373
400
|
)}
|
|
374
401
|
onReset={() => clearSuspenseCache()}
|
|
375
402
|
>
|
|
376
|
-
<ReplaneProvider
|
|
403
|
+
<ReplaneProvider connection={connection} loader={<Loading />}>
|
|
377
404
|
<App />
|
|
378
405
|
</ReplaneProvider>
|
|
379
406
|
</ErrorBoundary>;
|
package/dist/index.cjs
CHANGED
|
@@ -31,11 +31,6 @@ const react_jsx_runtime = __toESM(require("react/jsx-runtime"));
|
|
|
31
31
|
//#region src/context.ts
|
|
32
32
|
const ReplaneContext = (0, react.createContext)(null);
|
|
33
33
|
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region src/version.ts
|
|
36
|
-
const VERSION = "0.9.0";
|
|
37
|
-
const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
|
|
38
|
-
|
|
39
34
|
//#endregion
|
|
40
35
|
//#region src/useReplaneClient.ts
|
|
41
36
|
const suspenseCache = new Map();
|
|
@@ -45,29 +40,21 @@ function getCacheKey(options) {
|
|
|
45
40
|
/**
|
|
46
41
|
* Creates a Replane client and connects it.
|
|
47
42
|
*/
|
|
48
|
-
async function createAndConnectClient(options) {
|
|
43
|
+
async function createAndConnectClient(options, connection) {
|
|
49
44
|
const client = new __replanejs_sdk.Replane({
|
|
50
45
|
logger: options.logger,
|
|
51
46
|
context: options.context,
|
|
52
47
|
defaults: options.defaults
|
|
53
48
|
});
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
sdkKey: options.sdkKey,
|
|
57
|
-
connectTimeoutMs: options.connectTimeoutMs,
|
|
58
|
-
retryDelayMs: options.retryDelayMs,
|
|
59
|
-
requestTimeoutMs: options.requestTimeoutMs,
|
|
60
|
-
inactivityTimeoutMs: options.inactivityTimeoutMs,
|
|
61
|
-
fetchFn: options.fetchFn,
|
|
62
|
-
agent: options.agent ?? DEFAULT_AGENT
|
|
63
|
-
});
|
|
49
|
+
if (!connection) return client;
|
|
50
|
+
await client.connect(connection);
|
|
64
51
|
return client;
|
|
65
52
|
}
|
|
66
53
|
/**
|
|
67
54
|
* Hook to manage Replane client creation internally.
|
|
68
55
|
* Handles loading state and cleanup.
|
|
69
56
|
*/
|
|
70
|
-
function useReplaneClientInternal(options) {
|
|
57
|
+
function useReplaneClientInternal(options, originalConnection) {
|
|
71
58
|
const [state, setState] = (0, react.useState)({
|
|
72
59
|
status: "loading",
|
|
73
60
|
client: null,
|
|
@@ -75,11 +62,13 @@ function useReplaneClientInternal(options) {
|
|
|
75
62
|
});
|
|
76
63
|
const clientRef = (0, react.useRef)(null);
|
|
77
64
|
const optionsRef = (0, react.useRef)(options);
|
|
65
|
+
const connectionJson = JSON.stringify(originalConnection);
|
|
78
66
|
(0, react.useEffect)(() => {
|
|
67
|
+
const connection = JSON.parse(connectionJson);
|
|
79
68
|
let cancelled = false;
|
|
80
69
|
async function initClient() {
|
|
81
70
|
try {
|
|
82
|
-
const client = await createAndConnectClient(optionsRef.current);
|
|
71
|
+
const client = await createAndConnectClient(optionsRef.current, connection);
|
|
83
72
|
if (cancelled) {
|
|
84
73
|
client.disconnect();
|
|
85
74
|
return;
|
|
@@ -108,22 +97,22 @@ function useReplaneClientInternal(options) {
|
|
|
108
97
|
clientRef.current = null;
|
|
109
98
|
}
|
|
110
99
|
};
|
|
111
|
-
}, []);
|
|
100
|
+
}, [connectionJson]);
|
|
112
101
|
return state;
|
|
113
102
|
}
|
|
114
103
|
/**
|
|
115
104
|
* Hook for Suspense-based client creation.
|
|
116
105
|
* Throws a promise while loading, throws error on failure.
|
|
117
106
|
*/
|
|
118
|
-
function useReplaneClientSuspense(options) {
|
|
119
|
-
const cacheKey = getCacheKey(
|
|
107
|
+
function useReplaneClientSuspense(options, connection) {
|
|
108
|
+
const cacheKey = getCacheKey(connection);
|
|
120
109
|
const cached = suspenseCache.get(cacheKey);
|
|
121
110
|
if (cached) {
|
|
122
111
|
if (cached.error) throw cached.error;
|
|
123
112
|
if (cached.result) return cached.result;
|
|
124
113
|
throw cached.promise;
|
|
125
114
|
}
|
|
126
|
-
const promise = createAndConnectClient(options).then((client) => {
|
|
115
|
+
const promise = createAndConnectClient(options, connection).then((client) => {
|
|
127
116
|
const entry = suspenseCache.get(cacheKey);
|
|
128
117
|
if (entry) entry.result = client;
|
|
129
118
|
return client;
|
|
@@ -136,11 +125,11 @@ function useReplaneClientSuspense(options) {
|
|
|
136
125
|
throw promise;
|
|
137
126
|
}
|
|
138
127
|
/**
|
|
139
|
-
* Clear the suspense cache for a specific
|
|
128
|
+
* Clear the suspense cache for a specific connection configuration.
|
|
140
129
|
* Useful for testing or when you need to force re-initialization.
|
|
141
130
|
*/
|
|
142
|
-
function clearSuspenseCache(
|
|
143
|
-
if (
|
|
131
|
+
function clearSuspenseCache(connection) {
|
|
132
|
+
if (connection) suspenseCache.delete(getCacheKey(connection));
|
|
144
133
|
else suspenseCache.clear();
|
|
145
134
|
}
|
|
146
135
|
|
|
@@ -150,9 +139,14 @@ function clearSuspenseCache(options) {
|
|
|
150
139
|
* Type guard to check if props contain a pre-created client.
|
|
151
140
|
*/
|
|
152
141
|
function hasClient(props) {
|
|
153
|
-
return "client" in props && props.client
|
|
142
|
+
return "client" in props && !!props.client;
|
|
154
143
|
}
|
|
155
144
|
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/version.ts
|
|
147
|
+
const VERSION = "0.9.2";
|
|
148
|
+
const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
|
|
149
|
+
|
|
156
150
|
//#endregion
|
|
157
151
|
//#region src/provider.tsx
|
|
158
152
|
/**
|
|
@@ -166,44 +160,23 @@ function ReplaneProviderWithClient({ client, children }) {
|
|
|
166
160
|
});
|
|
167
161
|
}
|
|
168
162
|
/**
|
|
169
|
-
* Internal provider component for
|
|
163
|
+
* Internal provider component for creating a Replane client asynchronously.
|
|
170
164
|
* Creates a Replane client synchronously and connects in background.
|
|
171
165
|
*/
|
|
172
|
-
function
|
|
166
|
+
function AsyncReplaneProvider({ children, connection,...options }) {
|
|
173
167
|
const replaneRef = (0, react.useRef)(void 0);
|
|
174
|
-
if (!replaneRef.current) replaneRef.current = new __replanejs_sdk.Replane(
|
|
175
|
-
|
|
176
|
-
logger: options.logger,
|
|
177
|
-
context: options.context,
|
|
178
|
-
defaults: options.defaults
|
|
179
|
-
});
|
|
168
|
+
if (!replaneRef.current) replaneRef.current = new __replanejs_sdk.Replane(options);
|
|
169
|
+
const connectionJson = connection ? JSON.stringify(connection) : void 0;
|
|
180
170
|
(0, react.useEffect)(() => {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
connectTimeoutMs: options.connectTimeoutMs,
|
|
185
|
-
retryDelayMs: options.retryDelayMs,
|
|
186
|
-
requestTimeoutMs: options.requestTimeoutMs,
|
|
187
|
-
inactivityTimeoutMs: options.inactivityTimeoutMs,
|
|
188
|
-
fetchFn: options.fetchFn,
|
|
189
|
-
agent: options.agent ?? DEFAULT_AGENT
|
|
190
|
-
}).catch((err) => {
|
|
171
|
+
const parsedConnection = connectionJson ? JSON.parse(connectionJson) : void 0;
|
|
172
|
+
if (!parsedConnection) return;
|
|
173
|
+
replaneRef.current.connect(parsedConnection).catch((err) => {
|
|
191
174
|
(options.logger ?? console)?.error("Failed to connect Replane client", err);
|
|
192
175
|
});
|
|
193
176
|
return () => {
|
|
194
177
|
replaneRef.current.disconnect();
|
|
195
178
|
};
|
|
196
|
-
}, [
|
|
197
|
-
options.agent,
|
|
198
|
-
options.baseUrl,
|
|
199
|
-
options.connectTimeoutMs,
|
|
200
|
-
options.fetchFn,
|
|
201
|
-
options.inactivityTimeoutMs,
|
|
202
|
-
options.logger,
|
|
203
|
-
options.requestTimeoutMs,
|
|
204
|
-
options.retryDelayMs,
|
|
205
|
-
options.sdkKey
|
|
206
|
-
]);
|
|
179
|
+
}, [connectionJson, options.logger]);
|
|
207
180
|
const value = (0, react.useMemo)(() => ({ replane: replaneRef.current }), []);
|
|
208
181
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
|
|
209
182
|
value,
|
|
@@ -214,8 +187,9 @@ function ReplaneProviderWithSnapshot({ options, snapshot, children }) {
|
|
|
214
187
|
* Internal provider component for options-based client creation (non-suspense).
|
|
215
188
|
* Throws errors during rendering so they can be caught by Error Boundaries.
|
|
216
189
|
*/
|
|
217
|
-
function
|
|
218
|
-
|
|
190
|
+
function LoaderReplaneProvider({ children, loader, connection,...options }) {
|
|
191
|
+
if (!connection) throw new Error("Connection is required when using Loader");
|
|
192
|
+
const state = useReplaneClientInternal(options, connection);
|
|
219
193
|
if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: loader ?? null });
|
|
220
194
|
if (state.status === "error") throw state.error;
|
|
221
195
|
const value = { replane: state.client };
|
|
@@ -227,8 +201,9 @@ function ReplaneProviderWithOptions({ options, children, loader }) {
|
|
|
227
201
|
/**
|
|
228
202
|
* Internal provider component for options-based client creation with Suspense.
|
|
229
203
|
*/
|
|
230
|
-
function
|
|
231
|
-
|
|
204
|
+
function SuspenseReplaneProvider({ connection, children,...options }) {
|
|
205
|
+
if (!connection) throw new Error("Connection is required when using Suspense");
|
|
206
|
+
const client = useReplaneClientSuspense(options, connection);
|
|
232
207
|
const value = (0, react.useMemo)(() => ({ replane: client }), [client]);
|
|
233
208
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
|
|
234
209
|
value,
|
|
@@ -293,13 +268,21 @@ function ReplaneProviderWithSuspense({ options, children }) {
|
|
|
293
268
|
* allowing them to be caught by React Error Boundaries.
|
|
294
269
|
*/
|
|
295
270
|
function ReplaneProvider(props) {
|
|
271
|
+
const originalConnection = props.connection;
|
|
272
|
+
const connection = (0, react.useMemo)(() => originalConnection ? {
|
|
273
|
+
...originalConnection,
|
|
274
|
+
agent: originalConnection.agent ?? DEFAULT_AGENT
|
|
275
|
+
} : void 0, [originalConnection]);
|
|
296
276
|
if (hasClient(props)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneProviderWithClient, { ...props });
|
|
297
|
-
if (props.snapshot) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
277
|
+
if (props.snapshot || !connection || props.async) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AsyncReplaneProvider, { ...props });
|
|
278
|
+
if (props.suspense) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SuspenseReplaneProvider, {
|
|
279
|
+
...props,
|
|
280
|
+
connection
|
|
281
|
+
});
|
|
282
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderReplaneProvider, {
|
|
298
283
|
...props,
|
|
299
|
-
|
|
284
|
+
connection
|
|
300
285
|
});
|
|
301
|
-
if (props.suspense) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneProviderWithSuspense, { ...props });
|
|
302
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneProviderWithOptions, { ...props });
|
|
303
286
|
}
|
|
304
287
|
|
|
305
288
|
//#endregion
|
package/dist/index.d.cts
CHANGED
|
@@ -1,66 +1,9 @@
|
|
|
1
1
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
-
import { ConnectOptions, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, Replane, Replane as Replane$1, ReplaneContext,
|
|
2
|
+
import { ConnectOptions, ConnectOptions as ConnectOptions$1, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, Replane, Replane as Replane$1, ReplaneContext, ReplaneError, ReplaneErrorCode, ReplaneLogger, ReplaneOptions, ReplaneOptions as ReplaneOptions$1, ReplaneSnapshot, getReplaneSnapshot } from "@replanejs/sdk";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
type UntypedReplaneConfig = Record<string, unknown>;
|
|
7
|
-
/**
|
|
8
|
-
* Combined options for ReplaneProvider.
|
|
9
|
-
* Includes both constructor options (context, logger, defaults) and connection options.
|
|
10
|
-
*/
|
|
11
|
-
interface ReplaneProviderOptions<T extends object = UntypedReplaneConfig> {
|
|
12
|
-
/**
|
|
13
|
-
* Base URL of the Replane instance (no trailing slash).
|
|
14
|
-
* @example "https://app.replane.dev"
|
|
15
|
-
*/
|
|
16
|
-
baseUrl: string;
|
|
17
|
-
/**
|
|
18
|
-
* Project SDK key for authorization.
|
|
19
|
-
* @example "rp_XXXXXXXXX"
|
|
20
|
-
*/
|
|
21
|
-
sdkKey: string;
|
|
22
|
-
/**
|
|
23
|
-
* Default context for all config evaluations.
|
|
24
|
-
* Can be overridden per-request in `client.get()`.
|
|
25
|
-
*/
|
|
26
|
-
context?: ReplaneContext$1;
|
|
27
|
-
/**
|
|
28
|
-
* Optional logger (defaults to console).
|
|
29
|
-
*/
|
|
30
|
-
logger?: ReplaneLogger$1;
|
|
31
|
-
/**
|
|
32
|
-
* Default values to use before connection is established.
|
|
33
|
-
*/
|
|
34
|
-
defaults?: { [K in keyof T]?: T[K] };
|
|
35
|
-
/**
|
|
36
|
-
* Optional timeout in ms for the initial connection.
|
|
37
|
-
* @default 5000
|
|
38
|
-
*/
|
|
39
|
-
connectTimeoutMs?: number;
|
|
40
|
-
/**
|
|
41
|
-
* Delay between retries in ms.
|
|
42
|
-
* @default 200
|
|
43
|
-
*/
|
|
44
|
-
retryDelayMs?: number;
|
|
45
|
-
/**
|
|
46
|
-
* Optional timeout in ms for individual requests.
|
|
47
|
-
* @default 2000
|
|
48
|
-
*/
|
|
49
|
-
requestTimeoutMs?: number;
|
|
50
|
-
/**
|
|
51
|
-
* Timeout in ms for SSE connection inactivity.
|
|
52
|
-
* @default 30000
|
|
53
|
-
*/
|
|
54
|
-
inactivityTimeoutMs?: number;
|
|
55
|
-
/**
|
|
56
|
-
* Custom fetch implementation (useful for tests / polyfills).
|
|
57
|
-
*/
|
|
58
|
-
fetchFn?: typeof fetch;
|
|
59
|
-
/**
|
|
60
|
-
* Agent identifier sent in User-Agent header.
|
|
61
|
-
*/
|
|
62
|
-
agent?: string;
|
|
63
|
-
}
|
|
64
7
|
/**
|
|
65
8
|
* Props for ReplaneProvider when using a pre-created client.
|
|
66
9
|
*/
|
|
@@ -72,17 +15,13 @@ interface ReplaneProviderWithClientProps<T extends object = UntypedReplaneConfig
|
|
|
72
15
|
/**
|
|
73
16
|
* Props for ReplaneProvider when letting it manage the client internally.
|
|
74
17
|
*/
|
|
75
|
-
interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfig> {
|
|
76
|
-
/** Options to create or restore the Replane client */
|
|
77
|
-
options: ReplaneProviderOptions<T>;
|
|
18
|
+
interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfig> extends ReplaneOptions$1<T> {
|
|
78
19
|
children: ReactNode;
|
|
79
20
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* instead of fetching configs from the server.
|
|
83
|
-
* The `options` will be used for live updates connection if provided.
|
|
21
|
+
* Connection options for connecting to the Replane server.
|
|
22
|
+
* Pass null to explicitly skip connection (client will use defaults/snapshot only).
|
|
84
23
|
*/
|
|
85
|
-
|
|
24
|
+
connection: ConnectOptions$1 | null;
|
|
86
25
|
/**
|
|
87
26
|
* Optional loading component to show while the client is initializing.
|
|
88
27
|
* If not provided and suspense is false/undefined, children will not render until ready.
|
|
@@ -96,11 +35,17 @@ interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfi
|
|
|
96
35
|
* @default false
|
|
97
36
|
*/
|
|
98
37
|
suspense?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* If true, the client will be connected asynchronously. Make sure to provide defaults or snapshot.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
async?: boolean;
|
|
99
43
|
}
|
|
100
44
|
type ReplaneProviderProps<T extends object = UntypedReplaneConfig> = ReplaneProviderWithClientProps<T> | ReplaneProviderWithOptionsProps<T>;
|
|
101
45
|
/**
|
|
102
46
|
* Type guard to check if props contain a pre-created client.
|
|
103
47
|
*/
|
|
48
|
+
|
|
104
49
|
//#endregion
|
|
105
50
|
//#region src/provider.d.ts
|
|
106
51
|
/**
|
|
@@ -208,10 +153,10 @@ declare function createConfigHook<TConfigs extends object>(): <K extends keyof T
|
|
|
208
153
|
//#endregion
|
|
209
154
|
//#region src/useReplaneClient.d.ts
|
|
210
155
|
/**
|
|
211
|
-
* Clear the suspense cache for a specific
|
|
156
|
+
* Clear the suspense cache for a specific connection configuration.
|
|
212
157
|
* Useful for testing or when you need to force re-initialization.
|
|
213
158
|
*/
|
|
214
|
-
declare function clearSuspenseCache
|
|
159
|
+
declare function clearSuspenseCache(connection?: ConnectOptions$1): void;
|
|
215
160
|
//#endregion
|
|
216
|
-
export { type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type
|
|
161
|
+
export { type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, type ReplaneSnapshot, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
|
|
217
162
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/provider.tsx","../src/hooks.ts","../src/useReplaneClient.ts"],"sourcesContent":[],"mappings":";;;;;KAGY,oBAAA,GAAuB;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/provider.tsx","../src/hooks.ts","../src/useReplaneClient.ts"],"sourcesContent":[],"mappings":";;;;;KAGY,oBAAA,GAAuB;AASnC;;;AAEkB,UAFD,8BAEC,CAAA,UAAA,MAAA,GAFiD,oBAEjD,CAAA,CAAA;EAAC;EAAF,MACL,EADF,SACE,CADM,CACN,CAAA;EAAS,QAAA,EAAT,SAAS;AAMrB;;;;AAGY,UAHK,+BAGL,CAAA,UAAA,MAAA,GAFS,oBAET,CAAA,SADF,gBACE,CADa,CACb,CAAA,CAAA;EAAS,QAKP,EALF,SAKE;EAAc;;AANJ;AA2BxB;EAAgC,UAAA,EArBlB,gBAqBkB,GAAA,IAAA;EAAA;;;;;EAEG,MAAA,CAAA,EAjBxB,SAiBwB;;;;AC+GnC;;;EAA+E,QAAtB,CAAA,EAAA,OAAA;EAAoB;AAAG;;;;AC5JhF;AAA0B,KF2Cd,oBE3Cc,CAAA,UAAA,MAAA,GF2C0B,oBE3C1B,CAAA,GF4CtB,8BE5CsB,CF4CS,CE5CT,CAAA,GF6CtB,+BE7CsB,CF6CU,CE7CV,CAAA;;;;;;;;;;;AFJ1B;AASA;;;;;;AAGqB;AAMrB;;;;;;;;AAEwB;AA2BxB;;;;;;;AAEmC;;;;AC+GnC;;;;;AAAgF;;;;AC5JhF;;;;;AAA8E;AAQ9E;;;;;AAA4E;AAqC5E;;;;AAC4C,iBD8G5B,eC9G4B,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,ED8Ga,oBC9Gb,CD8GkC,CC9GlC,CAAA,CAAA,ED8GoC,kBAAA,CAAA,GAAA,CAAA,OC9GpC;AAuB5C;;;iBArEgB,8BAA8B,yBAAyB,UAAQ;iBAQ/D,qCAAqC,mBAAiB,KAAK;;AFZ3E;AASA;;;;;;AAGqB;AAMrB;;;;;;;;AAEwB;AA2BZ,iBEEI,iBFFgB,CAAA,iBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,GAAA,GEGK,SFHL,CEGa,QFHb,CAAA;;;;;;;AAEG;;;;AC+GnC;;;;;AAAgF;;;iBCvFhE,8DACiC,gBACvC,aACI,mBAAiB,SAAS,QACnC,SAAS;AAzEd;;;;;;;AF6CI,iBGmGY,kBAAA,CHnGZ,UAAA,CAAA,EGmG4C,gBHnG5C,CAAA,EAAA,IAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,66 +1,9 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
-
import { ConnectOptions, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, Replane, Replane as Replane$1, ReplaneContext,
|
|
2
|
+
import { ConnectOptions, ConnectOptions as ConnectOptions$1, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, Replane, Replane as Replane$1, ReplaneContext, ReplaneError, ReplaneErrorCode, ReplaneLogger, ReplaneOptions, ReplaneOptions as ReplaneOptions$1, ReplaneSnapshot, getReplaneSnapshot } from "@replanejs/sdk";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
6
6
|
type UntypedReplaneConfig = Record<string, unknown>;
|
|
7
|
-
/**
|
|
8
|
-
* Combined options for ReplaneProvider.
|
|
9
|
-
* Includes both constructor options (context, logger, defaults) and connection options.
|
|
10
|
-
*/
|
|
11
|
-
interface ReplaneProviderOptions<T extends object = UntypedReplaneConfig> {
|
|
12
|
-
/**
|
|
13
|
-
* Base URL of the Replane instance (no trailing slash).
|
|
14
|
-
* @example "https://app.replane.dev"
|
|
15
|
-
*/
|
|
16
|
-
baseUrl: string;
|
|
17
|
-
/**
|
|
18
|
-
* Project SDK key for authorization.
|
|
19
|
-
* @example "rp_XXXXXXXXX"
|
|
20
|
-
*/
|
|
21
|
-
sdkKey: string;
|
|
22
|
-
/**
|
|
23
|
-
* Default context for all config evaluations.
|
|
24
|
-
* Can be overridden per-request in `client.get()`.
|
|
25
|
-
*/
|
|
26
|
-
context?: ReplaneContext$1;
|
|
27
|
-
/**
|
|
28
|
-
* Optional logger (defaults to console).
|
|
29
|
-
*/
|
|
30
|
-
logger?: ReplaneLogger$1;
|
|
31
|
-
/**
|
|
32
|
-
* Default values to use before connection is established.
|
|
33
|
-
*/
|
|
34
|
-
defaults?: { [K in keyof T]?: T[K] };
|
|
35
|
-
/**
|
|
36
|
-
* Optional timeout in ms for the initial connection.
|
|
37
|
-
* @default 5000
|
|
38
|
-
*/
|
|
39
|
-
connectTimeoutMs?: number;
|
|
40
|
-
/**
|
|
41
|
-
* Delay between retries in ms.
|
|
42
|
-
* @default 200
|
|
43
|
-
*/
|
|
44
|
-
retryDelayMs?: number;
|
|
45
|
-
/**
|
|
46
|
-
* Optional timeout in ms for individual requests.
|
|
47
|
-
* @default 2000
|
|
48
|
-
*/
|
|
49
|
-
requestTimeoutMs?: number;
|
|
50
|
-
/**
|
|
51
|
-
* Timeout in ms for SSE connection inactivity.
|
|
52
|
-
* @default 30000
|
|
53
|
-
*/
|
|
54
|
-
inactivityTimeoutMs?: number;
|
|
55
|
-
/**
|
|
56
|
-
* Custom fetch implementation (useful for tests / polyfills).
|
|
57
|
-
*/
|
|
58
|
-
fetchFn?: typeof fetch;
|
|
59
|
-
/**
|
|
60
|
-
* Agent identifier sent in User-Agent header.
|
|
61
|
-
*/
|
|
62
|
-
agent?: string;
|
|
63
|
-
}
|
|
64
7
|
/**
|
|
65
8
|
* Props for ReplaneProvider when using a pre-created client.
|
|
66
9
|
*/
|
|
@@ -72,17 +15,13 @@ interface ReplaneProviderWithClientProps<T extends object = UntypedReplaneConfig
|
|
|
72
15
|
/**
|
|
73
16
|
* Props for ReplaneProvider when letting it manage the client internally.
|
|
74
17
|
*/
|
|
75
|
-
interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfig> {
|
|
76
|
-
/** Options to create or restore the Replane client */
|
|
77
|
-
options: ReplaneProviderOptions<T>;
|
|
18
|
+
interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfig> extends ReplaneOptions$1<T> {
|
|
78
19
|
children: ReactNode;
|
|
79
20
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* instead of fetching configs from the server.
|
|
83
|
-
* The `options` will be used for live updates connection if provided.
|
|
21
|
+
* Connection options for connecting to the Replane server.
|
|
22
|
+
* Pass null to explicitly skip connection (client will use defaults/snapshot only).
|
|
84
23
|
*/
|
|
85
|
-
|
|
24
|
+
connection: ConnectOptions$1 | null;
|
|
86
25
|
/**
|
|
87
26
|
* Optional loading component to show while the client is initializing.
|
|
88
27
|
* If not provided and suspense is false/undefined, children will not render until ready.
|
|
@@ -96,11 +35,17 @@ interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfi
|
|
|
96
35
|
* @default false
|
|
97
36
|
*/
|
|
98
37
|
suspense?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* If true, the client will be connected asynchronously. Make sure to provide defaults or snapshot.
|
|
40
|
+
* @default false
|
|
41
|
+
*/
|
|
42
|
+
async?: boolean;
|
|
99
43
|
}
|
|
100
44
|
type ReplaneProviderProps<T extends object = UntypedReplaneConfig> = ReplaneProviderWithClientProps<T> | ReplaneProviderWithOptionsProps<T>;
|
|
101
45
|
/**
|
|
102
46
|
* Type guard to check if props contain a pre-created client.
|
|
103
47
|
*/
|
|
48
|
+
|
|
104
49
|
//#endregion
|
|
105
50
|
//#region src/provider.d.ts
|
|
106
51
|
/**
|
|
@@ -208,10 +153,10 @@ declare function createConfigHook<TConfigs extends object>(): <K extends keyof T
|
|
|
208
153
|
//#endregion
|
|
209
154
|
//#region src/useReplaneClient.d.ts
|
|
210
155
|
/**
|
|
211
|
-
* Clear the suspense cache for a specific
|
|
156
|
+
* Clear the suspense cache for a specific connection configuration.
|
|
212
157
|
* Useful for testing or when you need to force re-initialization.
|
|
213
158
|
*/
|
|
214
|
-
declare function clearSuspenseCache
|
|
159
|
+
declare function clearSuspenseCache(connection?: ConnectOptions$1): void;
|
|
215
160
|
//#endregion
|
|
216
|
-
export { type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type
|
|
161
|
+
export { type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, type ReplaneSnapshot, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
|
|
217
162
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/provider.tsx","../src/hooks.ts","../src/useReplaneClient.ts"],"sourcesContent":[],"mappings":";;;;;KAGY,oBAAA,GAAuB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/provider.tsx","../src/hooks.ts","../src/useReplaneClient.ts"],"sourcesContent":[],"mappings":";;;;;KAGY,oBAAA,GAAuB;AASnC;;;AAEkB,UAFD,8BAEC,CAAA,UAAA,MAAA,GAFiD,oBAEjD,CAAA,CAAA;EAAC;EAAF,MACL,EADF,SACE,CADM,CACN,CAAA;EAAS,QAAA,EAAT,SAAS;AAMrB;;;;AAGY,UAHK,+BAGL,CAAA,UAAA,MAAA,GAFS,oBAET,CAAA,SADF,gBACE,CADa,CACb,CAAA,CAAA;EAAS,QAKP,EALF,SAKE;EAAc;;AANJ;AA2BxB;EAAgC,UAAA,EArBlB,gBAqBkB,GAAA,IAAA;EAAA;;;;;EAEG,MAAA,CAAA,EAjBxB,SAiBwB;;;;AC+GnC;;;EAA+E,QAAtB,CAAA,EAAA,OAAA;EAAoB;AAAG;;;;AC5JhF;AAA0B,KF2Cd,oBE3Cc,CAAA,UAAA,MAAA,GF2C0B,oBE3C1B,CAAA,GF4CtB,8BE5CsB,CF4CS,CE5CT,CAAA,GF6CtB,+BE7CsB,CF6CU,CE7CV,CAAA;;;;;;;;;;;AFJ1B;AASA;;;;;;AAGqB;AAMrB;;;;;;;;AAEwB;AA2BxB;;;;;;;AAEmC;;;;AC+GnC;;;;;AAAgF;;;;AC5JhF;;;;;AAA8E;AAQ9E;;;;;AAA4E;AAqC5E;;;;AAC4C,iBD8G5B,eC9G4B,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,ED8Ga,oBC9Gb,CD8GkC,CC9GlC,CAAA,CAAA,ED8GoC,kBAAA,CAAA,GAAA,CAAA,OC9GpC;AAuB5C;;;iBArEgB,8BAA8B,yBAAyB,UAAQ;iBAQ/D,qCAAqC,mBAAiB,KAAK;;AFZ3E;AASA;;;;;;AAGqB;AAMrB;;;;;;;;AAEwB;AA2BZ,iBEEI,iBFFgB,CAAA,iBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,GAAA,GEGK,SFHL,CEGa,QFHb,CAAA;;;;;;;AAEG;;;;AC+GnC;;;;;AAAgF;;;iBCvFhE,8DACiC,gBACvC,aACI,mBAAiB,SAAS,QACnC,SAAS;AAzEd;;;;;;;AF6CI,iBGmGY,kBAAA,CHnGZ,UAAA,CAAA,EGmG4C,gBHnG5C,CAAA,EAAA,IAAA"}
|
package/dist/index.js
CHANGED
|
@@ -6,12 +6,7 @@ import { Replane, Replane as Replane$1, ReplaneError, ReplaneErrorCode, getRepla
|
|
|
6
6
|
import { Fragment, jsx } from "react/jsx-runtime";
|
|
7
7
|
|
|
8
8
|
//#region src/context.ts
|
|
9
|
-
const ReplaneContext
|
|
10
|
-
|
|
11
|
-
//#endregion
|
|
12
|
-
//#region src/version.ts
|
|
13
|
-
const VERSION = "0.9.0";
|
|
14
|
-
const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
|
|
9
|
+
const ReplaneContext = createContext(null);
|
|
15
10
|
|
|
16
11
|
//#endregion
|
|
17
12
|
//#region src/useReplaneClient.ts
|
|
@@ -22,29 +17,21 @@ function getCacheKey(options) {
|
|
|
22
17
|
/**
|
|
23
18
|
* Creates a Replane client and connects it.
|
|
24
19
|
*/
|
|
25
|
-
async function createAndConnectClient(options) {
|
|
20
|
+
async function createAndConnectClient(options, connection) {
|
|
26
21
|
const client = new Replane$1({
|
|
27
22
|
logger: options.logger,
|
|
28
23
|
context: options.context,
|
|
29
24
|
defaults: options.defaults
|
|
30
25
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
sdkKey: options.sdkKey,
|
|
34
|
-
connectTimeoutMs: options.connectTimeoutMs,
|
|
35
|
-
retryDelayMs: options.retryDelayMs,
|
|
36
|
-
requestTimeoutMs: options.requestTimeoutMs,
|
|
37
|
-
inactivityTimeoutMs: options.inactivityTimeoutMs,
|
|
38
|
-
fetchFn: options.fetchFn,
|
|
39
|
-
agent: options.agent ?? DEFAULT_AGENT
|
|
40
|
-
});
|
|
26
|
+
if (!connection) return client;
|
|
27
|
+
await client.connect(connection);
|
|
41
28
|
return client;
|
|
42
29
|
}
|
|
43
30
|
/**
|
|
44
31
|
* Hook to manage Replane client creation internally.
|
|
45
32
|
* Handles loading state and cleanup.
|
|
46
33
|
*/
|
|
47
|
-
function useReplaneClientInternal(options) {
|
|
34
|
+
function useReplaneClientInternal(options, originalConnection) {
|
|
48
35
|
const [state, setState] = useState({
|
|
49
36
|
status: "loading",
|
|
50
37
|
client: null,
|
|
@@ -52,11 +39,13 @@ function useReplaneClientInternal(options) {
|
|
|
52
39
|
});
|
|
53
40
|
const clientRef = useRef(null);
|
|
54
41
|
const optionsRef = useRef(options);
|
|
42
|
+
const connectionJson = JSON.stringify(originalConnection);
|
|
55
43
|
useEffect(() => {
|
|
44
|
+
const connection = JSON.parse(connectionJson);
|
|
56
45
|
let cancelled = false;
|
|
57
46
|
async function initClient() {
|
|
58
47
|
try {
|
|
59
|
-
const client = await createAndConnectClient(optionsRef.current);
|
|
48
|
+
const client = await createAndConnectClient(optionsRef.current, connection);
|
|
60
49
|
if (cancelled) {
|
|
61
50
|
client.disconnect();
|
|
62
51
|
return;
|
|
@@ -85,22 +74,22 @@ function useReplaneClientInternal(options) {
|
|
|
85
74
|
clientRef.current = null;
|
|
86
75
|
}
|
|
87
76
|
};
|
|
88
|
-
}, []);
|
|
77
|
+
}, [connectionJson]);
|
|
89
78
|
return state;
|
|
90
79
|
}
|
|
91
80
|
/**
|
|
92
81
|
* Hook for Suspense-based client creation.
|
|
93
82
|
* Throws a promise while loading, throws error on failure.
|
|
94
83
|
*/
|
|
95
|
-
function useReplaneClientSuspense(options) {
|
|
96
|
-
const cacheKey = getCacheKey(
|
|
84
|
+
function useReplaneClientSuspense(options, connection) {
|
|
85
|
+
const cacheKey = getCacheKey(connection);
|
|
97
86
|
const cached = suspenseCache.get(cacheKey);
|
|
98
87
|
if (cached) {
|
|
99
88
|
if (cached.error) throw cached.error;
|
|
100
89
|
if (cached.result) return cached.result;
|
|
101
90
|
throw cached.promise;
|
|
102
91
|
}
|
|
103
|
-
const promise = createAndConnectClient(options).then((client) => {
|
|
92
|
+
const promise = createAndConnectClient(options, connection).then((client) => {
|
|
104
93
|
const entry = suspenseCache.get(cacheKey);
|
|
105
94
|
if (entry) entry.result = client;
|
|
106
95
|
return client;
|
|
@@ -113,11 +102,11 @@ function useReplaneClientSuspense(options) {
|
|
|
113
102
|
throw promise;
|
|
114
103
|
}
|
|
115
104
|
/**
|
|
116
|
-
* Clear the suspense cache for a specific
|
|
105
|
+
* Clear the suspense cache for a specific connection configuration.
|
|
117
106
|
* Useful for testing or when you need to force re-initialization.
|
|
118
107
|
*/
|
|
119
|
-
function clearSuspenseCache(
|
|
120
|
-
if (
|
|
108
|
+
function clearSuspenseCache(connection) {
|
|
109
|
+
if (connection) suspenseCache.delete(getCacheKey(connection));
|
|
121
110
|
else suspenseCache.clear();
|
|
122
111
|
}
|
|
123
112
|
|
|
@@ -127,9 +116,14 @@ function clearSuspenseCache(options) {
|
|
|
127
116
|
* Type guard to check if props contain a pre-created client.
|
|
128
117
|
*/
|
|
129
118
|
function hasClient(props) {
|
|
130
|
-
return "client" in props && props.client
|
|
119
|
+
return "client" in props && !!props.client;
|
|
131
120
|
}
|
|
132
121
|
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/version.ts
|
|
124
|
+
const VERSION = "0.9.2";
|
|
125
|
+
const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
|
|
126
|
+
|
|
133
127
|
//#endregion
|
|
134
128
|
//#region src/provider.tsx
|
|
135
129
|
/**
|
|
@@ -137,52 +131,31 @@ function hasClient(props) {
|
|
|
137
131
|
*/
|
|
138
132
|
function ReplaneProviderWithClient({ client, children }) {
|
|
139
133
|
const value = useMemo(() => ({ replane: client }), [client]);
|
|
140
|
-
return /* @__PURE__ */ jsx(ReplaneContext
|
|
134
|
+
return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
|
|
141
135
|
value,
|
|
142
136
|
children
|
|
143
137
|
});
|
|
144
138
|
}
|
|
145
139
|
/**
|
|
146
|
-
* Internal provider component for
|
|
140
|
+
* Internal provider component for creating a Replane client asynchronously.
|
|
147
141
|
* Creates a Replane client synchronously and connects in background.
|
|
148
142
|
*/
|
|
149
|
-
function
|
|
143
|
+
function AsyncReplaneProvider({ children, connection,...options }) {
|
|
150
144
|
const replaneRef = useRef(void 0);
|
|
151
|
-
if (!replaneRef.current) replaneRef.current = new Replane$1(
|
|
152
|
-
|
|
153
|
-
logger: options.logger,
|
|
154
|
-
context: options.context,
|
|
155
|
-
defaults: options.defaults
|
|
156
|
-
});
|
|
145
|
+
if (!replaneRef.current) replaneRef.current = new Replane$1(options);
|
|
146
|
+
const connectionJson = connection ? JSON.stringify(connection) : void 0;
|
|
157
147
|
useEffect(() => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
connectTimeoutMs: options.connectTimeoutMs,
|
|
162
|
-
retryDelayMs: options.retryDelayMs,
|
|
163
|
-
requestTimeoutMs: options.requestTimeoutMs,
|
|
164
|
-
inactivityTimeoutMs: options.inactivityTimeoutMs,
|
|
165
|
-
fetchFn: options.fetchFn,
|
|
166
|
-
agent: options.agent ?? DEFAULT_AGENT
|
|
167
|
-
}).catch((err) => {
|
|
148
|
+
const parsedConnection = connectionJson ? JSON.parse(connectionJson) : void 0;
|
|
149
|
+
if (!parsedConnection) return;
|
|
150
|
+
replaneRef.current.connect(parsedConnection).catch((err) => {
|
|
168
151
|
(options.logger ?? console)?.error("Failed to connect Replane client", err);
|
|
169
152
|
});
|
|
170
153
|
return () => {
|
|
171
154
|
replaneRef.current.disconnect();
|
|
172
155
|
};
|
|
173
|
-
}, [
|
|
174
|
-
options.agent,
|
|
175
|
-
options.baseUrl,
|
|
176
|
-
options.connectTimeoutMs,
|
|
177
|
-
options.fetchFn,
|
|
178
|
-
options.inactivityTimeoutMs,
|
|
179
|
-
options.logger,
|
|
180
|
-
options.requestTimeoutMs,
|
|
181
|
-
options.retryDelayMs,
|
|
182
|
-
options.sdkKey
|
|
183
|
-
]);
|
|
156
|
+
}, [connectionJson, options.logger]);
|
|
184
157
|
const value = useMemo(() => ({ replane: replaneRef.current }), []);
|
|
185
|
-
return /* @__PURE__ */ jsx(ReplaneContext
|
|
158
|
+
return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
|
|
186
159
|
value,
|
|
187
160
|
children
|
|
188
161
|
});
|
|
@@ -191,12 +164,13 @@ function ReplaneProviderWithSnapshot({ options, snapshot, children }) {
|
|
|
191
164
|
* Internal provider component for options-based client creation (non-suspense).
|
|
192
165
|
* Throws errors during rendering so they can be caught by Error Boundaries.
|
|
193
166
|
*/
|
|
194
|
-
function
|
|
195
|
-
|
|
167
|
+
function LoaderReplaneProvider({ children, loader, connection,...options }) {
|
|
168
|
+
if (!connection) throw new Error("Connection is required when using Loader");
|
|
169
|
+
const state = useReplaneClientInternal(options, connection);
|
|
196
170
|
if (state.status === "loading") return /* @__PURE__ */ jsx(Fragment, { children: loader ?? null });
|
|
197
171
|
if (state.status === "error") throw state.error;
|
|
198
172
|
const value = { replane: state.client };
|
|
199
|
-
return /* @__PURE__ */ jsx(ReplaneContext
|
|
173
|
+
return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
|
|
200
174
|
value,
|
|
201
175
|
children
|
|
202
176
|
});
|
|
@@ -204,10 +178,11 @@ function ReplaneProviderWithOptions({ options, children, loader }) {
|
|
|
204
178
|
/**
|
|
205
179
|
* Internal provider component for options-based client creation with Suspense.
|
|
206
180
|
*/
|
|
207
|
-
function
|
|
208
|
-
|
|
181
|
+
function SuspenseReplaneProvider({ connection, children,...options }) {
|
|
182
|
+
if (!connection) throw new Error("Connection is required when using Suspense");
|
|
183
|
+
const client = useReplaneClientSuspense(options, connection);
|
|
209
184
|
const value = useMemo(() => ({ replane: client }), [client]);
|
|
210
|
-
return /* @__PURE__ */ jsx(ReplaneContext
|
|
185
|
+
return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
|
|
211
186
|
value,
|
|
212
187
|
children
|
|
213
188
|
});
|
|
@@ -270,19 +245,27 @@ function ReplaneProviderWithSuspense({ options, children }) {
|
|
|
270
245
|
* allowing them to be caught by React Error Boundaries.
|
|
271
246
|
*/
|
|
272
247
|
function ReplaneProvider(props) {
|
|
248
|
+
const originalConnection = props.connection;
|
|
249
|
+
const connection = useMemo(() => originalConnection ? {
|
|
250
|
+
...originalConnection,
|
|
251
|
+
agent: originalConnection.agent ?? DEFAULT_AGENT
|
|
252
|
+
} : void 0, [originalConnection]);
|
|
273
253
|
if (hasClient(props)) return /* @__PURE__ */ jsx(ReplaneProviderWithClient, { ...props });
|
|
274
|
-
if (props.snapshot) return /* @__PURE__ */ jsx(
|
|
254
|
+
if (props.snapshot || !connection || props.async) return /* @__PURE__ */ jsx(AsyncReplaneProvider, { ...props });
|
|
255
|
+
if (props.suspense) return /* @__PURE__ */ jsx(SuspenseReplaneProvider, {
|
|
256
|
+
...props,
|
|
257
|
+
connection
|
|
258
|
+
});
|
|
259
|
+
return /* @__PURE__ */ jsx(LoaderReplaneProvider, {
|
|
275
260
|
...props,
|
|
276
|
-
|
|
261
|
+
connection
|
|
277
262
|
});
|
|
278
|
-
if (props.suspense) return /* @__PURE__ */ jsx(ReplaneProviderWithSuspense, { ...props });
|
|
279
|
-
return /* @__PURE__ */ jsx(ReplaneProviderWithOptions, { ...props });
|
|
280
263
|
}
|
|
281
264
|
|
|
282
265
|
//#endregion
|
|
283
266
|
//#region src/hooks.ts
|
|
284
267
|
function useReplane() {
|
|
285
|
-
const context = useContext(ReplaneContext
|
|
268
|
+
const context = useContext(ReplaneContext);
|
|
286
269
|
if (!context) throw new Error("useReplane must be used within a ReplaneProvider");
|
|
287
270
|
return context.replane;
|
|
288
271
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["ReplaneContext","options: ReplaneProviderOptions<T>","Replane","err: unknown","options?: ReplaneProviderOptions<T>","props: ReplaneProviderProps<T>","ReplaneContext","Replane","value: ReplaneContextValue<T>","props: ReplaneProviderProps<T>","ReplaneContext","name: string","options?: GetConfigOptions<T>","callback: () => void","name: K","options?: GetConfigOptions<TConfigs[K]>"],"sources":["../src/context.ts","../src/version.ts","../src/useReplaneClient.ts","../src/types.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext } from \"react\";\nimport type { ReplaneContextValue } from \"./types\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const ReplaneContext = createContext<ReplaneContextValue<any> | null>(null);\n","// Auto-generated - do not edit manually\nexport const VERSION = \"0.9.0\";\nexport const DEFAULT_AGENT = `replane-js-react/${VERSION}`;\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { Replane } from \"@replanejs/sdk\";\nimport { DEFAULT_AGENT } from \"./version\";\nimport type { ReplaneProviderOptions } from \"./types\";\n\ntype ClientState<T extends object> =\n | { status: \"loading\"; client: null; error: null }\n | { status: \"ready\"; client: Replane<T>; error: null }\n | { status: \"error\"; client: null; error: Error };\n\n// Cache for suspense promise tracking\nconst suspenseCache = new Map<\n string,\n {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n promise: Promise<Replane<any>>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result?: Replane<any>;\n error?: Error;\n }\n>();\n\nfunction getCacheKey<T extends object>(options: ReplaneProviderOptions<T>): string {\n return `${options.baseUrl}:${options.sdkKey}`;\n}\n\n/**\n * Creates a Replane client and connects it.\n */\nasync function createAndConnectClient<T extends object>(\n options: ReplaneProviderOptions<T>\n): Promise<Replane<T>> {\n const client = new Replane<T>({\n logger: options.logger,\n context: options.context,\n defaults: options.defaults,\n });\n\n await client.connect({\n baseUrl: options.baseUrl,\n sdkKey: options.sdkKey,\n connectTimeoutMs: options.connectTimeoutMs,\n retryDelayMs: options.retryDelayMs,\n requestTimeoutMs: options.requestTimeoutMs,\n inactivityTimeoutMs: options.inactivityTimeoutMs,\n fetchFn: options.fetchFn,\n agent: options.agent ?? DEFAULT_AGENT,\n });\n\n return client;\n}\n\ntype ErrorConstructor = new (message: string, options?: { cause?: unknown }) => Error;\n\n/**\n * Hook to manage Replane client creation internally.\n * Handles loading state and cleanup.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useReplaneClientInternal<T extends object = any>(\n options: ReplaneProviderOptions<T>\n): ClientState<T> {\n const [state, setState] = useState<ClientState<T>>({\n status: \"loading\",\n client: null,\n error: null,\n });\n const clientRef = useRef<Replane<T> | null>(null);\n const optionsRef = useRef(options);\n\n useEffect(() => {\n let cancelled = false;\n\n async function initClient() {\n try {\n const client = await createAndConnectClient<T>(optionsRef.current);\n if (cancelled) {\n client.disconnect();\n return;\n }\n clientRef.current = client;\n setState({ status: \"ready\", client, error: null });\n } catch (err) {\n if (cancelled) return;\n const error =\n err instanceof Error ? err : new (Error as ErrorConstructor)(String(err), { cause: err });\n setState({ status: \"error\", client: null, error });\n }\n }\n\n initClient();\n\n return () => {\n cancelled = true;\n if (clientRef.current) {\n clientRef.current.disconnect();\n clientRef.current = null;\n }\n };\n }, []);\n\n return state;\n}\n\n/**\n * Hook for Suspense-based client creation.\n * Throws a promise while loading, throws error on failure.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useReplaneClientSuspense<T extends object = any>(\n options: ReplaneProviderOptions<T>\n): Replane<T> {\n const cacheKey = getCacheKey(options);\n const cached = suspenseCache.get(cacheKey);\n\n if (cached) {\n if (cached.error) {\n throw cached.error;\n }\n if (cached.result) {\n return cached.result as Replane<T>;\n }\n // Still loading, throw the promise\n throw cached.promise;\n }\n\n // First time - create the promise\n const promise = createAndConnectClient<T>(options)\n .then((client) => {\n const entry = suspenseCache.get(cacheKey);\n if (entry) {\n entry.result = client;\n }\n return client;\n })\n .catch((err: unknown) => {\n const entry = suspenseCache.get(cacheKey);\n if (entry) {\n entry.error = err instanceof Error ? err : new Error(String(err));\n }\n throw err;\n });\n\n suspenseCache.set(cacheKey, { promise });\n throw promise;\n}\n\n/**\n * Clear the suspense cache for a specific options configuration.\n * Useful for testing or when you need to force re-initialization.\n */\nexport function clearSuspenseCache<T extends object>(options?: ReplaneProviderOptions<T>): void {\n if (options) {\n suspenseCache.delete(getCacheKey(options));\n } else {\n suspenseCache.clear();\n }\n}\n","import type { Replane, ReplaneSnapshot, ReplaneContext, ReplaneLogger } from \"@replanejs/sdk\";\nimport type { ReactNode } from \"react\";\n\nexport type UntypedReplaneConfig = Record<string, unknown>;\n\nexport interface ReplaneContextValue<T extends object = UntypedReplaneConfig> {\n replane: Replane<T>;\n}\n\n/**\n * Combined options for ReplaneProvider.\n * Includes both constructor options (context, logger, defaults) and connection options.\n */\nexport interface ReplaneProviderOptions<T extends object = UntypedReplaneConfig> {\n /**\n * Base URL of the Replane instance (no trailing slash).\n * @example \"https://app.replane.dev\"\n */\n baseUrl: string;\n /**\n * Project SDK key for authorization.\n * @example \"rp_XXXXXXXXX\"\n */\n sdkKey: string;\n /**\n * Default context for all config evaluations.\n * Can be overridden per-request in `client.get()`.\n */\n context?: ReplaneContext;\n /**\n * Optional logger (defaults to console).\n */\n logger?: ReplaneLogger;\n /**\n * Default values to use before connection is established.\n */\n defaults?: { [K in keyof T]?: T[K] };\n /**\n * Optional timeout in ms for the initial connection.\n * @default 5000\n */\n connectTimeoutMs?: number;\n /**\n * Delay between retries in ms.\n * @default 200\n */\n retryDelayMs?: number;\n /**\n * Optional timeout in ms for individual requests.\n * @default 2000\n */\n requestTimeoutMs?: number;\n /**\n * Timeout in ms for SSE connection inactivity.\n * @default 30000\n */\n inactivityTimeoutMs?: number;\n /**\n * Custom fetch implementation (useful for tests / polyfills).\n */\n fetchFn?: typeof fetch;\n /**\n * Agent identifier sent in User-Agent header.\n */\n agent?: string;\n}\n\n/**\n * Props for ReplaneProvider when using a pre-created client.\n */\nexport interface ReplaneProviderWithClientProps<T extends object = UntypedReplaneConfig> {\n /** Pre-created Replane client instance */\n client: Replane<T>;\n children: ReactNode;\n}\n\n/**\n * Props for ReplaneProvider when letting it manage the client internally.\n */\nexport interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfig> {\n /** Options to create or restore the Replane client */\n options: ReplaneProviderOptions<T>;\n children: ReactNode;\n /**\n * Optional snapshot from server-side rendering.\n * When provided, the client will be restored from the snapshot synchronously\n * instead of fetching configs from the server.\n * The `options` will be used for live updates connection if provided.\n */\n snapshot?: ReplaneSnapshot<T>;\n /**\n * Optional loading component to show while the client is initializing.\n * If not provided and suspense is false/undefined, children will not render until ready.\n * Ignored when snapshot is provided (restoration is synchronous).\n */\n loader?: ReactNode;\n /**\n * If true, uses React Suspense for loading state.\n * The provider will throw a promise that Suspense can catch.\n * Ignored when snapshot is provided (restoration is synchronous).\n * @default false\n */\n suspense?: boolean;\n}\n\nexport type ReplaneProviderProps<T extends object = UntypedReplaneConfig> =\n | ReplaneProviderWithClientProps<T>\n | ReplaneProviderWithOptionsProps<T>;\n\n/**\n * Type guard to check if props contain a pre-created client.\n */\nexport function hasClient<T extends object>(\n props: ReplaneProviderProps<T>\n): props is ReplaneProviderWithClientProps<T> {\n return \"client\" in props && props.client !== undefined;\n}\n\n/**\n * Type guard to check if props contain options (with or without snapshot).\n */\nexport function hasOptions<T extends object>(\n props: ReplaneProviderProps<T>\n): props is ReplaneProviderWithOptionsProps<T> {\n return \"options\" in props && props.options !== undefined;\n}\n","\"use client\";\n\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { Replane } from \"@replanejs/sdk\";\nimport { ReplaneContext } from \"./context\";\nimport { useReplaneClientInternal, useReplaneClientSuspense } from \"./useReplaneClient\";\nimport type {\n ReplaneProviderProps,\n ReplaneProviderWithClientProps,\n ReplaneProviderWithOptionsProps,\n ReplaneContextValue,\n} from \"./types\";\nimport { hasClient } from \"./types\";\nimport { DEFAULT_AGENT } from \"./version\";\n\n/**\n * Internal provider component for pre-created client.\n */\nfunction ReplaneProviderWithClient<T extends object>({\n client,\n children,\n}: ReplaneProviderWithClientProps<T>) {\n const value = useMemo<ReplaneContextValue<T>>(() => ({ replane: client }), [client]);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Internal provider component for restoring client from snapshot.\n * Creates a Replane client synchronously and connects in background.\n */\nfunction ReplaneProviderWithSnapshot<T extends object>({\n options,\n snapshot,\n children,\n}: ReplaneProviderWithOptionsProps<T> & {\n snapshot: NonNullable<ReplaneProviderWithOptionsProps<T>[\"snapshot\"]>;\n}) {\n const replaneRef = useRef<Replane<T>>(undefined as unknown as Replane<T>);\n\n if (!replaneRef.current) {\n replaneRef.current = new Replane<T>({\n snapshot,\n logger: options.logger,\n context: options.context,\n defaults: options.defaults,\n });\n }\n\n useEffect(() => {\n replaneRef.current\n .connect({\n baseUrl: options.baseUrl,\n sdkKey: options.sdkKey,\n connectTimeoutMs: options.connectTimeoutMs,\n retryDelayMs: options.retryDelayMs,\n requestTimeoutMs: options.requestTimeoutMs,\n inactivityTimeoutMs: options.inactivityTimeoutMs,\n fetchFn: options.fetchFn,\n agent: options.agent ?? DEFAULT_AGENT,\n })\n .catch((err) => {\n (options.logger ?? console)?.error(\"Failed to connect Replane client\", err);\n });\n\n return () => {\n replaneRef.current.disconnect();\n };\n }, [\n options.agent,\n options.baseUrl,\n options.connectTimeoutMs,\n options.fetchFn,\n options.inactivityTimeoutMs,\n options.logger,\n options.requestTimeoutMs,\n options.retryDelayMs,\n options.sdkKey,\n ]);\n\n const value = useMemo<ReplaneContextValue<T>>(() => ({ replane: replaneRef.current }), []);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Internal provider component for options-based client creation (non-suspense).\n * Throws errors during rendering so they can be caught by Error Boundaries.\n */\nfunction ReplaneProviderWithOptions<T extends object>({\n options,\n children,\n loader,\n}: ReplaneProviderWithOptionsProps<T>) {\n const state = useReplaneClientInternal<T>(options);\n\n if (state.status === \"loading\") {\n return <>{loader ?? null}</>;\n }\n\n if (state.status === \"error\") {\n // Throw error during render so it can be caught by Error Boundary\n throw state.error;\n }\n\n const value: ReplaneContextValue<T> = { replane: state.client };\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Internal provider component for options-based client creation with Suspense.\n */\nfunction ReplaneProviderWithSuspense<T extends object>({\n options,\n children,\n}: ReplaneProviderWithOptionsProps<T>) {\n const client = useReplaneClientSuspense<T>(options);\n const value = useMemo<ReplaneContextValue<T>>(() => ({ replane: client }), [client]);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Provider component that makes a Replane client available to the component tree.\n *\n * Can be used in several ways:\n *\n * 1. With a pre-created client:\n * ```tsx\n * const client = new Replane({ defaults: { ... } });\n * await client.connect({ baseUrl: '...', sdkKey: '...' });\n * <ReplaneProvider client={client}>\n * <App />\n * </ReplaneProvider>\n * ```\n *\n * 2. With options (client managed internally):\n * ```tsx\n * <ErrorBoundary fallback={<ErrorMessage />}>\n * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * loader={<LoadingSpinner />}\n * >\n * <App />\n * </ReplaneProvider>\n * </ErrorBoundary>\n * ```\n *\n * 3. With Suspense:\n * ```tsx\n * <ErrorBoundary fallback={<ErrorMessage />}>\n * <Suspense fallback={<LoadingSpinner />}>\n * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * suspense\n * >\n * <App />\n * </ReplaneProvider>\n * </Suspense>\n * </ErrorBoundary>\n * ```\n *\n * 4. With a snapshot (for SSR/hydration):\n * ```tsx\n * // On the server, get a snapshot from the client\n * const snapshot = serverClient.getSnapshot();\n *\n * // On the client, restore from the snapshot with live updates\n * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * snapshot={snapshot}\n * >\n * <App />\n * </ReplaneProvider>\n * ```\n *\n * Errors during client initialization are thrown during rendering,\n * allowing them to be caught by React Error Boundaries.\n */\nexport function ReplaneProvider<T extends object>(props: ReplaneProviderProps<T>) {\n if (hasClient(props)) {\n return <ReplaneProviderWithClient {...props} />;\n }\n\n // Has options - check if snapshot is provided\n if (props.snapshot) {\n return <ReplaneProviderWithSnapshot {...props} snapshot={props.snapshot} />;\n }\n\n if (props.suspense) {\n return <ReplaneProviderWithSuspense {...props} />;\n }\n\n return <ReplaneProviderWithOptions {...props} />;\n}\n","\"use client\";\n\nimport { useCallback, useContext, useSyncExternalStore } from \"react\";\nimport { ReplaneContext } from \"./context\";\nimport type { UntypedReplaneConfig } from \"./types\";\nimport type { Replane, GetConfigOptions } from \"@replanejs/sdk\";\n\nexport function useReplane<T extends object = UntypedReplaneConfig>(): Replane<T> {\n const context = useContext(ReplaneContext);\n if (!context) {\n throw new Error(\"useReplane must be used within a ReplaneProvider\");\n }\n return context.replane as Replane<T>;\n}\n\nexport function useConfig<T>(name: string, options?: GetConfigOptions<T>): T {\n const client = useReplane();\n\n const subscribe = useCallback(\n (callback: () => void) => {\n return client.subscribe(name, callback);\n },\n [client, name]\n );\n\n const get = useCallback(() => {\n return client.get(name, options) as T;\n }, [client, name, options]);\n\n const value = useSyncExternalStore(subscribe, get, get);\n\n return value;\n}\n\n/**\n * Creates a typed version of useReplane hook.\n *\n * @example\n * ```tsx\n * interface AppConfigs {\n * theme: { darkMode: boolean };\n * features: { beta: boolean };\n * }\n *\n * const useAppReplane = createReplaneHook<AppConfigs>();\n *\n * function MyComponent() {\n * const replane = useAppReplane();\n * // replane.get(\"theme\") returns { darkMode: boolean }\n * }\n * ```\n */\nexport function createReplaneHook<TConfigs extends object>() {\n return function useTypedReplane(): Replane<TConfigs> {\n return useReplane<TConfigs>();\n };\n}\n\n/**\n * Creates a typed version of useConfig hook.\n *\n * @example\n * ```tsx\n * interface AppConfigs {\n * theme: { darkMode: boolean };\n * features: { beta: boolean };\n * }\n *\n * const useAppConfig = createConfigHook<AppConfigs>();\n *\n * function MyComponent() {\n * const theme = useAppConfig(\"theme\");\n * // theme is typed as { darkMode: boolean }\n * }\n * ```\n */\nexport function createConfigHook<TConfigs extends object>() {\n return function useTypedConfig<K extends keyof TConfigs>(\n name: K,\n options?: GetConfigOptions<TConfigs[K]>\n ): TConfigs[K] {\n return useConfig<TConfigs[K]>(String(name), options);\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["options: ConnectOptions","options: ReplaneOptions<T>","connection: ConnectOptions","Replane","originalConnection: ConnectOptions","err: unknown","connection?: ConnectOptions","props: ReplaneProviderProps<T>","Replane","value: ReplaneContextValue<T>","props: ReplaneProviderProps<T>","name: string","options?: GetConfigOptions<T>","callback: () => void","name: K","options?: GetConfigOptions<TConfigs[K]>"],"sources":["../src/context.ts","../src/useReplaneClient.ts","../src/types.ts","../src/version.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["\"use client\";\n\nimport { createContext } from \"react\";\nimport type { ReplaneContextValue } from \"./types\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const ReplaneContext = createContext<ReplaneContextValue<any> | null>(null);\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { Replane, type ConnectOptions, type ReplaneOptions } from \"@replanejs/sdk\";\n\ntype ClientState<T extends object> =\n | { status: \"loading\"; client: null; error: null }\n | { status: \"ready\"; client: Replane<T>; error: null }\n | { status: \"error\"; client: null; error: Error };\n\n// Cache for suspense promise tracking\nconst suspenseCache = new Map<\n string,\n {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n promise: Promise<Replane<any>>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result?: Replane<any>;\n error?: Error;\n }\n>();\n\nfunction getCacheKey(options: ConnectOptions): string {\n return `${options.baseUrl}:${options.sdkKey}`;\n}\n\n/**\n * Creates a Replane client and connects it.\n */\nasync function createAndConnectClient<T extends object>(\n options: ReplaneOptions<T>,\n connection: ConnectOptions\n): Promise<Replane<T>> {\n const client = new Replane<T>({\n logger: options.logger,\n context: options.context,\n defaults: options.defaults,\n });\n\n if (!connection) {\n return client;\n }\n\n await client.connect(connection);\n\n return client;\n}\n\ntype ErrorConstructor = new (message: string, options?: { cause?: unknown }) => Error;\n\n/**\n * Hook to manage Replane client creation internally.\n * Handles loading state and cleanup.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useReplaneClientInternal<T extends object = any>(\n options: ReplaneOptions<T>,\n originalConnection: ConnectOptions\n): ClientState<T> {\n const [state, setState] = useState<ClientState<T>>({\n status: \"loading\",\n client: null,\n error: null,\n });\n const clientRef = useRef<Replane<T> | null>(null);\n const optionsRef = useRef(options);\n\n const connectionJson = JSON.stringify(originalConnection);\n useEffect(() => {\n const connection = JSON.parse(connectionJson);\n let cancelled = false;\n\n async function initClient() {\n try {\n const client = await createAndConnectClient<T>(optionsRef.current, connection);\n if (cancelled) {\n client.disconnect();\n return;\n }\n clientRef.current = client;\n setState({ status: \"ready\", client, error: null });\n } catch (err) {\n if (cancelled) return;\n const error =\n err instanceof Error ? err : new (Error as ErrorConstructor)(String(err), { cause: err });\n setState({ status: \"error\", client: null, error });\n }\n }\n\n initClient();\n\n return () => {\n cancelled = true;\n if (clientRef.current) {\n clientRef.current.disconnect();\n clientRef.current = null;\n }\n };\n }, [connectionJson]);\n\n return state;\n}\n\n/**\n * Hook for Suspense-based client creation.\n * Throws a promise while loading, throws error on failure.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useReplaneClientSuspense<T extends object = any>(\n options: ReplaneOptions<T>,\n connection: ConnectOptions\n): Replane<T> {\n const cacheKey = getCacheKey(connection);\n const cached = suspenseCache.get(cacheKey);\n\n if (cached) {\n if (cached.error) {\n throw cached.error;\n }\n if (cached.result) {\n return cached.result as Replane<T>;\n }\n // Still loading, throw the promise\n throw cached.promise;\n }\n\n // First time - create the promise\n const promise = createAndConnectClient<T>(options, connection)\n .then((client) => {\n const entry = suspenseCache.get(cacheKey);\n if (entry) {\n entry.result = client;\n }\n return client;\n })\n .catch((err: unknown) => {\n const entry = suspenseCache.get(cacheKey);\n if (entry) {\n entry.error = err instanceof Error ? err : new Error(String(err));\n }\n throw err;\n });\n\n suspenseCache.set(cacheKey, { promise });\n throw promise;\n}\n\n/**\n * Clear the suspense cache for a specific connection configuration.\n * Useful for testing or when you need to force re-initialization.\n */\nexport function clearSuspenseCache(connection?: ConnectOptions): void {\n if (connection) {\n suspenseCache.delete(getCacheKey(connection));\n } else {\n suspenseCache.clear();\n }\n}\n","import type { Replane, ReplaneOptions, ConnectOptions } from \"@replanejs/sdk\";\nimport type { ReactNode } from \"react\";\n\nexport type UntypedReplaneConfig = Record<string, unknown>;\n\nexport interface ReplaneContextValue<T extends object = UntypedReplaneConfig> {\n replane: Replane<T>;\n}\n\n/**\n * Props for ReplaneProvider when using a pre-created client.\n */\nexport interface ReplaneProviderWithClientProps<T extends object = UntypedReplaneConfig> {\n /** Pre-created Replane client instance */\n client: Replane<T>;\n children: ReactNode;\n}\n\n/**\n * Props for ReplaneProvider when letting it manage the client internally.\n */\nexport interface ReplaneProviderWithOptionsProps<\n T extends object = UntypedReplaneConfig,\n> extends ReplaneOptions<T> {\n children: ReactNode;\n /**\n * Connection options for connecting to the Replane server.\n * Pass null to explicitly skip connection (client will use defaults/snapshot only).\n */\n connection: ConnectOptions | null;\n /**\n * Optional loading component to show while the client is initializing.\n * If not provided and suspense is false/undefined, children will not render until ready.\n * Ignored when snapshot is provided (restoration is synchronous).\n */\n loader?: ReactNode;\n /**\n * If true, uses React Suspense for loading state.\n * The provider will throw a promise that Suspense can catch.\n * Ignored when snapshot is provided (restoration is synchronous).\n * @default false\n */\n suspense?: boolean;\n /**\n * If true, the client will be connected asynchronously. Make sure to provide defaults or snapshot.\n * @default false\n */\n async?: boolean;\n}\n\nexport type ReplaneProviderProps<T extends object = UntypedReplaneConfig> =\n | ReplaneProviderWithClientProps<T>\n | ReplaneProviderWithOptionsProps<T>;\n\n/**\n * Type guard to check if props contain a pre-created client.\n */\nexport function hasClient<T extends object>(\n props: ReplaneProviderProps<T>\n): props is ReplaneProviderWithClientProps<T> {\n return \"client\" in props && !!props.client;\n}\n\n/**\n * Type guard to check if props contain options (with or without snapshot).\n */\nexport function hasOptions<T extends object>(\n props: ReplaneProviderProps<T>\n): props is ReplaneProviderWithOptionsProps<T> {\n return !hasClient(props);\n}\n","// Auto-generated - do not edit manually\nexport const VERSION = \"0.9.2\";\nexport const DEFAULT_AGENT = `replane-js-react/${VERSION}`;\n","\"use client\";\n\nimport { useEffect, useMemo, useRef } from \"react\";\nimport { Replane, type ConnectOptions } from \"@replanejs/sdk\";\nimport { ReplaneContext } from \"./context\";\nimport { useReplaneClientInternal, useReplaneClientSuspense } from \"./useReplaneClient\";\nimport type {\n ReplaneProviderProps,\n ReplaneProviderWithClientProps,\n ReplaneProviderWithOptionsProps,\n ReplaneContextValue,\n} from \"./types\";\nimport { hasClient } from \"./types\";\nimport { DEFAULT_AGENT } from \"./version\";\n\n/**\n * Internal provider component for pre-created client.\n */\nfunction ReplaneProviderWithClient<T extends object>({\n client,\n children,\n}: ReplaneProviderWithClientProps<T>) {\n const value = useMemo<ReplaneContextValue<T>>(() => ({ replane: client }), [client]);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Internal provider component for creating a Replane client asynchronously.\n * Creates a Replane client synchronously and connects in background.\n */\nfunction AsyncReplaneProvider<T extends object>({\n children,\n connection,\n ...options\n}: ReplaneProviderWithOptionsProps<T>) {\n const replaneRef = useRef<Replane<T>>(undefined as unknown as Replane<T>);\n\n if (!replaneRef.current) {\n replaneRef.current = new Replane<T>(options);\n }\n\n const connectionJson = connection ? JSON.stringify(connection) : undefined;\n\n useEffect(() => {\n const parsedConnection = connectionJson ? JSON.parse(connectionJson) : undefined;\n if (!parsedConnection) {\n return;\n }\n\n replaneRef.current.connect(parsedConnection).catch((err) => {\n (options.logger ?? console)?.error(\"Failed to connect Replane client\", err);\n });\n\n return () => {\n replaneRef.current.disconnect();\n };\n }, [connectionJson, options.logger]);\n\n const value = useMemo<ReplaneContextValue<T>>(() => ({ replane: replaneRef.current }), []);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Internal provider component for options-based client creation (non-suspense).\n * Throws errors during rendering so they can be caught by Error Boundaries.\n */\nfunction LoaderReplaneProvider<T extends object>({\n children,\n loader,\n connection,\n ...options\n}: ReplaneProviderWithOptionsProps<T> & { connection: ConnectOptions }) {\n if (!connection) {\n throw new Error(\"Connection is required when using Loader\");\n }\n const state = useReplaneClientInternal<T>(options, connection);\n\n if (state.status === \"loading\") {\n return <>{loader ?? null}</>;\n }\n\n if (state.status === \"error\") {\n // Throw error during render so it can be caught by Error Boundary\n throw state.error;\n }\n\n const value: ReplaneContextValue<T> = { replane: state.client };\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Internal provider component for options-based client creation with Suspense.\n */\nfunction SuspenseReplaneProvider<T extends object>({\n connection,\n children,\n ...options\n}: ReplaneProviderWithOptionsProps<T> & { connection: ConnectOptions }) {\n if (!connection) {\n throw new Error(\"Connection is required when using Suspense\");\n }\n const client = useReplaneClientSuspense<T>(options, connection);\n const value = useMemo<ReplaneContextValue<T>>(() => ({ replane: client }), [client]);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Provider component that makes a Replane client available to the component tree.\n *\n * Can be used in several ways:\n *\n * 1. With a pre-created client:\n * ```tsx\n * const client = new Replane({ defaults: { ... } });\n * await client.connect({ baseUrl: '...', sdkKey: '...' });\n * <ReplaneProvider client={client}>\n * <App />\n * </ReplaneProvider>\n * ```\n *\n * 2. With options (client managed internally):\n * ```tsx\n * <ErrorBoundary fallback={<ErrorMessage />}>\n * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * loader={<LoadingSpinner />}\n * >\n * <App />\n * </ReplaneProvider>\n * </ErrorBoundary>\n * ```\n *\n * 3. With Suspense:\n * ```tsx\n * <ErrorBoundary fallback={<ErrorMessage />}>\n * <Suspense fallback={<LoadingSpinner />}>\n * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * suspense\n * >\n * <App />\n * </ReplaneProvider>\n * </Suspense>\n * </ErrorBoundary>\n * ```\n *\n * 4. With a snapshot (for SSR/hydration):\n * ```tsx\n * // On the server, get a snapshot from the client\n * const snapshot = serverClient.getSnapshot();\n *\n * // On the client, restore from the snapshot with live updates\n * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * snapshot={snapshot}\n * >\n * <App />\n * </ReplaneProvider>\n * ```\n *\n * Errors during client initialization are thrown during rendering,\n * allowing them to be caught by React Error Boundaries.\n */\nexport function ReplaneProvider<T extends object>(props: ReplaneProviderProps<T>) {\n const originalConnection = (props as { connection?: ConnectOptions }).connection;\n const connection = useMemo(\n () =>\n originalConnection\n ? {\n ...originalConnection,\n agent: originalConnection.agent ?? DEFAULT_AGENT,\n }\n : undefined,\n [originalConnection]\n );\n\n if (hasClient(props)) {\n return <ReplaneProviderWithClient {...props} />;\n }\n\n if (props.snapshot || !connection || props.async) {\n return <AsyncReplaneProvider {...props} />;\n }\n\n if (props.suspense) {\n return <SuspenseReplaneProvider {...props} connection={connection} />;\n }\n\n return <LoaderReplaneProvider {...props} connection={connection} />;\n}\n","\"use client\";\n\nimport { useCallback, useContext, useSyncExternalStore } from \"react\";\nimport { ReplaneContext } from \"./context\";\nimport type { UntypedReplaneConfig } from \"./types\";\nimport type { Replane, GetConfigOptions } from \"@replanejs/sdk\";\n\nexport function useReplane<T extends object = UntypedReplaneConfig>(): Replane<T> {\n const context = useContext(ReplaneContext);\n if (!context) {\n throw new Error(\"useReplane must be used within a ReplaneProvider\");\n }\n return context.replane as Replane<T>;\n}\n\nexport function useConfig<T>(name: string, options?: GetConfigOptions<T>): T {\n const client = useReplane();\n\n const subscribe = useCallback(\n (callback: () => void) => {\n return client.subscribe(name, callback);\n },\n [client, name]\n );\n\n const get = useCallback(() => {\n return client.get(name, options) as T;\n }, [client, name, options]);\n\n const value = useSyncExternalStore(subscribe, get, get);\n\n return value;\n}\n\n/**\n * Creates a typed version of useReplane hook.\n *\n * @example\n * ```tsx\n * interface AppConfigs {\n * theme: { darkMode: boolean };\n * features: { beta: boolean };\n * }\n *\n * const useAppReplane = createReplaneHook<AppConfigs>();\n *\n * function MyComponent() {\n * const replane = useAppReplane();\n * // replane.get(\"theme\") returns { darkMode: boolean }\n * }\n * ```\n */\nexport function createReplaneHook<TConfigs extends object>() {\n return function useTypedReplane(): Replane<TConfigs> {\n return useReplane<TConfigs>();\n };\n}\n\n/**\n * Creates a typed version of useConfig hook.\n *\n * @example\n * ```tsx\n * interface AppConfigs {\n * theme: { darkMode: boolean };\n * features: { beta: boolean };\n * }\n *\n * const useAppConfig = createConfigHook<AppConfigs>();\n *\n * function MyComponent() {\n * const theme = useAppConfig(\"theme\");\n * // theme is typed as { darkMode: boolean }\n * }\n * ```\n */\nexport function createConfigHook<TConfigs extends object>() {\n return function useTypedConfig<K extends keyof TConfigs>(\n name: K,\n options?: GetConfigOptions<TConfigs[K]>\n ): TConfigs[K] {\n return useConfig<TConfigs[K]>(String(name), options);\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@replanejs/react",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "React SDK for Replane - feature flags and remote configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"react": ">=18.0.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@replanejs/sdk": "^0.9.
|
|
43
|
+
"@replanejs/sdk": "^0.9.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@testing-library/jest-dom": "^6.9.1",
|