@faststats/react 0.6.0 → 0.8.0

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/README.md CHANGED
@@ -2,3 +2,41 @@
2
2
 
3
3
  > [!IMPORTANT]
4
4
  > This repository is in early development and not intended for production use.
5
+
6
+ The React provider owns one explicit browser SDK instance. Components access
7
+ that instance through hooks rather than process-wide functions.
8
+
9
+ ```tsx
10
+ "use client";
11
+
12
+ import { Analytics, useTrack } from "@faststats/react";
13
+
14
+ export function App() {
15
+ return <Analytics siteKey="site_123" />;
16
+ }
17
+
18
+ export function UpgradeButton() {
19
+ const track = useTrack();
20
+ return <button onClick={() => track("upgrade_clicked")}>Upgrade</button>;
21
+ }
22
+ ```
23
+
24
+ Extensions are available from tree-shakeable React subpath exports:
25
+
26
+ ```tsx
27
+ "use client";
28
+
29
+ import { Analytics } from "@faststats/react";
30
+ import { errorTracking } from "@faststats/react/error";
31
+ import { sessionReplay } from "@faststats/react/replay";
32
+ import { webVitals } from "@faststats/react/web-vitals";
33
+
34
+ export function FastStats() {
35
+ return (
36
+ <Analytics
37
+ siteKey="site_123"
38
+ extensions={[errorTracking(), webVitals(), sessionReplay()]}
39
+ />
40
+ );
41
+ }
42
+ ```
@@ -0,0 +1,2 @@
1
+ import { ErrorTrackingExtensionOptions, errorTracking } from "@faststats/web/error";
2
+ export { type ErrorTrackingExtensionOptions, errorTracking };
package/dist/error.js ADDED
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import { errorTracking } from "@faststats/web/error";
3
+ export { errorTracking };
package/dist/index.d.ts CHANGED
@@ -1,29 +1,16 @@
1
- import { ConsentMode, FeatureFlagEvaluation, FeatureFlagEvaluation as FeatureFlagEvaluation$1, IdentifyOptions, PendingBehavior, WebAnalyticsOptions, WebAnalyticsOptions as WebAnalyticsOptions$1, getInstance, identify, identify as identify$1, logout, logout as logout$1, optIn, optIn as optIn$1, optOut, optOut as optOut$1, reportError, setConsentMode, setConsentMode as setConsentMode$1, trackEvent, trackEvent as trackEvent$1 } from "@faststats/web";
2
-
1
+ import { AnalyticsExtension, ConsentMode, ConsentMode as ConsentMode$1, IdentifyOptions, IdentifyOptions as IdentifyOptions$1, IdentifyUser, WebAnalytics, WebAnalyticsOptions, WebAnalyticsOptions as WebAnalyticsOptions$1 } from "@faststats/web";
3
2
  //#region src/analytics.d.ts
4
3
  type AnalyticsProps = WebAnalyticsOptions$1;
5
4
  declare function Analytics(props: AnalyticsProps): null;
6
5
  //#endregion
7
6
  //#region src/hooks.d.ts
8
- declare function useTrack(): typeof trackEvent$1;
9
- declare function useEvent(name: string, extra?: Record<string, unknown>): () => void;
10
- declare function useIdentify(): typeof identify$1;
11
- declare function useLogout(): typeof logout$1;
12
- declare function useConsentMode(): typeof setConsentMode$1;
13
- declare function useOptIn(): typeof optIn$1;
14
- declare function useOptOut(): typeof optOut$1;
7
+ declare function useAnalytics(): WebAnalytics | null;
8
+ declare function useTrack(): (name: string, properties?: Record<string, unknown>) => void | undefined;
9
+ declare function useEvent(name: string, properties?: Record<string, unknown>): () => void;
10
+ declare function useIdentify(): (userOrExternalId: IdentifyUser | string, email?: string, options?: IdentifyOptions$1) => Promise<boolean>;
11
+ declare function useLogout(): (resetAnonymousIdentity?: boolean) => void | undefined;
12
+ declare function useConsentMode(): (mode: ConsentMode$1) => void | undefined;
13
+ declare function useOptIn(): () => void | undefined;
14
+ declare function useOptOut(): () => void | undefined;
15
15
  //#endregion
16
- //#region src/use-flag.d.ts
17
- type UseFlagOptions = {
18
- attributes?: Record<string, unknown>;
19
- externalId?: string;
20
- skip?: boolean;
21
- };
22
- type UseFlagResult = FeatureFlagEvaluation$1 & {
23
- isLoading: boolean;
24
- error: Error | null;
25
- refetch: () => void;
26
- };
27
- declare function useFlag(flagKey: string, options?: UseFlagOptions): UseFlagResult;
28
- //#endregion
29
- export { Analytics, type AnalyticsProps, type ConsentMode, type FeatureFlagEvaluation, type IdentifyOptions, type PendingBehavior, type UseFlagOptions, type UseFlagResult, type WebAnalyticsOptions, getInstance, identify, logout, optIn, optOut, reportError, setConsentMode, trackEvent, useConsentMode, useEvent, useFlag, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
16
+ export { Analytics, type AnalyticsExtension, type AnalyticsProps, type ConsentMode, type IdentifyOptions, type WebAnalyticsOptions, useAnalytics, useConsentMode, useEvent, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
- import { WebAnalytics, getInstance, getInstance as getInstance$1, identify, identify as identify$1, logout, logout as logout$1, optIn, optIn as optIn$1, optOut, optOut as optOut$1, reportError, setConsentMode, setConsentMode as setConsentMode$1, trackEvent, trackEvent as trackEvent$1 } from "@faststats/web";
3
- import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
2
+ import { WebAnalytics } from "@faststats/web";
3
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
4
4
  //#region src/instance.ts
5
5
  let instance = null;
6
6
  const listeners = /* @__PURE__ */ new Set();
@@ -20,7 +20,7 @@ function setInstance(wa) {
20
20
  //#endregion
21
21
  //#region src/analytics.tsx
22
22
  const SDK_NAME = "@faststats/react";
23
- const SDK_VERSION = "0.6.0";
23
+ const SDK_VERSION = "0.8.0";
24
24
  function Analytics(props) {
25
25
  const ownedRef = useRef(null);
26
26
  const propsRef = useRef(props);
@@ -28,7 +28,6 @@ function Analytics(props) {
28
28
  useEffect(() => {
29
29
  if (typeof window === "undefined") return;
30
30
  if (ownedRef.current) return;
31
- getInstance$1()?.destroy();
32
31
  const wa = new WebAnalytics({
33
32
  ...propsRef.current,
34
33
  sdkName: SDK_NAME,
@@ -36,119 +35,63 @@ function Analytics(props) {
36
35
  });
37
36
  ownedRef.current = wa;
38
37
  setInstance(wa);
39
- window.__faststats = { getInstance: getInstance$1 };
38
+ wa.start();
40
39
  return () => {
41
40
  if (ownedRef.current === wa) {
42
41
  wa.destroy();
43
42
  ownedRef.current = null;
44
43
  }
45
44
  setInstance(null);
46
- window.__faststats = void 0;
47
45
  };
48
46
  }, []);
49
47
  useEffect(() => {
50
- const consentMode = props.consent?.mode;
48
+ const consentMode = props.consent;
51
49
  if (consentMode === void 0) return;
52
50
  ownedRef.current?.setConsentMode(consentMode);
53
- }, [props.consent?.mode]);
51
+ }, [props.consent]);
54
52
  return null;
55
53
  }
56
54
  //#endregion
57
55
  //#region src/hooks.ts
56
+ function useAnalytics() {
57
+ return useSyncExternalStore(subscribeToInstance, getInstanceSnapshot, () => null);
58
+ }
58
59
  function useTrack() {
59
- return trackEvent$1;
60
+ const analytics = useAnalytics();
61
+ return useCallback((name, properties) => analytics?.track(name, properties), [analytics]);
60
62
  }
61
- function useEvent(name, extra) {
63
+ function useEvent(name, properties) {
64
+ const track = useTrack();
62
65
  return useCallback(() => {
63
- trackEvent$1(name, extra ?? {});
64
- }, [name, extra]);
66
+ track(name, properties);
67
+ }, [
68
+ name,
69
+ properties,
70
+ track
71
+ ]);
65
72
  }
66
73
  function useIdentify() {
67
- return identify$1;
74
+ const analytics = useAnalytics();
75
+ return useCallback((userOrExternalId, email, options) => {
76
+ if (!analytics) return Promise.resolve(false);
77
+ return typeof userOrExternalId === "string" ? analytics.identify(userOrExternalId, email, options) : analytics.identify(userOrExternalId);
78
+ }, [analytics]);
68
79
  }
69
80
  function useLogout() {
70
- return logout$1;
81
+ const analytics = useAnalytics();
82
+ return useCallback((resetAnonymousIdentity = true) => analytics?.logout(resetAnonymousIdentity), [analytics]);
71
83
  }
72
84
  function useConsentMode() {
73
- return setConsentMode$1;
85
+ const analytics = useAnalytics();
86
+ return useCallback((mode) => analytics?.setConsentMode(mode), [analytics]);
74
87
  }
75
88
  function useOptIn() {
76
- return optIn$1;
89
+ const setConsentMode = useConsentMode();
90
+ return useCallback(() => setConsentMode("granted"), [setConsentMode]);
77
91
  }
78
92
  function useOptOut() {
79
- return optOut$1;
80
- }
81
- //#endregion
82
- //#region src/use-flag.ts
83
- const defaultState = {
84
- value: "false",
85
- isLoading: false,
86
- error: null
87
- };
88
- let hasWarnedAboutMissingInstance = false;
89
- function useFlag(flagKey, options) {
90
- const wa = useSyncExternalStore(subscribeToInstance, getInstanceSnapshot, () => null);
91
- const attributes = options?.attributes;
92
- const externalId = options?.externalId;
93
- const skip = options?.skip ?? false;
94
- const [state, setState] = useState({
95
- ...defaultState,
96
- isLoading: !skip
97
- });
98
- const abortRef = useRef(null);
99
- const fetchFlag = useCallback(() => {
100
- abortRef.current?.abort();
101
- abortRef.current = null;
102
- if (skip || !wa) {
103
- if (!skip && !wa && !hasWarnedAboutMissingInstance && true) {
104
- hasWarnedAboutMissingInstance = true;
105
- console.warn("[faststats/react] useFlag requires an active <Analytics /> instance.");
106
- }
107
- setState(defaultState);
108
- return;
109
- }
110
- const ac = new AbortController();
111
- abortRef.current = ac;
112
- setState({
113
- ...defaultState,
114
- isLoading: true
115
- });
116
- wa.checkFeatureFlag(flagKey, attributes, {
117
- externalId,
118
- signal: ac.signal
119
- }).then((result) => {
120
- if (ac.signal.aborted) return;
121
- setState({
122
- ...result,
123
- isLoading: false,
124
- error: null
125
- });
126
- }).catch((e) => {
127
- if (ac.signal.aborted) return;
128
- const err = e instanceof Error ? e : new Error(String(e));
129
- if (err.name === "AbortError") return;
130
- setState({
131
- ...defaultState,
132
- error: err
133
- });
134
- });
135
- }, [
136
- wa,
137
- flagKey,
138
- attributes,
139
- externalId,
140
- skip
141
- ]);
142
- useEffect(() => {
143
- fetchFlag();
144
- return () => {
145
- abortRef.current?.abort();
146
- };
147
- }, [fetchFlag]);
148
- return {
149
- ...state,
150
- refetch: fetchFlag
151
- };
93
+ const setConsentMode = useConsentMode();
94
+ return useCallback(() => setConsentMode("denied"), [setConsentMode]);
152
95
  }
153
96
  //#endregion
154
- export { Analytics, getInstance, identify, logout, optIn, optOut, reportError, setConsentMode, trackEvent, useConsentMode, useEvent, useFlag, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
97
+ export { Analytics, useAnalytics, useConsentMode, useEvent, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
@@ -0,0 +1,2 @@
1
+ import { outboundLinks } from "@faststats/web/outbound-links";
2
+ export { outboundLinks };
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import { outboundLinks } from "@faststats/web/outbound-links";
3
+ export { outboundLinks };
@@ -0,0 +1,2 @@
1
+ import { SessionReplayExtensionOptions, sessionReplay } from "@faststats/web/replay";
2
+ export { type SessionReplayExtensionOptions, sessionReplay };
package/dist/replay.js ADDED
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import { sessionReplay } from "@faststats/web/replay";
3
+ export { sessionReplay };
@@ -0,0 +1,2 @@
1
+ import { WebVitalsExtensionOptions, webVitals } from "@faststats/web/web-vitals";
2
+ export { type WebVitalsExtensionOptions, webVitals };
@@ -0,0 +1,3 @@
1
+ "use client";
2
+ import { webVitals } from "@faststats/web/web-vitals";
3
+ export { webVitals };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "type": "git",
5
5
  "url": "https://github.com/faststats-dev/faststats-javascript.git"
6
6
  },
7
- "version": "0.6.0",
7
+ "version": "0.8.0",
8
8
  "type": "module",
9
9
  "main": "./dist/index.js",
10
10
  "module": "./dist/index.js",
@@ -18,6 +18,26 @@
18
18
  "types": "./dist/index.d.ts",
19
19
  "import": "./dist/index.js",
20
20
  "default": "./dist/index.js"
21
+ },
22
+ "./outbound-links": {
23
+ "types": "./dist/outbound-links.d.ts",
24
+ "import": "./dist/outbound-links.js",
25
+ "default": "./dist/outbound-links.js"
26
+ },
27
+ "./error": {
28
+ "types": "./dist/error.d.ts",
29
+ "import": "./dist/error.js",
30
+ "default": "./dist/error.js"
31
+ },
32
+ "./web-vitals": {
33
+ "types": "./dist/web-vitals.d.ts",
34
+ "import": "./dist/web-vitals.js",
35
+ "default": "./dist/web-vitals.js"
36
+ },
37
+ "./replay": {
38
+ "types": "./dist/replay.d.ts",
39
+ "import": "./dist/replay.js",
40
+ "default": "./dist/replay.js"
21
41
  }
22
42
  },
23
43
  "files": [
@@ -31,13 +51,13 @@
31
51
  "react": ">=18"
32
52
  },
33
53
  "dependencies": {
34
- "@faststats/web": "^0.6.0"
54
+ "@faststats/web": "^0.8.0"
35
55
  },
36
56
  "devDependencies": {
37
- "@types/node": "^25.9.3",
38
- "@types/react": "^19.0.0",
39
- "react": "^19.0.0",
40
- "tsdown": "^0.22.3",
57
+ "@types/node": "^26.2.0",
58
+ "@types/react": "^19.2.18",
59
+ "react": "^19.2.8",
60
+ "tsdown": "^0.22.14",
41
61
  "typescript": "^6.0.3"
42
62
  }
43
63
  }