@faststats/react 0.7.0 → 0.8.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/README.md +38 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.js +3 -0
- package/dist/index.d.ts +10 -23
- package/dist/index.js +33 -92
- package/dist/outbound-links.d.ts +2 -0
- package/dist/outbound-links.js +3 -0
- package/dist/replay.d.ts +2 -0
- package/dist/replay.js +3 -0
- package/dist/web-vitals.d.ts +2 -0
- package/dist/web-vitals.js +3 -0
- package/package.json +26 -6
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
|
+
```
|
package/dist/error.d.ts
ADDED
package/dist/error.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,29 +1,16 @@
|
|
|
1
|
-
import {
|
|
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
|
|
9
|
-
declare function
|
|
10
|
-
declare function
|
|
11
|
-
declare function
|
|
12
|
-
declare function
|
|
13
|
-
declare function
|
|
14
|
-
declare function
|
|
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
|
-
|
|
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
|
|
3
|
-
import { useCallback, useEffect, useRef,
|
|
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.
|
|
23
|
+
const SDK_VERSION = "0.8.1";
|
|
24
24
|
function Analytics(props) {
|
|
25
25
|
const ownedRef = useRef(null);
|
|
26
26
|
const propsRef = useRef(props);
|
|
@@ -28,11 +28,6 @@ function Analytics(props) {
|
|
|
28
28
|
useEffect(() => {
|
|
29
29
|
if (typeof window === "undefined") return;
|
|
30
30
|
if (ownedRef.current) return;
|
|
31
|
-
const existing = getInstance$1();
|
|
32
|
-
if (existing) {
|
|
33
|
-
setInstance(existing);
|
|
34
|
-
return () => setInstance(null);
|
|
35
|
-
}
|
|
36
31
|
const wa = new WebAnalytics({
|
|
37
32
|
...propsRef.current,
|
|
38
33
|
sdkName: SDK_NAME,
|
|
@@ -40,6 +35,7 @@ function Analytics(props) {
|
|
|
40
35
|
});
|
|
41
36
|
ownedRef.current = wa;
|
|
42
37
|
setInstance(wa);
|
|
38
|
+
wa.start();
|
|
43
39
|
return () => {
|
|
44
40
|
if (ownedRef.current === wa) {
|
|
45
41
|
wa.destroy();
|
|
@@ -49,108 +45,53 @@ function Analytics(props) {
|
|
|
49
45
|
};
|
|
50
46
|
}, []);
|
|
51
47
|
useEffect(() => {
|
|
52
|
-
const consentMode = props.consent
|
|
48
|
+
const consentMode = props.consent;
|
|
53
49
|
if (consentMode === void 0) return;
|
|
54
50
|
ownedRef.current?.setConsentMode(consentMode);
|
|
55
|
-
}, [props.consent
|
|
51
|
+
}, [props.consent]);
|
|
56
52
|
return null;
|
|
57
53
|
}
|
|
58
54
|
//#endregion
|
|
59
55
|
//#region src/hooks.ts
|
|
56
|
+
function useAnalytics() {
|
|
57
|
+
return useSyncExternalStore(subscribeToInstance, getInstanceSnapshot, () => null);
|
|
58
|
+
}
|
|
60
59
|
function useTrack() {
|
|
61
|
-
|
|
60
|
+
const analytics = useAnalytics();
|
|
61
|
+
return useCallback((name, properties) => analytics?.track(name, properties), [analytics]);
|
|
62
62
|
}
|
|
63
|
-
function useEvent(name,
|
|
63
|
+
function useEvent(name, properties) {
|
|
64
|
+
const track = useTrack();
|
|
64
65
|
return useCallback(() => {
|
|
65
|
-
|
|
66
|
-
}, [
|
|
66
|
+
track(name, properties);
|
|
67
|
+
}, [
|
|
68
|
+
name,
|
|
69
|
+
properties,
|
|
70
|
+
track
|
|
71
|
+
]);
|
|
67
72
|
}
|
|
68
73
|
function useIdentify() {
|
|
69
|
-
|
|
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]);
|
|
70
79
|
}
|
|
71
80
|
function useLogout() {
|
|
72
|
-
|
|
81
|
+
const analytics = useAnalytics();
|
|
82
|
+
return useCallback((resetAnonymousIdentity = true) => analytics?.logout(resetAnonymousIdentity), [analytics]);
|
|
73
83
|
}
|
|
74
84
|
function useConsentMode() {
|
|
75
|
-
|
|
85
|
+
const analytics = useAnalytics();
|
|
86
|
+
return useCallback((mode) => analytics?.setConsentMode(mode), [analytics]);
|
|
76
87
|
}
|
|
77
88
|
function useOptIn() {
|
|
78
|
-
|
|
89
|
+
const setConsentMode = useConsentMode();
|
|
90
|
+
return useCallback(() => setConsentMode("granted"), [setConsentMode]);
|
|
79
91
|
}
|
|
80
92
|
function useOptOut() {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region src/use-flag.ts
|
|
85
|
-
const defaultState = {
|
|
86
|
-
value: "false",
|
|
87
|
-
isLoading: false,
|
|
88
|
-
error: null
|
|
89
|
-
};
|
|
90
|
-
let hasWarnedAboutMissingInstance = false;
|
|
91
|
-
function useFlag(flagKey, options) {
|
|
92
|
-
const wa = useSyncExternalStore(subscribeToInstance, getInstanceSnapshot, () => null);
|
|
93
|
-
const attributes = options?.attributes;
|
|
94
|
-
const externalId = options?.externalId;
|
|
95
|
-
const skip = options?.skip ?? false;
|
|
96
|
-
const [state, setState] = useState({
|
|
97
|
-
...defaultState,
|
|
98
|
-
isLoading: !skip
|
|
99
|
-
});
|
|
100
|
-
const abortRef = useRef(null);
|
|
101
|
-
const fetchFlag = useCallback(() => {
|
|
102
|
-
abortRef.current?.abort();
|
|
103
|
-
abortRef.current = null;
|
|
104
|
-
if (skip || !wa) {
|
|
105
|
-
if (!skip && !wa && !hasWarnedAboutMissingInstance && true) {
|
|
106
|
-
hasWarnedAboutMissingInstance = true;
|
|
107
|
-
console.warn("[faststats/react] useFlag requires an active <Analytics /> instance.");
|
|
108
|
-
}
|
|
109
|
-
setState(defaultState);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const ac = new AbortController();
|
|
113
|
-
abortRef.current = ac;
|
|
114
|
-
setState({
|
|
115
|
-
...defaultState,
|
|
116
|
-
isLoading: true
|
|
117
|
-
});
|
|
118
|
-
wa.checkFeatureFlag(flagKey, attributes, {
|
|
119
|
-
externalId,
|
|
120
|
-
signal: ac.signal
|
|
121
|
-
}).then((result) => {
|
|
122
|
-
if (ac.signal.aborted) return;
|
|
123
|
-
setState({
|
|
124
|
-
...result,
|
|
125
|
-
isLoading: false,
|
|
126
|
-
error: null
|
|
127
|
-
});
|
|
128
|
-
}).catch((e) => {
|
|
129
|
-
if (ac.signal.aborted) return;
|
|
130
|
-
const err = e instanceof Error ? e : new Error(String(e));
|
|
131
|
-
if (err.name === "AbortError") return;
|
|
132
|
-
setState({
|
|
133
|
-
...defaultState,
|
|
134
|
-
error: err
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
}, [
|
|
138
|
-
wa,
|
|
139
|
-
flagKey,
|
|
140
|
-
attributes,
|
|
141
|
-
externalId,
|
|
142
|
-
skip
|
|
143
|
-
]);
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
fetchFlag();
|
|
146
|
-
return () => {
|
|
147
|
-
abortRef.current?.abort();
|
|
148
|
-
};
|
|
149
|
-
}, [fetchFlag]);
|
|
150
|
-
return {
|
|
151
|
-
...state,
|
|
152
|
-
refetch: fetchFlag
|
|
153
|
-
};
|
|
93
|
+
const setConsentMode = useConsentMode();
|
|
94
|
+
return useCallback(() => setConsentMode("denied"), [setConsentMode]);
|
|
154
95
|
}
|
|
155
96
|
//#endregion
|
|
156
|
-
export { Analytics,
|
|
97
|
+
export { Analytics, useAnalytics, useConsentMode, useEvent, useIdentify, useLogout, useOptIn, useOptOut, useTrack };
|
package/dist/replay.d.ts
ADDED
package/dist/replay.js
ADDED
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.
|
|
7
|
+
"version": "0.8.1",
|
|
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.
|
|
54
|
+
"@faststats/web": "^0.8.1"
|
|
35
55
|
},
|
|
36
56
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^
|
|
38
|
-
"@types/react": "^19.
|
|
39
|
-
"react": "^19.
|
|
40
|
-
"tsdown": "^0.22.
|
|
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
|
}
|