@replanejs/react 0.8.20 → 0.9.0

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 CHANGED
@@ -75,27 +75,32 @@ import { ErrorBoundary } from "react-error-boundary";
75
75
 
76
76
  #### Client Options
77
77
 
78
- The `options` prop accepts all options from `@replanejs/sdk`. Key options:
79
-
80
- | Option | Type | Required | Description |
81
- | ------------------------- | ---------------------- | -------- | ------------------------------------------ |
82
- | `baseUrl` | `string` | Yes | Replane server URL |
83
- | `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
- | `required` | `string[]` or `object` | No | Configs that must exist for initialization |
87
- | `initializationTimeoutMs` | `number` | No | SDK initialization timeout (default: 5000) |
88
-
89
- See [`@replanejs/sdk` documentation](https://github.com/replane-dev/replane-javascript/tree/main/packages/sdk#options) for the complete list of options.
78
+ The `options` prop accepts the following options:
79
+
80
+ | Option | Type | Required | Description |
81
+ | -------------------- | --------------------- | -------- | -------------------------------------------- |
82
+ | `baseUrl` | `string` | Yes | Replane server URL |
83
+ | `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
+ | `connectTimeoutMs` | `number` | No | SDK connection timeout (default: 5000) |
87
+ | `requestTimeoutMs` | `number` | No | Timeout for SSE requests (default: 2000) |
88
+ | `retryDelayMs` | `number` | No | Base delay between retries (default: 200) |
89
+ | `inactivityTimeoutMs`| `number` | No | SSE inactivity timeout (default: 30000) |
90
+ | `fetchFn` | `typeof fetch` | No | Custom fetch implementation |
91
+ | `logger` | `ReplaneLogger` | No | Custom logger (default: console) |
92
+
93
+ See [`@replanejs/sdk` documentation](https://github.com/replane-dev/replane-javascript/tree/main/packages/sdk#api) for more details.
90
94
 
91
95
  #### 2. With pre-created client
92
96
 
93
97
  Use this when you need more control over client lifecycle:
94
98
 
95
99
  ```tsx
96
- import { createReplaneClient } from "@replanejs/sdk";
100
+ import { Replane } from "@replanejs/sdk";
97
101
 
98
- const client = await createReplaneClient({
102
+ const client = new Replane();
103
+ await client.connect({
99
104
  baseUrl: "https://your-replane-server.com",
100
105
  sdkKey: "your-sdk-key",
101
106
  });
@@ -131,26 +136,24 @@ Restore a client from a snapshot obtained on the server. This is synchronous and
131
136
 
132
137
  ```tsx
133
138
  // On the server
134
- const serverClient = await createReplaneClient({ baseUrl: "...", sdkKey: "..." });
139
+ const serverClient = new Replane();
140
+ await serverClient.connect({ baseUrl: "...", sdkKey: "..." });
135
141
  const snapshot = serverClient.getSnapshot();
136
142
  // Pass snapshot to client via props, context, or serialized HTML
137
143
 
138
144
  // On the client
139
145
  <ReplaneProvider
140
- restoreOptions={{
141
- snapshot,
142
- // Optional: connect for live updates
143
- connection: {
144
- baseUrl: "https://your-replane-server.com",
145
- sdkKey: "your-sdk-key",
146
- },
146
+ options={{
147
+ baseUrl: "https://your-replane-server.com",
148
+ sdkKey: "your-sdk-key",
147
149
  }}
150
+ snapshot={snapshot}
148
151
  >
149
152
  <App />
150
153
  </ReplaneProvider>;
151
154
  ```
152
155
 
153
- The restored client is immediately available with no loading state. If `connection` is provided, it will establish a connection for real-time updates in the background.
156
+ The restored client is immediately available with no loading state. The provider will establish a connection for real-time updates in the background.
154
157
 
155
158
  ### useConfig
156
159
 
package/dist/index.cjs CHANGED
@@ -33,7 +33,7 @@ const ReplaneContext = (0, react.createContext)(null);
33
33
 
34
34
  //#endregion
35
35
  //#region src/version.ts
36
- const VERSION = "0.8.20";
36
+ const VERSION = "0.9.0";
37
37
  const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
38
38
 
39
39
  //#endregion
@@ -43,7 +43,28 @@ function getCacheKey(options) {
43
43
  return `${options.baseUrl}:${options.sdkKey}`;
44
44
  }
45
45
  /**
46
- * Hook to manage ReplaneClient creation internally.
46
+ * Creates a Replane client and connects it.
47
+ */
48
+ async function createAndConnectClient(options) {
49
+ const client = new __replanejs_sdk.Replane({
50
+ logger: options.logger,
51
+ context: options.context,
52
+ defaults: options.defaults
53
+ });
54
+ await client.connect({
55
+ baseUrl: options.baseUrl,
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
+ });
64
+ return client;
65
+ }
66
+ /**
67
+ * Hook to manage Replane client creation internally.
47
68
  * Handles loading state and cleanup.
48
69
  */
49
70
  function useReplaneClientInternal(options) {
@@ -58,12 +79,9 @@ function useReplaneClientInternal(options) {
58
79
  let cancelled = false;
59
80
  async function initClient() {
60
81
  try {
61
- const client = await (0, __replanejs_sdk.createReplaneClient)({
62
- ...optionsRef.current,
63
- agent: optionsRef.current.agent ?? DEFAULT_AGENT
64
- });
82
+ const client = await createAndConnectClient(optionsRef.current);
65
83
  if (cancelled) {
66
- client.close();
84
+ client.disconnect();
67
85
  return;
68
86
  }
69
87
  clientRef.current = client;
@@ -86,7 +104,7 @@ function useReplaneClientInternal(options) {
86
104
  return () => {
87
105
  cancelled = true;
88
106
  if (clientRef.current) {
89
- clientRef.current.close();
107
+ clientRef.current.disconnect();
90
108
  clientRef.current = null;
91
109
  }
92
110
  };
@@ -105,10 +123,7 @@ function useReplaneClientSuspense(options) {
105
123
  if (cached.result) return cached.result;
106
124
  throw cached.promise;
107
125
  }
108
- const promise = (0, __replanejs_sdk.createReplaneClient)({
109
- ...options,
110
- agent: options.agent ?? DEFAULT_AGENT
111
- }).then((client) => {
126
+ const promise = createAndConnectClient(options).then((client) => {
112
127
  const entry = suspenseCache.get(cacheKey);
113
128
  if (entry) entry.result = client;
114
129
  return client;
@@ -129,101 +144,6 @@ function clearSuspenseCache(options) {
129
144
  else suspenseCache.clear();
130
145
  }
131
146
 
132
- //#endregion
133
- //#region src/hooks.ts
134
- function useReplane() {
135
- const context = (0, react.useContext)(ReplaneContext);
136
- if (!context) throw new Error("useReplane must be used within a ReplaneProvider");
137
- return context.client;
138
- }
139
- function useConfig(name, options) {
140
- const client = useReplane();
141
- const subscribe = (0, react.useCallback)((callback) => {
142
- return client.subscribe(name, callback);
143
- }, [client, name]);
144
- const get = (0, react.useCallback)(() => {
145
- return client.get(name, options);
146
- }, [
147
- client,
148
- name,
149
- options
150
- ]);
151
- const value = (0, react.useSyncExternalStore)(subscribe, get, get);
152
- return value;
153
- }
154
- /**
155
- * Creates a typed version of useReplane hook.
156
- *
157
- * @example
158
- * ```tsx
159
- * interface AppConfigs {
160
- * theme: { darkMode: boolean };
161
- * features: { beta: boolean };
162
- * }
163
- *
164
- * const useAppReplane = createReplaneHook<AppConfigs>();
165
- *
166
- * function MyComponent() {
167
- * const replane = useAppReplane();
168
- * // replane.get("theme") returns { darkMode: boolean }
169
- * }
170
- * ```
171
- */
172
- function createReplaneHook() {
173
- return function useTypedReplane() {
174
- return useReplane();
175
- };
176
- }
177
- /**
178
- * Creates a typed version of useConfig hook.
179
- *
180
- * @example
181
- * ```tsx
182
- * interface AppConfigs {
183
- * theme: { darkMode: boolean };
184
- * features: { beta: boolean };
185
- * }
186
- *
187
- * const useAppConfig = createConfigHook<AppConfigs>();
188
- *
189
- * function MyComponent() {
190
- * const theme = useAppConfig("theme");
191
- * // theme is typed as { darkMode: boolean }
192
- * }
193
- * ```
194
- */
195
- function createConfigHook() {
196
- return function useTypedConfig(name, options) {
197
- return useConfig(String(name), options);
198
- };
199
- }
200
- /**
201
- * Hook for creating stateful resources with cleanup support.
202
- * Unlike useMemo, this guarantees cleanup when dependencies change or on unmount.
203
- *
204
- * @param factory - Function that creates the resource
205
- * @param cleanup - Function that cleans up the resource
206
- * @param deps - Dependencies array (resource is recreated when these change)
207
- */
208
- function useStateful(factory, cleanup, deps) {
209
- const valueRef = (0, react.useRef)(null);
210
- const initializedRef = (0, react.useRef)(false);
211
- if (!initializedRef.current) {
212
- valueRef.current = factory();
213
- initializedRef.current = true;
214
- }
215
- (0, react.useEffect)(() => {
216
- if (valueRef.current === null) valueRef.current = factory();
217
- return () => {
218
- if (valueRef.current !== null) {
219
- cleanup(valueRef.current);
220
- valueRef.current = null;
221
- }
222
- };
223
- }, deps);
224
- return valueRef.current;
225
- }
226
-
227
147
  //#endregion
228
148
  //#region src/types.ts
229
149
  /**
@@ -239,7 +159,7 @@ function hasClient(props) {
239
159
  * Internal provider component for pre-created client.
240
160
  */
241
161
  function ReplaneProviderWithClient({ client, children }) {
242
- const value = (0, react.useMemo)(() => ({ client }), [client]);
162
+ const value = (0, react.useMemo)(() => ({ replane: client }), [client]);
243
163
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
244
164
  value,
245
165
  children
@@ -247,24 +167,44 @@ function ReplaneProviderWithClient({ client, children }) {
247
167
  }
248
168
  /**
249
169
  * Internal provider component for restoring client from snapshot.
250
- * Uses restoreReplaneClient which is synchronous.
170
+ * Creates a Replane client synchronously and connects in background.
251
171
  */
252
172
  function ReplaneProviderWithSnapshot({ options, snapshot, children }) {
253
- const client = useStateful(() => (0, __replanejs_sdk.restoreReplaneClient)({
173
+ const replaneRef = (0, react.useRef)(void 0);
174
+ if (!replaneRef.current) replaneRef.current = new __replanejs_sdk.Replane({
254
175
  snapshot,
255
- connection: {
176
+ logger: options.logger,
177
+ context: options.context,
178
+ defaults: options.defaults
179
+ });
180
+ (0, react.useEffect)(() => {
181
+ replaneRef.current.connect({
256
182
  baseUrl: options.baseUrl,
257
183
  sdkKey: options.sdkKey,
258
- fetchFn: options.fetchFn,
259
- requestTimeoutMs: options.requestTimeoutMs,
184
+ connectTimeoutMs: options.connectTimeoutMs,
260
185
  retryDelayMs: options.retryDelayMs,
186
+ requestTimeoutMs: options.requestTimeoutMs,
261
187
  inactivityTimeoutMs: options.inactivityTimeoutMs,
262
- logger: options.logger,
188
+ fetchFn: options.fetchFn,
263
189
  agent: options.agent ?? DEFAULT_AGENT
264
- },
265
- context: options.context
266
- }), (c) => c.close(), [snapshot, options]);
267
- const value = (0, react.useMemo)(() => ({ client }), [client]);
190
+ }).catch((err) => {
191
+ (options.logger ?? console)?.error("Failed to connect Replane client", err);
192
+ });
193
+ return () => {
194
+ replaneRef.current.disconnect();
195
+ };
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
+ ]);
207
+ const value = (0, react.useMemo)(() => ({ replane: replaneRef.current }), []);
268
208
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
269
209
  value,
270
210
  children
@@ -278,7 +218,7 @@ function ReplaneProviderWithOptions({ options, children, loader }) {
278
218
  const state = useReplaneClientInternal(options);
279
219
  if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: loader ?? null });
280
220
  if (state.status === "error") throw state.error;
281
- const value = { client: state.client };
221
+ const value = { replane: state.client };
282
222
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
283
223
  value,
284
224
  children
@@ -289,20 +229,21 @@ function ReplaneProviderWithOptions({ options, children, loader }) {
289
229
  */
290
230
  function ReplaneProviderWithSuspense({ options, children }) {
291
231
  const client = useReplaneClientSuspense(options);
292
- const value = (0, react.useMemo)(() => ({ client }), [client]);
232
+ const value = (0, react.useMemo)(() => ({ replane: client }), [client]);
293
233
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
294
234
  value,
295
235
  children
296
236
  });
297
237
  }
298
238
  /**
299
- * Provider component that makes a ReplaneClient available to the component tree.
239
+ * Provider component that makes a Replane client available to the component tree.
300
240
  *
301
- * Can be used in three ways:
241
+ * Can be used in several ways:
302
242
  *
303
243
  * 1. With a pre-created client:
304
244
  * ```tsx
305
- * const client = await createReplaneClient({ ... });
245
+ * const client = new Replane({ defaults: { ... } });
246
+ * await client.connect({ baseUrl: '...', sdkKey: '...' });
306
247
  * <ReplaneProvider client={client}>
307
248
  * <App />
308
249
  * </ReplaneProvider>
@@ -362,6 +303,93 @@ function ReplaneProvider(props) {
362
303
  }
363
304
 
364
305
  //#endregion
306
+ //#region src/hooks.ts
307
+ function useReplane() {
308
+ const context = (0, react.useContext)(ReplaneContext);
309
+ if (!context) throw new Error("useReplane must be used within a ReplaneProvider");
310
+ return context.replane;
311
+ }
312
+ function useConfig(name, options) {
313
+ const client = useReplane();
314
+ const subscribe = (0, react.useCallback)((callback) => {
315
+ return client.subscribe(name, callback);
316
+ }, [client, name]);
317
+ const get = (0, react.useCallback)(() => {
318
+ return client.get(name, options);
319
+ }, [
320
+ client,
321
+ name,
322
+ options
323
+ ]);
324
+ const value = (0, react.useSyncExternalStore)(subscribe, get, get);
325
+ return value;
326
+ }
327
+ /**
328
+ * Creates a typed version of useReplane hook.
329
+ *
330
+ * @example
331
+ * ```tsx
332
+ * interface AppConfigs {
333
+ * theme: { darkMode: boolean };
334
+ * features: { beta: boolean };
335
+ * }
336
+ *
337
+ * const useAppReplane = createReplaneHook<AppConfigs>();
338
+ *
339
+ * function MyComponent() {
340
+ * const replane = useAppReplane();
341
+ * // replane.get("theme") returns { darkMode: boolean }
342
+ * }
343
+ * ```
344
+ */
345
+ function createReplaneHook() {
346
+ return function useTypedReplane() {
347
+ return useReplane();
348
+ };
349
+ }
350
+ /**
351
+ * Creates a typed version of useConfig hook.
352
+ *
353
+ * @example
354
+ * ```tsx
355
+ * interface AppConfigs {
356
+ * theme: { darkMode: boolean };
357
+ * features: { beta: boolean };
358
+ * }
359
+ *
360
+ * const useAppConfig = createConfigHook<AppConfigs>();
361
+ *
362
+ * function MyComponent() {
363
+ * const theme = useAppConfig("theme");
364
+ * // theme is typed as { darkMode: boolean }
365
+ * }
366
+ * ```
367
+ */
368
+ function createConfigHook() {
369
+ return function useTypedConfig(name, options) {
370
+ return useConfig(String(name), options);
371
+ };
372
+ }
373
+
374
+ //#endregion
375
+ Object.defineProperty(exports, 'Replane', {
376
+ enumerable: true,
377
+ get: function () {
378
+ return __replanejs_sdk.Replane;
379
+ }
380
+ });
381
+ Object.defineProperty(exports, 'ReplaneError', {
382
+ enumerable: true,
383
+ get: function () {
384
+ return __replanejs_sdk.ReplaneError;
385
+ }
386
+ });
387
+ Object.defineProperty(exports, 'ReplaneErrorCode', {
388
+ enumerable: true,
389
+ get: function () {
390
+ return __replanejs_sdk.ReplaneErrorCode;
391
+ }
392
+ });
365
393
  exports.ReplaneProvider = ReplaneProvider;
366
394
  exports.clearSuspenseCache = clearSuspenseCache;
367
395
  exports.createConfigHook = createConfigHook;
package/dist/index.d.cts CHANGED
@@ -1,23 +1,80 @@
1
1
  import * as react_jsx_runtime0 from "react/jsx-runtime";
2
- import { GetConfigOptions, GetReplaneSnapshotOptions, ReplaneClient, ReplaneClientOptions, ReplaneSnapshot, getReplaneSnapshot } from "@replanejs/sdk";
2
+ import { ConnectOptions, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, Replane, Replane as Replane$1, ReplaneContext, ReplaneContext as ReplaneContext$1, ReplaneError, ReplaneErrorCode, ReplaneLogger, ReplaneLogger as ReplaneLogger$1, ReplaneOptions, ReplaneSnapshot, ReplaneSnapshot as ReplaneSnapshot$1, 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
+ }
7
64
  /**
8
65
  * Props for ReplaneProvider when using a pre-created client.
9
66
  */
10
67
  interface ReplaneProviderWithClientProps<T extends object = UntypedReplaneConfig> {
11
- /** Pre-created ReplaneClient instance */
12
- client: ReplaneClient<T>;
68
+ /** Pre-created Replane client instance */
69
+ client: Replane$1<T>;
13
70
  children: ReactNode;
14
71
  }
15
72
  /**
16
73
  * Props for ReplaneProvider when letting it manage the client internally.
17
74
  */
18
75
  interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfig> {
19
- /** Options to create or restore the ReplaneClient */
20
- options: ReplaneClientOptions<T>;
76
+ /** Options to create or restore the Replane client */
77
+ options: ReplaneProviderOptions<T>;
21
78
  children: ReactNode;
22
79
  /**
23
80
  * Optional snapshot from server-side rendering.
@@ -25,7 +82,7 @@ interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfi
25
82
  * instead of fetching configs from the server.
26
83
  * The `options` will be used for live updates connection if provided.
27
84
  */
28
- snapshot?: ReplaneSnapshot<T>;
85
+ snapshot?: ReplaneSnapshot$1<T>;
29
86
  /**
30
87
  * Optional loading component to show while the client is initializing.
31
88
  * If not provided and suspense is false/undefined, children will not render until ready.
@@ -44,17 +101,17 @@ type ReplaneProviderProps<T extends object = UntypedReplaneConfig> = ReplaneProv
44
101
  /**
45
102
  * Type guard to check if props contain a pre-created client.
46
103
  */
47
-
48
104
  //#endregion
49
105
  //#region src/provider.d.ts
50
106
  /**
51
- * Provider component that makes a ReplaneClient available to the component tree.
107
+ * Provider component that makes a Replane client available to the component tree.
52
108
  *
53
- * Can be used in three ways:
109
+ * Can be used in several ways:
54
110
  *
55
111
  * 1. With a pre-created client:
56
112
  * ```tsx
57
- * const client = await createReplaneClient({ ... });
113
+ * const client = new Replane({ defaults: { ... } });
114
+ * await client.connect({ baseUrl: '...', sdkKey: '...' });
58
115
  * <ReplaneProvider client={client}>
59
116
  * <App />
60
117
  * </ReplaneProvider>
@@ -107,8 +164,8 @@ declare function ReplaneProvider<T extends object>(props: ReplaneProviderProps<T
107
164
  //# sourceMappingURL=provider.d.ts.map
108
165
  //#endregion
109
166
  //#region src/hooks.d.ts
110
- declare function useReplane<T extends object = UntypedReplaneConfig>(): ReplaneClient<T>;
111
- declare function useConfig<T>(name: string, options?: GetConfigOptions<T>): T;
167
+ declare function useReplane<T extends object = UntypedReplaneConfig>(): Replane$1<T>;
168
+ declare function useConfig<T>(name: string, options?: GetConfigOptions$1<T>): T;
112
169
  /**
113
170
  * Creates a typed version of useReplane hook.
114
171
  *
@@ -127,7 +184,7 @@ declare function useConfig<T>(name: string, options?: GetConfigOptions<T>): T;
127
184
  * }
128
185
  * ```
129
186
  */
130
- declare function createReplaneHook<TConfigs extends object>(): () => ReplaneClient<TConfigs>;
187
+ declare function createReplaneHook<TConfigs extends object>(): () => Replane$1<TConfigs>;
131
188
  /**
132
189
  * Creates a typed version of useConfig hook.
133
190
  *
@@ -146,22 +203,15 @@ declare function createReplaneHook<TConfigs extends object>(): () => ReplaneClie
146
203
  * }
147
204
  * ```
148
205
  */
149
- declare function createConfigHook<TConfigs extends object>(): <K extends keyof TConfigs>(name: K, options?: GetConfigOptions<TConfigs[K]>) => TConfigs[K];
150
- /**
151
- * Hook for creating stateful resources with cleanup support.
152
- * Unlike useMemo, this guarantees cleanup when dependencies change or on unmount.
153
- *
154
- * @param factory - Function that creates the resource
155
- * @param cleanup - Function that cleans up the resource
156
- * @param deps - Dependencies array (resource is recreated when these change)
157
- */
206
+ declare function createConfigHook<TConfigs extends object>(): <K extends keyof TConfigs>(name: K, options?: GetConfigOptions$1<TConfigs[K]>) => TConfigs[K];
207
+ //# sourceMappingURL=hooks.d.ts.map
158
208
  //#endregion
159
209
  //#region src/useReplaneClient.d.ts
160
210
  /**
161
211
  * Clear the suspense cache for a specific options configuration.
162
212
  * Useful for testing or when you need to force re-initialization.
163
213
  */
164
- declare function clearSuspenseCache<T extends object>(options?: ReplaneClientOptions<T>): void;
214
+ declare function clearSuspenseCache<T extends object>(options?: ReplaneProviderOptions<T>): void;
165
215
  //#endregion
166
- export { type GetReplaneSnapshotOptions, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
216
+ export { type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type ReplaneProviderOptions, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, type ReplaneSnapshot, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
167
217
  //# sourceMappingURL=index.d.cts.map
@@ -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":";;;;;KAOY,oBAAA,GAAuB;AASnC;;;AAEwB,UAFP,8BAEO,CAAA,UAAA,MAAA,GAF2C,oBAE3C,CAAA,CAAA;EAAC;EAAF,MACX,EADF,aACE,CADY,CACZ,CAAA;EAAS,QAAA,EAAT,SAAS;AAMrB;;;;AAEW,UAFM,+BAEN,CAAA,UAAA,MAAA,GAFyD,oBAEzD,CAAA,CAAA;EAAoB;EACV,OAOQ,EARlB,oBAQkB,CARG,CAQH,CAAA;EAAC,QAAjB,EAPD,SAOC;EAAe;AAMR;AAUpB;;;;EACoC,QAAhC,CAAA,EAjBS,eAiBT,CAjByB,CAiBzB,CAAA;EAA8B;;AACC;;;WAZxB;ECgHK;;;;;AAAgE;;;KDtGpE,wCAAwC,wBAChD,+BAA+B,KAC/B,gCAAgC;AE9CpC;;;;;;;;;;AFAA;AASA;;;;;;AAGqB;AAMrB;;;;;;;;;AAgBoB;AAUpB;;;;;;;AAEmC;;;;ACoGnC;;;;;AAAgF;;;;AClJhF;;;;;AAAoF;AAQpF;;;;;AAA4E;AAqC5E;;AACmD,iBDoGnC,eCpGmC,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,EDoGM,oBCpGN,CDoG2B,CCpG3B,CAAA,CAAA,EDoG6B,kBAAA,CAAA,GAAA,CAAA,OCpG7B;;;;iBA9CnC,8BAA8B,yBAAyB,cAAc;iBAQrE,qCAAqC,iBAAiB,KAAK;;AFR3E;AASA;;;;;;AAGqB;AAMrB;;;;;;;;;AAgBoB,iBEWJ,iBFXI,CAAA,iBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,GAAA,GEYiB,aFZjB,CEY+B,QFZ/B,CAAA;AAUpB;;;;;;;AAEmC;;;;ACoGnC;;;;;AAAgF;;iBC7EhE,8DACiC,gBACvC,aACI,iBAAiB,SAAS,QACnC,SAAS;;AAzEd;;;;;AAAoF;AAQpF;;;;;;;AFsCoC,iBGgFpB,kBHhFoB,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,CAAA,EGgF2B,oBHhF3B,CGgFgD,CHhFhD,CAAA,CAAA,EAAA,IAAA"}
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;AAUnC;;;;AAmBW,UAnBM,sBAmBN,CAAA,UAAA,MAAA,GAnBgD,oBAmBhD,CAAA,CAAA;EAAa;;;;EA4BA,OAAA,EAAA,MAAA;EAUP;;;;EAEE,MAAT,EAAA,MAAA;EAAO;AACI;AAMrB;;EAAgD,OAAoB,CAAA,EAnDxD,gBAmDwD;EAAoB;;;EAGnE,MAOQ,CAAA,EAzDlB,eAyDkB;EAAC;;AAMV;EAUR,QAAA,CAAA,EAAA,QAAoB,MArEL,CAqEK,IArEA,CAqEA,CArEE,CAqEF,CAAA,EAAA;EAAwC;;;;EAErC,gBAAA,CAAA,EAAA,MAAA;;;;ACqEnC;EAA+B,YAAA,CAAA,EAAA,MAAA;EAAA;;;AAAiD;;;;ACzKhF;;EAA0B,mBAAoB,CAAA,EAAA,MAAA;EAAoB;;AAAY;EAQ9D,OAAA,CAAA,EAAA,OF6CG,KE7CM;EAAA;;;EAA4C,KAAM,CAAA,EAAA,MAAA;AAAC;AAqC5E;;;AACqC,UFiBpB,8BEjBoB,CAAA,UAAA,MAAA,GFiB8B,oBEjB9B,CAAA,CAAA;EAAO;EAuB5B,MAAA,EFJN,SEIM,CFJE,CEIF,CAAA;EAAgB,QAAA,EFHpB,SEGoB;;;;;AAGlB,UFAG,+BEAH,CAAA,UAAA,MAAA,GFAsD,oBEAtD,CAAA,CAAA;EAAgB;EACjB,OAAC,EFCH,sBEDG,CFCoB,CEDpB,CAAA;EAAC,QAAA,EFEH,SEFG;;;;ACyEf;;;EAAuF,QAAxB,CAAA,EHhElD,iBGgEkD,CHhElC,CGgEkC,CAAA;EAAsB;;;;;WH1D1E;;;;;;;;;KAUC,wCAAwC,wBAChD,+BAA+B,KAC/B,gCAAgC;;;;;;;;;;AAxGpC;AAUA;;;;;;;;;AA+CwB;AAUxB;;;;;;AAGqB;AAMrB;;;;;;;;;AAgBoB;AAUpB;;;;;;;AAEmC;;;;ACqEnC;;;;;AAAgF;;;;ACzKhF;;;;;AAA8E,iBDyK9D,eCzK8D,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,EDyKrB,oBCzKqB,CDyKA,CCzKA,CAAA,CAAA,EDyKE,kBAAA,CAAA,GAAA,CAAA,OCzKF;AAQ9E;;;iBARgB,8BAA8B,yBAAyB,UAAQ;iBAQ/D,qCAAqC,mBAAiB,KAAK;;AFZ3E;AAUA;;;;;;;;;AA+CwB;AAUxB;;;;;;AAGqB,iBErBL,iBFqBK,CAAA,iBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,GAAA,GEpBgB,SFoBhB,CEpBwB,QFoBxB,CAAA;AAMrB;;;;;;;;;AAgBoB;AAUpB;;;;;;;AAEmC;iBE/BnB,8DACiC,gBACvC,aACI,mBAAiB,SAAS,QACnC,SAAS;;;;;;;;AFSe,iBGgEb,kBHhEa,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,CAAA,EGgEkC,sBHhElC,CGgEyD,CHhEzD,CAAA,CAAA,EAAA,IAAA"}
package/dist/index.d.ts CHANGED
@@ -1,23 +1,80 @@
1
1
  import { ReactNode } from "react";
2
- import { GetConfigOptions, GetReplaneSnapshotOptions, ReplaneClient, ReplaneClientOptions, ReplaneSnapshot, getReplaneSnapshot } from "@replanejs/sdk";
2
+ import { ConnectOptions, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, Replane, Replane as Replane$1, ReplaneContext, ReplaneContext as ReplaneContext$1, ReplaneError, ReplaneErrorCode, ReplaneLogger, ReplaneLogger as ReplaneLogger$1, ReplaneOptions, ReplaneSnapshot, ReplaneSnapshot as ReplaneSnapshot$1, 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
+ }
7
64
  /**
8
65
  * Props for ReplaneProvider when using a pre-created client.
9
66
  */
10
67
  interface ReplaneProviderWithClientProps<T extends object = UntypedReplaneConfig> {
11
- /** Pre-created ReplaneClient instance */
12
- client: ReplaneClient<T>;
68
+ /** Pre-created Replane client instance */
69
+ client: Replane$1<T>;
13
70
  children: ReactNode;
14
71
  }
15
72
  /**
16
73
  * Props for ReplaneProvider when letting it manage the client internally.
17
74
  */
18
75
  interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfig> {
19
- /** Options to create or restore the ReplaneClient */
20
- options: ReplaneClientOptions<T>;
76
+ /** Options to create or restore the Replane client */
77
+ options: ReplaneProviderOptions<T>;
21
78
  children: ReactNode;
22
79
  /**
23
80
  * Optional snapshot from server-side rendering.
@@ -25,7 +82,7 @@ interface ReplaneProviderWithOptionsProps<T extends object = UntypedReplaneConfi
25
82
  * instead of fetching configs from the server.
26
83
  * The `options` will be used for live updates connection if provided.
27
84
  */
28
- snapshot?: ReplaneSnapshot<T>;
85
+ snapshot?: ReplaneSnapshot$1<T>;
29
86
  /**
30
87
  * Optional loading component to show while the client is initializing.
31
88
  * If not provided and suspense is false/undefined, children will not render until ready.
@@ -44,17 +101,17 @@ type ReplaneProviderProps<T extends object = UntypedReplaneConfig> = ReplaneProv
44
101
  /**
45
102
  * Type guard to check if props contain a pre-created client.
46
103
  */
47
-
48
104
  //#endregion
49
105
  //#region src/provider.d.ts
50
106
  /**
51
- * Provider component that makes a ReplaneClient available to the component tree.
107
+ * Provider component that makes a Replane client available to the component tree.
52
108
  *
53
- * Can be used in three ways:
109
+ * Can be used in several ways:
54
110
  *
55
111
  * 1. With a pre-created client:
56
112
  * ```tsx
57
- * const client = await createReplaneClient({ ... });
113
+ * const client = new Replane({ defaults: { ... } });
114
+ * await client.connect({ baseUrl: '...', sdkKey: '...' });
58
115
  * <ReplaneProvider client={client}>
59
116
  * <App />
60
117
  * </ReplaneProvider>
@@ -107,8 +164,8 @@ declare function ReplaneProvider<T extends object>(props: ReplaneProviderProps<T
107
164
  //# sourceMappingURL=provider.d.ts.map
108
165
  //#endregion
109
166
  //#region src/hooks.d.ts
110
- declare function useReplane<T extends object = UntypedReplaneConfig>(): ReplaneClient<T>;
111
- declare function useConfig<T>(name: string, options?: GetConfigOptions<T>): T;
167
+ declare function useReplane<T extends object = UntypedReplaneConfig>(): Replane$1<T>;
168
+ declare function useConfig<T>(name: string, options?: GetConfigOptions$1<T>): T;
112
169
  /**
113
170
  * Creates a typed version of useReplane hook.
114
171
  *
@@ -127,7 +184,7 @@ declare function useConfig<T>(name: string, options?: GetConfigOptions<T>): T;
127
184
  * }
128
185
  * ```
129
186
  */
130
- declare function createReplaneHook<TConfigs extends object>(): () => ReplaneClient<TConfigs>;
187
+ declare function createReplaneHook<TConfigs extends object>(): () => Replane$1<TConfigs>;
131
188
  /**
132
189
  * Creates a typed version of useConfig hook.
133
190
  *
@@ -146,22 +203,15 @@ declare function createReplaneHook<TConfigs extends object>(): () => ReplaneClie
146
203
  * }
147
204
  * ```
148
205
  */
149
- declare function createConfigHook<TConfigs extends object>(): <K extends keyof TConfigs>(name: K, options?: GetConfigOptions<TConfigs[K]>) => TConfigs[K];
150
- /**
151
- * Hook for creating stateful resources with cleanup support.
152
- * Unlike useMemo, this guarantees cleanup when dependencies change or on unmount.
153
- *
154
- * @param factory - Function that creates the resource
155
- * @param cleanup - Function that cleans up the resource
156
- * @param deps - Dependencies array (resource is recreated when these change)
157
- */
206
+ declare function createConfigHook<TConfigs extends object>(): <K extends keyof TConfigs>(name: K, options?: GetConfigOptions$1<TConfigs[K]>) => TConfigs[K];
207
+ //# sourceMappingURL=hooks.d.ts.map
158
208
  //#endregion
159
209
  //#region src/useReplaneClient.d.ts
160
210
  /**
161
211
  * Clear the suspense cache for a specific options configuration.
162
212
  * Useful for testing or when you need to force re-initialization.
163
213
  */
164
- declare function clearSuspenseCache<T extends object>(options?: ReplaneClientOptions<T>): void;
214
+ declare function clearSuspenseCache<T extends object>(options?: ReplaneProviderOptions<T>): void;
165
215
  //#endregion
166
- export { type GetReplaneSnapshotOptions, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
216
+ export { type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type ReplaneProviderOptions, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, type ReplaneSnapshot, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
167
217
  //# sourceMappingURL=index.d.ts.map
@@ -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":";;;;;KAOY,oBAAA,GAAuB;AASnC;;;AAEwB,UAFP,8BAEO,CAAA,UAAA,MAAA,GAF2C,oBAE3C,CAAA,CAAA;EAAC;EAAF,MACX,EADF,aACE,CADY,CACZ,CAAA;EAAS,QAAA,EAAT,SAAS;AAMrB;;;;AAEW,UAFM,+BAEN,CAAA,UAAA,MAAA,GAFyD,oBAEzD,CAAA,CAAA;EAAoB;EACV,OAOQ,EARlB,oBAQkB,CARG,CAQH,CAAA;EAAC,QAAjB,EAPD,SAOC;EAAe;AAMR;AAUpB;;;;EACoC,QAAhC,CAAA,EAjBS,eAiBT,CAjByB,CAiBzB,CAAA;EAA8B;;AACC;;;WAZxB;ECgHK;;;;;AAAgE;;;KDtGpE,wCAAwC,wBAChD,+BAA+B,KAC/B,gCAAgC;AE9CpC;;;;;;;;;;AFAA;AASA;;;;;;AAGqB;AAMrB;;;;;;;;;AAgBoB;AAUpB;;;;;;;AAEmC;;;;ACoGnC;;;;;AAAgF;;;;AClJhF;;;;;AAAoF;AAQpF;;;;;AAA4E;AAqC5E;;AACmD,iBDoGnC,eCpGmC,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,EDoGM,oBCpGN,CDoG2B,CCpG3B,CAAA,CAAA,EDoG6B,kBAAA,CAAA,GAAA,CAAA,OCpG7B;;;;iBA9CnC,8BAA8B,yBAAyB,cAAc;iBAQrE,qCAAqC,iBAAiB,KAAK;;AFR3E;AASA;;;;;;AAGqB;AAMrB;;;;;;;;;AAgBoB,iBEWJ,iBFXI,CAAA,iBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,GAAA,GEYiB,aFZjB,CEY+B,QFZ/B,CAAA;AAUpB;;;;;;;AAEmC;;;;ACoGnC;;;;;AAAgF;;iBC7EhE,8DACiC,gBACvC,aACI,iBAAiB,SAAS,QACnC,SAAS;;AAzEd;;;;;AAAoF;AAQpF;;;;;;;AFsCoC,iBGgFpB,kBHhFoB,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,CAAA,EGgF2B,oBHhF3B,CGgFgD,CHhFhD,CAAA,CAAA,EAAA,IAAA"}
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;AAUnC;;;;AAmBW,UAnBM,sBAmBN,CAAA,UAAA,MAAA,GAnBgD,oBAmBhD,CAAA,CAAA;EAAa;;;;EA4BA,OAAA,EAAA,MAAA;EAUP;;;;EAEE,MAAT,EAAA,MAAA;EAAO;AACI;AAMrB;;EAAgD,OAAoB,CAAA,EAnDxD,gBAmDwD;EAAoB;;;EAGnE,MAOQ,CAAA,EAzDlB,eAyDkB;EAAC;;AAMV;EAUR,QAAA,CAAA,EAAA,QAAoB,MArEL,CAqEK,IArEA,CAqEA,CArEE,CAqEF,CAAA,EAAA;EAAwC;;;;EAErC,gBAAA,CAAA,EAAA,MAAA;;;;ACqEnC;EAA+B,YAAA,CAAA,EAAA,MAAA;EAAA;;;AAAiD;;;;ACzKhF;;EAA0B,mBAAoB,CAAA,EAAA,MAAA;EAAoB;;AAAY;EAQ9D,OAAA,CAAA,EAAA,OF6CG,KE7CM;EAAA;;;EAA4C,KAAM,CAAA,EAAA,MAAA;AAAC;AAqC5E;;;AACqC,UFiBpB,8BEjBoB,CAAA,UAAA,MAAA,GFiB8B,oBEjB9B,CAAA,CAAA;EAAO;EAuB5B,MAAA,EFJN,SEIM,CFJE,CEIF,CAAA;EAAgB,QAAA,EFHpB,SEGoB;;;;;AAGlB,UFAG,+BEAH,CAAA,UAAA,MAAA,GFAsD,oBEAtD,CAAA,CAAA;EAAgB;EACjB,OAAC,EFCH,sBEDG,CFCoB,CEDpB,CAAA;EAAC,QAAA,EFEH,SEFG;;;;ACyEf;;;EAAuF,QAAxB,CAAA,EHhElD,iBGgEkD,CHhElC,CGgEkC,CAAA;EAAsB;;;;;WH1D1E;;;;;;;;;KAUC,wCAAwC,wBAChD,+BAA+B,KAC/B,gCAAgC;;;;;;;;;;AAxGpC;AAUA;;;;;;;;;AA+CwB;AAUxB;;;;;;AAGqB;AAMrB;;;;;;;;;AAgBoB;AAUpB;;;;;;;AAEmC;;;;ACqEnC;;;;;AAAgF;;;;ACzKhF;;;;;AAA8E,iBDyK9D,eCzK8D,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,EDyKrB,oBCzKqB,CDyKA,CCzKA,CAAA,CAAA,EDyKE,kBAAA,CAAA,GAAA,CAAA,OCzKF;AAQ9E;;;iBARgB,8BAA8B,yBAAyB,UAAQ;iBAQ/D,qCAAqC,mBAAiB,KAAK;;AFZ3E;AAUA;;;;;;;;;AA+CwB;AAUxB;;;;;;AAGqB,iBErBL,iBFqBK,CAAA,iBAAA,MAAA,CAAA,CAAA,CAAA,EAAA,GAAA,GEpBgB,SFoBhB,CEpBwB,QFoBxB,CAAA;AAMrB;;;;;;;;;AAgBoB;AAUpB;;;;;;;AAEmC;iBE/BnB,8DACiC,gBACvC,aACI,mBAAiB,SAAS,QACnC,SAAS;;;;;;;;AFSe,iBGgEb,kBHhEa,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,CAAA,EGgEkC,sBHhElC,CGgEyD,CHhEzD,CAAA,CAAA,EAAA,IAAA"}
package/dist/index.js CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
 
4
4
  import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
5
- import { createReplaneClient, getReplaneSnapshot, restoreReplaneClient } from "@replanejs/sdk";
5
+ import { Replane, Replane as Replane$1, ReplaneError, ReplaneErrorCode, getReplaneSnapshot } from "@replanejs/sdk";
6
6
  import { Fragment, jsx } from "react/jsx-runtime";
7
7
 
8
8
  //#region src/context.ts
9
- const ReplaneContext = createContext(null);
9
+ const ReplaneContext$1 = createContext(null);
10
10
 
11
11
  //#endregion
12
12
  //#region src/version.ts
13
- const VERSION = "0.8.20";
13
+ const VERSION = "0.9.0";
14
14
  const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
15
15
 
16
16
  //#endregion
@@ -20,7 +20,28 @@ function getCacheKey(options) {
20
20
  return `${options.baseUrl}:${options.sdkKey}`;
21
21
  }
22
22
  /**
23
- * Hook to manage ReplaneClient creation internally.
23
+ * Creates a Replane client and connects it.
24
+ */
25
+ async function createAndConnectClient(options) {
26
+ const client = new Replane$1({
27
+ logger: options.logger,
28
+ context: options.context,
29
+ defaults: options.defaults
30
+ });
31
+ await client.connect({
32
+ baseUrl: options.baseUrl,
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
+ });
41
+ return client;
42
+ }
43
+ /**
44
+ * Hook to manage Replane client creation internally.
24
45
  * Handles loading state and cleanup.
25
46
  */
26
47
  function useReplaneClientInternal(options) {
@@ -35,12 +56,9 @@ function useReplaneClientInternal(options) {
35
56
  let cancelled = false;
36
57
  async function initClient() {
37
58
  try {
38
- const client = await createReplaneClient({
39
- ...optionsRef.current,
40
- agent: optionsRef.current.agent ?? DEFAULT_AGENT
41
- });
59
+ const client = await createAndConnectClient(optionsRef.current);
42
60
  if (cancelled) {
43
- client.close();
61
+ client.disconnect();
44
62
  return;
45
63
  }
46
64
  clientRef.current = client;
@@ -63,7 +81,7 @@ function useReplaneClientInternal(options) {
63
81
  return () => {
64
82
  cancelled = true;
65
83
  if (clientRef.current) {
66
- clientRef.current.close();
84
+ clientRef.current.disconnect();
67
85
  clientRef.current = null;
68
86
  }
69
87
  };
@@ -82,10 +100,7 @@ function useReplaneClientSuspense(options) {
82
100
  if (cached.result) return cached.result;
83
101
  throw cached.promise;
84
102
  }
85
- const promise = createReplaneClient({
86
- ...options,
87
- agent: options.agent ?? DEFAULT_AGENT
88
- }).then((client) => {
103
+ const promise = createAndConnectClient(options).then((client) => {
89
104
  const entry = suspenseCache.get(cacheKey);
90
105
  if (entry) entry.result = client;
91
106
  return client;
@@ -106,101 +121,6 @@ function clearSuspenseCache(options) {
106
121
  else suspenseCache.clear();
107
122
  }
108
123
 
109
- //#endregion
110
- //#region src/hooks.ts
111
- function useReplane() {
112
- const context = useContext(ReplaneContext);
113
- if (!context) throw new Error("useReplane must be used within a ReplaneProvider");
114
- return context.client;
115
- }
116
- function useConfig(name, options) {
117
- const client = useReplane();
118
- const subscribe = useCallback((callback) => {
119
- return client.subscribe(name, callback);
120
- }, [client, name]);
121
- const get = useCallback(() => {
122
- return client.get(name, options);
123
- }, [
124
- client,
125
- name,
126
- options
127
- ]);
128
- const value = useSyncExternalStore(subscribe, get, get);
129
- return value;
130
- }
131
- /**
132
- * Creates a typed version of useReplane hook.
133
- *
134
- * @example
135
- * ```tsx
136
- * interface AppConfigs {
137
- * theme: { darkMode: boolean };
138
- * features: { beta: boolean };
139
- * }
140
- *
141
- * const useAppReplane = createReplaneHook<AppConfigs>();
142
- *
143
- * function MyComponent() {
144
- * const replane = useAppReplane();
145
- * // replane.get("theme") returns { darkMode: boolean }
146
- * }
147
- * ```
148
- */
149
- function createReplaneHook() {
150
- return function useTypedReplane() {
151
- return useReplane();
152
- };
153
- }
154
- /**
155
- * Creates a typed version of useConfig hook.
156
- *
157
- * @example
158
- * ```tsx
159
- * interface AppConfigs {
160
- * theme: { darkMode: boolean };
161
- * features: { beta: boolean };
162
- * }
163
- *
164
- * const useAppConfig = createConfigHook<AppConfigs>();
165
- *
166
- * function MyComponent() {
167
- * const theme = useAppConfig("theme");
168
- * // theme is typed as { darkMode: boolean }
169
- * }
170
- * ```
171
- */
172
- function createConfigHook() {
173
- return function useTypedConfig(name, options) {
174
- return useConfig(String(name), options);
175
- };
176
- }
177
- /**
178
- * Hook for creating stateful resources with cleanup support.
179
- * Unlike useMemo, this guarantees cleanup when dependencies change or on unmount.
180
- *
181
- * @param factory - Function that creates the resource
182
- * @param cleanup - Function that cleans up the resource
183
- * @param deps - Dependencies array (resource is recreated when these change)
184
- */
185
- function useStateful(factory, cleanup, deps) {
186
- const valueRef = useRef(null);
187
- const initializedRef = useRef(false);
188
- if (!initializedRef.current) {
189
- valueRef.current = factory();
190
- initializedRef.current = true;
191
- }
192
- useEffect(() => {
193
- if (valueRef.current === null) valueRef.current = factory();
194
- return () => {
195
- if (valueRef.current !== null) {
196
- cleanup(valueRef.current);
197
- valueRef.current = null;
198
- }
199
- };
200
- }, deps);
201
- return valueRef.current;
202
- }
203
-
204
124
  //#endregion
205
125
  //#region src/types.ts
206
126
  /**
@@ -216,33 +136,53 @@ function hasClient(props) {
216
136
  * Internal provider component for pre-created client.
217
137
  */
218
138
  function ReplaneProviderWithClient({ client, children }) {
219
- const value = useMemo(() => ({ client }), [client]);
220
- return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
139
+ const value = useMemo(() => ({ replane: client }), [client]);
140
+ return /* @__PURE__ */ jsx(ReplaneContext$1.Provider, {
221
141
  value,
222
142
  children
223
143
  });
224
144
  }
225
145
  /**
226
146
  * Internal provider component for restoring client from snapshot.
227
- * Uses restoreReplaneClient which is synchronous.
147
+ * Creates a Replane client synchronously and connects in background.
228
148
  */
229
149
  function ReplaneProviderWithSnapshot({ options, snapshot, children }) {
230
- const client = useStateful(() => restoreReplaneClient({
150
+ const replaneRef = useRef(void 0);
151
+ if (!replaneRef.current) replaneRef.current = new Replane$1({
231
152
  snapshot,
232
- connection: {
153
+ logger: options.logger,
154
+ context: options.context,
155
+ defaults: options.defaults
156
+ });
157
+ useEffect(() => {
158
+ replaneRef.current.connect({
233
159
  baseUrl: options.baseUrl,
234
160
  sdkKey: options.sdkKey,
235
- fetchFn: options.fetchFn,
236
- requestTimeoutMs: options.requestTimeoutMs,
161
+ connectTimeoutMs: options.connectTimeoutMs,
237
162
  retryDelayMs: options.retryDelayMs,
163
+ requestTimeoutMs: options.requestTimeoutMs,
238
164
  inactivityTimeoutMs: options.inactivityTimeoutMs,
239
- logger: options.logger,
165
+ fetchFn: options.fetchFn,
240
166
  agent: options.agent ?? DEFAULT_AGENT
241
- },
242
- context: options.context
243
- }), (c) => c.close(), [snapshot, options]);
244
- const value = useMemo(() => ({ client }), [client]);
245
- return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
167
+ }).catch((err) => {
168
+ (options.logger ?? console)?.error("Failed to connect Replane client", err);
169
+ });
170
+ return () => {
171
+ replaneRef.current.disconnect();
172
+ };
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
+ ]);
184
+ const value = useMemo(() => ({ replane: replaneRef.current }), []);
185
+ return /* @__PURE__ */ jsx(ReplaneContext$1.Provider, {
246
186
  value,
247
187
  children
248
188
  });
@@ -255,8 +195,8 @@ function ReplaneProviderWithOptions({ options, children, loader }) {
255
195
  const state = useReplaneClientInternal(options);
256
196
  if (state.status === "loading") return /* @__PURE__ */ jsx(Fragment, { children: loader ?? null });
257
197
  if (state.status === "error") throw state.error;
258
- const value = { client: state.client };
259
- return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
198
+ const value = { replane: state.client };
199
+ return /* @__PURE__ */ jsx(ReplaneContext$1.Provider, {
260
200
  value,
261
201
  children
262
202
  });
@@ -266,20 +206,21 @@ function ReplaneProviderWithOptions({ options, children, loader }) {
266
206
  */
267
207
  function ReplaneProviderWithSuspense({ options, children }) {
268
208
  const client = useReplaneClientSuspense(options);
269
- const value = useMemo(() => ({ client }), [client]);
270
- return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
209
+ const value = useMemo(() => ({ replane: client }), [client]);
210
+ return /* @__PURE__ */ jsx(ReplaneContext$1.Provider, {
271
211
  value,
272
212
  children
273
213
  });
274
214
  }
275
215
  /**
276
- * Provider component that makes a ReplaneClient available to the component tree.
216
+ * Provider component that makes a Replane client available to the component tree.
277
217
  *
278
- * Can be used in three ways:
218
+ * Can be used in several ways:
279
219
  *
280
220
  * 1. With a pre-created client:
281
221
  * ```tsx
282
- * const client = await createReplaneClient({ ... });
222
+ * const client = new Replane({ defaults: { ... } });
223
+ * await client.connect({ baseUrl: '...', sdkKey: '...' });
283
224
  * <ReplaneProvider client={client}>
284
225
  * <App />
285
226
  * </ReplaneProvider>
@@ -339,5 +280,74 @@ function ReplaneProvider(props) {
339
280
  }
340
281
 
341
282
  //#endregion
342
- export { ReplaneProvider, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
283
+ //#region src/hooks.ts
284
+ function useReplane() {
285
+ const context = useContext(ReplaneContext$1);
286
+ if (!context) throw new Error("useReplane must be used within a ReplaneProvider");
287
+ return context.replane;
288
+ }
289
+ function useConfig(name, options) {
290
+ const client = useReplane();
291
+ const subscribe = useCallback((callback) => {
292
+ return client.subscribe(name, callback);
293
+ }, [client, name]);
294
+ const get = useCallback(() => {
295
+ return client.get(name, options);
296
+ }, [
297
+ client,
298
+ name,
299
+ options
300
+ ]);
301
+ const value = useSyncExternalStore(subscribe, get, get);
302
+ return value;
303
+ }
304
+ /**
305
+ * Creates a typed version of useReplane hook.
306
+ *
307
+ * @example
308
+ * ```tsx
309
+ * interface AppConfigs {
310
+ * theme: { darkMode: boolean };
311
+ * features: { beta: boolean };
312
+ * }
313
+ *
314
+ * const useAppReplane = createReplaneHook<AppConfigs>();
315
+ *
316
+ * function MyComponent() {
317
+ * const replane = useAppReplane();
318
+ * // replane.get("theme") returns { darkMode: boolean }
319
+ * }
320
+ * ```
321
+ */
322
+ function createReplaneHook() {
323
+ return function useTypedReplane() {
324
+ return useReplane();
325
+ };
326
+ }
327
+ /**
328
+ * Creates a typed version of useConfig hook.
329
+ *
330
+ * @example
331
+ * ```tsx
332
+ * interface AppConfigs {
333
+ * theme: { darkMode: boolean };
334
+ * features: { beta: boolean };
335
+ * }
336
+ *
337
+ * const useAppConfig = createConfigHook<AppConfigs>();
338
+ *
339
+ * function MyComponent() {
340
+ * const theme = useAppConfig("theme");
341
+ * // theme is typed as { darkMode: boolean }
342
+ * }
343
+ * ```
344
+ */
345
+ function createConfigHook() {
346
+ return function useTypedConfig(name, options) {
347
+ return useConfig(String(name), options);
348
+ };
349
+ }
350
+
351
+ //#endregion
352
+ export { Replane, ReplaneError, ReplaneErrorCode, ReplaneProvider, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
343
353
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["options: ReplaneClientOptions<T>","err: unknown","options?: ReplaneClientOptions<T>","name: string","options?: GetConfigOptions<T>","callback: () => void","name: K","options?: GetConfigOptions<TConfigs[K]>","factory: () => T","cleanup: (value: T) => void","deps: React.DependencyList","props: ReplaneProviderProps<T>","value: ReplaneContextValue<T>","props: ReplaneProviderProps<T>"],"sources":["../src/context.ts","../src/version.ts","../src/useReplaneClient.ts","../src/hooks.ts","../src/types.ts","../src/provider.tsx"],"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.8.20\";\nexport const DEFAULT_AGENT = `replane-js-react/${VERSION}`;\n","\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { createReplaneClient } from \"@replanejs/sdk\";\nimport type { ReplaneClient, ReplaneClientOptions } from \"@replanejs/sdk\";\nimport { DEFAULT_AGENT } from \"./version\";\n\ntype ClientState<T extends object> =\n | { status: \"loading\"; client: null; error: null }\n | { status: \"ready\"; client: ReplaneClient<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<ReplaneClient<any>>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result?: ReplaneClient<any>;\n error?: Error;\n }\n>();\n\nfunction getCacheKey<T extends object>(options: ReplaneClientOptions<T>): string {\n return `${options.baseUrl}:${options.sdkKey}`;\n}\n\ntype ErrorConstructor = new (message: string, options?: { cause?: unknown }) => Error;\n\n/**\n * Hook to manage ReplaneClient 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: ReplaneClientOptions<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<ReplaneClient<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 createReplaneClient<T>({\n ...optionsRef.current,\n agent: optionsRef.current.agent ?? DEFAULT_AGENT,\n });\n if (cancelled) {\n client.close();\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.close();\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: ReplaneClientOptions<T>\n): ReplaneClient<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 ReplaneClient<T>;\n }\n // Still loading, throw the promise\n throw cached.promise;\n }\n\n // First time - create the promise\n const promise = createReplaneClient<T>({\n ...options,\n agent: options.agent ?? DEFAULT_AGENT,\n })\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?: ReplaneClientOptions<T>): void {\n if (options) {\n suspenseCache.delete(getCacheKey(options));\n } else {\n suspenseCache.clear();\n }\n}\n","\"use client\";\n\nimport { useCallback, useContext, useEffect, useRef, useSyncExternalStore } from \"react\";\nimport { ReplaneContext } from \"./context\";\nimport type { UntypedReplaneConfig } from \"./types\";\nimport type { ReplaneClient, GetConfigOptions } from \"@replanejs/sdk\";\n\nexport function useReplane<T extends object = UntypedReplaneConfig>(): ReplaneClient<T> {\n const context = useContext(ReplaneContext);\n if (!context) {\n throw new Error(\"useReplane must be used within a ReplaneProvider\");\n }\n return context.client as ReplaneClient<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(): ReplaneClient<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\n/**\n * Hook for creating stateful resources with cleanup support.\n * Unlike useMemo, this guarantees cleanup when dependencies change or on unmount.\n *\n * @param factory - Function that creates the resource\n * @param cleanup - Function that cleans up the resource\n * @param deps - Dependencies array (resource is recreated when these change)\n */\nexport function useStateful<T>(\n factory: () => T,\n cleanup: (value: T) => void,\n deps: React.DependencyList\n): T {\n const valueRef = useRef<T | null>(null);\n const initializedRef = useRef(false);\n\n // Create initial value synchronously on first render\n if (!initializedRef.current) {\n valueRef.current = factory();\n initializedRef.current = true;\n }\n\n useEffect(() => {\n // On mount or deps change, we may need to recreate\n // If this is not the initial mount, recreate the value\n if (valueRef.current === null) {\n valueRef.current = factory();\n }\n\n return () => {\n if (valueRef.current !== null) {\n cleanup(valueRef.current);\n valueRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n\n return valueRef.current as T;\n}\n","import type {\n ReplaneClient,\n ReplaneClientOptions,\n ReplaneSnapshot,\n} from \"@replanejs/sdk\";\nimport type { ReactNode } from \"react\";\n\nexport type UntypedReplaneConfig = Record<string, unknown>;\n\nexport interface ReplaneContextValue<T extends object = UntypedReplaneConfig> {\n client: ReplaneClient<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 ReplaneClient instance */\n client: ReplaneClient<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 ReplaneClient */\n options: ReplaneClientOptions<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 { useMemo } from \"react\";\nimport { restoreReplaneClient } from \"@replanejs/sdk\";\nimport { ReplaneContext } from \"./context\";\nimport { useReplaneClientInternal, useReplaneClientSuspense } from \"./useReplaneClient\";\nimport { useStateful } from \"./hooks\";\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>>(() => ({ client }), [client]);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Internal provider component for restoring client from snapshot.\n * Uses restoreReplaneClient which is synchronous.\n */\nfunction ReplaneProviderWithSnapshot<T extends object>({\n options,\n snapshot,\n children,\n}: ReplaneProviderWithOptionsProps<T> & {\n snapshot: NonNullable<ReplaneProviderWithOptionsProps<T>[\"snapshot\"]>;\n}) {\n const client = useStateful(\n () =>\n restoreReplaneClient<T>({\n snapshot,\n connection: {\n baseUrl: options.baseUrl,\n sdkKey: options.sdkKey,\n fetchFn: options.fetchFn,\n requestTimeoutMs: options.requestTimeoutMs,\n retryDelayMs: options.retryDelayMs,\n inactivityTimeoutMs: options.inactivityTimeoutMs,\n logger: options.logger,\n agent: options.agent ?? DEFAULT_AGENT,\n },\n context: options.context,\n }),\n (c) => c.close(),\n [snapshot, options]\n );\n const value = useMemo<ReplaneContextValue<T>>(() => ({ client }), [client]);\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> = { client: 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>>(() => ({ client }), [client]);\n return <ReplaneContext.Provider value={value}>{children}</ReplaneContext.Provider>;\n}\n\n/**\n * Provider component that makes a ReplaneClient available to the component tree.\n *\n * Can be used in three ways:\n *\n * 1. With a pre-created client:\n * ```tsx\n * const client = await createReplaneClient({ ... });\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"],"mappings":";;;;;;;;AAMA,MAAa,iBAAiB,cAA+C,KAAK;;;;ACLlF,MAAa,UAAU;AACvB,MAAa,iBAAiB,mBAAmB,QAAQ;;;;ACWzD,MAAM,gBAAgB,IAAI;AAW1B,SAAS,YAA8BA,SAA0C;AAC/E,SAAQ,EAAE,QAAQ,QAAQ,GAAG,QAAQ,OAAO;AAC7C;;;;;AASD,SAAgB,yBACdA,SACgB;CAChB,MAAM,CAAC,OAAO,SAAS,GAAG,SAAyB;EACjD,QAAQ;EACR,QAAQ;EACR,OAAO;CACR,EAAC;CACF,MAAM,YAAY,OAAgC,KAAK;CACvD,MAAM,aAAa,OAAO,QAAQ;AAElC,WAAU,MAAM;EACd,IAAI,YAAY;EAEhB,eAAe,aAAa;AAC1B,OAAI;IACF,MAAM,SAAS,MAAM,oBAAuB;KAC1C,GAAG,WAAW;KACd,OAAO,WAAW,QAAQ,SAAS;IACpC,EAAC;AACF,QAAI,WAAW;AACb,YAAO,OAAO;AACd;IACD;AACD,cAAU,UAAU;AACpB,aAAS;KAAE,QAAQ;KAAS;KAAQ,OAAO;IAAM,EAAC;GACnD,SAAQ,KAAK;AACZ,QAAI,UAAW;IACf,MAAM,QACJ,eAAe,QAAQ,MAAM,IAAK,MAA2B,OAAO,IAAI,EAAE,EAAE,OAAO,IAAK;AAC1F,aAAS;KAAE,QAAQ;KAAS,QAAQ;KAAM;IAAO,EAAC;GACnD;EACF;AAED,cAAY;AAEZ,SAAO,MAAM;AACX,eAAY;AACZ,OAAI,UAAU,SAAS;AACrB,cAAU,QAAQ,OAAO;AACzB,cAAU,UAAU;GACrB;EACF;CACF,GAAE,CAAE,EAAC;AAEN,QAAO;AACR;;;;;AAOD,SAAgB,yBACdA,SACkB;CAClB,MAAM,WAAW,YAAY,QAAQ;CACrC,MAAM,SAAS,cAAc,IAAI,SAAS;AAE1C,KAAI,QAAQ;AACV,MAAI,OAAO,MACT,OAAM,OAAO;AAEf,MAAI,OAAO,OACT,QAAO,OAAO;AAGhB,QAAM,OAAO;CACd;CAGD,MAAM,UAAU,oBAAuB;EACrC,GAAG;EACH,OAAO,QAAQ,SAAS;CACzB,EAAC,CACC,KAAK,CAAC,WAAW;EAChB,MAAM,QAAQ,cAAc,IAAI,SAAS;AACzC,MAAI,MACF,OAAM,SAAS;AAEjB,SAAO;CACR,EAAC,CACD,MAAM,CAACC,QAAiB;EACvB,MAAM,QAAQ,cAAc,IAAI,SAAS;AACzC,MAAI,MACF,OAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,IAAI;AAElE,QAAM;CACP,EAAC;AAEJ,eAAc,IAAI,UAAU,EAAE,QAAS,EAAC;AACxC,OAAM;AACP;;;;;AAMD,SAAgB,mBAAqCC,SAAyC;AAC5F,KAAI,QACF,eAAc,OAAO,YAAY,QAAQ,CAAC;KAE1C,eAAc,OAAO;AAExB;;;;ACpID,SAAgB,aAAwE;CACtF,MAAM,UAAU,WAAW,eAAe;AAC1C,MAAK,QACH,OAAM,IAAI,MAAM;AAElB,QAAO,QAAQ;AAChB;AAED,SAAgB,UAAaC,MAAcC,SAAkC;CAC3E,MAAM,SAAS,YAAY;CAE3B,MAAM,YAAY,YAChB,CAACC,aAAyB;AACxB,SAAO,OAAO,UAAU,MAAM,SAAS;CACxC,GACD,CAAC,QAAQ,IAAK,EACf;CAED,MAAM,MAAM,YAAY,MAAM;AAC5B,SAAO,OAAO,IAAI,MAAM,QAAQ;CACjC,GAAE;EAAC;EAAQ;EAAM;CAAQ,EAAC;CAE3B,MAAM,QAAQ,qBAAqB,WAAW,KAAK,IAAI;AAEvD,QAAO;AACR;;;;;;;;;;;;;;;;;;;AAoBD,SAAgB,oBAA6C;AAC3D,QAAO,SAAS,kBAA2C;AACzD,SAAO,YAAsB;CAC9B;AACF;;;;;;;;;;;;;;;;;;;AAoBD,SAAgB,mBAA4C;AAC1D,QAAO,SAAS,eACdC,MACAC,SACa;AACb,SAAO,UAAuB,OAAO,KAAK,EAAE,QAAQ;CACrD;AACF;;;;;;;;;AAUD,SAAgB,YACdC,SACAC,SACAC,MACG;CACH,MAAM,WAAW,OAAiB,KAAK;CACvC,MAAM,iBAAiB,OAAO,MAAM;AAGpC,MAAK,eAAe,SAAS;AAC3B,WAAS,UAAU,SAAS;AAC5B,iBAAe,UAAU;CAC1B;AAED,WAAU,MAAM;AAGd,MAAI,SAAS,YAAY,KACvB,UAAS,UAAU,SAAS;AAG9B,SAAO,MAAM;AACX,OAAI,SAAS,YAAY,MAAM;AAC7B,YAAQ,SAAS,QAAQ;AACzB,aAAS,UAAU;GACpB;EACF;CAEF,GAAE,KAAK;AAER,QAAO,SAAS;AACjB;;;;;;;AClED,SAAgB,UACdC,OAC4C;AAC5C,QAAO,YAAY,SAAS,MAAM;AACnC;;;;;;;AC3CD,SAAS,0BAA4C,EACnD,QACA,UACkC,EAAE;CACpC,MAAM,QAAQ,QAAgC,OAAO,EAAE,OAAQ,IAAG,CAAC,MAAO,EAAC;AAC3E,wBAAO,IAAC,eAAe;EAAgB;EAAQ;GAAmC;AACnF;;;;;AAMD,SAAS,4BAA8C,EACrD,SACA,UACA,UAGD,EAAE;CACD,MAAM,SAAS,YACb,MACE,qBAAwB;EACtB;EACA,YAAY;GACV,SAAS,QAAQ;GACjB,QAAQ,QAAQ;GAChB,SAAS,QAAQ;GACjB,kBAAkB,QAAQ;GAC1B,cAAc,QAAQ;GACtB,qBAAqB,QAAQ;GAC7B,QAAQ,QAAQ;GAChB,OAAO,QAAQ,SAAS;EACzB;EACD,SAAS,QAAQ;CAClB,EAAC,EACJ,CAAC,MAAM,EAAE,OAAO,EAChB,CAAC,UAAU,OAAQ,EACpB;CACD,MAAM,QAAQ,QAAgC,OAAO,EAAE,OAAQ,IAAG,CAAC,MAAO,EAAC;AAC3E,wBAAO,IAAC,eAAe;EAAgB;EAAQ;GAAmC;AACnF;;;;;AAMD,SAAS,2BAA6C,EACpD,SACA,UACA,QACmC,EAAE;CACrC,MAAM,QAAQ,yBAA4B,QAAQ;AAElD,KAAI,MAAM,WAAW,UACnB,wBAAO,0BAAG,UAAU,OAAQ;AAG9B,KAAI,MAAM,WAAW,QAEnB,OAAM,MAAM;CAGd,MAAMC,QAAgC,EAAE,QAAQ,MAAM,OAAQ;AAC9D,wBAAO,IAAC,eAAe;EAAgB;EAAQ;GAAmC;AACnF;;;;AAKD,SAAS,4BAA8C,EACrD,SACA,UACmC,EAAE;CACrC,MAAM,SAAS,yBAA4B,QAAQ;CACnD,MAAM,QAAQ,QAAgC,OAAO,EAAE,OAAQ,IAAG,CAAC,MAAO,EAAC;AAC3E,wBAAO,IAAC,eAAe;EAAgB;EAAQ;GAAmC;AACnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DD,SAAgB,gBAAkCC,OAAgC;AAChF,KAAI,UAAU,MAAM,CAClB,wBAAO,IAAC,6BAA0B,GAAI,QAAS;AAIjD,KAAI,MAAM,SACR,wBAAO,IAAC;EAA4B,GAAI;EAAO,UAAU,MAAM;GAAY;AAG7E,KAAI,MAAM,SACR,wBAAO,IAAC,+BAA4B,GAAI,QAAS;AAGnD,wBAAO,IAAC,8BAA2B,GAAI,QAAS;AACjD"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@replanejs/react",
3
- "version": "0.8.20",
3
+ "version": "0.9.0",
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.8.20"
43
+ "@replanejs/sdk": "^0.9.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@testing-library/jest-dom": "^6.9.1",