@replanejs/react 0.9.2 → 1.0.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 +26 -7
- package/dist/index.cjs +7 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,10 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">Replane React SDK</h1>
|
|
2
|
+
<p align="center">Dynamic configuration for React applications.</p>
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
<p align="center">
|
|
5
|
+
<a href="https://cloud.replane.dev"><img src="https://img.shields.io/badge/Try-Replane%20Cloud-blue" alt="Replane Cloud"></a>
|
|
6
|
+
<a href="https://www.npmjs.com/package/@replanejs/react"><img src="https://img.shields.io/npm/v/@replanejs/react" alt="npm"></a>
|
|
7
|
+
<a href="https://github.com/replane-dev/replane-javascript/blob/main/LICENSE"><img src="https://img.shields.io/github/license/replane-dev/replane-javascript" alt="License"></a>
|
|
8
|
+
<a href="https://github.com/orgs/replane-dev/discussions"><img src="https://img.shields.io/badge/discussions-join-blue?logo=github" alt="Community"></a>
|
|
9
|
+
</p>
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
<picture>
|
|
12
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/replane-dev/replane/main/public/replane-window-screenshot-dark-v1.png">
|
|
13
|
+
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/replane-dev/replane/main/public/replane-window-screenshot-light-with-border-v2.jpg">
|
|
14
|
+
<img alt="Replane Screenshot" src="https://raw.githubusercontent.com/replane-dev/replane/main/public/replane-window-screenshot-light-with-border-v2.jpg">
|
|
15
|
+
</picture>
|
|
16
|
+
|
|
17
|
+
[Replane](https://github.com/replane-dev/replane) is a dynamic configuration manager. Store feature flags, app settings, and operational config in one place—with version history, optional approvals, and realtime sync to your services. No redeploys needed.
|
|
18
|
+
|
|
19
|
+
## Why Dynamic Configuration?
|
|
20
|
+
|
|
21
|
+
- **Feature flags** – toggle features, run A/B tests, roll out to user segments
|
|
22
|
+
- **Operational tuning** – adjust limits, TTLs, and timeouts without redeploying
|
|
23
|
+
- **Per-environment settings** – different values for production, staging, dev
|
|
24
|
+
- **Incident response** – instantly revert to a known-good version
|
|
25
|
+
- **Cross-service configuration** – share settings with realtime sync
|
|
26
|
+
- **Non-engineer access** – safe editing with schema validation
|
|
8
27
|
|
|
9
28
|
## Installation
|
|
10
29
|
|
|
@@ -30,7 +49,7 @@ function App() {
|
|
|
30
49
|
return (
|
|
31
50
|
<ReplaneProvider
|
|
32
51
|
connection={{
|
|
33
|
-
baseUrl: "https://
|
|
52
|
+
baseUrl: "https://cloud.replane.dev", // or your self-hosted URL
|
|
34
53
|
sdkKey: "your-sdk-key",
|
|
35
54
|
}}
|
|
36
55
|
loader={<div>Loading...</div>}
|
|
@@ -129,7 +148,7 @@ Integrates with React Suspense for loading states:
|
|
|
129
148
|
<Suspense fallback={<LoadingSpinner />}>
|
|
130
149
|
<ReplaneProvider
|
|
131
150
|
connection={{
|
|
132
|
-
baseUrl: "https://
|
|
151
|
+
baseUrl: "https://cloud.replane.dev", // or your self-hosted URL
|
|
133
152
|
sdkKey: "your-sdk-key",
|
|
134
153
|
}}
|
|
135
154
|
suspense
|
package/dist/index.cjs
CHANGED
|
@@ -144,7 +144,7 @@ function hasClient(props) {
|
|
|
144
144
|
|
|
145
145
|
//#endregion
|
|
146
146
|
//#region src/version.ts
|
|
147
|
-
const VERSION = "0.
|
|
147
|
+
const VERSION = "1.0.0";
|
|
148
148
|
const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
|
|
149
149
|
|
|
150
150
|
//#endregion
|
|
@@ -355,6 +355,12 @@ function createConfigHook() {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
//#endregion
|
|
358
|
+
Object.defineProperty(exports, 'InMemoryReplaneClient', {
|
|
359
|
+
enumerable: true,
|
|
360
|
+
get: function () {
|
|
361
|
+
return __replanejs_sdk.InMemoryReplaneClient;
|
|
362
|
+
}
|
|
363
|
+
});
|
|
358
364
|
Object.defineProperty(exports, 'Replane', {
|
|
359
365
|
enumerable: true,
|
|
360
366
|
get: function () {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
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";
|
|
2
|
+
import { Condition, ConnectOptions, ConnectOptions as ConnectOptions$1, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, InMemoryReplaneClient, InMemoryReplaneClientOptions, Override, Replane, Replane as Replane$1, ReplaneContext, ReplaneError, ReplaneErrorCode, ReplaneLogger, ReplaneOptions, ReplaneOptions as ReplaneOptions$1, ReplaneSnapshot, SetConfigOptions, getReplaneSnapshot } from "@replanejs/sdk";
|
|
3
3
|
import { ReactNode } from "react";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
@@ -158,5 +158,5 @@ declare function createConfigHook<TConfigs extends object>(): <K extends keyof T
|
|
|
158
158
|
*/
|
|
159
159
|
declare function clearSuspenseCache(connection?: ConnectOptions$1): void;
|
|
160
160
|
//#endregion
|
|
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 };
|
|
161
|
+
export { type Condition, type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, InMemoryReplaneClient, type InMemoryReplaneClientOptions, type Override, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, type ReplaneSnapshot, type SetConfigOptions, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
|
|
162
162
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
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";
|
|
2
|
+
import { Condition, ConnectOptions, ConnectOptions as ConnectOptions$1, GetConfigOptions, GetConfigOptions as GetConfigOptions$1, GetReplaneSnapshotOptions, InMemoryReplaneClient, InMemoryReplaneClientOptions, Override, Replane, Replane as Replane$1, ReplaneContext, ReplaneError, ReplaneErrorCode, ReplaneLogger, ReplaneOptions, ReplaneOptions as ReplaneOptions$1, ReplaneSnapshot, SetConfigOptions, getReplaneSnapshot } from "@replanejs/sdk";
|
|
3
3
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
@@ -158,5 +158,5 @@ declare function createConfigHook<TConfigs extends object>(): <K extends keyof T
|
|
|
158
158
|
*/
|
|
159
159
|
declare function clearSuspenseCache(connection?: ConnectOptions$1): void;
|
|
160
160
|
//#endregion
|
|
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 };
|
|
161
|
+
export { type Condition, type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, InMemoryReplaneClient, type InMemoryReplaneClientOptions, type Override, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, type ReplaneSnapshot, type SetConfigOptions, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
|
|
162
162
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
|
-
import { Replane, Replane as Replane$1, ReplaneError, ReplaneErrorCode, getReplaneSnapshot } from "@replanejs/sdk";
|
|
5
|
+
import { InMemoryReplaneClient, 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
|
|
@@ -121,7 +121,7 @@ function hasClient(props) {
|
|
|
121
121
|
|
|
122
122
|
//#endregion
|
|
123
123
|
//#region src/version.ts
|
|
124
|
-
const VERSION = "0.
|
|
124
|
+
const VERSION = "1.0.0";
|
|
125
125
|
const DEFAULT_AGENT = `replane-js-react/${VERSION}`;
|
|
126
126
|
|
|
127
127
|
//#endregion
|
|
@@ -332,5 +332,5 @@ function createConfigHook() {
|
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
//#endregion
|
|
335
|
-
export { Replane, ReplaneError, ReplaneErrorCode, ReplaneProvider, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
|
|
335
|
+
export { InMemoryReplaneClient, Replane, ReplaneError, ReplaneErrorCode, ReplaneProvider, clearSuspenseCache, createConfigHook, createReplaneHook, getReplaneSnapshot, useConfig, useReplane };
|
|
336
336
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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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"}
|
|
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 = \"1.0.0\";\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.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "React SDK for Replane - feature flags and remote configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -40,17 +40,17 @@
|
|
|
40
40
|
"react": ">=18.0.0"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@replanejs/sdk": "^0.
|
|
43
|
+
"@replanejs/sdk": "^1.0.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@testing-library/jest-dom": "^6.9.1",
|
|
47
47
|
"@testing-library/react": "^16.3.1",
|
|
48
48
|
"@types/node": "^22.19.3",
|
|
49
|
-
"@types/react": "^
|
|
50
|
-
"@types/react-dom": "^
|
|
49
|
+
"@types/react": "^19.2.7",
|
|
50
|
+
"@types/react-dom": "^19.2.3",
|
|
51
51
|
"jsdom": "^27.3.0",
|
|
52
|
-
"react": "^
|
|
53
|
-
"react-dom": "^
|
|
52
|
+
"react": "^19.2.3",
|
|
53
|
+
"react-dom": "^19.2.3",
|
|
54
54
|
"tsdown": "^0.11.9",
|
|
55
55
|
"typescript": "^5.4.0",
|
|
56
56
|
"vitest": "^3.2.4"
|