@quonfig/react 0.0.10 → 0.0.12

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/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  Changelog
2
2
 
3
+ ## 0.0.12 - 2026-05-03
4
+
5
+ - **chore!: narrow `react` peer dep to `^18 || ^19` (qfg-bsji).** The advertised range
6
+ (`^16 || ^17 || ^18 || ^19`) was inaccurate since 0.0.10 — `QuonfigProvider` calls
7
+ `React.useSyncExternalStore`, which only exists in React 18+. Install-time signal now matches
8
+ runtime reality. Consumers on React 16/17 were already broken at runtime.
9
+ - **feat: `useFlag(key)` per-key selector hook (qfg-lkpm.6).** Subscribes to a single flag's value
10
+ via `useSyncExternalStore` against the underlying client's notify list. Components using
11
+ `useFlag('foo')` no longer re-render when an unrelated flag changes — `useQuonfig()` continues to
12
+ re-render on every `dataVersion` bump as before.
13
+ - **fix: replace module-level `globalQuonfigIsTaken` flag with `QuonfigClientContext`
14
+ (qfg-lkpm.6).** The flag never reset on unmount, so a Provider that mounted, unmounted, and
15
+ remounted at the top of the tree received a fresh `Quonfig()` instead of the module singleton. The
16
+ new context-based ownership keys off React tree position: a top-level provider claims the
17
+ singleton, nested providers mint fresh clients.
18
+ - **docs: Next.js / RSC integration guide.** Documents `initialFlags`, App Router and Pages Router
19
+ patterns, and hydration-mismatch caveats.
20
+
21
+ ## 0.0.10 - 2026-05-02
22
+
23
+ - **Fix (provider): re-render on poll updates + close client on unmount (qfg-daxq, qfg-2acr).**
24
+ Previously, poll-driven config mutations were invisible to React because the provider's value
25
+ `useMemo` only depended on `contextKey`/`loading`/`instanceHash`/`settings`. Now wires the
26
+ `@quonfig/javascript@>=0.0.14` `subscribe()`/`dataVersion` API through
27
+ `React.useSyncExternalStore` so every config mutation (poll fetch, `setConfig`, hydrate) triggers
28
+ a re-render. Also adds a mount-only `useEffect` cleanup that calls `quonfigClient.close()` when
29
+ `QuonfigProvider` unmounts (drains telemetry, stops polling, stops telemetry timers) and resets
30
+ the StrictMode init guard so a remount cleanly re-inits — previously an SPA route swap that
31
+ unmounted the provider left the underlying singleton polling forever and held undrained telemetry.
32
+ - **Peer dep:** `@quonfig/javascript` peer floor bumped to `>=0.0.14`.
33
+
3
34
  ## 0.0.2 - 2025-10-12
4
35
 
5
36
  - Support re-hydration of flags via QuonfigProvider
package/README.md CHANGED
@@ -37,13 +37,14 @@ const WrappedApp = () => {
37
37
 
38
38
  Here's an explanation of each provider prop:
39
39
 
40
- | property | required | type | purpose |
41
- | ------------------- | -------- | ----------------- | ----------------------------------------------------------------------------- |
42
- | `sdkKey` | yes | `string` | your Quonfig SDK key |
43
- | `onError` | no | `(error) => void` | callback invoked if quonfig fails to initialize |
44
- | `contextAttributes` | no | `Contexts` | this is the context attributes object you passed when setting up the provider |
45
- | `timeout` | no | `number` | initialization timeout (defaults to 10 seconds) |
46
- | `pollInterval` | no | `number` | configures quonfig to poll for updates every `pollInterval` ms. |
40
+ | property | required | type | purpose |
41
+ | ------------------- | -------- | ------------------------- | ----------------------------------------------------------------------------------------------------------- |
42
+ | `sdkKey` | yes | `string` | your Quonfig SDK key |
43
+ | `onError` | no | `(error) => void` | callback invoked if quonfig fails to initialize |
44
+ | `contextAttributes` | no | `Contexts` | this is the context attributes object you passed when setting up the provider |
45
+ | `timeout` | no | `number` | initialization timeout (defaults to 10 seconds) |
46
+ | `pollInterval` | no | `number` | configures quonfig to poll for updates every `pollInterval` ms. |
47
+ | `initialFlags` | no | `Record<string, unknown>` | seed flag values evaluated on the server — see [Next.js / RSC integration](#nextjs--rsc-integration) below. |
47
48
 
48
49
  ### Usage in Your Components
49
50
 
@@ -78,6 +79,117 @@ Here's an explanation of each property:
78
79
  | `quonfig` | N/A | the underlying JavaScript quonfig instance |
79
80
  | `keys` | N/A | an array of all the flag and config names in the current configuration |
80
81
 
82
+ ### `useFlag` — per-key selector hook
83
+
84
+ `useQuonfig()` re-renders every consumer when _any_ flag value changes. For components that only
85
+ care about one flag, use `useFlag(key)` — it subscribes to that single key and skips re-renders when
86
+ unrelated flags change.
87
+
88
+ ```javascript
89
+ import { useFlag } from "@quonfig/react";
90
+
91
+ const Logo = () => {
92
+ const showNewLogo = useFlag("new-logo");
93
+ return <img src={showNewLogo ? newLogo : logo} alt="logo" />;
94
+ };
95
+ ```
96
+
97
+ If you have a typed config (via `@quonfig/cli generate`), `useFlag` returns the type declared for
98
+ the key.
99
+
100
+ ## Next.js / RSC integration
101
+
102
+ The provider runs on the client (it uses `fetch` and `useEffect`), but you often want to seed flag
103
+ values on the server so the first paint reflects real data instead of defaults. Pass evaluated flags
104
+ through the `initialFlags` prop on `QuonfigProvider`.
105
+
106
+ ### App Router (RSC)
107
+
108
+ Fetch flags in a Server Component, then pass the evaluated map to a Client Component that owns the
109
+ provider. `QuonfigProvider` itself must be inside a Client Component because of its hooks.
110
+
111
+ ```javascript
112
+ // app/quonfig-wrapper.tsx — Client Component
113
+ "use client";
114
+
115
+ import { QuonfigProvider } from "@quonfig/react";
116
+
117
+ export function QuonfigWrapper({ children, initialFlags, contextAttributes }) {
118
+ return (
119
+ <QuonfigProvider
120
+ sdkKey={process.env.NEXT_PUBLIC_QUONFIG_API_KEY!}
121
+ contextAttributes={contextAttributes}
122
+ initialFlags={initialFlags}
123
+ >
124
+ {children}
125
+ </QuonfigProvider>
126
+ );
127
+ }
128
+ ```
129
+
130
+ ```javascript
131
+ // app/layout.tsx — Server Component
132
+ import { QuonfigWrapper } from "./quonfig-wrapper";
133
+
134
+ export default async function RootLayout({ children }) {
135
+ // Evaluate flags on the server with @quonfig/node (or similar) and
136
+ // hand the flat key/value map to the client provider.
137
+ const initialFlags = await evaluateFlagsOnServer({
138
+ user: { id: "1" },
139
+ });
140
+
141
+ return (
142
+ <html>
143
+ <body>
144
+ <QuonfigWrapper initialFlags={initialFlags} contextAttributes={{ user: { id: "1" } }}>
145
+ {children}
146
+ </QuonfigWrapper>
147
+ </body>
148
+ </html>
149
+ );
150
+ }
151
+ ```
152
+
153
+ `initialFlags` is the flat-map shape `{ flagKey: value }`, e.g.
154
+ `{ "new-logo": true, "retry-count": 3 }`. When `initialFlags` is set, the provider hydrates
155
+ synchronously on first render — no loading flicker — and skips the initial fetch. Subsequent context
156
+ changes still trigger fetches.
157
+
158
+ ### Pages Router (`getServerSideProps`)
159
+
160
+ ```javascript
161
+ export async function getServerSideProps({ req }) {
162
+ const initialFlags = await evaluateFlagsOnServer(extractContext(req));
163
+ return { props: { initialFlags } };
164
+ }
165
+
166
+ export default function Page({ initialFlags }) {
167
+ return (
168
+ <QuonfigProvider sdkKey={process.env.NEXT_PUBLIC_QUONFIG_API_KEY} initialFlags={initialFlags}>
169
+ <App />
170
+ </QuonfigProvider>
171
+ );
172
+ }
173
+ ```
174
+
175
+ ### Hydration mismatch caveats
176
+
177
+ - Pass the **same** `contextAttributes` on the server and the client. If the client has different
178
+ inputs (e.g. it adds attributes from `document` or `window`), the first client render will fetch a
179
+ different evaluation and flag values can flicker.
180
+ - `initialFlags` only applies on the first render. Don't expect changing it later to swap flags out
181
+ — use `setConfig`/poll for that.
182
+ - Don't combine `initialFlags` with `pollInterval` unless you want both — the provider warns at
183
+ runtime if you do.
184
+
185
+ ## SSR / multi-tenant rendering
186
+
187
+ `QuonfigProvider` is a client-only component (it uses `useEffect` and `fetch`), so it does not run
188
+ during SSR. The provider's client identity is keyed by React tree position via
189
+ `QuonfigClientContext`: a top-level provider claims the module singleton (so `import { quonfig }`
190
+ consumers see the same instance), and any nested `QuonfigProvider` mints a fresh `Quonfig()` so its
191
+ config can't leak into the parent tree.
192
+
81
193
  ## Usage in your test suite
82
194
 
83
195
  Wrap the component under test in a `QuonfigTestProvider` and provide a config object to set up your
package/dist/index.cjs CHANGED
@@ -12,7 +12,32 @@ var React__default = /*#__PURE__*/_interopDefault(React);
12
12
  // src/version.ts
13
13
  var version_default = "0.0.9";
14
14
 
15
+ // src/sdkLogger.ts
16
+ var NOOP = () => {
17
+ };
18
+ var PREFIX = "[quonfig]";
19
+ function defaultSdkLogger() {
20
+ return {
21
+ /* eslint-disable no-console */
22
+ debug: (message, ...args) => console.debug(PREFIX, message, ...args),
23
+ info: (message, ...args) => console.info(PREFIX, message, ...args),
24
+ warn: (message, ...args) => console.warn(PREFIX, message, ...args),
25
+ error: (message, ...args) => console.error(PREFIX, message, ...args)
26
+ /* eslint-enable no-console */
27
+ };
28
+ }
29
+ function normalizeLogger(logger) {
30
+ if (!logger) return defaultSdkLogger();
31
+ return {
32
+ debug: logger.debug ? logger.debug.bind(logger) : NOOP,
33
+ info: logger.info ? logger.info.bind(logger) : NOOP,
34
+ warn: logger.warn.bind(logger),
35
+ error: logger.error.bind(logger)
36
+ };
37
+ }
38
+
15
39
  // src/QuonfigProvider.tsx
40
+ var QuonfigClientContext = React__default.default.createContext(null);
16
41
  var defaultContext = {
17
42
  get: (_key) => void 0,
18
43
  getDuration: (_key) => void 0,
@@ -46,18 +71,26 @@ function createQuonfigHook(TypesafeClass) {
46
71
  }
47
72
  var useBaseQuonfig = () => React__default.default.useContext(QuonfigContext);
48
73
  var useQuonfig = () => useBaseQuonfig();
49
- var globalQuonfigIsTaken = false;
50
- var assignQuonfigClient = () => {
51
- if (globalQuonfigIsTaken) {
52
- return new javascript.Quonfig();
53
- }
54
- globalQuonfigIsTaken = true;
55
- return javascript.quonfig;
74
+ function useFlag(key) {
75
+ var _a;
76
+ const client = (_a = React__default.default.useContext(QuonfigClientContext)) != null ? _a : javascript.quonfig;
77
+ const subscribe = React__default.default.useCallback(
78
+ (onChange) => client.subscribe(onChange),
79
+ [client]
80
+ );
81
+ const getSnapshot = React__default.default.useCallback(() => client.get(key), [client, key]);
82
+ const getServerSnapshot = React__default.default.useCallback(() => void 0, []);
83
+ return React__default.default.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
84
+ }
85
+ var useQuonfigClient = () => {
86
+ const parentClient = React__default.default.useContext(QuonfigClientContext);
87
+ const parentClientRef = React__default.default.useRef(parentClient);
88
+ return React__default.default.useMemo(() => parentClientRef.current ? new javascript.Quonfig() : javascript.quonfig, []);
56
89
  };
57
- var getContextKey = (contextAttributes, onError) => {
90
+ var getContextKey = (contextAttributes, logger, onError) => {
58
91
  try {
59
92
  if (Object.keys(contextAttributes).length === 0) {
60
- console.warn(
93
+ logger.warn(
61
94
  "QuonfigProvider: You haven't passed any contextAttributes. See https://docs.quonfig.com/docs/sdks/react#using-context"
62
95
  );
63
96
  }
@@ -70,9 +103,8 @@ var getContextKey = (contextAttributes, onError) => {
70
103
  function QuonfigProvider({
71
104
  sdkKey,
72
105
  contextAttributes = {},
73
- onError = (e) => {
74
- console.error(e);
75
- },
106
+ onError: userOnError,
107
+ logger,
76
108
  initialFlags,
77
109
  children,
78
110
  timeout,
@@ -84,6 +116,18 @@ function QuonfigProvider({
84
116
  collectEvaluationSummaries,
85
117
  collectLoggerNames
86
118
  }) {
119
+ const normalizedLogger = React__default.default.useMemo(() => normalizeLogger(logger), [logger]);
120
+ const onError = React__default.default.useCallback(
121
+ (e) => {
122
+ if (userOnError) {
123
+ userOnError(e);
124
+ } else {
125
+ const message = e instanceof Error ? e.message : String(e);
126
+ normalizedLogger.error(message, e);
127
+ }
128
+ },
129
+ [userOnError, normalizedLogger]
130
+ );
87
131
  const settings = {
88
132
  sdkKey,
89
133
  apiUrl,
@@ -100,13 +144,13 @@ function QuonfigProvider({
100
144
  const [loading, setLoading] = React__default.default.useState(true);
101
145
  const [initialLoad, setInitialLoad] = React__default.default.useState(true);
102
146
  const [loadedContextKey, setLoadedContextKey] = React__default.default.useState("");
103
- const quonfigClient = React__default.default.useMemo(() => assignQuonfigClient(), []);
147
+ const quonfigClient = useQuonfigClient();
104
148
  const dataVersion = React__default.default.useSyncExternalStore(
105
149
  React__default.default.useCallback((onChange) => quonfigClient.subscribe(onChange), [quonfigClient]),
106
150
  React__default.default.useCallback(() => quonfigClient.dataVersion, [quonfigClient]),
107
151
  React__default.default.useCallback(() => 0, [])
108
152
  );
109
- const contextKey = getContextKey(contextAttributes, onError);
153
+ const contextKey = getContextKey(contextAttributes, normalizedLogger, onError);
110
154
  if (initialFlags && initialLoad) {
111
155
  quonfigClient.hydrate(initialFlags);
112
156
  setInitialLoad(false);
@@ -114,7 +158,7 @@ function QuonfigProvider({
114
158
  setLoading(false);
115
159
  mostRecentlyLoadingContextKey.current = contextKey;
116
160
  if (pollInterval) {
117
- console.warn("Polling is not supported when hydrating flags via initialFlags");
161
+ normalizedLogger.warn("Polling is not supported when hydrating flags via initialFlags");
118
162
  }
119
163
  }
120
164
  React__default.default.useEffect(() => {
@@ -196,7 +240,7 @@ function QuonfigProvider({
196
240
  };
197
241
  return baseContext;
198
242
  }, [loadedContextKey, loading, quonfigClient.instanceHash, settings, dataVersion]);
199
- return /* @__PURE__ */ React__default.default.createElement(QuonfigContext.Provider, { value }, children);
243
+ return /* @__PURE__ */ React__default.default.createElement(QuonfigClientContext.Provider, { value: quonfigClient }, /* @__PURE__ */ React__default.default.createElement(QuonfigContext.Provider, { value }, children));
200
244
  }
201
245
  function QuonfigTestProvider({
202
246
  sdkKey,
@@ -206,7 +250,7 @@ function QuonfigTestProvider({
206
250
  const get = (key) => config[key];
207
251
  const getDuration = (key) => config[key];
208
252
  const isEnabled = (key) => !!get(key);
209
- const quonfigClient = React__default.default.useMemo(() => assignQuonfigClient(), []);
253
+ const quonfigClient = useQuonfigClient();
210
254
  const value = React__default.default.useMemo(() => {
211
255
  quonfigClient.get = get;
212
256
  quonfigClient.getDuration = getDuration;
@@ -223,7 +267,7 @@ function QuonfigTestProvider({
223
267
  };
224
268
  return baseContext;
225
269
  }, [config, quonfigClient, sdkKey]);
226
- return /* @__PURE__ */ React__default.default.createElement(QuonfigContext.Provider, { value }, children);
270
+ return /* @__PURE__ */ React__default.default.createElement(QuonfigClientContext.Provider, { value: quonfigClient }, /* @__PURE__ */ React__default.default.createElement(QuonfigContext.Provider, { value }, children));
227
271
  }
228
272
 
229
273
  Object.defineProperty(exports, "quonfig", {
@@ -233,6 +277,7 @@ Object.defineProperty(exports, "quonfig", {
233
277
  exports.QuonfigProvider = QuonfigProvider;
234
278
  exports.QuonfigTestProvider = QuonfigTestProvider;
235
279
  exports.createQuonfigHook = createQuonfigHook;
280
+ exports.useFlag = useFlag;
236
281
  exports.useQuonfig = useQuonfig;
237
282
  //# sourceMappingURL=index.cjs.map
238
283
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/QuonfigProvider.tsx","../src/QuonfigTestProvider.tsx"],"names":["quonfig","React","Quonfig","encodeContexts"],"mappings":"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","file":"index.cjs","sourcesContent":["// AUTO-GENERATED from package.json by scripts/generate-version.mjs — do not edit.\nexport default \"0.0.9\";\n","import React, { PropsWithChildren } from \"react\";\nimport {\n quonfig,\n type InitOptions,\n type ConfigValue,\n type Contexts,\n Quonfig,\n TypedFrontEndConfigurationRaw,\n FrontEndConfigurationRaw,\n Duration,\n encodeContexts,\n} from \"@quonfig/javascript\";\nimport reactSdkVersion from \"./version\";\n\n// @quonfig/cli#generate will create interfaces into this namespace for React to consume\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface FrontEndConfigurationAccessor {}\n\nexport type TypedFrontEndConfigurationAccessor = keyof FrontEndConfigurationAccessor extends never\n ? Record<string, unknown>\n : {\n [TypedFlagKey in keyof FrontEndConfigurationAccessor]: FrontEndConfigurationAccessor[TypedFlagKey];\n };\n\ntype ClassMethods<T> = { [K in keyof T]: T[K] };\n\ntype QuonfigTypesafeClass<T = unknown> = new (\n // eslint-disable-next-line no-shadow\n quonfig: Quonfig\n) => T;\n\ntype SharedSettings = Partial<\n Pick<\n InitOptions,\n | \"sdkKey\"\n | \"apiUrls\"\n | \"domain\"\n | \"timeout\"\n | \"collectEvaluationSummaries\"\n | \"collectLoggerNames\"\n >\n> & {\n // Convenience alias for a single API URL — normalized to apiUrls=[apiUrl]\n // before being passed to the underlying SDK, which only accepts apiUrls.\n apiUrl?: string;\n // We need to redefine the afterEvaluationCallback type to ensure proper dynamic resolution of K\n afterEvaluationCallback?: <K extends keyof TypedFrontEndConfigurationRaw>(\n key: K,\n value: TypedFrontEndConfigurationRaw[K],\n contexts: Contexts | undefined\n ) => void;\n pollInterval?: number;\n onError?: (error: Error) => void;\n};\n\nexport type BaseContext = {\n get: <K extends keyof TypedFrontEndConfigurationRaw>(key: K) => TypedFrontEndConfigurationRaw[K];\n getDuration: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends Duration\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => Duration | undefined;\n contextAttributes: Contexts;\n isEnabled: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends boolean\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => boolean;\n loading: boolean;\n quonfig: typeof quonfig;\n keys: (keyof TypedFrontEndConfigurationRaw)[];\n settings: SharedSettings;\n};\n\nexport type ProvidedContext = BaseContext & ClassMethods<QuonfigTypesafeClass>;\n\nexport const defaultContext: BaseContext = {\n get: (_key) => undefined,\n getDuration: (_key) => undefined,\n isEnabled: (_key) => false,\n keys: [],\n loading: true,\n contextAttributes: {},\n quonfig,\n settings: {},\n};\n\nexport const QuonfigContext = React.createContext<ProvidedContext>(\n defaultContext as ProvidedContext\n);\n\n// This is a factory function that creates a fully typed useQuonfig hook for a specific QuonfigTypesafe class\nexport function createQuonfigHook<T>(TypesafeClass: QuonfigTypesafeClass<T>) {\n return function useQuonfigHook(): BaseContext & T {\n const baseContext = React.useContext(QuonfigContext);\n\n // Memoize the typesafe instance to prevent unnecessary constructor calls\n const typesafeInstance = React.useMemo(() => {\n const instance = new TypesafeClass(baseContext.quonfig);\n\n // Copy baseContext properties to typesafeInstance except for `get` + `quonfig`\n Object.assign(instance as any, {\n getDuration: baseContext.getDuration,\n contextAttributes: baseContext.contextAttributes,\n isEnabled: baseContext.isEnabled,\n loading: baseContext.loading,\n keys: baseContext.keys,\n settings: baseContext.settings,\n });\n\n return instance;\n }, [baseContext]);\n\n return typesafeInstance as BaseContext & T;\n };\n}\n\n// Basic hook for general use - requires type parameter\nexport const useBaseQuonfig = () => React.useContext(QuonfigContext);\n\n// General hook that returns the context with any explicit type\nexport const useQuonfig = (): ProvidedContext => useBaseQuonfig() as unknown as ProvidedContext;\n\nlet globalQuonfigIsTaken = false;\n\nexport const assignQuonfigClient = () => {\n if (globalQuonfigIsTaken) {\n return new Quonfig();\n }\n\n globalQuonfigIsTaken = true;\n return quonfig;\n};\n\nexport type QuonfigProviderProps = SharedSettings & {\n sdkKey: string;\n contextAttributes?: Contexts;\n initialFlags?: Record<string, unknown>;\n};\n\nconst getContextKey = (contextAttributes: Contexts, onError: (e: Error) => void): string => {\n try {\n if (Object.keys(contextAttributes).length === 0) {\n // eslint-disable-next-line no-console\n console.warn(\n \"QuonfigProvider: You haven't passed any contextAttributes. See https://docs.quonfig.com/docs/sdks/react#using-context\"\n );\n }\n\n return encodeContexts(contextAttributes);\n } catch (e) {\n onError(e as Error);\n return \"\";\n }\n};\n\nfunction QuonfigProvider({\n sdkKey,\n contextAttributes = {},\n onError = (e: unknown) => {\n // eslint-disable-next-line no-console\n console.error(e);\n },\n initialFlags,\n children,\n timeout,\n apiUrl,\n apiUrls,\n domain,\n pollInterval,\n afterEvaluationCallback = undefined,\n collectEvaluationSummaries,\n collectLoggerNames,\n}: PropsWithChildren<QuonfigProviderProps>) {\n const settings = {\n sdkKey,\n apiUrl,\n apiUrls,\n domain,\n timeout,\n pollInterval,\n onError,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n // We use this state to prevent a double-init when useEffect fires due to\n // StrictMode\n const mostRecentlyLoadingContextKey = React.useRef<string | undefined>(undefined);\n // We use this state to pass the loading state to the Provider (updating\n // currentLoadingContextKey won't trigger an update)\n const [loading, setLoading] = React.useState(true);\n const [initialLoad, setInitialLoad] = React.useState(true);\n // Here we track the current identity so we can reload our config when it\n // changes\n const [loadedContextKey, setLoadedContextKey] = React.useState(\"\");\n\n const quonfigClient: Quonfig = React.useMemo(() => assignQuonfigClient(), []);\n\n // qfg-daxq: re-render when the underlying client's in-memory config changes\n // (poll fetch, setConfig, hydrate). Without this the singleton mutates in\n // place and React never sees the update.\n const dataVersion = React.useSyncExternalStore(\n React.useCallback((onChange) => quonfigClient.subscribe(onChange), [quonfigClient]),\n React.useCallback(() => quonfigClient.dataVersion, [quonfigClient]),\n React.useCallback(() => 0, [])\n );\n\n const contextKey = getContextKey(contextAttributes, onError);\n\n if (initialFlags && initialLoad) {\n quonfigClient.hydrate(initialFlags);\n setInitialLoad(false);\n setLoadedContextKey(contextKey);\n setLoading(false);\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (pollInterval) {\n // eslint-disable-next-line no-console\n console.warn(\"Polling is not supported when hydrating flags via initialFlags\");\n }\n }\n\n React.useEffect(() => {\n setInitialLoad(false);\n\n if (mostRecentlyLoadingContextKey.current === contextKey) {\n return;\n }\n\n setLoading(true);\n try {\n if (mostRecentlyLoadingContextKey.current === undefined) {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (!sdkKey) {\n throw new Error(\"QuonfigProvider: sdkKey is required\");\n }\n\n quonfigClient.clientName = \"react\";\n quonfigClient.clientVersion = reactSdkVersion;\n\n const resolvedApiUrls = apiUrls ?? (apiUrl ? [apiUrl] : undefined);\n\n const initOptions: InitOptions = {\n context: contextAttributes,\n sdkKey,\n apiUrls: resolvedApiUrls,\n domain,\n timeout,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n quonfigClient\n .init(initOptions)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n\n if (pollInterval) {\n quonfigClient.poll({ frequencyInMs: pollInterval });\n }\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n } else {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n quonfigClient\n .updateContext(contextAttributes)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n }\n } catch (e) {\n setLoading(false);\n onError(e as Error);\n }\n }, [\n sdkKey,\n loadedContextKey,\n contextKey,\n loading,\n setLoading,\n onError,\n quonfigClient.instanceHash,\n ]);\n\n // qfg-2acr: drain telemetry + stop polling/telemetry timers when the\n // provider unmounts so route swaps don't leave the singleton polling\n // forever. Mount-only deps so context-attribute changes don't tear down\n // the SDK. In React StrictMode the synthetic unmount fires too — we\n // reset the init-guard ref so the next mount cleanly re-inits.\n React.useEffect(\n () => () => {\n quonfigClient.close().catch(() => {});\n mostRecentlyLoadingContextKey.current = undefined;\n },\n [quonfigClient]\n );\n\n const value = React.useMemo(() => {\n const baseContext: ProvidedContext = {\n isEnabled: quonfigClient.isEnabled.bind(quonfigClient),\n contextAttributes,\n get: quonfigClient.get.bind(quonfigClient),\n getDuration: quonfigClient.getDuration.bind(quonfigClient),\n keys: Object.keys(quonfigClient.extract()),\n quonfig: quonfigClient,\n loading,\n settings,\n };\n\n return baseContext;\n }, [loadedContextKey, loading, quonfigClient.instanceHash, settings, dataVersion]);\n\n return <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>;\n}\n\nexport { QuonfigProvider, ConfigValue, SharedSettings, QuonfigTypesafeClass };\n","import React, { PropsWithChildren } from \"react\";\nimport { QuonfigContext, assignQuonfigClient, ProvidedContext } from \"./QuonfigProvider\";\n\nexport type QuonfigTestProviderProps = {\n config: Record<string, any>;\n sdkKey?: string;\n};\n\nfunction QuonfigTestProvider({\n sdkKey,\n config,\n children,\n}: PropsWithChildren<QuonfigTestProviderProps>) {\n const get = (key: string) => config[key];\n const getDuration = (key: string) => config[key];\n const isEnabled = (key: string) => !!get(key);\n\n const quonfigClient = React.useMemo(() => assignQuonfigClient(), []);\n\n const value = React.useMemo(() => {\n quonfigClient.get = get;\n quonfigClient.getDuration = getDuration;\n quonfigClient.isEnabled = isEnabled;\n\n const baseContext: ProvidedContext = {\n isEnabled,\n contextAttributes: config.contextAttributes,\n get,\n getDuration,\n loading: false,\n quonfig: quonfigClient,\n keys: Object.keys(config),\n settings: { sdkKey: sdkKey ?? \"fake-sdk-key-via-the-test-provider\" },\n };\n\n return baseContext;\n }, [config, quonfigClient, sdkKey]);\n\n return <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>;\n}\n\nexport { QuonfigTestProvider };\n"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/sdkLogger.ts","../src/QuonfigProvider.tsx","../src/QuonfigTestProvider.tsx"],"names":["React","quonfig","Quonfig","encodeContexts"],"mappings":"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","file":"index.cjs","sourcesContent":["// AUTO-GENERATED from package.json by scripts/generate-version.mjs — do not edit.\nexport default \"0.0.9\";\n","/**\n * Pluggable logger interface for SDK-internal warnings and errors.\n *\n * Shape mirrors @quonfig/node so the contract is uniform across the\n * TypeScript SDKs: `warn` and `error` are required; `debug` and `info` are\n * optional and become no-ops when the supplied logger does not implement\n * them.\n */\nexport interface Logger {\n debug?(message: string, ...args: unknown[]): void;\n info?(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\nexport interface NormalizedLogger {\n debug(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\nconst NOOP = (): void => {};\n\nconst PREFIX = \"[quonfig]\";\n\nfunction defaultSdkLogger(): NormalizedLogger {\n return {\n /* eslint-disable no-console */\n debug: (message, ...args) => console.debug(PREFIX, message, ...args),\n info: (message, ...args) => console.info(PREFIX, message, ...args),\n warn: (message, ...args) => console.warn(PREFIX, message, ...args),\n error: (message, ...args) => console.error(PREFIX, message, ...args),\n /* eslint-enable no-console */\n };\n}\n\nexport function normalizeLogger(logger: Logger | undefined): NormalizedLogger {\n if (!logger) return defaultSdkLogger();\n return {\n debug: logger.debug ? logger.debug.bind(logger) : NOOP,\n info: logger.info ? logger.info.bind(logger) : NOOP,\n warn: logger.warn.bind(logger),\n error: logger.error.bind(logger),\n };\n}\n","import React, { PropsWithChildren } from \"react\";\nimport {\n quonfig,\n type InitOptions,\n type ConfigValue,\n type Contexts,\n Quonfig,\n TypedFrontEndConfigurationRaw,\n FrontEndConfigurationRaw,\n Duration,\n encodeContexts,\n} from \"@quonfig/javascript\";\nimport reactSdkVersion from \"./version\";\nimport { normalizeLogger, type Logger, type NormalizedLogger } from \"./sdkLogger\";\n\n// qfg-lkpm.6: per-tree owner context. The value is the active Quonfig client\n// for this provider subtree. A null value means no Provider is above us, so\n// the next mount can claim the module-level singleton; a non-null value\n// signals \"you're nested, mint a fresh client\". This replaces the previous\n// module-level `globalQuonfigIsTaken` flag, which never reset and so leaked\n// state across test runs and (in principle) across SSR render trees.\nconst QuonfigClientContext = React.createContext<Quonfig | null>(null);\n\n// @quonfig/cli#generate will create interfaces into this namespace for React to consume\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface FrontEndConfigurationAccessor {}\n\nexport type TypedFrontEndConfigurationAccessor = keyof FrontEndConfigurationAccessor extends never\n ? Record<string, unknown>\n : {\n [TypedFlagKey in keyof FrontEndConfigurationAccessor]: FrontEndConfigurationAccessor[TypedFlagKey];\n };\n\ntype ClassMethods<T> = { [K in keyof T]: T[K] };\n\ntype QuonfigTypesafeClass<T = unknown> = new (\n // eslint-disable-next-line no-shadow\n quonfig: Quonfig\n) => T;\n\ntype SharedSettings = Partial<\n Pick<\n InitOptions,\n | \"sdkKey\"\n | \"apiUrls\"\n | \"domain\"\n | \"timeout\"\n | \"collectEvaluationSummaries\"\n | \"collectLoggerNames\"\n >\n> & {\n // Convenience alias for a single API URL — normalized to apiUrls=[apiUrl]\n // before being passed to the underlying SDK, which only accepts apiUrls.\n apiUrl?: string;\n // We need to redefine the afterEvaluationCallback type to ensure proper dynamic resolution of K\n afterEvaluationCallback?: <K extends keyof TypedFrontEndConfigurationRaw>(\n key: K,\n value: TypedFrontEndConfigurationRaw[K],\n contexts: Contexts | undefined\n ) => void;\n pollInterval?: number;\n onError?: (error: Error) => void;\n};\n\nexport type BaseContext = {\n get: <K extends keyof TypedFrontEndConfigurationRaw>(key: K) => TypedFrontEndConfigurationRaw[K];\n getDuration: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends Duration\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => Duration | undefined;\n contextAttributes: Contexts;\n isEnabled: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends boolean\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => boolean;\n loading: boolean;\n quonfig: typeof quonfig;\n keys: (keyof TypedFrontEndConfigurationRaw)[];\n settings: SharedSettings;\n};\n\nexport type ProvidedContext = BaseContext & ClassMethods<QuonfigTypesafeClass>;\n\nexport const defaultContext: BaseContext = {\n get: (_key) => undefined,\n getDuration: (_key) => undefined,\n isEnabled: (_key) => false,\n keys: [],\n loading: true,\n contextAttributes: {},\n quonfig,\n settings: {},\n};\n\nexport const QuonfigContext = React.createContext<ProvidedContext>(\n defaultContext as ProvidedContext\n);\n\n// This is a factory function that creates a fully typed useQuonfig hook for a specific QuonfigTypesafe class\nexport function createQuonfigHook<T>(TypesafeClass: QuonfigTypesafeClass<T>) {\n return function useQuonfigHook(): BaseContext & T {\n const baseContext = React.useContext(QuonfigContext);\n\n // Memoize the typesafe instance to prevent unnecessary constructor calls\n const typesafeInstance = React.useMemo(() => {\n const instance = new TypesafeClass(baseContext.quonfig);\n\n // Copy baseContext properties to typesafeInstance except for `get` + `quonfig`\n Object.assign(instance as any, {\n getDuration: baseContext.getDuration,\n contextAttributes: baseContext.contextAttributes,\n isEnabled: baseContext.isEnabled,\n loading: baseContext.loading,\n keys: baseContext.keys,\n settings: baseContext.settings,\n });\n\n return instance;\n }, [baseContext]);\n\n return typesafeInstance as BaseContext & T;\n };\n}\n\n// Basic hook for general use - requires type parameter\nexport const useBaseQuonfig = () => React.useContext(QuonfigContext);\n\n// General hook that returns the context with any explicit type\nexport const useQuonfig = (): ProvidedContext => useBaseQuonfig() as unknown as ProvidedContext;\n\n// qfg-lkpm.6: per-key selector hook. Subscribes via the underlying client's\n// notify list rather than React context, so a flag mutation only re-renders\n// components whose selected key actually changed. `useQuonfig()` re-renders\n// on every dataVersion bump because the context value identity changes; this\n// hook bypasses that.\nexport function useFlag<K extends keyof TypedFrontEndConfigurationRaw>(\n key: K\n): TypedFrontEndConfigurationRaw[K];\nexport function useFlag(key: string): ConfigValue;\nexport function useFlag(key: string): ConfigValue {\n const client = React.useContext(QuonfigClientContext) ?? quonfig;\n const subscribe = React.useCallback(\n (onChange: () => void) => client.subscribe(onChange),\n [client]\n );\n const getSnapshot = React.useCallback(() => client.get(key), [client, key]);\n const getServerSnapshot = React.useCallback((): ConfigValue => undefined, []);\n return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n// qfg-lkpm.6: replacement for the old module-level `globalQuonfigIsTaken`\n// flag. A provider asks \"is there already a Quonfig client above me in the\n// React tree?\". If yes, mint a fresh client (multi-tenant nesting); if no,\n// claim the module singleton so non-React code that imports `quonfig`\n// directly sees the same instance.\nexport const useQuonfigClient = (): Quonfig => {\n const parentClient = React.useContext(QuonfigClientContext);\n // Mount-only: parent context is fixed for the lifetime of this provider.\n const parentClientRef = React.useRef(parentClient);\n return React.useMemo(() => (parentClientRef.current ? new Quonfig() : quonfig), []);\n};\n\nexport type QuonfigProviderProps = SharedSettings & {\n sdkKey: string;\n contextAttributes?: Contexts;\n initialFlags?: Record<string, unknown>;\n logger?: Logger;\n};\n\nconst getContextKey = (\n contextAttributes: Contexts,\n logger: NormalizedLogger,\n onError: (e: Error) => void\n): string => {\n try {\n if (Object.keys(contextAttributes).length === 0) {\n logger.warn(\n \"QuonfigProvider: You haven't passed any contextAttributes. See https://docs.quonfig.com/docs/sdks/react#using-context\"\n );\n }\n\n return encodeContexts(contextAttributes);\n } catch (e) {\n onError(e as Error);\n return \"\";\n }\n};\n\nfunction QuonfigProvider({\n sdkKey,\n contextAttributes = {},\n onError: userOnError,\n logger,\n initialFlags,\n children,\n timeout,\n apiUrl,\n apiUrls,\n domain,\n pollInterval,\n afterEvaluationCallback = undefined,\n collectEvaluationSummaries,\n collectLoggerNames,\n}: PropsWithChildren<QuonfigProviderProps>) {\n const normalizedLogger = React.useMemo(() => normalizeLogger(logger), [logger]);\n const onError = React.useCallback(\n (e: unknown) => {\n if (userOnError) {\n userOnError(e as Error);\n } else {\n const message = e instanceof Error ? e.message : String(e);\n normalizedLogger.error(message, e);\n }\n },\n [userOnError, normalizedLogger]\n );\n const settings = {\n sdkKey,\n apiUrl,\n apiUrls,\n domain,\n timeout,\n pollInterval,\n onError,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n // We use this state to prevent a double-init when useEffect fires due to\n // StrictMode\n const mostRecentlyLoadingContextKey = React.useRef<string | undefined>(undefined);\n // We use this state to pass the loading state to the Provider (updating\n // currentLoadingContextKey won't trigger an update)\n const [loading, setLoading] = React.useState(true);\n const [initialLoad, setInitialLoad] = React.useState(true);\n // Here we track the current identity so we can reload our config when it\n // changes\n const [loadedContextKey, setLoadedContextKey] = React.useState(\"\");\n\n const quonfigClient: Quonfig = useQuonfigClient();\n\n // qfg-daxq: re-render when the underlying client's in-memory config changes\n // (poll fetch, setConfig, hydrate). Without this the singleton mutates in\n // place and React never sees the update.\n const dataVersion = React.useSyncExternalStore(\n React.useCallback((onChange) => quonfigClient.subscribe(onChange), [quonfigClient]),\n React.useCallback(() => quonfigClient.dataVersion, [quonfigClient]),\n React.useCallback(() => 0, [])\n );\n\n const contextKey = getContextKey(contextAttributes, normalizedLogger, onError);\n\n if (initialFlags && initialLoad) {\n quonfigClient.hydrate(initialFlags);\n setInitialLoad(false);\n setLoadedContextKey(contextKey);\n setLoading(false);\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (pollInterval) {\n normalizedLogger.warn(\"Polling is not supported when hydrating flags via initialFlags\");\n }\n }\n\n React.useEffect(() => {\n setInitialLoad(false);\n\n if (mostRecentlyLoadingContextKey.current === contextKey) {\n return;\n }\n\n setLoading(true);\n try {\n if (mostRecentlyLoadingContextKey.current === undefined) {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (!sdkKey) {\n throw new Error(\"QuonfigProvider: sdkKey is required\");\n }\n\n quonfigClient.clientName = \"react\";\n quonfigClient.clientVersion = reactSdkVersion;\n\n const resolvedApiUrls = apiUrls ?? (apiUrl ? [apiUrl] : undefined);\n\n const initOptions: InitOptions = {\n context: contextAttributes,\n sdkKey,\n apiUrls: resolvedApiUrls,\n domain,\n timeout,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n quonfigClient\n .init(initOptions)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n\n if (pollInterval) {\n quonfigClient.poll({ frequencyInMs: pollInterval });\n }\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n } else {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n quonfigClient\n .updateContext(contextAttributes)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n }\n } catch (e) {\n setLoading(false);\n onError(e as Error);\n }\n }, [\n sdkKey,\n loadedContextKey,\n contextKey,\n loading,\n setLoading,\n onError,\n quonfigClient.instanceHash,\n ]);\n\n // qfg-2acr: drain telemetry + stop polling/telemetry timers when the\n // provider unmounts so route swaps don't leave the singleton polling\n // forever. Mount-only deps so context-attribute changes don't tear down\n // the SDK. In React StrictMode the synthetic unmount fires too — we\n // reset the init-guard ref so the next mount cleanly re-inits.\n React.useEffect(\n () => () => {\n quonfigClient.close().catch(() => {});\n mostRecentlyLoadingContextKey.current = undefined;\n },\n [quonfigClient]\n );\n\n const value = React.useMemo(() => {\n const baseContext: ProvidedContext = {\n isEnabled: quonfigClient.isEnabled.bind(quonfigClient),\n contextAttributes,\n get: quonfigClient.get.bind(quonfigClient),\n getDuration: quonfigClient.getDuration.bind(quonfigClient),\n keys: Object.keys(quonfigClient.extract()),\n quonfig: quonfigClient,\n loading,\n settings,\n };\n\n return baseContext;\n }, [loadedContextKey, loading, quonfigClient.instanceHash, settings, dataVersion]);\n\n return (\n <QuonfigClientContext.Provider value={quonfigClient}>\n <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>\n </QuonfigClientContext.Provider>\n );\n}\n\nexport { QuonfigProvider, QuonfigClientContext, ConfigValue, SharedSettings, QuonfigTypesafeClass };\n","import React, { PropsWithChildren } from \"react\";\nimport {\n QuonfigContext,\n QuonfigClientContext,\n useQuonfigClient,\n ProvidedContext,\n} from \"./QuonfigProvider\";\n\nexport type QuonfigTestProviderProps = {\n config: Record<string, any>;\n sdkKey?: string;\n};\n\nfunction QuonfigTestProvider({\n sdkKey,\n config,\n children,\n}: PropsWithChildren<QuonfigTestProviderProps>) {\n const get = (key: string) => config[key];\n const getDuration = (key: string) => config[key];\n const isEnabled = (key: string) => !!get(key);\n\n const quonfigClient = useQuonfigClient();\n\n const value = React.useMemo(() => {\n quonfigClient.get = get;\n quonfigClient.getDuration = getDuration;\n quonfigClient.isEnabled = isEnabled;\n\n const baseContext: ProvidedContext = {\n isEnabled,\n contextAttributes: config.contextAttributes,\n get,\n getDuration,\n loading: false,\n quonfig: quonfigClient,\n keys: Object.keys(config),\n settings: { sdkKey: sdkKey ?? \"fake-sdk-key-via-the-test-provider\" },\n };\n\n return baseContext;\n }, [config, quonfigClient, sdkKey]);\n\n return (\n <QuonfigClientContext.Provider value={quonfigClient}>\n <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>\n </QuonfigClientContext.Provider>\n );\n}\n\nexport { QuonfigTestProvider };\n"]}
package/dist/index.d.mts CHANGED
@@ -1,7 +1,22 @@
1
- import { InitOptions, TypedFrontEndConfigurationRaw, Contexts, FrontEndConfigurationRaw, Duration, quonfig, Quonfig } from '@quonfig/javascript';
1
+ import { InitOptions, TypedFrontEndConfigurationRaw, Contexts, FrontEndConfigurationRaw, Duration, quonfig, Quonfig, ConfigValue } from '@quonfig/javascript';
2
2
  export { ConfigValue, Contexts, quonfig } from '@quonfig/javascript';
3
3
  import React, { PropsWithChildren } from 'react';
4
4
 
5
+ /**
6
+ * Pluggable logger interface for SDK-internal warnings and errors.
7
+ *
8
+ * Shape mirrors @quonfig/node so the contract is uniform across the
9
+ * TypeScript SDKs: `warn` and `error` are required; `debug` and `info` are
10
+ * optional and become no-ops when the supplied logger does not implement
11
+ * them.
12
+ */
13
+ interface Logger {
14
+ debug?(message: string, ...args: unknown[]): void;
15
+ info?(message: string, ...args: unknown[]): void;
16
+ warn(message: string, ...args: unknown[]): void;
17
+ error(message: string, ...args: unknown[]): void;
18
+ }
19
+
5
20
  interface FrontEndConfigurationAccessor {
6
21
  }
7
22
  type TypedFrontEndConfigurationAccessor = keyof FrontEndConfigurationAccessor extends never ? Record<string, unknown> : {
@@ -34,12 +49,15 @@ type BaseContext = {
34
49
  type ProvidedContext = BaseContext & ClassMethods<QuonfigTypesafeClass>;
35
50
  declare function createQuonfigHook<T>(TypesafeClass: QuonfigTypesafeClass<T>): () => BaseContext & T;
36
51
  declare const useQuonfig: () => ProvidedContext;
52
+ declare function useFlag<K extends keyof TypedFrontEndConfigurationRaw>(key: K): TypedFrontEndConfigurationRaw[K];
53
+ declare function useFlag(key: string): ConfigValue;
37
54
  type QuonfigProviderProps = SharedSettings & {
38
55
  sdkKey: string;
39
56
  contextAttributes?: Contexts;
40
57
  initialFlags?: Record<string, unknown>;
58
+ logger?: Logger;
41
59
  };
42
- declare function QuonfigProvider({ sdkKey, contextAttributes, onError, initialFlags, children, timeout, apiUrl, apiUrls, domain, pollInterval, afterEvaluationCallback, collectEvaluationSummaries, collectLoggerNames, }: PropsWithChildren<QuonfigProviderProps>): React.JSX.Element;
60
+ declare function QuonfigProvider({ sdkKey, contextAttributes, onError: userOnError, logger, initialFlags, children, timeout, apiUrl, apiUrls, domain, pollInterval, afterEvaluationCallback, collectEvaluationSummaries, collectLoggerNames, }: PropsWithChildren<QuonfigProviderProps>): React.JSX.Element;
43
61
 
44
62
  type QuonfigTestProviderProps = {
45
63
  config: Record<string, any>;
@@ -47,4 +65,4 @@ type QuonfigTestProviderProps = {
47
65
  };
48
66
  declare function QuonfigTestProvider({ sdkKey, config, children, }: PropsWithChildren<QuonfigTestProviderProps>): React.JSX.Element;
49
67
 
50
- export { type FrontEndConfigurationAccessor, type ProvidedContext, QuonfigProvider, type QuonfigProviderProps, QuonfigTestProvider, type QuonfigTestProviderProps, type QuonfigTypesafeClass, type SharedSettings, type TypedFrontEndConfigurationAccessor, createQuonfigHook, useQuonfig };
68
+ export { type FrontEndConfigurationAccessor, type Logger, type ProvidedContext, QuonfigProvider, type QuonfigProviderProps, QuonfigTestProvider, type QuonfigTestProviderProps, type QuonfigTypesafeClass, type SharedSettings, type TypedFrontEndConfigurationAccessor, createQuonfigHook, useFlag, useQuonfig };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,22 @@
1
- import { InitOptions, TypedFrontEndConfigurationRaw, Contexts, FrontEndConfigurationRaw, Duration, quonfig, Quonfig } from '@quonfig/javascript';
1
+ import { InitOptions, TypedFrontEndConfigurationRaw, Contexts, FrontEndConfigurationRaw, Duration, quonfig, Quonfig, ConfigValue } from '@quonfig/javascript';
2
2
  export { ConfigValue, Contexts, quonfig } from '@quonfig/javascript';
3
3
  import React, { PropsWithChildren } from 'react';
4
4
 
5
+ /**
6
+ * Pluggable logger interface for SDK-internal warnings and errors.
7
+ *
8
+ * Shape mirrors @quonfig/node so the contract is uniform across the
9
+ * TypeScript SDKs: `warn` and `error` are required; `debug` and `info` are
10
+ * optional and become no-ops when the supplied logger does not implement
11
+ * them.
12
+ */
13
+ interface Logger {
14
+ debug?(message: string, ...args: unknown[]): void;
15
+ info?(message: string, ...args: unknown[]): void;
16
+ warn(message: string, ...args: unknown[]): void;
17
+ error(message: string, ...args: unknown[]): void;
18
+ }
19
+
5
20
  interface FrontEndConfigurationAccessor {
6
21
  }
7
22
  type TypedFrontEndConfigurationAccessor = keyof FrontEndConfigurationAccessor extends never ? Record<string, unknown> : {
@@ -34,12 +49,15 @@ type BaseContext = {
34
49
  type ProvidedContext = BaseContext & ClassMethods<QuonfigTypesafeClass>;
35
50
  declare function createQuonfigHook<T>(TypesafeClass: QuonfigTypesafeClass<T>): () => BaseContext & T;
36
51
  declare const useQuonfig: () => ProvidedContext;
52
+ declare function useFlag<K extends keyof TypedFrontEndConfigurationRaw>(key: K): TypedFrontEndConfigurationRaw[K];
53
+ declare function useFlag(key: string): ConfigValue;
37
54
  type QuonfigProviderProps = SharedSettings & {
38
55
  sdkKey: string;
39
56
  contextAttributes?: Contexts;
40
57
  initialFlags?: Record<string, unknown>;
58
+ logger?: Logger;
41
59
  };
42
- declare function QuonfigProvider({ sdkKey, contextAttributes, onError, initialFlags, children, timeout, apiUrl, apiUrls, domain, pollInterval, afterEvaluationCallback, collectEvaluationSummaries, collectLoggerNames, }: PropsWithChildren<QuonfigProviderProps>): React.JSX.Element;
60
+ declare function QuonfigProvider({ sdkKey, contextAttributes, onError: userOnError, logger, initialFlags, children, timeout, apiUrl, apiUrls, domain, pollInterval, afterEvaluationCallback, collectEvaluationSummaries, collectLoggerNames, }: PropsWithChildren<QuonfigProviderProps>): React.JSX.Element;
43
61
 
44
62
  type QuonfigTestProviderProps = {
45
63
  config: Record<string, any>;
@@ -47,4 +65,4 @@ type QuonfigTestProviderProps = {
47
65
  };
48
66
  declare function QuonfigTestProvider({ sdkKey, config, children, }: PropsWithChildren<QuonfigTestProviderProps>): React.JSX.Element;
49
67
 
50
- export { type FrontEndConfigurationAccessor, type ProvidedContext, QuonfigProvider, type QuonfigProviderProps, QuonfigTestProvider, type QuonfigTestProviderProps, type QuonfigTypesafeClass, type SharedSettings, type TypedFrontEndConfigurationAccessor, createQuonfigHook, useQuonfig };
68
+ export { type FrontEndConfigurationAccessor, type Logger, type ProvidedContext, QuonfigProvider, type QuonfigProviderProps, QuonfigTestProvider, type QuonfigTestProviderProps, type QuonfigTypesafeClass, type SharedSettings, type TypedFrontEndConfigurationAccessor, createQuonfigHook, useFlag, useQuonfig };
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { quonfig, encodeContexts, Quonfig } from '@quonfig/javascript';
1
+ import { quonfig, Quonfig, encodeContexts } from '@quonfig/javascript';
2
2
  export { quonfig } from '@quonfig/javascript';
3
3
  import React from 'react';
4
4
 
@@ -7,7 +7,32 @@ import React from 'react';
7
7
  // src/version.ts
8
8
  var version_default = "0.0.9";
9
9
 
10
+ // src/sdkLogger.ts
11
+ var NOOP = () => {
12
+ };
13
+ var PREFIX = "[quonfig]";
14
+ function defaultSdkLogger() {
15
+ return {
16
+ /* eslint-disable no-console */
17
+ debug: (message, ...args) => console.debug(PREFIX, message, ...args),
18
+ info: (message, ...args) => console.info(PREFIX, message, ...args),
19
+ warn: (message, ...args) => console.warn(PREFIX, message, ...args),
20
+ error: (message, ...args) => console.error(PREFIX, message, ...args)
21
+ /* eslint-enable no-console */
22
+ };
23
+ }
24
+ function normalizeLogger(logger) {
25
+ if (!logger) return defaultSdkLogger();
26
+ return {
27
+ debug: logger.debug ? logger.debug.bind(logger) : NOOP,
28
+ info: logger.info ? logger.info.bind(logger) : NOOP,
29
+ warn: logger.warn.bind(logger),
30
+ error: logger.error.bind(logger)
31
+ };
32
+ }
33
+
10
34
  // src/QuonfigProvider.tsx
35
+ var QuonfigClientContext = React.createContext(null);
11
36
  var defaultContext = {
12
37
  get: (_key) => void 0,
13
38
  getDuration: (_key) => void 0,
@@ -41,18 +66,26 @@ function createQuonfigHook(TypesafeClass) {
41
66
  }
42
67
  var useBaseQuonfig = () => React.useContext(QuonfigContext);
43
68
  var useQuonfig = () => useBaseQuonfig();
44
- var globalQuonfigIsTaken = false;
45
- var assignQuonfigClient = () => {
46
- if (globalQuonfigIsTaken) {
47
- return new Quonfig();
48
- }
49
- globalQuonfigIsTaken = true;
50
- return quonfig;
69
+ function useFlag(key) {
70
+ var _a;
71
+ const client = (_a = React.useContext(QuonfigClientContext)) != null ? _a : quonfig;
72
+ const subscribe = React.useCallback(
73
+ (onChange) => client.subscribe(onChange),
74
+ [client]
75
+ );
76
+ const getSnapshot = React.useCallback(() => client.get(key), [client, key]);
77
+ const getServerSnapshot = React.useCallback(() => void 0, []);
78
+ return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
79
+ }
80
+ var useQuonfigClient = () => {
81
+ const parentClient = React.useContext(QuonfigClientContext);
82
+ const parentClientRef = React.useRef(parentClient);
83
+ return React.useMemo(() => parentClientRef.current ? new Quonfig() : quonfig, []);
51
84
  };
52
- var getContextKey = (contextAttributes, onError) => {
85
+ var getContextKey = (contextAttributes, logger, onError) => {
53
86
  try {
54
87
  if (Object.keys(contextAttributes).length === 0) {
55
- console.warn(
88
+ logger.warn(
56
89
  "QuonfigProvider: You haven't passed any contextAttributes. See https://docs.quonfig.com/docs/sdks/react#using-context"
57
90
  );
58
91
  }
@@ -65,9 +98,8 @@ var getContextKey = (contextAttributes, onError) => {
65
98
  function QuonfigProvider({
66
99
  sdkKey,
67
100
  contextAttributes = {},
68
- onError = (e) => {
69
- console.error(e);
70
- },
101
+ onError: userOnError,
102
+ logger,
71
103
  initialFlags,
72
104
  children,
73
105
  timeout,
@@ -79,6 +111,18 @@ function QuonfigProvider({
79
111
  collectEvaluationSummaries,
80
112
  collectLoggerNames
81
113
  }) {
114
+ const normalizedLogger = React.useMemo(() => normalizeLogger(logger), [logger]);
115
+ const onError = React.useCallback(
116
+ (e) => {
117
+ if (userOnError) {
118
+ userOnError(e);
119
+ } else {
120
+ const message = e instanceof Error ? e.message : String(e);
121
+ normalizedLogger.error(message, e);
122
+ }
123
+ },
124
+ [userOnError, normalizedLogger]
125
+ );
82
126
  const settings = {
83
127
  sdkKey,
84
128
  apiUrl,
@@ -95,13 +139,13 @@ function QuonfigProvider({
95
139
  const [loading, setLoading] = React.useState(true);
96
140
  const [initialLoad, setInitialLoad] = React.useState(true);
97
141
  const [loadedContextKey, setLoadedContextKey] = React.useState("");
98
- const quonfigClient = React.useMemo(() => assignQuonfigClient(), []);
142
+ const quonfigClient = useQuonfigClient();
99
143
  const dataVersion = React.useSyncExternalStore(
100
144
  React.useCallback((onChange) => quonfigClient.subscribe(onChange), [quonfigClient]),
101
145
  React.useCallback(() => quonfigClient.dataVersion, [quonfigClient]),
102
146
  React.useCallback(() => 0, [])
103
147
  );
104
- const contextKey = getContextKey(contextAttributes, onError);
148
+ const contextKey = getContextKey(contextAttributes, normalizedLogger, onError);
105
149
  if (initialFlags && initialLoad) {
106
150
  quonfigClient.hydrate(initialFlags);
107
151
  setInitialLoad(false);
@@ -109,7 +153,7 @@ function QuonfigProvider({
109
153
  setLoading(false);
110
154
  mostRecentlyLoadingContextKey.current = contextKey;
111
155
  if (pollInterval) {
112
- console.warn("Polling is not supported when hydrating flags via initialFlags");
156
+ normalizedLogger.warn("Polling is not supported when hydrating flags via initialFlags");
113
157
  }
114
158
  }
115
159
  React.useEffect(() => {
@@ -191,7 +235,7 @@ function QuonfigProvider({
191
235
  };
192
236
  return baseContext;
193
237
  }, [loadedContextKey, loading, quonfigClient.instanceHash, settings, dataVersion]);
194
- return /* @__PURE__ */ React.createElement(QuonfigContext.Provider, { value }, children);
238
+ return /* @__PURE__ */ React.createElement(QuonfigClientContext.Provider, { value: quonfigClient }, /* @__PURE__ */ React.createElement(QuonfigContext.Provider, { value }, children));
195
239
  }
196
240
  function QuonfigTestProvider({
197
241
  sdkKey,
@@ -201,7 +245,7 @@ function QuonfigTestProvider({
201
245
  const get = (key) => config[key];
202
246
  const getDuration = (key) => config[key];
203
247
  const isEnabled = (key) => !!get(key);
204
- const quonfigClient = React.useMemo(() => assignQuonfigClient(), []);
248
+ const quonfigClient = useQuonfigClient();
205
249
  const value = React.useMemo(() => {
206
250
  quonfigClient.get = get;
207
251
  quonfigClient.getDuration = getDuration;
@@ -218,9 +262,9 @@ function QuonfigTestProvider({
218
262
  };
219
263
  return baseContext;
220
264
  }, [config, quonfigClient, sdkKey]);
221
- return /* @__PURE__ */ React.createElement(QuonfigContext.Provider, { value }, children);
265
+ return /* @__PURE__ */ React.createElement(QuonfigClientContext.Provider, { value: quonfigClient }, /* @__PURE__ */ React.createElement(QuonfigContext.Provider, { value }, children));
222
266
  }
223
267
 
224
- export { QuonfigProvider, QuonfigTestProvider, createQuonfigHook, useQuonfig };
268
+ export { QuonfigProvider, QuonfigTestProvider, createQuonfigHook, useFlag, useQuonfig };
225
269
  //# sourceMappingURL=index.mjs.map
226
270
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/QuonfigProvider.tsx","../src/QuonfigTestProvider.tsx"],"names":["React"],"mappings":";;;;;;;AACA,IAAO,eAAA,GAAQ,OAAA;;;ACuFR,IAAM,cAAA,GAA8B;AAAA,EACzC,GAAA,EAAK,CAAC,IAAA,KAAS,MAAA;AAAA,EACf,WAAA,EAAa,CAAC,IAAA,KAAS,MAAA;AAAA,EACvB,SAAA,EAAW,CAAC,IAAA,KAAS,KAAA;AAAA,EACrB,MAAM,EAAC;AAAA,EACP,OAAA,EAAS,IAAA;AAAA,EACT,mBAAmB,EAAC;AAAA,EACpB,OAAA;AAAA,EACA,UAAU;AACZ,CAAA;AAEO,IAAM,iBAAiB,KAAA,CAAM,aAAA;AAAA,EAClC;AACF,CAAA;AAGO,SAAS,kBAAqB,aAAA,EAAwC;AAC3E,EAAA,OAAO,SAAS,cAAA,GAAkC;AAChD,IAAA,MAAM,WAAA,GAAc,KAAA,CAAM,UAAA,CAAW,cAAc,CAAA;AAGnD,IAAA,MAAM,gBAAA,GAAmB,KAAA,CAAM,OAAA,CAAQ,MAAM;AAC3C,MAAA,MAAM,QAAA,GAAW,IAAI,aAAA,CAAc,WAAA,CAAY,OAAO,CAAA;AAGtD,MAAA,MAAA,CAAO,OAAO,QAAA,EAAiB;AAAA,QAC7B,aAAa,WAAA,CAAY,WAAA;AAAA,QACzB,mBAAmB,WAAA,CAAY,iBAAA;AAAA,QAC/B,WAAW,WAAA,CAAY,SAAA;AAAA,QACvB,SAAS,WAAA,CAAY,OAAA;AAAA,QACrB,MAAM,WAAA,CAAY,IAAA;AAAA,QAClB,UAAU,WAAA,CAAY;AAAA,OACvB,CAAA;AAED,MAAA,OAAO,QAAA;AAAA,IACT,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,IAAA,OAAO,gBAAA;AAAA,EACT,CAAA;AACF;AAGO,IAAM,cAAA,GAAiB,MAAM,KAAA,CAAM,UAAA,CAAW,cAAc,CAAA;AAG5D,IAAM,UAAA,GAAa,MAAuB,cAAA;AAEjD,IAAI,oBAAA,GAAuB,KAAA;AAEpB,IAAM,sBAAsB,MAAM;AACvC,EAAA,IAAI,oBAAA,EAAsB;AACxB,IAAA,OAAO,IAAI,OAAA,EAAQ;AAAA,EACrB;AAEA,EAAA,oBAAA,GAAuB,IAAA;AACvB,EAAA,OAAO,OAAA;AACT,CAAA;AAQA,IAAM,aAAA,GAAgB,CAAC,iBAAA,EAA6B,OAAA,KAAwC;AAC1F,EAAA,IAAI;AACF,IAAA,IAAI,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAA,CAAE,WAAW,CAAA,EAAG;AAE/C,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN;AAAA,OACF;AAAA,IACF;AAEA,IAAA,OAAO,eAAe,iBAAiB,CAAA;AAAA,EACzC,SAAS,CAAA,EAAG;AACV,IAAA,OAAA,CAAQ,CAAU,CAAA;AAClB,IAAA,OAAO,EAAA;AAAA,EACT;AACF,CAAA;AAEA,SAAS,eAAA,CAAgB;AAAA,EACvB,MAAA;AAAA,EACA,oBAAoB,EAAC;AAAA,EACrB,OAAA,GAAU,CAAC,CAAA,KAAe;AAExB,IAAA,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,EACjB,CAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,YAAA;AAAA,EACA,uBAAA,GAA0B,MAAA;AAAA,EAC1B,0BAAA;AAAA,EACA;AACF,CAAA,EAA4C;AAC1C,EAAA,MAAM,QAAA,GAAW;AAAA,IACf,MAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA;AAAA,IACA,uBAAA;AAAA,IACA,0BAAA;AAAA,IACA;AAAA,GACF;AAIA,EAAA,MAAM,6BAAA,GAAgC,KAAA,CAAM,MAAA,CAA2B,MAAS,CAAA;AAGhF,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,KAAA,CAAM,SAAS,IAAI,CAAA;AACjD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,KAAA,CAAM,SAAS,IAAI,CAAA;AAGzD,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,KAAA,CAAM,SAAS,EAAE,CAAA;AAEjE,EAAA,MAAM,gBAAyB,KAAA,CAAM,OAAA,CAAQ,MAAM,mBAAA,EAAoB,EAAG,EAAE,CAAA;AAK5E,EAAA,MAAM,cAAc,KAAA,CAAM,oBAAA;AAAA,IACxB,KAAA,CAAM,WAAA,CAAY,CAAC,QAAA,KAAa,aAAA,CAAc,UAAU,QAAQ,CAAA,EAAG,CAAC,aAAa,CAAC,CAAA;AAAA,IAClF,MAAM,WAAA,CAAY,MAAM,cAAc,WAAA,EAAa,CAAC,aAAa,CAAC,CAAA;AAAA,IAClE,KAAA,CAAM,WAAA,CAAY,MAAM,CAAA,EAAG,EAAE;AAAA,GAC/B;AAEA,EAAA,MAAM,UAAA,GAAa,aAAA,CAAc,iBAAA,EAAmB,OAAO,CAAA;AAE3D,EAAA,IAAI,gBAAgB,WAAA,EAAa;AAC/B,IAAA,aAAA,CAAc,QAAQ,YAAY,CAAA;AAClC,IAAA,cAAA,CAAe,KAAK,CAAA;AACpB,IAAA,mBAAA,CAAoB,UAAU,CAAA;AAC9B,IAAA,UAAA,CAAW,KAAK,CAAA;AAChB,IAAA,6BAAA,CAA8B,OAAA,GAAU,UAAA;AAExC,IAAA,IAAI,YAAA,EAAc;AAEhB,MAAA,OAAA,CAAQ,KAAK,gEAAgE,CAAA;AAAA,IAC/E;AAAA,EACF;AAEA,EAAA,KAAA,CAAM,UAAU,MAAM;AACpB,IAAA,cAAA,CAAe,KAAK,CAAA;AAEpB,IAAA,IAAI,6BAAA,CAA8B,YAAY,UAAA,EAAY;AACxD,MAAA;AAAA,IACF;AAEA,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAA,IAAI;AACF,MAAA,IAAI,6BAAA,CAA8B,YAAY,KAAA,CAAA,EAAW;AACvD,QAAA,6BAAA,CAA8B,OAAA,GAAU,UAAA;AAExC,QAAA,IAAI,CAAC,MAAA,EAAQ;AACX,UAAA,MAAM,IAAI,MAAM,qCAAqC,CAAA;AAAA,QACvD;AAEA,QAAA,aAAA,CAAc,UAAA,GAAa,OAAA;AAC3B,QAAA,aAAA,CAAc,aAAA,GAAgB,eAAA;AAE9B,QAAA,MAAM,eAAA,GAAkB,OAAA,IAAA,IAAA,GAAA,OAAA,GAAY,MAAA,GAAS,CAAC,MAAM,CAAA,GAAI,KAAA,CAAA;AAExD,QAAA,MAAM,WAAA,GAA2B;AAAA,UAC/B,OAAA,EAAS,iBAAA;AAAA,UACT,MAAA;AAAA,UACA,OAAA,EAAS,eAAA;AAAA,UACT,MAAA;AAAA,UACA,OAAA;AAAA,UACA,uBAAA;AAAA,UACA,0BAAA;AAAA,UACA;AAAA,SACF;AAEA,QAAA,aAAA,CACG,IAAA,CAAK,WAAW,CAAA,CAChB,IAAA,CAAK,MAAM;AACV,UAAA,mBAAA,CAAoB,UAAU,CAAA;AAC9B,UAAA,UAAA,CAAW,KAAK,CAAA;AAEhB,UAAA,IAAI,YAAA,EAAc;AAChB,YAAA,aAAA,CAAc,IAAA,CAAK,EAAE,aAAA,EAAe,YAAA,EAAc,CAAA;AAAA,UACpD;AAAA,QACF,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,MAAA,KAAgB;AACtB,UAAA,UAAA,CAAW,KAAK,CAAA;AAChB,UAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,QAChB,CAAC,CAAA;AAAA,MACL,CAAA,MAAO;AACL,QAAA,6BAAA,CAA8B,OAAA,GAAU,UAAA;AAExC,QAAA,aAAA,CACG,aAAA,CAAc,iBAAiB,CAAA,CAC/B,IAAA,CAAK,MAAM;AACV,UAAA,mBAAA,CAAoB,UAAU,CAAA;AAC9B,UAAA,UAAA,CAAW,KAAK,CAAA;AAAA,QAClB,CAAC,CAAA,CACA,KAAA,CAAM,CAAC,MAAA,KAAgB;AACtB,UAAA,UAAA,CAAW,KAAK,CAAA;AAChB,UAAA,OAAA,CAAQ,MAAM,CAAA;AAAA,QAChB,CAAC,CAAA;AAAA,MACL;AAAA,IACF,SAAS,CAAA,EAAG;AACV,MAAA,UAAA,CAAW,KAAK,CAAA;AAChB,MAAA,OAAA,CAAQ,CAAU,CAAA;AAAA,IACpB;AAAA,EACF,CAAA,EAAG;AAAA,IACD,MAAA;AAAA,IACA,gBAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA,aAAA,CAAc;AAAA,GACf,CAAA;AAOD,EAAA,KAAA,CAAM,SAAA;AAAA,IACJ,MAAM,MAAM;AACV,MAAA,aAAA,CAAc,KAAA,EAAM,CAAE,KAAA,CAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AACpC,MAAA,6BAAA,CAA8B,OAAA,GAAU,MAAA;AAAA,IAC1C,CAAA;AAAA,IACA,CAAC,aAAa;AAAA,GAChB;AAEA,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,MAAM;AAChC,IAAA,MAAM,WAAA,GAA+B;AAAA,MACnC,SAAA,EAAW,aAAA,CAAc,SAAA,CAAU,IAAA,CAAK,aAAa,CAAA;AAAA,MACrD,iBAAA;AAAA,MACA,GAAA,EAAK,aAAA,CAAc,GAAA,CAAI,IAAA,CAAK,aAAa,CAAA;AAAA,MACzC,WAAA,EAAa,aAAA,CAAc,WAAA,CAAY,IAAA,CAAK,aAAa,CAAA;AAAA,MACzD,IAAA,EAAM,MAAA,CAAO,IAAA,CAAK,aAAA,CAAc,SAAS,CAAA;AAAA,MACzC,OAAA,EAAS,aAAA;AAAA,MACT,OAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,OAAO,WAAA;AAAA,EACT,CAAA,EAAG,CAAC,gBAAA,EAAkB,OAAA,EAAS,cAAc,YAAA,EAAc,QAAA,EAAU,WAAW,CAAC,CAAA;AAEjF,EAAA,uBAAO,KAAA,CAAA,aAAA,CAAC,cAAA,CAAe,QAAA,EAAf,EAAwB,SAAe,QAAS,CAAA;AAC1D;AC3UA,SAAS,mBAAA,CAAoB;AAAA,EAC3B,MAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA,EAAgD;AAC9C,EAAA,MAAM,GAAA,GAAM,CAAC,GAAA,KAAgB,MAAA,CAAO,GAAG,CAAA;AACvC,EAAA,MAAM,WAAA,GAAc,CAAC,GAAA,KAAgB,MAAA,CAAO,GAAG,CAAA;AAC/C,EAAA,MAAM,YAAY,CAAC,GAAA,KAAgB,CAAC,CAAC,IAAI,GAAG,CAAA;AAE5C,EAAA,MAAM,gBAAgBA,KAAAA,CAAM,OAAA,CAAQ,MAAM,mBAAA,EAAoB,EAAG,EAAE,CAAA;AAEnE,EAAA,MAAM,KAAA,GAAQA,KAAAA,CAAM,OAAA,CAAQ,MAAM;AAChC,IAAA,aAAA,CAAc,GAAA,GAAM,GAAA;AACpB,IAAA,aAAA,CAAc,WAAA,GAAc,WAAA;AAC5B,IAAA,aAAA,CAAc,SAAA,GAAY,SAAA;AAE1B,IAAA,MAAM,WAAA,GAA+B;AAAA,MACnC,SAAA;AAAA,MACA,mBAAmB,MAAA,CAAO,iBAAA;AAAA,MAC1B,GAAA;AAAA,MACA,WAAA;AAAA,MACA,OAAA,EAAS,KAAA;AAAA,MACT,OAAA,EAAS,aAAA;AAAA,MACT,IAAA,EAAM,MAAA,CAAO,IAAA,CAAK,MAAM,CAAA;AAAA,MACxB,QAAA,EAAU,EAAE,MAAA,EAAQ,MAAA,IAAA,IAAA,GAAA,MAAA,GAAU,oCAAA;AAAqC,KACrE;AAEA,IAAA,OAAO,WAAA;AAAA,EACT,CAAA,EAAG,CAAC,MAAA,EAAQ,aAAA,EAAe,MAAM,CAAC,CAAA;AAElC,EAAA,uBAAOA,KAAAA,CAAA,aAAA,CAAC,eAAe,QAAA,EAAf,EAAwB,SAAe,QAAS,CAAA;AAC1D","file":"index.mjs","sourcesContent":["// AUTO-GENERATED from package.json by scripts/generate-version.mjs — do not edit.\nexport default \"0.0.9\";\n","import React, { PropsWithChildren } from \"react\";\nimport {\n quonfig,\n type InitOptions,\n type ConfigValue,\n type Contexts,\n Quonfig,\n TypedFrontEndConfigurationRaw,\n FrontEndConfigurationRaw,\n Duration,\n encodeContexts,\n} from \"@quonfig/javascript\";\nimport reactSdkVersion from \"./version\";\n\n// @quonfig/cli#generate will create interfaces into this namespace for React to consume\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface FrontEndConfigurationAccessor {}\n\nexport type TypedFrontEndConfigurationAccessor = keyof FrontEndConfigurationAccessor extends never\n ? Record<string, unknown>\n : {\n [TypedFlagKey in keyof FrontEndConfigurationAccessor]: FrontEndConfigurationAccessor[TypedFlagKey];\n };\n\ntype ClassMethods<T> = { [K in keyof T]: T[K] };\n\ntype QuonfigTypesafeClass<T = unknown> = new (\n // eslint-disable-next-line no-shadow\n quonfig: Quonfig\n) => T;\n\ntype SharedSettings = Partial<\n Pick<\n InitOptions,\n | \"sdkKey\"\n | \"apiUrls\"\n | \"domain\"\n | \"timeout\"\n | \"collectEvaluationSummaries\"\n | \"collectLoggerNames\"\n >\n> & {\n // Convenience alias for a single API URL — normalized to apiUrls=[apiUrl]\n // before being passed to the underlying SDK, which only accepts apiUrls.\n apiUrl?: string;\n // We need to redefine the afterEvaluationCallback type to ensure proper dynamic resolution of K\n afterEvaluationCallback?: <K extends keyof TypedFrontEndConfigurationRaw>(\n key: K,\n value: TypedFrontEndConfigurationRaw[K],\n contexts: Contexts | undefined\n ) => void;\n pollInterval?: number;\n onError?: (error: Error) => void;\n};\n\nexport type BaseContext = {\n get: <K extends keyof TypedFrontEndConfigurationRaw>(key: K) => TypedFrontEndConfigurationRaw[K];\n getDuration: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends Duration\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => Duration | undefined;\n contextAttributes: Contexts;\n isEnabled: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends boolean\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => boolean;\n loading: boolean;\n quonfig: typeof quonfig;\n keys: (keyof TypedFrontEndConfigurationRaw)[];\n settings: SharedSettings;\n};\n\nexport type ProvidedContext = BaseContext & ClassMethods<QuonfigTypesafeClass>;\n\nexport const defaultContext: BaseContext = {\n get: (_key) => undefined,\n getDuration: (_key) => undefined,\n isEnabled: (_key) => false,\n keys: [],\n loading: true,\n contextAttributes: {},\n quonfig,\n settings: {},\n};\n\nexport const QuonfigContext = React.createContext<ProvidedContext>(\n defaultContext as ProvidedContext\n);\n\n// This is a factory function that creates a fully typed useQuonfig hook for a specific QuonfigTypesafe class\nexport function createQuonfigHook<T>(TypesafeClass: QuonfigTypesafeClass<T>) {\n return function useQuonfigHook(): BaseContext & T {\n const baseContext = React.useContext(QuonfigContext);\n\n // Memoize the typesafe instance to prevent unnecessary constructor calls\n const typesafeInstance = React.useMemo(() => {\n const instance = new TypesafeClass(baseContext.quonfig);\n\n // Copy baseContext properties to typesafeInstance except for `get` + `quonfig`\n Object.assign(instance as any, {\n getDuration: baseContext.getDuration,\n contextAttributes: baseContext.contextAttributes,\n isEnabled: baseContext.isEnabled,\n loading: baseContext.loading,\n keys: baseContext.keys,\n settings: baseContext.settings,\n });\n\n return instance;\n }, [baseContext]);\n\n return typesafeInstance as BaseContext & T;\n };\n}\n\n// Basic hook for general use - requires type parameter\nexport const useBaseQuonfig = () => React.useContext(QuonfigContext);\n\n// General hook that returns the context with any explicit type\nexport const useQuonfig = (): ProvidedContext => useBaseQuonfig() as unknown as ProvidedContext;\n\nlet globalQuonfigIsTaken = false;\n\nexport const assignQuonfigClient = () => {\n if (globalQuonfigIsTaken) {\n return new Quonfig();\n }\n\n globalQuonfigIsTaken = true;\n return quonfig;\n};\n\nexport type QuonfigProviderProps = SharedSettings & {\n sdkKey: string;\n contextAttributes?: Contexts;\n initialFlags?: Record<string, unknown>;\n};\n\nconst getContextKey = (contextAttributes: Contexts, onError: (e: Error) => void): string => {\n try {\n if (Object.keys(contextAttributes).length === 0) {\n // eslint-disable-next-line no-console\n console.warn(\n \"QuonfigProvider: You haven't passed any contextAttributes. See https://docs.quonfig.com/docs/sdks/react#using-context\"\n );\n }\n\n return encodeContexts(contextAttributes);\n } catch (e) {\n onError(e as Error);\n return \"\";\n }\n};\n\nfunction QuonfigProvider({\n sdkKey,\n contextAttributes = {},\n onError = (e: unknown) => {\n // eslint-disable-next-line no-console\n console.error(e);\n },\n initialFlags,\n children,\n timeout,\n apiUrl,\n apiUrls,\n domain,\n pollInterval,\n afterEvaluationCallback = undefined,\n collectEvaluationSummaries,\n collectLoggerNames,\n}: PropsWithChildren<QuonfigProviderProps>) {\n const settings = {\n sdkKey,\n apiUrl,\n apiUrls,\n domain,\n timeout,\n pollInterval,\n onError,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n // We use this state to prevent a double-init when useEffect fires due to\n // StrictMode\n const mostRecentlyLoadingContextKey = React.useRef<string | undefined>(undefined);\n // We use this state to pass the loading state to the Provider (updating\n // currentLoadingContextKey won't trigger an update)\n const [loading, setLoading] = React.useState(true);\n const [initialLoad, setInitialLoad] = React.useState(true);\n // Here we track the current identity so we can reload our config when it\n // changes\n const [loadedContextKey, setLoadedContextKey] = React.useState(\"\");\n\n const quonfigClient: Quonfig = React.useMemo(() => assignQuonfigClient(), []);\n\n // qfg-daxq: re-render when the underlying client's in-memory config changes\n // (poll fetch, setConfig, hydrate). Without this the singleton mutates in\n // place and React never sees the update.\n const dataVersion = React.useSyncExternalStore(\n React.useCallback((onChange) => quonfigClient.subscribe(onChange), [quonfigClient]),\n React.useCallback(() => quonfigClient.dataVersion, [quonfigClient]),\n React.useCallback(() => 0, [])\n );\n\n const contextKey = getContextKey(contextAttributes, onError);\n\n if (initialFlags && initialLoad) {\n quonfigClient.hydrate(initialFlags);\n setInitialLoad(false);\n setLoadedContextKey(contextKey);\n setLoading(false);\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (pollInterval) {\n // eslint-disable-next-line no-console\n console.warn(\"Polling is not supported when hydrating flags via initialFlags\");\n }\n }\n\n React.useEffect(() => {\n setInitialLoad(false);\n\n if (mostRecentlyLoadingContextKey.current === contextKey) {\n return;\n }\n\n setLoading(true);\n try {\n if (mostRecentlyLoadingContextKey.current === undefined) {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (!sdkKey) {\n throw new Error(\"QuonfigProvider: sdkKey is required\");\n }\n\n quonfigClient.clientName = \"react\";\n quonfigClient.clientVersion = reactSdkVersion;\n\n const resolvedApiUrls = apiUrls ?? (apiUrl ? [apiUrl] : undefined);\n\n const initOptions: InitOptions = {\n context: contextAttributes,\n sdkKey,\n apiUrls: resolvedApiUrls,\n domain,\n timeout,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n quonfigClient\n .init(initOptions)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n\n if (pollInterval) {\n quonfigClient.poll({ frequencyInMs: pollInterval });\n }\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n } else {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n quonfigClient\n .updateContext(contextAttributes)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n }\n } catch (e) {\n setLoading(false);\n onError(e as Error);\n }\n }, [\n sdkKey,\n loadedContextKey,\n contextKey,\n loading,\n setLoading,\n onError,\n quonfigClient.instanceHash,\n ]);\n\n // qfg-2acr: drain telemetry + stop polling/telemetry timers when the\n // provider unmounts so route swaps don't leave the singleton polling\n // forever. Mount-only deps so context-attribute changes don't tear down\n // the SDK. In React StrictMode the synthetic unmount fires too — we\n // reset the init-guard ref so the next mount cleanly re-inits.\n React.useEffect(\n () => () => {\n quonfigClient.close().catch(() => {});\n mostRecentlyLoadingContextKey.current = undefined;\n },\n [quonfigClient]\n );\n\n const value = React.useMemo(() => {\n const baseContext: ProvidedContext = {\n isEnabled: quonfigClient.isEnabled.bind(quonfigClient),\n contextAttributes,\n get: quonfigClient.get.bind(quonfigClient),\n getDuration: quonfigClient.getDuration.bind(quonfigClient),\n keys: Object.keys(quonfigClient.extract()),\n quonfig: quonfigClient,\n loading,\n settings,\n };\n\n return baseContext;\n }, [loadedContextKey, loading, quonfigClient.instanceHash, settings, dataVersion]);\n\n return <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>;\n}\n\nexport { QuonfigProvider, ConfigValue, SharedSettings, QuonfigTypesafeClass };\n","import React, { PropsWithChildren } from \"react\";\nimport { QuonfigContext, assignQuonfigClient, ProvidedContext } from \"./QuonfigProvider\";\n\nexport type QuonfigTestProviderProps = {\n config: Record<string, any>;\n sdkKey?: string;\n};\n\nfunction QuonfigTestProvider({\n sdkKey,\n config,\n children,\n}: PropsWithChildren<QuonfigTestProviderProps>) {\n const get = (key: string) => config[key];\n const getDuration = (key: string) => config[key];\n const isEnabled = (key: string) => !!get(key);\n\n const quonfigClient = React.useMemo(() => assignQuonfigClient(), []);\n\n const value = React.useMemo(() => {\n quonfigClient.get = get;\n quonfigClient.getDuration = getDuration;\n quonfigClient.isEnabled = isEnabled;\n\n const baseContext: ProvidedContext = {\n isEnabled,\n contextAttributes: config.contextAttributes,\n get,\n getDuration,\n loading: false,\n quonfig: quonfigClient,\n keys: Object.keys(config),\n settings: { sdkKey: sdkKey ?? \"fake-sdk-key-via-the-test-provider\" },\n };\n\n return baseContext;\n }, [config, quonfigClient, sdkKey]);\n\n return <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>;\n}\n\nexport { QuonfigTestProvider };\n"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/sdkLogger.ts","../src/QuonfigProvider.tsx","../src/QuonfigTestProvider.tsx"],"names":["React"],"mappings":"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","file":"index.mjs","sourcesContent":["// AUTO-GENERATED from package.json by scripts/generate-version.mjs — do not edit.\nexport default \"0.0.9\";\n","/**\n * Pluggable logger interface for SDK-internal warnings and errors.\n *\n * Shape mirrors @quonfig/node so the contract is uniform across the\n * TypeScript SDKs: `warn` and `error` are required; `debug` and `info` are\n * optional and become no-ops when the supplied logger does not implement\n * them.\n */\nexport interface Logger {\n debug?(message: string, ...args: unknown[]): void;\n info?(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\nexport interface NormalizedLogger {\n debug(message: string, ...args: unknown[]): void;\n info(message: string, ...args: unknown[]): void;\n warn(message: string, ...args: unknown[]): void;\n error(message: string, ...args: unknown[]): void;\n}\n\nconst NOOP = (): void => {};\n\nconst PREFIX = \"[quonfig]\";\n\nfunction defaultSdkLogger(): NormalizedLogger {\n return {\n /* eslint-disable no-console */\n debug: (message, ...args) => console.debug(PREFIX, message, ...args),\n info: (message, ...args) => console.info(PREFIX, message, ...args),\n warn: (message, ...args) => console.warn(PREFIX, message, ...args),\n error: (message, ...args) => console.error(PREFIX, message, ...args),\n /* eslint-enable no-console */\n };\n}\n\nexport function normalizeLogger(logger: Logger | undefined): NormalizedLogger {\n if (!logger) return defaultSdkLogger();\n return {\n debug: logger.debug ? logger.debug.bind(logger) : NOOP,\n info: logger.info ? logger.info.bind(logger) : NOOP,\n warn: logger.warn.bind(logger),\n error: logger.error.bind(logger),\n };\n}\n","import React, { PropsWithChildren } from \"react\";\nimport {\n quonfig,\n type InitOptions,\n type ConfigValue,\n type Contexts,\n Quonfig,\n TypedFrontEndConfigurationRaw,\n FrontEndConfigurationRaw,\n Duration,\n encodeContexts,\n} from \"@quonfig/javascript\";\nimport reactSdkVersion from \"./version\";\nimport { normalizeLogger, type Logger, type NormalizedLogger } from \"./sdkLogger\";\n\n// qfg-lkpm.6: per-tree owner context. The value is the active Quonfig client\n// for this provider subtree. A null value means no Provider is above us, so\n// the next mount can claim the module-level singleton; a non-null value\n// signals \"you're nested, mint a fresh client\". This replaces the previous\n// module-level `globalQuonfigIsTaken` flag, which never reset and so leaked\n// state across test runs and (in principle) across SSR render trees.\nconst QuonfigClientContext = React.createContext<Quonfig | null>(null);\n\n// @quonfig/cli#generate will create interfaces into this namespace for React to consume\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface FrontEndConfigurationAccessor {}\n\nexport type TypedFrontEndConfigurationAccessor = keyof FrontEndConfigurationAccessor extends never\n ? Record<string, unknown>\n : {\n [TypedFlagKey in keyof FrontEndConfigurationAccessor]: FrontEndConfigurationAccessor[TypedFlagKey];\n };\n\ntype ClassMethods<T> = { [K in keyof T]: T[K] };\n\ntype QuonfigTypesafeClass<T = unknown> = new (\n // eslint-disable-next-line no-shadow\n quonfig: Quonfig\n) => T;\n\ntype SharedSettings = Partial<\n Pick<\n InitOptions,\n | \"sdkKey\"\n | \"apiUrls\"\n | \"domain\"\n | \"timeout\"\n | \"collectEvaluationSummaries\"\n | \"collectLoggerNames\"\n >\n> & {\n // Convenience alias for a single API URL — normalized to apiUrls=[apiUrl]\n // before being passed to the underlying SDK, which only accepts apiUrls.\n apiUrl?: string;\n // We need to redefine the afterEvaluationCallback type to ensure proper dynamic resolution of K\n afterEvaluationCallback?: <K extends keyof TypedFrontEndConfigurationRaw>(\n key: K,\n value: TypedFrontEndConfigurationRaw[K],\n contexts: Contexts | undefined\n ) => void;\n pollInterval?: number;\n onError?: (error: Error) => void;\n};\n\nexport type BaseContext = {\n get: <K extends keyof TypedFrontEndConfigurationRaw>(key: K) => TypedFrontEndConfigurationRaw[K];\n getDuration: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends Duration\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => Duration | undefined;\n contextAttributes: Contexts;\n isEnabled: <\n K extends keyof FrontEndConfigurationRaw extends never\n ? string\n : {\n [IK in keyof TypedFrontEndConfigurationRaw]: TypedFrontEndConfigurationRaw[IK] extends boolean\n ? IK\n : never;\n }[keyof TypedFrontEndConfigurationRaw],\n >(\n key: K\n ) => boolean;\n loading: boolean;\n quonfig: typeof quonfig;\n keys: (keyof TypedFrontEndConfigurationRaw)[];\n settings: SharedSettings;\n};\n\nexport type ProvidedContext = BaseContext & ClassMethods<QuonfigTypesafeClass>;\n\nexport const defaultContext: BaseContext = {\n get: (_key) => undefined,\n getDuration: (_key) => undefined,\n isEnabled: (_key) => false,\n keys: [],\n loading: true,\n contextAttributes: {},\n quonfig,\n settings: {},\n};\n\nexport const QuonfigContext = React.createContext<ProvidedContext>(\n defaultContext as ProvidedContext\n);\n\n// This is a factory function that creates a fully typed useQuonfig hook for a specific QuonfigTypesafe class\nexport function createQuonfigHook<T>(TypesafeClass: QuonfigTypesafeClass<T>) {\n return function useQuonfigHook(): BaseContext & T {\n const baseContext = React.useContext(QuonfigContext);\n\n // Memoize the typesafe instance to prevent unnecessary constructor calls\n const typesafeInstance = React.useMemo(() => {\n const instance = new TypesafeClass(baseContext.quonfig);\n\n // Copy baseContext properties to typesafeInstance except for `get` + `quonfig`\n Object.assign(instance as any, {\n getDuration: baseContext.getDuration,\n contextAttributes: baseContext.contextAttributes,\n isEnabled: baseContext.isEnabled,\n loading: baseContext.loading,\n keys: baseContext.keys,\n settings: baseContext.settings,\n });\n\n return instance;\n }, [baseContext]);\n\n return typesafeInstance as BaseContext & T;\n };\n}\n\n// Basic hook for general use - requires type parameter\nexport const useBaseQuonfig = () => React.useContext(QuonfigContext);\n\n// General hook that returns the context with any explicit type\nexport const useQuonfig = (): ProvidedContext => useBaseQuonfig() as unknown as ProvidedContext;\n\n// qfg-lkpm.6: per-key selector hook. Subscribes via the underlying client's\n// notify list rather than React context, so a flag mutation only re-renders\n// components whose selected key actually changed. `useQuonfig()` re-renders\n// on every dataVersion bump because the context value identity changes; this\n// hook bypasses that.\nexport function useFlag<K extends keyof TypedFrontEndConfigurationRaw>(\n key: K\n): TypedFrontEndConfigurationRaw[K];\nexport function useFlag(key: string): ConfigValue;\nexport function useFlag(key: string): ConfigValue {\n const client = React.useContext(QuonfigClientContext) ?? quonfig;\n const subscribe = React.useCallback(\n (onChange: () => void) => client.subscribe(onChange),\n [client]\n );\n const getSnapshot = React.useCallback(() => client.get(key), [client, key]);\n const getServerSnapshot = React.useCallback((): ConfigValue => undefined, []);\n return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n\n// qfg-lkpm.6: replacement for the old module-level `globalQuonfigIsTaken`\n// flag. A provider asks \"is there already a Quonfig client above me in the\n// React tree?\". If yes, mint a fresh client (multi-tenant nesting); if no,\n// claim the module singleton so non-React code that imports `quonfig`\n// directly sees the same instance.\nexport const useQuonfigClient = (): Quonfig => {\n const parentClient = React.useContext(QuonfigClientContext);\n // Mount-only: parent context is fixed for the lifetime of this provider.\n const parentClientRef = React.useRef(parentClient);\n return React.useMemo(() => (parentClientRef.current ? new Quonfig() : quonfig), []);\n};\n\nexport type QuonfigProviderProps = SharedSettings & {\n sdkKey: string;\n contextAttributes?: Contexts;\n initialFlags?: Record<string, unknown>;\n logger?: Logger;\n};\n\nconst getContextKey = (\n contextAttributes: Contexts,\n logger: NormalizedLogger,\n onError: (e: Error) => void\n): string => {\n try {\n if (Object.keys(contextAttributes).length === 0) {\n logger.warn(\n \"QuonfigProvider: You haven't passed any contextAttributes. See https://docs.quonfig.com/docs/sdks/react#using-context\"\n );\n }\n\n return encodeContexts(contextAttributes);\n } catch (e) {\n onError(e as Error);\n return \"\";\n }\n};\n\nfunction QuonfigProvider({\n sdkKey,\n contextAttributes = {},\n onError: userOnError,\n logger,\n initialFlags,\n children,\n timeout,\n apiUrl,\n apiUrls,\n domain,\n pollInterval,\n afterEvaluationCallback = undefined,\n collectEvaluationSummaries,\n collectLoggerNames,\n}: PropsWithChildren<QuonfigProviderProps>) {\n const normalizedLogger = React.useMemo(() => normalizeLogger(logger), [logger]);\n const onError = React.useCallback(\n (e: unknown) => {\n if (userOnError) {\n userOnError(e as Error);\n } else {\n const message = e instanceof Error ? e.message : String(e);\n normalizedLogger.error(message, e);\n }\n },\n [userOnError, normalizedLogger]\n );\n const settings = {\n sdkKey,\n apiUrl,\n apiUrls,\n domain,\n timeout,\n pollInterval,\n onError,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n // We use this state to prevent a double-init when useEffect fires due to\n // StrictMode\n const mostRecentlyLoadingContextKey = React.useRef<string | undefined>(undefined);\n // We use this state to pass the loading state to the Provider (updating\n // currentLoadingContextKey won't trigger an update)\n const [loading, setLoading] = React.useState(true);\n const [initialLoad, setInitialLoad] = React.useState(true);\n // Here we track the current identity so we can reload our config when it\n // changes\n const [loadedContextKey, setLoadedContextKey] = React.useState(\"\");\n\n const quonfigClient: Quonfig = useQuonfigClient();\n\n // qfg-daxq: re-render when the underlying client's in-memory config changes\n // (poll fetch, setConfig, hydrate). Without this the singleton mutates in\n // place and React never sees the update.\n const dataVersion = React.useSyncExternalStore(\n React.useCallback((onChange) => quonfigClient.subscribe(onChange), [quonfigClient]),\n React.useCallback(() => quonfigClient.dataVersion, [quonfigClient]),\n React.useCallback(() => 0, [])\n );\n\n const contextKey = getContextKey(contextAttributes, normalizedLogger, onError);\n\n if (initialFlags && initialLoad) {\n quonfigClient.hydrate(initialFlags);\n setInitialLoad(false);\n setLoadedContextKey(contextKey);\n setLoading(false);\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (pollInterval) {\n normalizedLogger.warn(\"Polling is not supported when hydrating flags via initialFlags\");\n }\n }\n\n React.useEffect(() => {\n setInitialLoad(false);\n\n if (mostRecentlyLoadingContextKey.current === contextKey) {\n return;\n }\n\n setLoading(true);\n try {\n if (mostRecentlyLoadingContextKey.current === undefined) {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n if (!sdkKey) {\n throw new Error(\"QuonfigProvider: sdkKey is required\");\n }\n\n quonfigClient.clientName = \"react\";\n quonfigClient.clientVersion = reactSdkVersion;\n\n const resolvedApiUrls = apiUrls ?? (apiUrl ? [apiUrl] : undefined);\n\n const initOptions: InitOptions = {\n context: contextAttributes,\n sdkKey,\n apiUrls: resolvedApiUrls,\n domain,\n timeout,\n afterEvaluationCallback,\n collectEvaluationSummaries,\n collectLoggerNames,\n };\n\n quonfigClient\n .init(initOptions)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n\n if (pollInterval) {\n quonfigClient.poll({ frequencyInMs: pollInterval });\n }\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n } else {\n mostRecentlyLoadingContextKey.current = contextKey;\n\n quonfigClient\n .updateContext(contextAttributes)\n .then(() => {\n setLoadedContextKey(contextKey);\n setLoading(false);\n })\n .catch((reason: any) => {\n setLoading(false);\n onError(reason);\n });\n }\n } catch (e) {\n setLoading(false);\n onError(e as Error);\n }\n }, [\n sdkKey,\n loadedContextKey,\n contextKey,\n loading,\n setLoading,\n onError,\n quonfigClient.instanceHash,\n ]);\n\n // qfg-2acr: drain telemetry + stop polling/telemetry timers when the\n // provider unmounts so route swaps don't leave the singleton polling\n // forever. Mount-only deps so context-attribute changes don't tear down\n // the SDK. In React StrictMode the synthetic unmount fires too — we\n // reset the init-guard ref so the next mount cleanly re-inits.\n React.useEffect(\n () => () => {\n quonfigClient.close().catch(() => {});\n mostRecentlyLoadingContextKey.current = undefined;\n },\n [quonfigClient]\n );\n\n const value = React.useMemo(() => {\n const baseContext: ProvidedContext = {\n isEnabled: quonfigClient.isEnabled.bind(quonfigClient),\n contextAttributes,\n get: quonfigClient.get.bind(quonfigClient),\n getDuration: quonfigClient.getDuration.bind(quonfigClient),\n keys: Object.keys(quonfigClient.extract()),\n quonfig: quonfigClient,\n loading,\n settings,\n };\n\n return baseContext;\n }, [loadedContextKey, loading, quonfigClient.instanceHash, settings, dataVersion]);\n\n return (\n <QuonfigClientContext.Provider value={quonfigClient}>\n <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>\n </QuonfigClientContext.Provider>\n );\n}\n\nexport { QuonfigProvider, QuonfigClientContext, ConfigValue, SharedSettings, QuonfigTypesafeClass };\n","import React, { PropsWithChildren } from \"react\";\nimport {\n QuonfigContext,\n QuonfigClientContext,\n useQuonfigClient,\n ProvidedContext,\n} from \"./QuonfigProvider\";\n\nexport type QuonfigTestProviderProps = {\n config: Record<string, any>;\n sdkKey?: string;\n};\n\nfunction QuonfigTestProvider({\n sdkKey,\n config,\n children,\n}: PropsWithChildren<QuonfigTestProviderProps>) {\n const get = (key: string) => config[key];\n const getDuration = (key: string) => config[key];\n const isEnabled = (key: string) => !!get(key);\n\n const quonfigClient = useQuonfigClient();\n\n const value = React.useMemo(() => {\n quonfigClient.get = get;\n quonfigClient.getDuration = getDuration;\n quonfigClient.isEnabled = isEnabled;\n\n const baseContext: ProvidedContext = {\n isEnabled,\n contextAttributes: config.contextAttributes,\n get,\n getDuration,\n loading: false,\n quonfig: quonfigClient,\n keys: Object.keys(config),\n settings: { sdkKey: sdkKey ?? \"fake-sdk-key-via-the-test-provider\" },\n };\n\n return baseContext;\n }, [config, quonfigClient, sdkKey]);\n\n return (\n <QuonfigClientContext.Provider value={quonfigClient}>\n <QuonfigContext.Provider value={value}>{children}</QuonfigContext.Provider>\n </QuonfigClientContext.Provider>\n );\n}\n\nexport { QuonfigTestProvider };\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "packageManager": "yarn@4.11.0",
3
3
  "name": "@quonfig/react",
4
- "version": "0.0.10",
4
+ "version": "0.0.12",
5
5
  "description": "Feature Flags & Dynamic Configuration as a Service",
6
6
  "main": "dist/index.cjs",
7
7
  "module": "dist/index.mjs",
@@ -38,6 +38,19 @@
38
38
  "prettier": "prettier . -l",
39
39
  "prettier:fix": "prettier --write ."
40
40
  },
41
+ "size-limit": [
42
+ {
43
+ "name": "dist/index.mjs (gzipped)",
44
+ "path": "dist/index.mjs",
45
+ "limit": "2 kB",
46
+ "gzip": true,
47
+ "ignore": [
48
+ "react",
49
+ "react-dom",
50
+ "@quonfig/javascript"
51
+ ]
52
+ }
53
+ ],
41
54
  "author": "Jeffrey Chupp",
42
55
  "license": "ISC",
43
56
  "keywords": [
@@ -46,6 +59,7 @@
46
59
  ],
47
60
  "devDependencies": {
48
61
  "@quonfig/javascript": ">=0.0.14",
62
+ "@size-limit/preset-small-lib": "^11.1.6",
49
63
  "@testing-library/jest-dom": "^5.16.5",
50
64
  "@testing-library/react": "^13.3.0",
51
65
  "@types/jest": "^28.1.6",
@@ -68,6 +82,7 @@
68
82
  "prettier": "^3.0.0",
69
83
  "react": "^19.1.1",
70
84
  "react-dom": "^19.1.1",
85
+ "size-limit": "^11.1.6",
71
86
  "ts-jest": "^28.0.7",
72
87
  "ts-node": "^10.9.1",
73
88
  "tsup": "^8.4.0",
@@ -75,6 +90,6 @@
75
90
  },
76
91
  "peerDependencies": {
77
92
  "@quonfig/javascript": ">=0.0.14",
78
- "react": "^16 || ^17 || ^18 || ^19"
93
+ "react": "^18 || ^19"
79
94
  }
80
95
  }