@lovart-open/flags 0.0.2 → 0.0.3-canary.pr10.fd78749

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.
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,315 @@
1
+ import z from 'zod';
2
+ import { StatsigOptions, StatsigUser, StatsigClient } from '@statsig/js-client';
3
+ export * from '@statsig/react-bindings';
4
+
5
+ /**
6
+ * Core types for the feature flag and parameter store.
7
+ */
8
+ /**
9
+ * Statsig Feature Gate type (internal use).
10
+ */
11
+ interface FeatureGate {
12
+ value: boolean;
13
+ idType?: string | null;
14
+ details?: {
15
+ reason: string;
16
+ };
17
+ }
18
+ /**
19
+ * Statsig evaluation options.
20
+ */
21
+ interface EvaluationOptions {
22
+ /** Disable exposure logging */
23
+ disableExposureLog?: boolean;
24
+ }
25
+ /**
26
+ * Feature flag definition.
27
+ */
28
+ interface FlagDefinition {
29
+ /** Human-readable description */
30
+ description?: string;
31
+ /** Fixed value for test/E2E environments */
32
+ testOverride?: boolean;
33
+ /** Static override value (higher priority than remote) */
34
+ override?: boolean;
35
+ /** Mark as kept locally (no remote config needed) */
36
+ keep?: boolean;
37
+ }
38
+ /**
39
+ * Flag value source priority levels.
40
+ */
41
+ type FlagPriority = 'url' | 'test' | 'override' | 'remote' | 'fallback';
42
+ /**
43
+ * Flag state with value and source information.
44
+ */
45
+ interface FlagState {
46
+ flag: boolean;
47
+ source: FlagPriority;
48
+ }
49
+ /**
50
+ * Extract string keys from flag definitions.
51
+ */
52
+ type FlagKeyOf<TDefinitions extends Record<string, FlagDefinition>> = Extract<keyof TDefinitions, string>;
53
+ /**
54
+ * Snapshot of all flag states keyed by flag key.
55
+ */
56
+ type FlagSnapshot<TKey extends string = string> = Record<TKey, FlagState>;
57
+ /**
58
+ * Remote flag values (boolean map).
59
+ */
60
+ type RemoteFlagValues<TKey extends string = string> = Partial<Record<TKey, boolean>>;
61
+
62
+ /**
63
+ * Feature Flags factory module.
64
+ * Use createFlagStore to create type-safe flag stores and hooks.
65
+ */
66
+
67
+ interface ResolveOptions extends EvaluationOptions {
68
+ gate?: FeatureGate;
69
+ search?: string;
70
+ }
71
+ /**
72
+ * Parse URL query string for flag overrides.
73
+ * Format: ?ff.flag_name=1 or ?ff.flag_name=true
74
+ */
75
+ declare function parseUrlOverrides(search?: string): Record<string, boolean>;
76
+ /**
77
+ * Type-safe FlagStore class.
78
+ */
79
+ declare class FlagStore<TDefinitions extends Record<string, FlagDefinition>> {
80
+ private readonly definitions;
81
+ constructor(definitions: TDefinitions);
82
+ /** Get snapshot of all flag states */
83
+ get snapshot(): FlagSnapshot<Extract<keyof TDefinitions, string>>;
84
+ /** Get flag boolean value */
85
+ getFlag: <K extends keyof TDefinitions>(key: K, options?: EvaluationOptions) => boolean;
86
+ /** Get flag state with source info */
87
+ getFlagState<K extends keyof TDefinitions>(key: K, options?: EvaluationOptions): FlagState;
88
+ /**
89
+ * Resolve flag value with priority: URL > test > override > remote > fallback
90
+ */
91
+ resolve<K extends keyof TDefinitions>(key: K, options?: ResolveOptions): FlagState;
92
+ }
93
+ /**
94
+ * Create a type-safe Flag Store with React hooks.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const MY_FLAGS = {
99
+ * dark_mode: { description: 'Dark mode toggle' },
100
+ * new_checkout: { description: 'New checkout flow' },
101
+ * } as const satisfies Record<string, FlagDefinition>;
102
+ *
103
+ * export const { flagStore, useFlag, useFlagState } = createFlagStore(MY_FLAGS);
104
+ *
105
+ * // Full type safety and autocomplete
106
+ * const isDark = useFlag('dark_mode'); // ✓ autocomplete
107
+ * useFlag('unknown'); // ✗ compile error
108
+ * ```
109
+ */
110
+ declare function createFlagStore<T extends Record<string, FlagDefinition>>(definitions: T): {
111
+ /** FlagStore instance */
112
+ flagStore: FlagStore<T>;
113
+ /** React Hook: Get flag boolean value */
114
+ useFlag: (key: Extract<keyof T, string>, options?: EvaluationOptions) => boolean;
115
+ /** React Hook: Get flag state { flag, source } */
116
+ useFlagState: (key: Extract<keyof T, string>, options?: EvaluationOptions) => FlagState;
117
+ };
118
+
119
+ /**
120
+ * Simple logger interface.
121
+ */
122
+ type Logger = (message: string, data?: unknown) => void;
123
+ /**
124
+ * Set a custom logger implementation.
125
+ */
126
+ declare function setLogger(logger: Logger): void;
127
+ /**
128
+ * Get the current logger instance.
129
+ */
130
+ declare function getLogger(): Logger;
131
+
132
+ /**
133
+ * Statsig client initialization module.
134
+ * Provides singleton pattern for StatsigClient.
135
+ */
136
+
137
+ /**
138
+ * Bootstrap data structure for server-side rendering.
139
+ * Pass this from your BFF/SSR layer to enable zero-network initialization.
140
+ */
141
+ interface StatsigBootstrap {
142
+ user?: StatsigUser;
143
+ data: string;
144
+ }
145
+ interface InitOptions extends StatsigOptions {
146
+ /**
147
+ * Test environment indicator.
148
+ * When true or returns true, testOverride values will be used.
149
+ *
150
+ * - `boolean`: static value
151
+ * - `() => boolean`: dynamic function
152
+ * - `undefined`: testOverride disabled
153
+ */
154
+ isTestEnv?: boolean | (() => boolean);
155
+ /**
156
+ * Custom logger implementation.
157
+ */
158
+ logger?: Logger;
159
+ /**
160
+ * Bootstrap data from server (enables zero-network init).
161
+ * The data will be set before initializeSync() is called.
162
+ */
163
+ bootstrap?: StatsigBootstrap | null;
164
+ }
165
+ /**
166
+ * Initialize Statsig client (singleton).
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * // Basic initialization
171
+ * initStatsigClient('client-xxx', { userID: 'user-123' }, {
172
+ * environment: { tier: 'production' },
173
+ * isTestEnv: () => Boolean(window.__E2E__),
174
+ * });
175
+ *
176
+ * // With server-side bootstrap (zero network)
177
+ * initStatsigClient('client-xxx', { userID: 'user-123' }, {
178
+ * environment: { tier: 'production' },
179
+ * bootstrap: { data: '...' },
180
+ * });
181
+ * ```
182
+ */
183
+ declare function initStatsigClient(clientKey: string, user: StatsigUser, options?: InitOptions): StatsigClient;
184
+ /**
185
+ * Get Statsig client synchronously.
186
+ *
187
+ * @throws Error if not initialized
188
+ */
189
+ declare function getStatsigClientSync(): StatsigClient;
190
+ /**
191
+ * Check if currently in test environment.
192
+ * Returns false if isTestEnv was not configured.
193
+ */
194
+ declare function isTestEnv(): boolean;
195
+
196
+ /**
197
+ * Parameter Stores factory module.
198
+ * Use createParamStore to create type-safe parameter stores and hooks.
199
+ */
200
+
201
+ /** Parameter definition */
202
+ interface ParamDefinition<T = unknown> {
203
+ schema: z.ZodType<T>;
204
+ fallback: T;
205
+ description?: string;
206
+ testOverride?: T;
207
+ override?: T;
208
+ }
209
+ /** Parameter store definition */
210
+ interface ParamStoreDefinition<TParams extends Record<string, ParamDefinition<any>>> {
211
+ description?: string;
212
+ keep?: boolean;
213
+ params: TParams;
214
+ }
215
+ /** Helper function to define a single param with type inference */
216
+ declare function defineParam<T>(def: ParamDefinition<T>): ParamDefinition<T>;
217
+ /** Event name for schema mismatch errors */
218
+ declare const PARAM_SCHEMA_MISMATCH_EVENT = "param-schema-mismatch";
219
+ /** Detail payload for schema mismatch event */
220
+ interface ParamSchemaMismatchDetail {
221
+ storeKey: string;
222
+ paramKey: string;
223
+ source: string;
224
+ value: unknown;
225
+ error: z.ZodError;
226
+ }
227
+ /**
228
+ * Parse URL query string for param store overrides (standalone function).
229
+ * Format: ?fp.store.param=value or ?fp.store={"param":"value"}
230
+ *
231
+ * @example
232
+ * parseParamStoreUrlOverrides('?fp.config.debug=true')
233
+ * // => { config: { debug: true } }
234
+ */
235
+ declare function parseParamStoreUrlOverrides(search?: string): Record<string, Record<string, unknown>>;
236
+ /** Single param state with value and source */
237
+ interface ParamState<T> {
238
+ value: T;
239
+ source: 'url' | 'test' | 'override' | 'remote' | 'fallback';
240
+ error?: z.ZodError;
241
+ fallback?: T;
242
+ }
243
+ /** Store handle for accessing params */
244
+ interface ParamStoreHandle<TParams extends Record<string, ParamDefinition<any>>> {
245
+ get<P extends keyof TParams>(paramKey: P): z.infer<TParams[P]['schema']>;
246
+ getState<P extends keyof TParams>(paramKey: P): ParamState<z.infer<TParams[P]['schema']>>;
247
+ }
248
+ /** Infer value types from param definitions */
249
+ type ParamStoreValue<TParams extends Record<string, ParamDefinition<any>>> = {
250
+ [K in keyof TParams]: z.infer<TParams[K]['schema']>;
251
+ };
252
+ /** Statsig ParameterStore return type */
253
+ type StatsigParameterStore = ReturnType<ReturnType<typeof getStatsigClientSync>['getParameterStore']>;
254
+ /** Resolve options for param lookup */
255
+ interface ParamResolveOptions {
256
+ statsigStore?: StatsigParameterStore | null;
257
+ search?: string;
258
+ disableExposureLog?: boolean;
259
+ }
260
+ /**
261
+ * Type-safe ParamStore class.
262
+ */
263
+ declare class ParamStore<TStores extends Record<string, ParamStoreDefinition<any>>> {
264
+ private readonly definitions;
265
+ constructor(definitions: TStores);
266
+ /**
267
+ * Parse URL query string for param overrides.
268
+ * Format: ?fp.store.param=value or ?fp.store={"param":"value"}
269
+ */
270
+ private parseUrlOverrides;
271
+ /**
272
+ * Resolve param value with priority: URL > test > override > remote > fallback
273
+ */
274
+ private resolve;
275
+ /** Get param state with value and source info */
276
+ getParamState<K extends keyof TStores, P extends keyof TStores[K]['params']>(storeKey: K, paramKey: P, options?: ParamResolveOptions): ParamState<z.infer<TStores[K]['params'][P]['schema']>>;
277
+ /** Get param value directly */
278
+ getParam<K extends keyof TStores, P extends keyof TStores[K]['params']>(storeKey: K, paramKey: P, options?: ParamResolveOptions): z.infer<TStores[K]['params'][P]['schema']>;
279
+ /** Get store handle for multiple param access */
280
+ getStore<K extends keyof TStores>(storeKey: K, options?: ParamResolveOptions): ParamStoreHandle<TStores[K]['params']>;
281
+ }
282
+ /**
283
+ * Create a type-safe Param Store with React hooks.
284
+ *
285
+ * @example
286
+ * ```ts
287
+ * const MY_PARAMS = {
288
+ * homepage_cta: {
289
+ * description: 'Homepage CTA button',
290
+ * params: {
291
+ * text: defineParam({ schema: z.string(), fallback: 'Learn More' }),
292
+ * color: defineParam({ schema: z.enum(['red', 'blue']), fallback: 'blue' }),
293
+ * },
294
+ * },
295
+ * } as const satisfies Record<string, ParamStoreDefinition<any>>;
296
+ *
297
+ * export const { paramStore, useParam, useParamState } = createParamStore(MY_PARAMS);
298
+ *
299
+ * // Full type safety and autocomplete
300
+ * const text = useParam('homepage_cta', 'text'); // string type
301
+ * const color = useParam('homepage_cta', 'color'); // 'red' | 'blue' type
302
+ * ```
303
+ */
304
+ declare function createParamStore<T extends Record<string, ParamStoreDefinition<any>>>(definitions: T): {
305
+ /** ParamStore instance */
306
+ paramStore: ParamStore<T>;
307
+ /** React Hook: Get single param value */
308
+ useParam: <K extends Extract<keyof T, string>, P extends keyof T[K]["params"]>(storeKey: K, paramKey: P, options?: ParamResolveOptions) => z.infer<T[K]["params"][P]["schema"]>;
309
+ /** React Hook: Get single param state */
310
+ useParamState: <K extends Extract<keyof T, string>, P extends keyof T[K]["params"]>(storeKey: K, paramKey: P, options?: ParamResolveOptions) => ParamState<z.infer<T[K]["params"][P]["schema"]>>;
311
+ /** React Hook: Get store handle */
312
+ useParamStore: <K extends Extract<keyof T, string>>(storeKey: K, options?: ParamResolveOptions) => ParamStoreHandle<T[K]["params"]>;
313
+ };
314
+
315
+ export { type EvaluationOptions, type FeatureGate, type FlagDefinition, type FlagKeyOf, type FlagPriority, type FlagSnapshot, type FlagState, FlagStore, type InitOptions, type Logger, PARAM_SCHEMA_MISMATCH_EVENT, type ParamDefinition, type ParamResolveOptions, type ParamSchemaMismatchDetail, type ParamState, ParamStore, type ParamStoreDefinition, type ParamStoreHandle, type ParamStoreValue, type RemoteFlagValues, type ResolveOptions, type StatsigBootstrap, createFlagStore, createParamStore, defineParam, getLogger, getStatsigClientSync, initStatsigClient, isTestEnv, parseParamStoreUrlOverrides, parseUrlOverrides, setLogger };
@@ -0,0 +1,379 @@
1
+ // src/statsig/flags.ts
2
+ import { useFeatureGate } from "@statsig/react-bindings";
3
+
4
+ // src/statsig/client.ts
5
+ import { StatsigClient } from "@statsig/js-client";
6
+
7
+ // src/statsig/logger.ts
8
+ var defaultLogger = (message, data) => {
9
+ if (data !== void 0) {
10
+ console.info(`[statsig] ${message}`, data);
11
+ } else {
12
+ console.info(`[statsig] ${message}`);
13
+ }
14
+ };
15
+ var currentLogger = defaultLogger;
16
+ function setLogger(logger) {
17
+ currentLogger = logger;
18
+ }
19
+ function getLogger() {
20
+ return currentLogger;
21
+ }
22
+
23
+ // src/statsig/client.ts
24
+ var client = null;
25
+ var testEnvDetector = null;
26
+ function initStatsigClient(clientKey, user, options) {
27
+ if (client) return client;
28
+ const { isTestEnv: isTestEnv2, logger, bootstrap, ...statsigOptions } = options ?? {};
29
+ if (logger) {
30
+ setLogger(logger);
31
+ }
32
+ const log = getLogger();
33
+ if (typeof isTestEnv2 === "function") {
34
+ testEnvDetector = isTestEnv2;
35
+ } else if (typeof isTestEnv2 === "boolean") {
36
+ testEnvDetector = () => isTestEnv2;
37
+ }
38
+ if (bootstrap) {
39
+ log(`Initializing with bootstrap data (userID: '${user.userID}')`);
40
+ } else {
41
+ log(`Initializing in default mode (userID: '${user.userID}')`);
42
+ }
43
+ client = new StatsigClient(clientKey, user, statsigOptions);
44
+ if (bootstrap?.data) {
45
+ client.dataAdapter.setData(bootstrap.data);
46
+ }
47
+ client.initializeSync();
48
+ return client;
49
+ }
50
+ function getStatsigClientSync() {
51
+ if (!client) {
52
+ throw new Error("[@lovart-open/flags/statsig] Not initialized. Call initStatsigClient() first.");
53
+ }
54
+ return client;
55
+ }
56
+ function isTestEnv() {
57
+ return testEnvDetector?.() ?? false;
58
+ }
59
+
60
+ // src/statsig/flags.ts
61
+ var TRUE_LITERALS = /* @__PURE__ */ new Set(["1", "true"]);
62
+ var hasWindow = typeof window !== "undefined";
63
+ function parseUrlOverrides(search) {
64
+ if (!hasWindow && !search) return {};
65
+ const searchString = search ?? (hasWindow ? window.location.search : "");
66
+ const params = new URLSearchParams(searchString);
67
+ const overrides = {};
68
+ for (const [key, value] of params.entries()) {
69
+ if (key.startsWith("ff.")) {
70
+ overrides[key.slice(3)] = TRUE_LITERALS.has(value.trim().toLowerCase());
71
+ }
72
+ }
73
+ return overrides;
74
+ }
75
+ var FlagStore = class {
76
+ constructor(definitions) {
77
+ /** Get flag boolean value */
78
+ this.getFlag = (key, options) => this.getFlagState(key, options).flag;
79
+ this.definitions = definitions;
80
+ }
81
+ /** Get snapshot of all flag states */
82
+ get snapshot() {
83
+ const next = {};
84
+ for (const key in this.definitions) {
85
+ next[key] = this.resolve(key);
86
+ }
87
+ return next;
88
+ }
89
+ /** Get flag state with source info */
90
+ getFlagState(key, options) {
91
+ return this.resolve(key, options);
92
+ }
93
+ /**
94
+ * Resolve flag value with priority: URL > test > override > remote > fallback
95
+ */
96
+ resolve(key, options) {
97
+ const { gate, search, ...evaluationOptions } = options ?? {};
98
+ const def = this.definitions[key];
99
+ if (!def) return { flag: false, source: "fallback" };
100
+ const keyStr = key;
101
+ const urlOverrides = parseUrlOverrides(search);
102
+ if (urlOverrides[keyStr] !== void 0) {
103
+ return { flag: urlOverrides[keyStr], source: "url" };
104
+ }
105
+ if (isTestEnv() && def.testOverride !== void 0) {
106
+ return { flag: def.testOverride, source: "test" };
107
+ }
108
+ if (def.override !== void 0) {
109
+ return { flag: def.override, source: "override" };
110
+ }
111
+ try {
112
+ const currentGate = gate ?? getStatsigClientSync().getFeatureGate(keyStr, evaluationOptions);
113
+ return {
114
+ flag: currentGate.value,
115
+ source: currentGate.idType ? "remote" : "fallback"
116
+ };
117
+ } catch {
118
+ return { flag: false, source: "fallback" };
119
+ }
120
+ }
121
+ };
122
+ function createFlagStore(definitions) {
123
+ const store = new FlagStore(definitions);
124
+ function useFlagState(key, options) {
125
+ const featureGate = useFeatureGate(key, options);
126
+ return store.resolve(key, { gate: featureGate, ...options });
127
+ }
128
+ function useFlag(key, options) {
129
+ const featureGate = useFeatureGate(key, options);
130
+ return store.resolve(key, { gate: featureGate, ...options }).flag;
131
+ }
132
+ return {
133
+ /** FlagStore instance */
134
+ flagStore: store,
135
+ /** React Hook: Get flag boolean value */
136
+ useFlag,
137
+ /** React Hook: Get flag state { flag, source } */
138
+ useFlagState
139
+ };
140
+ }
141
+
142
+ // src/statsig/params.ts
143
+ import { merge, set } from "lodash-es";
144
+ import { useParameterStore } from "@statsig/react-bindings";
145
+ function defineParam(def) {
146
+ return def;
147
+ }
148
+ var hasWindow2 = typeof window !== "undefined";
149
+ var FP_PREFIX = "fp.";
150
+ var PARAM_SCHEMA_MISMATCH_EVENT = "param-schema-mismatch";
151
+ function dispatchSchemaMismatchEvent(detail) {
152
+ if (!hasWindow2) return;
153
+ window.dispatchEvent(new CustomEvent(PARAM_SCHEMA_MISMATCH_EVENT, { detail }));
154
+ }
155
+ function parseParamStoreUrlOverrides(search) {
156
+ const searchString = search ?? (hasWindow2 ? window.location.search : "");
157
+ const params = new URLSearchParams(searchString);
158
+ const result = {};
159
+ for (const [key, value] of params.entries()) {
160
+ if (!key.startsWith(FP_PREFIX)) continue;
161
+ const rest = key.slice(FP_PREFIX.length);
162
+ const dotIndex = rest.indexOf(".");
163
+ if (dotIndex >= 0) {
164
+ const path = rest;
165
+ const coerced = tryCoerceBasic(value);
166
+ set(result, path, coerced);
167
+ } else {
168
+ const path = rest;
169
+ try {
170
+ const obj = JSON.parse(value);
171
+ if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
172
+ result[path] = merge(result[path] ?? {}, obj);
173
+ }
174
+ } catch {
175
+ }
176
+ }
177
+ }
178
+ return result;
179
+ }
180
+ function tryCoerceBasic(raw) {
181
+ const trimmed = raw.trim();
182
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
183
+ try {
184
+ return JSON.parse(trimmed);
185
+ } catch {
186
+ }
187
+ }
188
+ const num = Number(raw);
189
+ if (!Number.isNaN(num) && raw !== "") return num;
190
+ if (raw === "true") return true;
191
+ if (raw === "false") return false;
192
+ if (raw === "null") return null;
193
+ return raw;
194
+ }
195
+ function tryCoerce(raw, def) {
196
+ const trimmed = raw.trim();
197
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) {
198
+ try {
199
+ return JSON.parse(trimmed);
200
+ } catch {
201
+ }
202
+ }
203
+ if (def?.schema) {
204
+ const num = Number(raw);
205
+ if (!Number.isNaN(num) && def.schema.safeParse(num).success) {
206
+ return num;
207
+ }
208
+ if (raw === "true" && def.schema.safeParse(true).success) return true;
209
+ if (raw === "false" && def.schema.safeParse(false).success) return false;
210
+ if (raw === "null" && def.schema.safeParse(null).success) return null;
211
+ }
212
+ return raw;
213
+ }
214
+ var ParamStore = class {
215
+ constructor(definitions) {
216
+ this.definitions = definitions;
217
+ }
218
+ /**
219
+ * Parse URL query string for param overrides.
220
+ * Format: ?fp.store.param=value or ?fp.store={"param":"value"}
221
+ */
222
+ parseUrlOverrides(search) {
223
+ const searchString = search ?? (hasWindow2 ? window.location.search : "");
224
+ const params = new URLSearchParams(searchString);
225
+ const result = {};
226
+ for (const [key, value] of params.entries()) {
227
+ if (!key.startsWith(FP_PREFIX)) continue;
228
+ const rest = key.slice(FP_PREFIX.length);
229
+ const dotIndex = rest.indexOf(".");
230
+ if (dotIndex >= 0) {
231
+ const path = rest;
232
+ const storeKey = rest.slice(0, dotIndex);
233
+ const paramKey = rest.slice(dotIndex + 1);
234
+ const storeDef = this.definitions[storeKey];
235
+ const paramDef = storeDef?.params[paramKey];
236
+ set(result, path, tryCoerce(value, paramDef));
237
+ } else {
238
+ const path = rest;
239
+ try {
240
+ const obj = JSON.parse(value);
241
+ if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
242
+ result[path] = merge(result[path] ?? {}, obj);
243
+ }
244
+ } catch {
245
+ }
246
+ }
247
+ }
248
+ return result;
249
+ }
250
+ /**
251
+ * Resolve param value with priority: URL > test > override > remote > fallback
252
+ */
253
+ resolve(storeKey, paramKey, options) {
254
+ const { statsigStore, search, ...evaluationOptions } = options ?? {};
255
+ const storeDef = this.definitions[storeKey];
256
+ if (!storeDef) {
257
+ throw new Error(`[ParamStore] Unknown store: ${String(storeKey)}`);
258
+ }
259
+ const def = storeDef.params[paramKey];
260
+ if (!def) {
261
+ throw new Error(`[ParamStore] Unknown param: ${String(storeKey)}.${String(paramKey)}`);
262
+ }
263
+ const schema = def.schema;
264
+ let value;
265
+ let source;
266
+ const urlOverrides = this.parseUrlOverrides(search);
267
+ const urlVal = urlOverrides[storeKey]?.[paramKey];
268
+ if (urlVal !== void 0) {
269
+ value = urlVal;
270
+ source = "url";
271
+ } else if (isTestEnv() && def.testOverride !== void 0) {
272
+ value = def.testOverride;
273
+ source = "test";
274
+ } else if (def.override !== void 0) {
275
+ value = def.override;
276
+ source = "override";
277
+ } else {
278
+ try {
279
+ const clientStore = statsigStore ?? getStatsigClientSync().getParameterStore(storeKey, evaluationOptions);
280
+ const config = clientStore?.__configuration?.[paramKey];
281
+ if (config !== void 0) {
282
+ value = clientStore.get(paramKey, def.fallback);
283
+ source = "remote";
284
+ } else {
285
+ value = def.fallback;
286
+ source = "fallback";
287
+ }
288
+ } catch {
289
+ value = def.fallback;
290
+ source = "fallback";
291
+ }
292
+ }
293
+ const parsed = schema.safeParse(value);
294
+ if (!parsed.success) {
295
+ console.error(`[ParamStore] Schema mismatch (${source}): ${String(storeKey)}.${String(paramKey)}`, value, parsed.error);
296
+ dispatchSchemaMismatchEvent({
297
+ storeKey: String(storeKey),
298
+ paramKey: String(paramKey),
299
+ source,
300
+ value,
301
+ error: parsed.error
302
+ });
303
+ const fallbackVal = def.fallback;
304
+ if (source === "remote") {
305
+ return { value: fallbackVal, source: "fallback", error: parsed.error };
306
+ }
307
+ return { value, source, error: parsed.error, fallback: fallbackVal };
308
+ }
309
+ return { value: parsed.data, source };
310
+ }
311
+ /** Get param state with value and source info */
312
+ getParamState(storeKey, paramKey, options) {
313
+ return this.resolve(storeKey, paramKey, options);
314
+ }
315
+ /** Get param value directly */
316
+ getParam(storeKey, paramKey, options) {
317
+ return this.resolve(storeKey, paramKey, options).value;
318
+ }
319
+ /** Get store handle for multiple param access */
320
+ getStore(storeKey, options) {
321
+ const { statsigStore, ...rest } = options ?? {};
322
+ let cachedStore = statsigStore;
323
+ if (cachedStore === void 0) {
324
+ try {
325
+ cachedStore = getStatsigClientSync().getParameterStore(storeKey, rest);
326
+ } catch {
327
+ cachedStore = null;
328
+ }
329
+ }
330
+ const opts = { ...rest, statsigStore: cachedStore };
331
+ return {
332
+ get: (paramKey) => this.resolve(storeKey, paramKey, opts).value,
333
+ getState: (paramKey) => this.resolve(storeKey, paramKey, opts)
334
+ };
335
+ }
336
+ };
337
+ function createParamStore(definitions) {
338
+ const store = new ParamStore(definitions);
339
+ function useParamStore(storeKey, options) {
340
+ const statsigStore = useParameterStore(storeKey, options);
341
+ return store.getStore(storeKey, { ...options, statsigStore });
342
+ }
343
+ function useParamState(storeKey, paramKey, options) {
344
+ const statsigStore = useParameterStore(storeKey, options);
345
+ return store.getParamState(storeKey, paramKey, { ...options, statsigStore });
346
+ }
347
+ function useParam(storeKey, paramKey, options) {
348
+ return useParamState(storeKey, paramKey, options).value;
349
+ }
350
+ return {
351
+ /** ParamStore instance */
352
+ paramStore: store,
353
+ /** React Hook: Get single param value */
354
+ useParam,
355
+ /** React Hook: Get single param state */
356
+ useParamState,
357
+ /** React Hook: Get store handle */
358
+ useParamStore
359
+ };
360
+ }
361
+
362
+ // src/statsig/index.ts
363
+ export * from "@statsig/react-bindings";
364
+ export {
365
+ FlagStore,
366
+ PARAM_SCHEMA_MISMATCH_EVENT,
367
+ ParamStore,
368
+ createFlagStore,
369
+ createParamStore,
370
+ defineParam,
371
+ getLogger,
372
+ getStatsigClientSync,
373
+ initStatsigClient,
374
+ isTestEnv,
375
+ parseParamStoreUrlOverrides,
376
+ parseUrlOverrides,
377
+ setLogger
378
+ };
379
+ //# sourceMappingURL=index.js.map