@replanejs/react 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +224 -0
- package/dist/index.d.cts +100 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +100 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +199 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dmitry Tilyupo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
const react = __toESM(require("react"));
|
|
25
|
+
const __replanejs_sdk = __toESM(require("@replanejs/sdk"));
|
|
26
|
+
const react_jsx_runtime = __toESM(require("react/jsx-runtime"));
|
|
27
|
+
|
|
28
|
+
//#region src/context.ts
|
|
29
|
+
const ReplaneContext = (0, react.createContext)(null);
|
|
30
|
+
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/useReplaneClient.ts
|
|
33
|
+
const suspenseCache = new Map();
|
|
34
|
+
function getCacheKey(options) {
|
|
35
|
+
return `${options.baseUrl}:${options.sdkKey}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Hook to manage ReplaneClient creation internally.
|
|
39
|
+
* Handles loading state and cleanup.
|
|
40
|
+
*/
|
|
41
|
+
function useReplaneClient(options, onError) {
|
|
42
|
+
const [state, setState] = (0, react.useState)({
|
|
43
|
+
status: "loading",
|
|
44
|
+
client: null,
|
|
45
|
+
error: null
|
|
46
|
+
});
|
|
47
|
+
const clientRef = (0, react.useRef)(null);
|
|
48
|
+
const optionsRef = (0, react.useRef)(options);
|
|
49
|
+
(0, react.useEffect)(() => {
|
|
50
|
+
let cancelled = false;
|
|
51
|
+
async function initClient() {
|
|
52
|
+
try {
|
|
53
|
+
const client = await (0, __replanejs_sdk.createReplaneClient)(optionsRef.current);
|
|
54
|
+
if (cancelled) {
|
|
55
|
+
client.close();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
clientRef.current = client;
|
|
59
|
+
setState({
|
|
60
|
+
status: "ready",
|
|
61
|
+
client,
|
|
62
|
+
error: null
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (cancelled) return;
|
|
66
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
67
|
+
setState({
|
|
68
|
+
status: "error",
|
|
69
|
+
client: null,
|
|
70
|
+
error
|
|
71
|
+
});
|
|
72
|
+
onError?.(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
initClient();
|
|
76
|
+
return () => {
|
|
77
|
+
cancelled = true;
|
|
78
|
+
if (clientRef.current) {
|
|
79
|
+
clientRef.current.close();
|
|
80
|
+
clientRef.current = null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}, []);
|
|
84
|
+
return state;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Hook for Suspense-based client creation.
|
|
88
|
+
* Throws a promise while loading, throws error on failure.
|
|
89
|
+
*/
|
|
90
|
+
function useReplaneClientSuspense(options) {
|
|
91
|
+
const cacheKey = getCacheKey(options);
|
|
92
|
+
const cached = suspenseCache.get(cacheKey);
|
|
93
|
+
if (cached) {
|
|
94
|
+
if (cached.error) throw cached.error;
|
|
95
|
+
if (cached.result) return cached.result;
|
|
96
|
+
throw cached.promise;
|
|
97
|
+
}
|
|
98
|
+
const promise = (0, __replanejs_sdk.createReplaneClient)(options).then((client) => {
|
|
99
|
+
const entry = suspenseCache.get(cacheKey);
|
|
100
|
+
if (entry) entry.result = client;
|
|
101
|
+
return client;
|
|
102
|
+
}).catch((err) => {
|
|
103
|
+
const entry = suspenseCache.get(cacheKey);
|
|
104
|
+
if (entry) entry.error = err instanceof Error ? err : new Error(String(err));
|
|
105
|
+
throw err;
|
|
106
|
+
});
|
|
107
|
+
suspenseCache.set(cacheKey, { promise });
|
|
108
|
+
throw promise;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Clear the suspense cache for a specific options configuration.
|
|
112
|
+
* Useful for testing or when you need to force re-initialization.
|
|
113
|
+
*/
|
|
114
|
+
function clearSuspenseCache(options) {
|
|
115
|
+
if (options) suspenseCache.delete(getCacheKey(options));
|
|
116
|
+
else suspenseCache.clear();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
//#endregion
|
|
120
|
+
//#region src/types.ts
|
|
121
|
+
/**
|
|
122
|
+
* Type guard to check if props contain a pre-created client.
|
|
123
|
+
*/
|
|
124
|
+
function hasClient(props) {
|
|
125
|
+
return "client" in props && props.client !== void 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region src/provider.tsx
|
|
130
|
+
/**
|
|
131
|
+
* Internal provider component for pre-created client.
|
|
132
|
+
*/
|
|
133
|
+
function ReplaneProviderWithClient({ client, children }) {
|
|
134
|
+
const value = (0, react.useMemo)(() => ({ client }), [client]);
|
|
135
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
|
|
136
|
+
value,
|
|
137
|
+
children
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Internal provider component for options-based client creation (non-suspense).
|
|
142
|
+
*/
|
|
143
|
+
function ReplaneProviderWithOptions({ options, children, loader, onError }) {
|
|
144
|
+
const state = useReplaneClient(options, onError);
|
|
145
|
+
if (state.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: loader ?? null });
|
|
146
|
+
if (state.status === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: loader ?? null });
|
|
147
|
+
const value = { client: state.client };
|
|
148
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
|
|
149
|
+
value,
|
|
150
|
+
children
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Internal provider component for options-based client creation with Suspense.
|
|
155
|
+
*/
|
|
156
|
+
function ReplaneProviderWithSuspense({ options, children }) {
|
|
157
|
+
const client = useReplaneClientSuspense(options);
|
|
158
|
+
const value = (0, react.useMemo)(() => ({ client }), [client]);
|
|
159
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneContext.Provider, {
|
|
160
|
+
value,
|
|
161
|
+
children
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Provider component that makes a ReplaneClient available to the component tree.
|
|
166
|
+
*
|
|
167
|
+
* Can be used in two ways:
|
|
168
|
+
*
|
|
169
|
+
* 1. With a pre-created client:
|
|
170
|
+
* ```tsx
|
|
171
|
+
* const client = await createReplaneClient({ ... });
|
|
172
|
+
* <ReplaneProvider client={client}>
|
|
173
|
+
* <App />
|
|
174
|
+
* </ReplaneProvider>
|
|
175
|
+
* ```
|
|
176
|
+
*
|
|
177
|
+
* 2. With options (client managed internally):
|
|
178
|
+
* ```tsx
|
|
179
|
+
* <ReplaneProvider
|
|
180
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
181
|
+
* loader={<LoadingSpinner />}
|
|
182
|
+
* >
|
|
183
|
+
* <App />
|
|
184
|
+
* </ReplaneProvider>
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* 3. With Suspense:
|
|
188
|
+
* ```tsx
|
|
189
|
+
* <Suspense fallback={<LoadingSpinner />}>
|
|
190
|
+
* <ReplaneProvider
|
|
191
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
192
|
+
* suspense
|
|
193
|
+
* >
|
|
194
|
+
* <App />
|
|
195
|
+
* </ReplaneProvider>
|
|
196
|
+
* </Suspense>
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
function ReplaneProvider(props) {
|
|
200
|
+
if (hasClient(props)) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneProviderWithClient, { ...props });
|
|
201
|
+
if (props.suspense) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneProviderWithSuspense, { ...props });
|
|
202
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReplaneProviderWithOptions, { ...props });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/hooks.ts
|
|
207
|
+
function useReplane() {
|
|
208
|
+
const context = (0, react.useContext)(ReplaneContext);
|
|
209
|
+
if (!context) throw new Error("useReplane must be used within a ReplaneProvider");
|
|
210
|
+
return context;
|
|
211
|
+
}
|
|
212
|
+
function useConfig(name, options) {
|
|
213
|
+
const { client } = useReplane();
|
|
214
|
+
const value = (0, react.useSyncExternalStore)((onStoreChange) => {
|
|
215
|
+
return client.subscribe(name, onStoreChange);
|
|
216
|
+
}, () => client.get(name, options), () => client.get(name, options));
|
|
217
|
+
return value;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
//#endregion
|
|
221
|
+
exports.ReplaneProvider = ReplaneProvider;
|
|
222
|
+
exports.clearSuspenseCache = clearSuspenseCache;
|
|
223
|
+
exports.useConfig = useConfig;
|
|
224
|
+
exports.useReplane = useReplane;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
import { ReplaneClient, ReplaneClientOptions } from "@replanejs/sdk";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
interface ReplaneContextValue<T extends object = any> {
|
|
7
|
+
client: ReplaneClient<T>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Props for ReplaneProvider when using a pre-created client.
|
|
11
|
+
*/
|
|
12
|
+
interface ReplaneProviderWithClientProps<T extends object = any> {
|
|
13
|
+
/** Pre-created ReplaneClient instance */
|
|
14
|
+
client: ReplaneClient<T>;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Props for ReplaneProvider when letting it manage the client internally.
|
|
19
|
+
*/
|
|
20
|
+
interface ReplaneProviderWithOptionsProps<T extends object = any> {
|
|
21
|
+
/** Options to create the ReplaneClient */
|
|
22
|
+
options: ReplaneClientOptions<T>;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Optional loading component to show while the client is initializing.
|
|
26
|
+
* If not provided and suspense is false/undefined, children will not render until ready.
|
|
27
|
+
*/
|
|
28
|
+
loader?: ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* If true, uses React Suspense for loading state.
|
|
31
|
+
* The provider will throw a promise that Suspense can catch.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
suspense?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Callback when client initialization fails.
|
|
37
|
+
*/
|
|
38
|
+
onError?: (error: Error) => void;
|
|
39
|
+
}
|
|
40
|
+
type ReplaneProviderProps<T extends object = any> = ReplaneProviderWithClientProps<T> | ReplaneProviderWithOptionsProps<T>;
|
|
41
|
+
/**
|
|
42
|
+
* Type guard to check if props contain a pre-created client.
|
|
43
|
+
*/
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/provider.d.ts
|
|
46
|
+
/**
|
|
47
|
+
* Provider component that makes a ReplaneClient available to the component tree.
|
|
48
|
+
*
|
|
49
|
+
* Can be used in two ways:
|
|
50
|
+
*
|
|
51
|
+
* 1. With a pre-created client:
|
|
52
|
+
* ```tsx
|
|
53
|
+
* const client = await createReplaneClient({ ... });
|
|
54
|
+
* <ReplaneProvider client={client}>
|
|
55
|
+
* <App />
|
|
56
|
+
* </ReplaneProvider>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* 2. With options (client managed internally):
|
|
60
|
+
* ```tsx
|
|
61
|
+
* <ReplaneProvider
|
|
62
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
63
|
+
* loader={<LoadingSpinner />}
|
|
64
|
+
* >
|
|
65
|
+
* <App />
|
|
66
|
+
* </ReplaneProvider>
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* 3. With Suspense:
|
|
70
|
+
* ```tsx
|
|
71
|
+
* <Suspense fallback={<LoadingSpinner />}>
|
|
72
|
+
* <ReplaneProvider
|
|
73
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
74
|
+
* suspense
|
|
75
|
+
* >
|
|
76
|
+
* <App />
|
|
77
|
+
* </ReplaneProvider>
|
|
78
|
+
* </Suspense>
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function ReplaneProvider<T extends object>(props: ReplaneProviderProps<T>): react_jsx_runtime0.JSX.Element;
|
|
82
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/hooks.d.ts
|
|
85
|
+
declare function useReplane<T extends object = any>(): ReplaneContextValue<T>;
|
|
86
|
+
declare function useConfig<T>(name: string, options?: {
|
|
87
|
+
context?: Record<string, string | number | boolean | null>;
|
|
88
|
+
}): T;
|
|
89
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/useReplaneClient.d.ts
|
|
93
|
+
/**
|
|
94
|
+
* Clear the suspense cache for a specific options configuration.
|
|
95
|
+
* Useful for testing or when you need to force re-initialization.
|
|
96
|
+
*/
|
|
97
|
+
declare function clearSuspenseCache<T extends object>(options?: ReplaneClientOptions<T>): void;
|
|
98
|
+
//#endregion
|
|
99
|
+
export { type ReplaneContextValue, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, clearSuspenseCache, useConfig, useReplane };
|
|
100
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/provider.tsx","../src/hooks.ts","../src/useReplaneClient.ts"],"sourcesContent":[],"mappings":";;;;;UAIiB;UACP,cAAc;;AADxB;;;AACU,UAOO,8BAPP,CAAA,UAAA,MAAA,GAAA,GAAA,CAAA,CAAA;EAAa;EAON,MAAA,EAEP,aAFO,CAEO,CAFP,CAAA;EAA8B,QAAA,EAGnC,SAHmC;;;;AAG1B;AAOJ,UAAA,+BAA+B,CAAA,UAAA,MAAA,GAAA,GAAA,CAAA,CAAA;EAAA;EAAA,OAEhB,EAArB,oBAAqB,CAAA,CAAA,CAAA;EAAC,QAAtB,EACC,SADD;EAAoB;;;AAgBN;EAIb,MAAA,CAAA,EAdD,SAcC;EAAoB;;;;;EAEG,QAAA,CAAA,EAAA,OAAA;;;;ECgDnB,OAAA,CAAA,EAAA,CAAA,KAAA,EDtDI,KCsDW,EAAA,GAAA,IAAA;;AAA+C,KDlDlE,oBCkDkE,CAAA,UAAA,MAAA,GAAA,GAAA,CAAA,GDjD1E,8BCiD0E,CDjD3C,CCiD2C,CAAA,GDhD1E,+BCgD0E,CDhD1C,CCgD0C,CAAA;;;AAAE;;;;;;;AD1FhF;;;;AACuB;AAOvB;;;;;AAGqB;AAOrB;;;;;;;AAkByB;AAIzB;;;;;;AAEmC;;;;ACgDnC;;AAA8E,iBAA9D,eAA8D,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,EAArB,oBAAqB,CAAA,CAAA,CAAA,CAAA,EAAE,kBAAA,CAAA,GAAA,CAAA,OAAF;;;;iBCzF9D,sCAAsC,oBAAoB;iBAQ1D;YAEQ;IACrB;AFZH;;;;AAwCA;;;;AAEoC,iBGgFpB,kBHhFoB,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,CAAA,EGgF2B,oBHhF3B,CGgFgD,CHhFhD,CAAA,CAAA,EAAA,IAAA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ReplaneClient, ReplaneClientOptions } from "@replanejs/sdk";
|
|
3
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/types.d.ts
|
|
6
|
+
interface ReplaneContextValue<T extends object = any> {
|
|
7
|
+
client: ReplaneClient<T>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Props for ReplaneProvider when using a pre-created client.
|
|
11
|
+
*/
|
|
12
|
+
interface ReplaneProviderWithClientProps<T extends object = any> {
|
|
13
|
+
/** Pre-created ReplaneClient instance */
|
|
14
|
+
client: ReplaneClient<T>;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Props for ReplaneProvider when letting it manage the client internally.
|
|
19
|
+
*/
|
|
20
|
+
interface ReplaneProviderWithOptionsProps<T extends object = any> {
|
|
21
|
+
/** Options to create the ReplaneClient */
|
|
22
|
+
options: ReplaneClientOptions<T>;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Optional loading component to show while the client is initializing.
|
|
26
|
+
* If not provided and suspense is false/undefined, children will not render until ready.
|
|
27
|
+
*/
|
|
28
|
+
loader?: ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* If true, uses React Suspense for loading state.
|
|
31
|
+
* The provider will throw a promise that Suspense can catch.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
suspense?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Callback when client initialization fails.
|
|
37
|
+
*/
|
|
38
|
+
onError?: (error: Error) => void;
|
|
39
|
+
}
|
|
40
|
+
type ReplaneProviderProps<T extends object = any> = ReplaneProviderWithClientProps<T> | ReplaneProviderWithOptionsProps<T>;
|
|
41
|
+
/**
|
|
42
|
+
* Type guard to check if props contain a pre-created client.
|
|
43
|
+
*/
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/provider.d.ts
|
|
46
|
+
/**
|
|
47
|
+
* Provider component that makes a ReplaneClient available to the component tree.
|
|
48
|
+
*
|
|
49
|
+
* Can be used in two ways:
|
|
50
|
+
*
|
|
51
|
+
* 1. With a pre-created client:
|
|
52
|
+
* ```tsx
|
|
53
|
+
* const client = await createReplaneClient({ ... });
|
|
54
|
+
* <ReplaneProvider client={client}>
|
|
55
|
+
* <App />
|
|
56
|
+
* </ReplaneProvider>
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* 2. With options (client managed internally):
|
|
60
|
+
* ```tsx
|
|
61
|
+
* <ReplaneProvider
|
|
62
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
63
|
+
* loader={<LoadingSpinner />}
|
|
64
|
+
* >
|
|
65
|
+
* <App />
|
|
66
|
+
* </ReplaneProvider>
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* 3. With Suspense:
|
|
70
|
+
* ```tsx
|
|
71
|
+
* <Suspense fallback={<LoadingSpinner />}>
|
|
72
|
+
* <ReplaneProvider
|
|
73
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
74
|
+
* suspense
|
|
75
|
+
* >
|
|
76
|
+
* <App />
|
|
77
|
+
* </ReplaneProvider>
|
|
78
|
+
* </Suspense>
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function ReplaneProvider<T extends object>(props: ReplaneProviderProps<T>): react_jsx_runtime0.JSX.Element;
|
|
82
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/hooks.d.ts
|
|
85
|
+
declare function useReplane<T extends object = any>(): ReplaneContextValue<T>;
|
|
86
|
+
declare function useConfig<T>(name: string, options?: {
|
|
87
|
+
context?: Record<string, string | number | boolean | null>;
|
|
88
|
+
}): T;
|
|
89
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
90
|
+
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/useReplaneClient.d.ts
|
|
93
|
+
/**
|
|
94
|
+
* Clear the suspense cache for a specific options configuration.
|
|
95
|
+
* Useful for testing or when you need to force re-initialization.
|
|
96
|
+
*/
|
|
97
|
+
declare function clearSuspenseCache<T extends object>(options?: ReplaneClientOptions<T>): void;
|
|
98
|
+
//#endregion
|
|
99
|
+
export { type ReplaneContextValue, ReplaneProvider, type ReplaneProviderProps, type ReplaneProviderWithClientProps, type ReplaneProviderWithOptionsProps, clearSuspenseCache, useConfig, useReplane };
|
|
100
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/provider.tsx","../src/hooks.ts","../src/useReplaneClient.ts"],"sourcesContent":[],"mappings":";;;;;UAIiB;UACP,cAAc;;AADxB;;;AACU,UAOO,8BAPP,CAAA,UAAA,MAAA,GAAA,GAAA,CAAA,CAAA;EAAa;EAON,MAAA,EAEP,aAFO,CAEO,CAFP,CAAA;EAA8B,QAAA,EAGnC,SAHmC;;;;AAG1B;AAOJ,UAAA,+BAA+B,CAAA,UAAA,MAAA,GAAA,GAAA,CAAA,CAAA;EAAA;EAAA,OAEhB,EAArB,oBAAqB,CAAA,CAAA,CAAA;EAAC,QAAtB,EACC,SADD;EAAoB;;;AAgBN;EAIb,MAAA,CAAA,EAdD,SAcC;EAAoB;;;;;EAEG,QAAA,CAAA,EAAA,OAAA;;;;ECgDnB,OAAA,CAAA,EAAA,CAAA,KAAA,EDtDI,KCsDW,EAAA,GAAA,IAAA;;AAA+C,KDlDlE,oBCkDkE,CAAA,UAAA,MAAA,GAAA,GAAA,CAAA,GDjD1E,8BCiD0E,CDjD3C,CCiD2C,CAAA,GDhD1E,+BCgD0E,CDhD1C,CCgD0C,CAAA;;;AAAE;;;;;;;AD1FhF;;;;AACuB;AAOvB;;;;;AAGqB;AAOrB;;;;;;;AAkByB;AAIzB;;;;;;AAEmC;;;;ACgDnC;;AAA8E,iBAA9D,eAA8D,CAAA,UAAA,MAAA,CAAA,CAAA,KAAA,EAArB,oBAAqB,CAAA,CAAA,CAAA,CAAA,EAAE,kBAAA,CAAA,GAAA,CAAA,OAAF;;;;iBCzF9D,sCAAsC,oBAAoB;iBAQ1D;YAEQ;IACrB;AFZH;;;;AAwCA;;;;AAEoC,iBGgFpB,kBHhFoB,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,CAAA,EGgF2B,oBHhF3B,CGgFgD,CHhFhD,CAAA,CAAA,EAAA,IAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { createReplaneClient } from "@replanejs/sdk";
|
|
3
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/context.ts
|
|
6
|
+
const ReplaneContext = createContext(null);
|
|
7
|
+
|
|
8
|
+
//#endregion
|
|
9
|
+
//#region src/useReplaneClient.ts
|
|
10
|
+
const suspenseCache = new Map();
|
|
11
|
+
function getCacheKey(options) {
|
|
12
|
+
return `${options.baseUrl}:${options.sdkKey}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Hook to manage ReplaneClient creation internally.
|
|
16
|
+
* Handles loading state and cleanup.
|
|
17
|
+
*/
|
|
18
|
+
function useReplaneClient(options, onError) {
|
|
19
|
+
const [state, setState] = useState({
|
|
20
|
+
status: "loading",
|
|
21
|
+
client: null,
|
|
22
|
+
error: null
|
|
23
|
+
});
|
|
24
|
+
const clientRef = useRef(null);
|
|
25
|
+
const optionsRef = useRef(options);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
let cancelled = false;
|
|
28
|
+
async function initClient() {
|
|
29
|
+
try {
|
|
30
|
+
const client = await createReplaneClient(optionsRef.current);
|
|
31
|
+
if (cancelled) {
|
|
32
|
+
client.close();
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
clientRef.current = client;
|
|
36
|
+
setState({
|
|
37
|
+
status: "ready",
|
|
38
|
+
client,
|
|
39
|
+
error: null
|
|
40
|
+
});
|
|
41
|
+
} catch (err) {
|
|
42
|
+
if (cancelled) return;
|
|
43
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
44
|
+
setState({
|
|
45
|
+
status: "error",
|
|
46
|
+
client: null,
|
|
47
|
+
error
|
|
48
|
+
});
|
|
49
|
+
onError?.(error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
initClient();
|
|
53
|
+
return () => {
|
|
54
|
+
cancelled = true;
|
|
55
|
+
if (clientRef.current) {
|
|
56
|
+
clientRef.current.close();
|
|
57
|
+
clientRef.current = null;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}, []);
|
|
61
|
+
return state;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Hook for Suspense-based client creation.
|
|
65
|
+
* Throws a promise while loading, throws error on failure.
|
|
66
|
+
*/
|
|
67
|
+
function useReplaneClientSuspense(options) {
|
|
68
|
+
const cacheKey = getCacheKey(options);
|
|
69
|
+
const cached = suspenseCache.get(cacheKey);
|
|
70
|
+
if (cached) {
|
|
71
|
+
if (cached.error) throw cached.error;
|
|
72
|
+
if (cached.result) return cached.result;
|
|
73
|
+
throw cached.promise;
|
|
74
|
+
}
|
|
75
|
+
const promise = createReplaneClient(options).then((client) => {
|
|
76
|
+
const entry = suspenseCache.get(cacheKey);
|
|
77
|
+
if (entry) entry.result = client;
|
|
78
|
+
return client;
|
|
79
|
+
}).catch((err) => {
|
|
80
|
+
const entry = suspenseCache.get(cacheKey);
|
|
81
|
+
if (entry) entry.error = err instanceof Error ? err : new Error(String(err));
|
|
82
|
+
throw err;
|
|
83
|
+
});
|
|
84
|
+
suspenseCache.set(cacheKey, { promise });
|
|
85
|
+
throw promise;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Clear the suspense cache for a specific options configuration.
|
|
89
|
+
* Useful for testing or when you need to force re-initialization.
|
|
90
|
+
*/
|
|
91
|
+
function clearSuspenseCache(options) {
|
|
92
|
+
if (options) suspenseCache.delete(getCacheKey(options));
|
|
93
|
+
else suspenseCache.clear();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/types.ts
|
|
98
|
+
/**
|
|
99
|
+
* Type guard to check if props contain a pre-created client.
|
|
100
|
+
*/
|
|
101
|
+
function hasClient(props) {
|
|
102
|
+
return "client" in props && props.client !== void 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/provider.tsx
|
|
107
|
+
/**
|
|
108
|
+
* Internal provider component for pre-created client.
|
|
109
|
+
*/
|
|
110
|
+
function ReplaneProviderWithClient({ client, children }) {
|
|
111
|
+
const value = useMemo(() => ({ client }), [client]);
|
|
112
|
+
return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
|
|
113
|
+
value,
|
|
114
|
+
children
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Internal provider component for options-based client creation (non-suspense).
|
|
119
|
+
*/
|
|
120
|
+
function ReplaneProviderWithOptions({ options, children, loader, onError }) {
|
|
121
|
+
const state = useReplaneClient(options, onError);
|
|
122
|
+
if (state.status === "loading") return /* @__PURE__ */ jsx(Fragment, { children: loader ?? null });
|
|
123
|
+
if (state.status === "error") return /* @__PURE__ */ jsx(Fragment, { children: loader ?? null });
|
|
124
|
+
const value = { client: state.client };
|
|
125
|
+
return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
|
|
126
|
+
value,
|
|
127
|
+
children
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Internal provider component for options-based client creation with Suspense.
|
|
132
|
+
*/
|
|
133
|
+
function ReplaneProviderWithSuspense({ options, children }) {
|
|
134
|
+
const client = useReplaneClientSuspense(options);
|
|
135
|
+
const value = useMemo(() => ({ client }), [client]);
|
|
136
|
+
return /* @__PURE__ */ jsx(ReplaneContext.Provider, {
|
|
137
|
+
value,
|
|
138
|
+
children
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Provider component that makes a ReplaneClient available to the component tree.
|
|
143
|
+
*
|
|
144
|
+
* Can be used in two ways:
|
|
145
|
+
*
|
|
146
|
+
* 1. With a pre-created client:
|
|
147
|
+
* ```tsx
|
|
148
|
+
* const client = await createReplaneClient({ ... });
|
|
149
|
+
* <ReplaneProvider client={client}>
|
|
150
|
+
* <App />
|
|
151
|
+
* </ReplaneProvider>
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* 2. With options (client managed internally):
|
|
155
|
+
* ```tsx
|
|
156
|
+
* <ReplaneProvider
|
|
157
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
158
|
+
* loader={<LoadingSpinner />}
|
|
159
|
+
* >
|
|
160
|
+
* <App />
|
|
161
|
+
* </ReplaneProvider>
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* 3. With Suspense:
|
|
165
|
+
* ```tsx
|
|
166
|
+
* <Suspense fallback={<LoadingSpinner />}>
|
|
167
|
+
* <ReplaneProvider
|
|
168
|
+
* options={{ baseUrl: '...', sdkKey: '...' }}
|
|
169
|
+
* suspense
|
|
170
|
+
* >
|
|
171
|
+
* <App />
|
|
172
|
+
* </ReplaneProvider>
|
|
173
|
+
* </Suspense>
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
function ReplaneProvider(props) {
|
|
177
|
+
if (hasClient(props)) return /* @__PURE__ */ jsx(ReplaneProviderWithClient, { ...props });
|
|
178
|
+
if (props.suspense) return /* @__PURE__ */ jsx(ReplaneProviderWithSuspense, { ...props });
|
|
179
|
+
return /* @__PURE__ */ jsx(ReplaneProviderWithOptions, { ...props });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/hooks.ts
|
|
184
|
+
function useReplane() {
|
|
185
|
+
const context = useContext(ReplaneContext);
|
|
186
|
+
if (!context) throw new Error("useReplane must be used within a ReplaneProvider");
|
|
187
|
+
return context;
|
|
188
|
+
}
|
|
189
|
+
function useConfig(name, options) {
|
|
190
|
+
const { client } = useReplane();
|
|
191
|
+
const value = useSyncExternalStore((onStoreChange) => {
|
|
192
|
+
return client.subscribe(name, onStoreChange);
|
|
193
|
+
}, () => client.get(name, options), () => client.get(name, options));
|
|
194
|
+
return value;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
export { ReplaneProvider, clearSuspenseCache, useConfig, useReplane };
|
|
199
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["options: ReplaneClientOptions<T>","onError?: (error: Error) => void","options?: ReplaneClientOptions<T>","props: ReplaneProviderProps<T>","value: ReplaneContextValue<T>","props: ReplaneProviderProps<T>","name: string","options?: { context?: Record<string, string | number | boolean | null> }"],"sources":["../src/context.ts","../src/useReplaneClient.ts","../src/types.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["import { 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","import { useEffect, useRef, useState } from \"react\";\nimport { createReplaneClient } from \"@replanejs/sdk\";\nimport type { ReplaneClient, ReplaneClientOptions } from \"@replanejs/sdk\";\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\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 useReplaneClient<T extends object = any>(\n options: ReplaneClientOptions<T>,\n onError?: (error: Error) => void\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>(optionsRef.current);\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 = err instanceof Error ? err : new Error(String(err));\n setState({ status: \"error\", client: null, error });\n onError?.(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 // We intentionally only run this effect once on mount\n // Options changes would require remounting the provider\n // eslint-disable-next-line react-hooks/exhaustive-deps\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>(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) => {\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","import type { ReplaneClient, ReplaneClientOptions } from \"@replanejs/sdk\";\nimport type { ReactNode } from \"react\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface ReplaneContextValue<T extends object = any> {\n client: ReplaneClient<T>;\n}\n\n/**\n * Props for ReplaneProvider when using a pre-created client.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface ReplaneProviderWithClientProps<T extends object = any> {\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 */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface ReplaneProviderWithOptionsProps<T extends object = any> {\n /** Options to create the ReplaneClient */\n options: ReplaneClientOptions<T>;\n children: ReactNode;\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 */\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 * @default false\n */\n suspense?: boolean;\n /**\n * Callback when client initialization fails.\n */\n onError?: (error: Error) => void;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type ReplaneProviderProps<T extends object = any> =\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","import { useMemo } from \"react\";\nimport { ReplaneContext } from \"./context\";\nimport { useReplaneClient, useReplaneClientSuspense } from \"./useReplaneClient\";\nimport type {\n ReplaneProviderProps,\n ReplaneProviderWithClientProps,\n ReplaneProviderWithOptionsProps,\n ReplaneContextValue,\n} from \"./types\";\nimport { hasClient } from \"./types\";\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 options-based client creation (non-suspense).\n */\nfunction ReplaneProviderWithOptions<T extends object>({\n options,\n children,\n loader,\n onError,\n}: ReplaneProviderWithOptionsProps<T>) {\n const state = useReplaneClient<T>(options, onError);\n\n if (state.status === \"loading\") {\n return <>{loader ?? null}</>;\n }\n\n if (state.status === \"error\") {\n // Error was already reported via onError callback\n // Return loader or null to prevent rendering children without a client\n return <>{loader ?? null}</>;\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 two 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 * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * loader={<LoadingSpinner />}\n * >\n * <App />\n * </ReplaneProvider>\n * ```\n *\n * 3. With Suspense:\n * ```tsx\n * <Suspense fallback={<LoadingSpinner />}>\n * <ReplaneProvider\n * options={{ baseUrl: '...', sdkKey: '...' }}\n * suspense\n * >\n * <App />\n * </ReplaneProvider>\n * </Suspense>\n * ```\n */\nexport function ReplaneProvider<T extends object>(props: ReplaneProviderProps<T>) {\n if (hasClient(props)) {\n return <ReplaneProviderWithClient {...props} />;\n }\n\n if (props.suspense) {\n return <ReplaneProviderWithSuspense {...props} />;\n }\n\n return <ReplaneProviderWithOptions {...props} />;\n}\n","import { useContext, useSyncExternalStore } from \"react\";\nimport { ReplaneContext } from \"./context\";\nimport type { ReplaneContextValue } from \"./types\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function useReplane<T extends object = any>(): ReplaneContextValue<T> {\n const context = useContext(ReplaneContext);\n if (!context) {\n throw new Error(\"useReplane must be used within a ReplaneProvider\");\n }\n return context as ReplaneContextValue<T>;\n}\n\nexport function useConfig<T>(\n name: string,\n options?: { context?: Record<string, string | number | boolean | null> }\n): T {\n const { client } = useReplane();\n\n const value = useSyncExternalStore(\n (onStoreChange) => {\n return client.subscribe(name, onStoreChange);\n },\n () => client.get(name, options) as T,\n () => client.get(name, options) as T\n );\n\n return value;\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@replanejs/react",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "React SDK for Replane - feature flags and remote configuration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"sideEffects": false,
|
|
20
|
+
"keywords": [
|
|
21
|
+
"replane",
|
|
22
|
+
"react",
|
|
23
|
+
"feature-flags",
|
|
24
|
+
"remote-config",
|
|
25
|
+
"configuration",
|
|
26
|
+
"sdk"
|
|
27
|
+
],
|
|
28
|
+
"author": "",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "https://github.com/replane-dev/replane-javascript",
|
|
33
|
+
"directory": "packages/react"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/replane-dev/replane-javascript/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/replane-dev/replane-javascript#readme",
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"react": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@replanejs/sdk": "0.6.2"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
47
|
+
"bumpp": "^10.1.0",
|
|
48
|
+
"@testing-library/react": "^16.3.1",
|
|
49
|
+
"@types/node": "^22.19.3",
|
|
50
|
+
"@types/react": "^18.2.0",
|
|
51
|
+
"@types/react-dom": "^18.3.7",
|
|
52
|
+
"jsdom": "^27.3.0",
|
|
53
|
+
"react": "^18.2.0",
|
|
54
|
+
"react-dom": "^18.3.1",
|
|
55
|
+
"tsdown": "^0.11.9",
|
|
56
|
+
"typescript": "^5.4.0",
|
|
57
|
+
"vitest": "^3.2.4"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18.0.0"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsdown",
|
|
67
|
+
"dev": "tsdown --watch",
|
|
68
|
+
"typecheck": "tsc --noEmit",
|
|
69
|
+
"test": "vitest run",
|
|
70
|
+
"test:watch": "vitest",
|
|
71
|
+
"release": "pnpm run build && bumpp && npm publish"
|
|
72
|
+
}
|
|
73
|
+
}
|