@faststats/react 0.1.9 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ConsentMode, WebAnalyticsOptions, reportError } from "@faststats/web";
1
+ import { ConsentMode, FeatureFlagEvaluation, WebAnalyticsOptions, reportError } from "@faststats/web";
2
2
 
3
3
  //#region src/analytics.d.ts
4
4
  type AnalyticsProps = WebAnalyticsOptions;
@@ -18,4 +18,17 @@ declare function useConsentMode(): (mode: ConsentMode) => void;
18
18
  declare function useOptIn(): () => void;
19
19
  declare function useOptOut(): () => void;
20
20
  //#endregion
21
- export { Analytics, reportError, useConsentMode, useEvent, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
21
+ //#region src/use-flag.d.ts
22
+ type UseFlagOptions = {
23
+ attributes?: Record<string, unknown>;
24
+ externalId?: string;
25
+ skip?: boolean;
26
+ };
27
+ type UseFlagResult = FeatureFlagEvaluation & {
28
+ isLoading: boolean;
29
+ error: Error | null;
30
+ refetch: () => void;
31
+ };
32
+ declare function useFlag(flagKey: string, options?: UseFlagOptions): UseFlagResult;
33
+ //#endregion
34
+ export { Analytics, type UseFlagOptions, type UseFlagResult, reportError, useConsentMode, useEvent, useFlag, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
package/dist/index.js CHANGED
@@ -1,10 +1,21 @@
1
1
  "use client";
2
2
  import { WebAnalytics, getInstance, reportError } from "@faststats/web";
3
- import { useEffect, useRef } from "react";
3
+ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
4
4
  //#region src/instance.ts
5
5
  let instance = null;
6
+ const listeners = /* @__PURE__ */ new Set();
7
+ function subscribeToInstance(onStoreChange) {
8
+ listeners.add(onStoreChange);
9
+ return () => {
10
+ listeners.delete(onStoreChange);
11
+ };
12
+ }
13
+ function getInstanceSnapshot() {
14
+ return instance;
15
+ }
6
16
  function setInstance(wa) {
7
17
  instance = wa;
18
+ for (const l of listeners) l();
8
19
  }
9
20
  //#endregion
10
21
  //#region src/analytics.tsx
@@ -81,4 +92,75 @@ function useOptOut() {
81
92
  };
82
93
  }
83
94
  //#endregion
84
- export { Analytics, reportError, useConsentMode, useEvent, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
95
+ //#region src/use-flag.ts
96
+ const defaultEvaluation = { enabled: false };
97
+ function stableSerialize(attributes) {
98
+ if (!attributes) return "";
99
+ const keys = Object.keys(attributes).sort();
100
+ const obj = {};
101
+ for (const k of keys) obj[k] = attributes[k];
102
+ return JSON.stringify(obj);
103
+ }
104
+ function useFlag(flagKey, options) {
105
+ const wa = useSyncExternalStore(subscribeToInstance, getInstanceSnapshot, () => null);
106
+ const attributes = options?.attributes;
107
+ const externalId = options?.externalId;
108
+ const skip = options?.skip ?? false;
109
+ const attrKey = useMemo(() => stableSerialize(attributes), [attributes]);
110
+ const [refetchCount, setRefetchCount] = useState(0);
111
+ const [evaluation, setEvaluation] = useState(defaultEvaluation);
112
+ const [isLoading, setIsLoading] = useState(!skip);
113
+ const [error, setError] = useState(null);
114
+ const refetch = useCallback(() => setRefetchCount((n) => n + 1), []);
115
+ useEffect(() => {
116
+ if (skip || typeof window === "undefined") {
117
+ setIsLoading(false);
118
+ setEvaluation(defaultEvaluation);
119
+ setError(null);
120
+ return;
121
+ }
122
+ if (!wa) {
123
+ setIsLoading(false);
124
+ setEvaluation(defaultEvaluation);
125
+ setError(null);
126
+ return;
127
+ }
128
+ const ac = new AbortController();
129
+ setIsLoading(true);
130
+ setError(null);
131
+ const requestAttributes = attrKey ? JSON.parse(attrKey) : void 0;
132
+ wa.checkFeatureFlag(flagKey, requestAttributes, {
133
+ externalId,
134
+ signal: ac.signal
135
+ }).then((result) => {
136
+ if (ac.signal.aborted) return;
137
+ setEvaluation(result);
138
+ setIsLoading(false);
139
+ }).catch((e) => {
140
+ if (ac.signal.aborted) return;
141
+ const err = e instanceof Error ? e : new Error(String(e));
142
+ if (err.name === "AbortError") return;
143
+ setError(err);
144
+ setEvaluation(defaultEvaluation);
145
+ setIsLoading(false);
146
+ });
147
+ return () => {
148
+ ac.abort();
149
+ };
150
+ }, [
151
+ wa,
152
+ flagKey,
153
+ externalId,
154
+ skip,
155
+ attrKey,
156
+ refetchCount
157
+ ]);
158
+ return {
159
+ ...evaluation,
160
+ isLoading,
161
+ error,
162
+ refetch
163
+ };
164
+ }
165
+ //#endregion
166
+ export { Analytics, reportError, useConsentMode, useEvent, useFlag, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/faststats-dev/web-analytics.git"
6
6
  },
7
- "version": "0.1.9",
7
+ "version": "0.2.1",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
10
10
  "module": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "react": ">=18"
32
32
  },
33
33
  "dependencies": {
34
- "@faststats/web": "^0.1.9"
34
+ "@faststats/web": "^0.2.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/react": "^19.0.0",