@supashiphq/react-sdk 0.7.7

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/dist/index.mjs ADDED
@@ -0,0 +1,442 @@
1
+ "use client";
2
+
3
+ // src/provider.tsx
4
+ import {
5
+ createContext,
6
+ useContext,
7
+ useMemo,
8
+ useCallback as useCallback2,
9
+ useRef as useRef2,
10
+ useEffect as useEffect2
11
+ } from "react";
12
+ import {
13
+ SupaClient
14
+ } from "@supashiphq/javascript-sdk";
15
+
16
+ // src/query.ts
17
+ import { useCallback, useEffect, useReducer, useRef } from "react";
18
+ function getInitialQueryState(initialData, enabled = true) {
19
+ return {
20
+ status: !enabled ? "idle" : initialData !== void 0 ? "success" : "idle",
21
+ data: initialData,
22
+ error: null,
23
+ isLoading: enabled && initialData === void 0,
24
+ isSuccess: initialData !== void 0,
25
+ isError: false,
26
+ isIdle: !enabled || initialData === void 0,
27
+ isFetching: false,
28
+ dataUpdatedAt: initialData !== void 0 ? Date.now() : 0
29
+ };
30
+ }
31
+ function queryReducer(state, action) {
32
+ switch (action.type) {
33
+ case "FETCH_START":
34
+ return {
35
+ ...state,
36
+ status: state.data === void 0 ? "loading" : state.status,
37
+ isLoading: state.data === void 0,
38
+ isIdle: false,
39
+ isFetching: true
40
+ };
41
+ case "FETCH_SUCCESS":
42
+ return {
43
+ ...state,
44
+ status: "success",
45
+ data: action.payload.data,
46
+ error: null,
47
+ isLoading: false,
48
+ isSuccess: true,
49
+ isError: false,
50
+ isIdle: false,
51
+ isFetching: false,
52
+ dataUpdatedAt: Date.now()
53
+ };
54
+ case "FETCH_ERROR":
55
+ return {
56
+ ...state,
57
+ status: "error",
58
+ error: action.payload.error,
59
+ isLoading: false,
60
+ isSuccess: !!state.data,
61
+ isError: true,
62
+ isIdle: false,
63
+ isFetching: false
64
+ };
65
+ default:
66
+ return state;
67
+ }
68
+ }
69
+ var QueryCache = class {
70
+ constructor() {
71
+ this.cache = /* @__PURE__ */ new Map();
72
+ this.timers = /* @__PURE__ */ new Map();
73
+ this.observers = /* @__PURE__ */ new Map();
74
+ }
75
+ getQuery(queryKey) {
76
+ const entry = this.cache.get(queryKey);
77
+ return entry ? entry.data : void 0;
78
+ }
79
+ isStale(queryKey, staleTime) {
80
+ const entry = this.cache.get(queryKey);
81
+ if (!entry) return true;
82
+ return Date.now() - entry.timestamp > staleTime;
83
+ }
84
+ setQuery(queryKey, data, cacheTime = 5 * 60 * 1e3) {
85
+ this.cache.set(queryKey, { data, timestamp: Date.now() });
86
+ if (this.timers.has(queryKey)) {
87
+ clearTimeout(this.timers.get(queryKey));
88
+ }
89
+ const timer = setTimeout(() => {
90
+ this.cache.delete(queryKey);
91
+ this.timers.delete(queryKey);
92
+ }, cacheTime);
93
+ this.timers.set(queryKey, timer);
94
+ }
95
+ subscribe(queryKey, callback) {
96
+ if (!this.observers.has(queryKey)) {
97
+ this.observers.set(queryKey, /* @__PURE__ */ new Set());
98
+ }
99
+ this.observers.get(queryKey).add(callback);
100
+ return () => {
101
+ const callbacks = this.observers.get(queryKey);
102
+ if (callbacks) {
103
+ callbacks.delete(callback);
104
+ if (callbacks.size === 0) {
105
+ this.observers.delete(queryKey);
106
+ }
107
+ }
108
+ };
109
+ }
110
+ notifyObservers(queryKey) {
111
+ const callbacks = this.observers.get(queryKey);
112
+ if (callbacks) {
113
+ callbacks.forEach((callback) => callback());
114
+ }
115
+ }
116
+ invalidateQuery(queryKey) {
117
+ this.cache.delete(queryKey);
118
+ if (this.timers.has(queryKey)) {
119
+ clearTimeout(this.timers.get(queryKey));
120
+ this.timers.delete(queryKey);
121
+ }
122
+ this.notifyObservers(queryKey);
123
+ }
124
+ invalidateQueries(queryKeyPrefix) {
125
+ for (const [key] of this.cache) {
126
+ if (key.startsWith(queryKeyPrefix)) {
127
+ this.invalidateQuery(key);
128
+ }
129
+ }
130
+ }
131
+ clear() {
132
+ this.cache.clear();
133
+ this.timers.forEach((timer) => clearTimeout(timer));
134
+ this.timers.clear();
135
+ this.observers.clear();
136
+ }
137
+ };
138
+ var queryCache = new QueryCache();
139
+ var QueryClient = class {
140
+ constructor(cache = queryCache) {
141
+ this.cache = cache;
142
+ }
143
+ /**
144
+ * Invalidates a specific query by its query key
145
+ * This will remove it from cache and trigger refetch in all components using this query
146
+ */
147
+ invalidateQueries(queryKey) {
148
+ const stringifiedKey = stableStringifyQueryKey(queryKey);
149
+ this.cache.invalidateQuery(stringifiedKey);
150
+ }
151
+ /**
152
+ * Invalidates all queries matching a partial query key
153
+ * For example, invalidateQueriesByPrefix(['feature']) will invalidate all feature queries
154
+ */
155
+ invalidateQueriesByPrefix(queryKeyPrefix) {
156
+ const stringifiedPrefix = stableStringifyQueryKey(queryKeyPrefix);
157
+ this.cache.invalidateQueries(stringifiedPrefix.slice(0, -1));
158
+ }
159
+ /**
160
+ * Clears all queries from the cache
161
+ */
162
+ clear() {
163
+ this.cache.clear();
164
+ }
165
+ };
166
+ var queryClient = new QueryClient();
167
+ function useQueryClient() {
168
+ return queryClient;
169
+ }
170
+ function stableStringifyQueryKey(queryKey) {
171
+ return JSON.stringify(queryKey, (_, val) => typeof val === "function" ? val.toString() : val);
172
+ }
173
+ function useQuery(queryKey, queryFn, options = {}) {
174
+ const {
175
+ enabled = true,
176
+ retry = 3,
177
+ retryDelay = 1e3,
178
+ staleTime = 0,
179
+ cacheTime = 5 * 60 * 1e3,
180
+ refetchOnWindowFocus = true,
181
+ initialData
182
+ } = options;
183
+ const stringifiedQueryKey = stableStringifyQueryKey(queryKey);
184
+ const queryFnRef = useRef(queryFn);
185
+ const optionsRef = useRef(options);
186
+ useEffect(() => {
187
+ queryFnRef.current = queryFn;
188
+ optionsRef.current = options;
189
+ }, [queryFn, options]);
190
+ const cachedData = queryCache.getQuery(stringifiedQueryKey);
191
+ const isStale = queryCache.isStale(stringifiedQueryKey, staleTime);
192
+ const initialStateData = cachedData !== void 0 ? cachedData : initialData;
193
+ const initialState = getInitialQueryState(initialStateData, enabled);
194
+ const [state, dispatch] = useReducer(queryReducer, initialState);
195
+ const executeFetch = useCallback(async () => {
196
+ if (!enabled) return;
197
+ dispatch({ type: "FETCH_START" });
198
+ let retryCount = 0;
199
+ const maxRetries = typeof retry === "boolean" ? retry ? 3 : 0 : retry;
200
+ const runQuery = async () => {
201
+ try {
202
+ const data = await queryFnRef.current();
203
+ queryCache.setQuery(stringifiedQueryKey, data, cacheTime);
204
+ dispatch({ type: "FETCH_SUCCESS", payload: { data } });
205
+ if (optionsRef.current.onSuccess) {
206
+ optionsRef.current.onSuccess(data);
207
+ }
208
+ if (optionsRef.current.onSettled) {
209
+ optionsRef.current.onSettled(data, null);
210
+ }
211
+ } catch (err) {
212
+ const error = err;
213
+ if (retryCount < maxRetries) {
214
+ retryCount++;
215
+ await new Promise((resolve) => setTimeout(resolve, retryDelay));
216
+ return runQuery();
217
+ }
218
+ dispatch({ type: "FETCH_ERROR", payload: { error } });
219
+ if (optionsRef.current.onError) {
220
+ optionsRef.current.onError(error);
221
+ }
222
+ if (optionsRef.current.onSettled) {
223
+ optionsRef.current.onSettled(void 0, error);
224
+ }
225
+ }
226
+ };
227
+ await runQuery();
228
+ }, [enabled, stringifiedQueryKey, retry, retryDelay, cacheTime]);
229
+ useEffect(() => {
230
+ if (enabled && (isStale || state.data === void 0)) {
231
+ executeFetch();
232
+ }
233
+ }, [enabled, isStale, executeFetch, state.data]);
234
+ useEffect(() => {
235
+ if (!refetchOnWindowFocus) return;
236
+ const handleFocus = () => {
237
+ if (enabled && isStale) {
238
+ executeFetch();
239
+ }
240
+ };
241
+ window.addEventListener("focus", handleFocus);
242
+ return () => window.removeEventListener("focus", handleFocus);
243
+ }, [enabled, isStale, executeFetch, refetchOnWindowFocus]);
244
+ useEffect(() => {
245
+ const unsubscribe = queryCache.subscribe(stringifiedQueryKey, () => {
246
+ executeFetch();
247
+ });
248
+ return unsubscribe;
249
+ }, [stringifiedQueryKey, executeFetch]);
250
+ const refetch = useCallback(async () => {
251
+ await executeFetch();
252
+ }, [executeFetch]);
253
+ return {
254
+ ...state,
255
+ refetch
256
+ };
257
+ }
258
+
259
+ // src/provider.tsx
260
+ import { jsx } from "react/jsx-runtime";
261
+ var SupaContext = createContext(null);
262
+ var DEFAULT_TOOLBAR_CONFIG = { enabled: "auto" };
263
+ function SupaProvider({
264
+ config,
265
+ toolbar,
266
+ children
267
+ }) {
268
+ const queryClient2 = useQueryClient();
269
+ const effectiveToolbar = toolbar ?? DEFAULT_TOOLBAR_CONFIG;
270
+ const handleOverrideChange = useCallback2(
271
+ (featureOverride, allOverrides) => {
272
+ if (typeof effectiveToolbar === "object" && effectiveToolbar.onOverrideChange) {
273
+ effectiveToolbar.onOverrideChange(featureOverride, allOverrides);
274
+ }
275
+ if (featureOverride.feature) {
276
+ queryClient2.invalidateQueriesByPrefix(["feature", featureOverride.feature]);
277
+ queryClient2.invalidateQueriesByPrefix(["features"]);
278
+ }
279
+ },
280
+ [effectiveToolbar, queryClient2]
281
+ );
282
+ const clientRef = useRef2(null);
283
+ if (!clientRef.current) {
284
+ const toolbarConfig = effectiveToolbar === false ? false : {
285
+ ...typeof effectiveToolbar === "object" ? effectiveToolbar : {},
286
+ onOverrideChange: handleOverrideChange
287
+ };
288
+ clientRef.current = new SupaClient({
289
+ ...config,
290
+ toolbar: toolbarConfig
291
+ });
292
+ }
293
+ const client = clientRef.current;
294
+ useEffect2(() => {
295
+ return () => {
296
+ if (clientRef.current) {
297
+ const client2 = clientRef.current;
298
+ if (client2.plugins && Array.isArray(client2.plugins)) {
299
+ client2.plugins.forEach((plugin) => {
300
+ if (plugin.cleanup) {
301
+ plugin.cleanup();
302
+ }
303
+ });
304
+ }
305
+ clientRef.current = null;
306
+ }
307
+ };
308
+ }, []);
309
+ const updateContext = useCallback2(
310
+ (context, mergeWithExisting = true) => {
311
+ client.updateContext(context, mergeWithExisting);
312
+ },
313
+ [client]
314
+ );
315
+ const getContext = useCallback2(() => {
316
+ return client.getContext();
317
+ }, [client]);
318
+ const contextValue = useMemo(
319
+ () => ({
320
+ client,
321
+ updateContext,
322
+ getContext
323
+ }),
324
+ [client, updateContext, getContext]
325
+ );
326
+ return /* @__PURE__ */ jsx(SupaContext.Provider, { value: contextValue, children });
327
+ }
328
+ function useClient() {
329
+ const context = useContext(SupaContext);
330
+ if (!context) {
331
+ throw new Error("useClient must be used within a SupaProvider");
332
+ }
333
+ return context.client;
334
+ }
335
+ function useFeatureContext() {
336
+ const context = useContext(SupaContext);
337
+ if (!context) {
338
+ throw new Error("useFeatureContext must be used within a SupaProvider");
339
+ }
340
+ return {
341
+ updateContext: context.updateContext,
342
+ getContext: context.getContext
343
+ };
344
+ }
345
+
346
+ // src/hooks.ts
347
+ var STALE_TIME = 5 * 60 * 1e3;
348
+ var CACHE_TIME = 10 * 60 * 1e3;
349
+ function useFeature(key, options) {
350
+ const client = useClient();
351
+ const { context, shouldFetch = true } = options ?? {};
352
+ const fallbackValue = client.getFeatureFallback(key);
353
+ const result = useQuery(
354
+ ["feature", key, context],
355
+ async () => {
356
+ return await client.getFeature(key, { context });
357
+ },
358
+ {
359
+ enabled: shouldFetch,
360
+ staleTime: STALE_TIME,
361
+ cacheTime: CACHE_TIME,
362
+ refetchOnWindowFocus: false
363
+ // Feature flags shouldn't refetch on focus
364
+ }
365
+ );
366
+ const { data, ...rest } = result;
367
+ return {
368
+ ...rest,
369
+ feature: data ?? fallbackValue
370
+ };
371
+ }
372
+ function useFeatures(featureNames, options) {
373
+ const client = useClient();
374
+ const { context, shouldFetch = true } = options ?? {};
375
+ const result = useQuery(
376
+ ["features", featureNames.join(","), context],
377
+ async () => {
378
+ const features = await client.getFeatures([...featureNames], { context });
379
+ return features;
380
+ },
381
+ {
382
+ enabled: shouldFetch,
383
+ staleTime: STALE_TIME,
384
+ cacheTime: CACHE_TIME,
385
+ refetchOnWindowFocus: false
386
+ // Feature flags shouldn't refetch on focus
387
+ }
388
+ );
389
+ const { data, ...rest } = result;
390
+ return {
391
+ ...rest,
392
+ features: data ?? {}
393
+ };
394
+ }
395
+
396
+ // src/utils.ts
397
+ function hasValue(value) {
398
+ return value !== void 0 && value !== null;
399
+ }
400
+
401
+ // src/components.tsx
402
+ import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
403
+ function SupaFeature({
404
+ feature,
405
+ context,
406
+ shouldFetch = true,
407
+ variations,
408
+ loading
409
+ }) {
410
+ const { feature: featureValue, isLoading } = useFeature(feature, {
411
+ context,
412
+ shouldFetch
413
+ });
414
+ if (isLoading && loading && variations[loading]) {
415
+ return /* @__PURE__ */ jsx2(Fragment, { children: variations[loading] });
416
+ }
417
+ if (isLoading) {
418
+ return null;
419
+ }
420
+ if (!hasValue(featureValue)) {
421
+ return null;
422
+ }
423
+ const valueKey = String(featureValue);
424
+ if (variations[valueKey]) {
425
+ return /* @__PURE__ */ jsx2(Fragment, { children: variations[valueKey] });
426
+ }
427
+ return null;
428
+ }
429
+ export {
430
+ QueryClient,
431
+ SupaFeature,
432
+ SupaProvider,
433
+ getInitialQueryState,
434
+ queryCache,
435
+ useClient,
436
+ useFeature,
437
+ useFeatureContext,
438
+ useFeatures,
439
+ useQuery,
440
+ useQueryClient
441
+ };
442
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.tsx","../src/query.ts","../src/hooks.ts","../src/utils.ts","../src/components.tsx"],"sourcesContent":["'use client'\n\nimport React, {\n createContext,\n useContext,\n ReactNode,\n useMemo,\n useCallback,\n useRef,\n useEffect,\n} from 'react'\nimport {\n SupaClient,\n SupaClientConfig as SupaProviderConfig,\n FeatureContext,\n FeatureValue,\n Features,\n SupaToolbarOverrideChange,\n SupaToolbarPluginConfig,\n} from '@supashiphq/javascript-sdk'\nimport { useQueryClient } from './query'\n\ninterface SupaContextValue<TFeatures extends Features<Record<string, FeatureValue>>> {\n client: SupaClient<TFeatures>\n updateContext: (context: FeatureContext, mergeWithExisting?: boolean) => void\n getContext: () => FeatureContext | undefined\n}\n\nconst SupaContext = createContext<SupaContextValue<any> | null>(null)\n\ninterface SupaProviderProps<TFeatures extends Features<Record<string, FeatureValue>>> {\n config: Omit<SupaProviderConfig, 'plugins' | 'toolbar'> & { features: TFeatures }\n toolbar?: SupaToolbarPluginConfig | boolean\n children: ReactNode\n}\n\n// Default toolbar config (stable reference)\nconst DEFAULT_TOOLBAR_CONFIG: SupaToolbarPluginConfig = { enabled: 'auto' }\n\nexport function SupaProvider<TFeatures extends Features<Record<string, FeatureValue>>>({\n config,\n toolbar,\n children,\n}: SupaProviderProps<TFeatures>): React.JSX.Element {\n const queryClient = useQueryClient()\n\n // Use default if toolbar not provided\n const effectiveToolbar = toolbar ?? DEFAULT_TOOLBAR_CONFIG\n\n // Stable callback for toolbar override changes\n const handleOverrideChange = useCallback(\n (\n featureOverride: SupaToolbarOverrideChange,\n allOverrides: Record<string, FeatureValue>\n ): void => {\n // Call user's onOverrideChange if provided\n if (typeof effectiveToolbar === 'object' && effectiveToolbar.onOverrideChange) {\n effectiveToolbar.onOverrideChange(featureOverride, allOverrides)\n }\n\n // Invalidate the query cache for the changed feature to trigger refetch\n if (featureOverride.feature) {\n // Invalidate single feature queries (useFeature)\n queryClient.invalidateQueriesByPrefix(['feature', featureOverride.feature])\n // Invalidate multi-feature queries (useFeatures) that include this feature\n queryClient.invalidateQueriesByPrefix(['features'])\n }\n },\n [effectiveToolbar, queryClient]\n )\n\n // Use ref to persist client across StrictMode double-renders in development\n const clientRef = useRef<SupaClient<TFeatures> | null>(null)\n\n // Initialize client only once to prevent duplicate toolbars in StrictMode\n if (!clientRef.current) {\n // Merge toolbar config with React Query cache invalidation\n const toolbarConfig =\n effectiveToolbar === false\n ? false\n : {\n ...(typeof effectiveToolbar === 'object' ? effectiveToolbar : {}),\n onOverrideChange: handleOverrideChange,\n }\n\n clientRef.current = new SupaClient<TFeatures>({\n ...config,\n toolbar: toolbarConfig,\n })\n }\n\n const client = clientRef.current\n\n // Cleanup client on unmount\n useEffect(() => {\n return () => {\n if (clientRef.current) {\n // Cleanup plugins (which includes toolbar cleanup)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const client = clientRef.current as any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (client.plugins && Array.isArray(client.plugins)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client.plugins.forEach((plugin: any) => {\n if (plugin.cleanup) {\n plugin.cleanup()\n }\n })\n }\n clientRef.current = null\n }\n }\n }, [])\n\n // Memoized context update function\n const updateContext = useCallback(\n (context: FeatureContext, mergeWithExisting: boolean = true) => {\n client.updateContext(context, mergeWithExisting)\n },\n [client]\n )\n\n // Memoized context getter function\n const getContext = useCallback(() => {\n return client.getContext()\n }, [client])\n\n const contextValue = useMemo(\n () => ({\n client,\n updateContext,\n getContext,\n }),\n [client, updateContext, getContext]\n )\n\n return <SupaContext.Provider value={contextValue}>{children}</SupaContext.Provider>\n}\n\nexport function useClient<\n TFeatures extends Features<Record<string, FeatureValue>>,\n>(): SupaClient<TFeatures> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useClient must be used within a SupaProvider')\n }\n return context.client as SupaClient<TFeatures>\n}\n\n/**\n * Hook to update the context dynamically\n * Useful when context depends on authentication or other async operations\n */\nexport function useFeatureContext(): Omit<SupaContextValue<any>, 'client'> {\n const context = useContext(SupaContext)\n if (!context) {\n throw new Error('useFeatureContext must be used within a SupaProvider')\n }\n return {\n updateContext: context.updateContext,\n getContext: context.getContext,\n }\n}\n","'use client'\n\nimport { useCallback, useEffect, useReducer, useRef } from 'react'\n\n// Query status types\nexport type QueryStatus = 'idle' | 'loading' | 'success' | 'error'\n\n// Base query state (managed by reducer)\nexport interface BaseQueryState<TData = unknown, TError = Error> {\n status: QueryStatus\n data: TData | undefined\n error: TError | null\n isLoading: boolean\n isSuccess: boolean\n isError: boolean\n isIdle: boolean\n isFetching: boolean\n dataUpdatedAt: number\n}\n\n// Query state with refetch method (returned by useQuery)\nexport interface QueryState<TData = unknown, TError = Error> extends BaseQueryState<TData, TError> {\n refetch: () => Promise<void>\n}\n\n// Initial state factory for queries\nexport function getInitialQueryState<TData, TError>(\n initialData?: TData,\n enabled: boolean = true\n): BaseQueryState<TData, TError> {\n return {\n status: !enabled ? 'idle' : initialData !== undefined ? 'success' : 'idle',\n data: initialData,\n error: null,\n isLoading: enabled && initialData === undefined,\n isSuccess: initialData !== undefined,\n isError: false,\n isIdle: !enabled || initialData === undefined,\n isFetching: false,\n dataUpdatedAt: initialData !== undefined ? Date.now() : 0,\n }\n}\n\n// Query options\nexport interface UseQueryOptions<TData = unknown, TError = Error> {\n enabled?: boolean\n retry?: number | boolean\n retryDelay?: number\n staleTime?: number\n cacheTime?: number\n refetchOnWindowFocus?: boolean\n initialData?: TData\n onSuccess?: (data: TData) => void\n onError?: (error: TError) => void\n onSettled?: (data: TData | undefined, error: TError | null) => void\n}\n\n// Query key type\nexport type QueryKey = unknown[]\n\n// Actions for the query reducer\ntype QueryAction<TData, TError> =\n | { type: 'FETCH_START' }\n | { type: 'FETCH_SUCCESS'; payload: { data: TData } }\n | { type: 'FETCH_ERROR'; payload: { error: TError } }\n\n// Reducer for query state\nfunction queryReducer<TData, TError>(\n state: BaseQueryState<TData, TError>,\n action: QueryAction<TData, TError>\n): BaseQueryState<TData, TError> {\n switch (action.type) {\n case 'FETCH_START':\n return {\n ...state,\n status: state.data === undefined ? 'loading' : state.status,\n isLoading: state.data === undefined,\n isIdle: false,\n isFetching: true,\n }\n case 'FETCH_SUCCESS':\n return {\n ...state,\n status: 'success',\n data: action.payload.data,\n error: null,\n isLoading: false,\n isSuccess: true,\n isError: false,\n isIdle: false,\n isFetching: false,\n dataUpdatedAt: Date.now(),\n }\n case 'FETCH_ERROR':\n return {\n ...state,\n status: 'error',\n error: action.payload.error,\n isLoading: false,\n isSuccess: !!state.data,\n isError: true,\n isIdle: false,\n isFetching: false,\n }\n default:\n return state\n }\n}\n\n// Cache implementation\nclass QueryCache {\n private cache: Map<string, { data: unknown; timestamp: number }> = new Map()\n private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()\n private observers: Map<string, Set<() => void>> = new Map()\n\n getQuery(queryKey: string): unknown {\n const entry = this.cache.get(queryKey)\n return entry ? entry.data : undefined\n }\n\n isStale(queryKey: string, staleTime: number): boolean {\n const entry = this.cache.get(queryKey)\n if (!entry) return true\n return Date.now() - entry.timestamp > staleTime\n }\n\n setQuery(queryKey: string, data: unknown, cacheTime: number = 5 * 60 * 1000): void {\n this.cache.set(queryKey, { data, timestamp: Date.now() })\n\n // Clear previous timer if it exists\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n }\n\n // Set new timer for cache expiration\n const timer = setTimeout(() => {\n this.cache.delete(queryKey)\n this.timers.delete(queryKey)\n }, cacheTime)\n\n this.timers.set(queryKey, timer)\n }\n\n subscribe(queryKey: string, callback: () => void): () => void {\n if (!this.observers.has(queryKey)) {\n this.observers.set(queryKey, new Set())\n }\n this.observers.get(queryKey)!.add(callback)\n\n // Return unsubscribe function\n return () => {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.delete(callback)\n if (callbacks.size === 0) {\n this.observers.delete(queryKey)\n }\n }\n }\n }\n\n private notifyObservers(queryKey: string): void {\n const callbacks = this.observers.get(queryKey)\n if (callbacks) {\n callbacks.forEach(callback => callback())\n }\n }\n\n invalidateQuery(queryKey: string): void {\n this.cache.delete(queryKey)\n if (this.timers.has(queryKey)) {\n clearTimeout(this.timers.get(queryKey)!)\n this.timers.delete(queryKey)\n }\n this.notifyObservers(queryKey)\n }\n\n invalidateQueries(queryKeyPrefix: string): void {\n for (const [key] of this.cache) {\n if (key.startsWith(queryKeyPrefix)) {\n this.invalidateQuery(key)\n }\n }\n }\n\n clear(): void {\n this.cache.clear()\n this.timers.forEach(timer => clearTimeout(timer))\n this.timers.clear()\n this.observers.clear()\n }\n}\n\n// Singleton instance of QueryCache\nexport const queryCache = new QueryCache()\n\n// Query Client class for managing queries\nexport class QueryClient {\n private cache: QueryCache\n\n constructor(cache: QueryCache = queryCache) {\n this.cache = cache\n }\n\n /**\n * Invalidates a specific query by its query key\n * This will remove it from cache and trigger refetch in all components using this query\n */\n invalidateQueries(queryKey: QueryKey): void {\n const stringifiedKey = stableStringifyQueryKey(queryKey)\n this.cache.invalidateQuery(stringifiedKey)\n }\n\n /**\n * Invalidates all queries matching a partial query key\n * For example, invalidateQueriesByPrefix(['feature']) will invalidate all feature queries\n */\n invalidateQueriesByPrefix(queryKeyPrefix: QueryKey): void {\n const stringifiedPrefix = stableStringifyQueryKey(queryKeyPrefix)\n this.cache.invalidateQueries(stringifiedPrefix.slice(0, -1)) // Remove closing bracket\n }\n\n /**\n * Clears all queries from the cache\n */\n clear(): void {\n this.cache.clear()\n }\n}\n\n// Singleton instance of QueryClient\nconst queryClient = new QueryClient()\n\n// Hook to access the query client\nexport function useQueryClient(): QueryClient {\n return queryClient\n}\n\n// Stable stringify for query keys\nfunction stableStringifyQueryKey(queryKey: QueryKey): string {\n return JSON.stringify(queryKey, (_, val) => (typeof val === 'function' ? val.toString() : val))\n}\n\n// The useQuery hook\nexport function useQuery<TData = unknown, TError = Error>(\n queryKey: QueryKey,\n queryFn: () => Promise<TData>,\n options: UseQueryOptions<TData, TError> = {}\n): QueryState<TData, TError> {\n const {\n enabled = true,\n retry = 3,\n retryDelay = 1000,\n staleTime = 0,\n cacheTime = 5 * 60 * 1000,\n refetchOnWindowFocus = true,\n initialData,\n } = options\n\n const stringifiedQueryKey = stableStringifyQueryKey(queryKey)\n const queryFnRef = useRef(queryFn)\n const optionsRef = useRef(options)\n\n // Update refs when dependencies change\n useEffect(() => {\n queryFnRef.current = queryFn\n optionsRef.current = options\n }, [queryFn, options])\n\n // Initialize state from cache or with default\n const cachedData = queryCache.getQuery(stringifiedQueryKey)\n const isStale = queryCache.isStale(stringifiedQueryKey, staleTime)\n\n // Use cached data for initial state even if stale to prevent unnecessary loading states\n const initialStateData = cachedData !== undefined ? (cachedData as TData) : initialData\n const initialState = getInitialQueryState<TData, TError>(initialStateData, enabled)\n const [state, dispatch] = useReducer(queryReducer<TData, TError>, initialState)\n\n const executeFetch = useCallback(async (): Promise<void> => {\n if (!enabled) return\n\n dispatch({ type: 'FETCH_START' })\n\n let retryCount = 0\n const maxRetries = typeof retry === 'boolean' ? (retry ? 3 : 0) : retry\n\n const runQuery = async (): Promise<void> => {\n try {\n const data = await queryFnRef.current()\n\n // Update cache\n queryCache.setQuery(stringifiedQueryKey, data, cacheTime)\n\n dispatch({ type: 'FETCH_SUCCESS', payload: { data } })\n\n if (optionsRef.current.onSuccess) {\n optionsRef.current.onSuccess(data)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(data, null)\n }\n } catch (err) {\n const error = err as TError\n\n if (retryCount < maxRetries) {\n retryCount++\n await new Promise(resolve => setTimeout(resolve, retryDelay))\n return runQuery()\n }\n\n dispatch({ type: 'FETCH_ERROR', payload: { error } })\n\n if (optionsRef.current.onError) {\n optionsRef.current.onError(error)\n }\n\n if (optionsRef.current.onSettled) {\n optionsRef.current.onSettled(undefined, error)\n }\n }\n }\n\n await runQuery()\n }, [enabled, stringifiedQueryKey, retry, retryDelay, cacheTime])\n\n // Execute query on mount and when dependencies change\n useEffect(() => {\n if (enabled && (isStale || state.data === undefined)) {\n executeFetch()\n }\n }, [enabled, isStale, executeFetch, state.data])\n\n // Handle window focus\n useEffect(() => {\n if (!refetchOnWindowFocus) return\n\n const handleFocus = (): void => {\n if (enabled && isStale) {\n executeFetch()\n }\n }\n\n window.addEventListener('focus', handleFocus)\n return () => window.removeEventListener('focus', handleFocus)\n }, [enabled, isStale, executeFetch, refetchOnWindowFocus])\n\n // Subscribe to cache invalidations\n useEffect(() => {\n const unsubscribe = queryCache.subscribe(stringifiedQueryKey, () => {\n // When query is invalidated, refetch\n executeFetch()\n })\n\n return unsubscribe\n }, [stringifiedQueryKey, executeFetch])\n\n // Memoize refetch function\n const refetch = useCallback(async () => {\n await executeFetch()\n }, [executeFetch])\n\n return {\n ...state,\n refetch,\n }\n}\n","'use client'\n\nimport { useClient } from './provider'\nimport { useQuery, QueryState } from './query'\nimport { FeatureValue, FeatureContext } from '@supashiphq/javascript-sdk'\nimport { FeatureKey, TypedFeatures, Features } from './types'\n\n// Custom return types for hooks with generics\nexport interface UseFeatureResult<T extends FeatureValue>\n extends Omit<QueryState<T | null>, 'data'> {\n feature: T | null\n}\n\nexport interface UseFeaturesResult<T extends Record<string, FeatureValue>>\n extends Omit<QueryState<T>, 'data'> {\n features: T\n}\n\nconst STALE_TIME = 5 * 60 * 1000 // 5 minutes\nconst CACHE_TIME = 10 * 60 * 1000 // 10 minutes\n\n/**\n * Returns the state of a given feature for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { feature, isLoading } = useFeature('my-feature')\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature value: {String(feature)}</div>\n * }\n * ```\n *\n * @remarks\n * For type-safe feature flags, augment the Features interface:\n * ```ts\n * declare module '@supashiphq/react-sdk' {\n * interface Features {\n * 'my-feature': { value: 'variant-a' | 'variant-b' }\n * 'dark-mode': { value: boolean }\n * }\n * }\n *\n * // Now get full type safety:\n * const { feature } = useFeature('my-feature')\n * // feature is typed as 'variant-a' | 'variant-b'\n * ```\n */\nexport function useFeature<TKey extends FeatureKey>(\n key: TKey,\n options?: { context?: FeatureContext; shouldFetch?: boolean }\n): TypedFeatures[TKey] {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n // Get the fallback value from feature definitions\n const fallbackValue = client.getFeatureFallback(key as string)\n\n const result = useQuery(\n ['feature', key, context],\n async (): Promise<FeatureValue> => {\n return await client.getFeature(key as string, { context })\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n feature: data ?? fallbackValue,\n } as TypedFeatures[TKey]\n}\n\n/**\n * Extract feature value type from Features interface\n */\ntype ExtractFeatureValue<T> = T extends { value: infer V } ? V : FeatureValue\n\n/**\n * Returns the state of multiple features for the current context.\n *\n * @example\n * ```ts\n * function MyComponent() {\n * const { features, isLoading } = useFeatures(['feature-a', 'feature-b'])\n * if (isLoading) return <div>Loading...</div>\n * return <div>Feature A: {String(features['feature-a'])}</div>\n * }\n * ```\n */\nexport function useFeatures<TKeys extends readonly FeatureKey[]>(\n featureNames: TKeys,\n options?: {\n context?: FeatureContext\n shouldFetch?: boolean\n }\n): UseFeaturesResult<{\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n}> {\n const client = useClient()\n const { context, shouldFetch = true } = options ?? {}\n\n type ResultType = {\n [K in TKeys[number]]: K extends keyof Features\n ? ExtractFeatureValue<Features[K]>\n : FeatureValue | null\n }\n\n const result = useQuery(\n ['features', featureNames.join(','), context],\n async (): Promise<ResultType> => {\n const features = await client.getFeatures([...featureNames] as string[], { context })\n return features as ResultType\n },\n {\n enabled: shouldFetch,\n staleTime: STALE_TIME,\n cacheTime: CACHE_TIME,\n refetchOnWindowFocus: false, // Feature flags shouldn't refetch on focus\n }\n )\n\n const { data, ...rest } = result\n return {\n ...rest,\n features: data ?? ({} as ResultType),\n }\n}\n","export function hasValue(value: unknown): boolean {\n return value !== undefined && value !== null\n}\n","'use client'\n\nimport React, { ReactNode } from 'react'\nimport { useFeature } from './hooks'\nimport { FeatureKey, FeatureContext } from './types'\nimport { hasValue } from './utils'\n\nexport interface SupaFeatureProps {\n /**\n * The feature flag key to evaluate\n */\n feature: FeatureKey\n\n /**\n * Context for feature evaluation\n */\n context?: FeatureContext\n\n /**\n * Whether to fetch the feature (default: true)\n */\n shouldFetch?: boolean\n\n /**\n * Variations object mapping feature values/keys to JSX elements\n */\n variations: Record<string, ReactNode>\n\n /**\n * Key in variations object to use for loading state\n */\n loading?: string\n}\n\n/**\n * SupaFeature component that conditionally renders variations based on feature flag values.\n * Uses the default value defined in the client configuration.\n *\n * @example\n * ```tsx\n * <SupaFeature\n * feature=\"new-header\"\n * variations={{\n * \"true\": <NewHeader />,\n * \"false\": <OldHeader />,\n * loading: <HeaderSkeleton />\n * }}\n * />\n * ```\n */\nexport function SupaFeature({\n feature,\n context,\n shouldFetch = true,\n variations,\n loading,\n}: SupaFeatureProps): React.JSX.Element | null {\n const { feature: featureValue, isLoading } = useFeature(feature, {\n context,\n shouldFetch,\n })\n\n // Show loading state if provided and currently loading\n if (isLoading && loading && variations[loading]) {\n return <>{variations[loading]}</>\n }\n\n // Don't render anything if still loading and no loader provided\n if (isLoading) {\n return null\n }\n\n // Don't render anything if no feature value (client config should provide defaults)\n if (!hasValue(featureValue)) {\n return null\n }\n\n // Convert feature value to string for object key lookup\n const valueKey = String(featureValue)\n\n // Find matching variation by exact key match\n if (variations[valueKey]) {\n return <>{variations[valueKey]}</>\n }\n\n // Don't render anything if no variation matches\n return null\n}\n"],"mappings":"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","names":["useCallback","useRef","useEffect","queryClient","useCallback","useRef","useEffect","client","jsx"]}
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@supashiphq/react-sdk",
3
+ "version": "0.7.7",
4
+ "description": "Supaship SDK for React",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./server": {
15
+ "types": "./dist/server.d.ts",
16
+ "import": "./dist/server.mjs",
17
+ "require": "./dist/server.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "scripts": {
26
+ "clean": "rimraf dist",
27
+ "build": "pnpm clean && pnpm build:tsup",
28
+ "build:tsup": "tsup",
29
+ "dev": "tsup --watch",
30
+ "test": "jest",
31
+ "test:watch": "jest --watch",
32
+ "test:coverage": "jest --coverage",
33
+ "lint": "eslint src --ext .ts,.tsx",
34
+ "lint:fix": "eslint src --ext .ts,.tsx --fix",
35
+ "prepublishOnly": "pnpm build"
36
+ },
37
+ "peerDependencies": {
38
+ "react": ">=16.8.0",
39
+ "react-dom": ">=16.8.0"
40
+ },
41
+ "dependencies": {
42
+ "@supashiphq/javascript-sdk": "^0.7.7"
43
+ },
44
+ "devDependencies": {
45
+ "@jest/globals": "^30.0.0",
46
+ "@testing-library/jest-dom": "^6.4.2",
47
+ "@testing-library/react": "^14.2.1",
48
+ "@testing-library/react-hooks": "^8.0.1",
49
+ "@types/jest": "^29.5.14",
50
+ "@types/node": "^20.0.0",
51
+ "@types/react": "^18.2.0",
52
+ "@types/react-dom": "^18.2.0",
53
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
54
+ "@typescript-eslint/parser": "^6.0.0",
55
+ "eslint": "^8.0.0",
56
+ "jest": "^29.7.0",
57
+ "jest-environment-jsdom": "^29.7.0",
58
+ "rimraf": "^6.0.1",
59
+ "ts-jest": "^29.1.0",
60
+ "typescript": "~5.3.3"
61
+ },
62
+ "keywords": [
63
+ "feature-flags",
64
+ "feature-toggles",
65
+ "react",
66
+ "sdk"
67
+ ],
68
+ "author": "Supaship",
69
+ "license": "MIT",
70
+ "repository": {
71
+ "type": "git",
72
+ "url": "https://github.com/supashiphq/sdk.git",
73
+ "directory": "packages/react"
74
+ },
75
+ "bugs": {
76
+ "url": "https://github.com/supashiphq/sdk/issues"
77
+ },
78
+ "homepage": "https://supaship.com"
79
+ }