@flagship.io/react-sdk 5.2.1 → 5.2.3
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 +8 -9
- package/dist/edge/src/FlagshipContext.js +6 -4
- package/dist/edge/src/FlagshipHooks.js +79 -53
- package/dist/edge/src/FlagshipProvider.js +150 -101
- package/dist/edge/src/hooks.js +42 -0
- package/dist/edge/src/sdkVersion.js +1 -1
- package/dist/edge/src/type.local.js +1 -0
- package/dist/index.node.cjs +297 -171
- package/dist/index.node.cjs.map +1 -1
- package/dist/src/FlagshipContext.d.ts +1 -1
- package/dist/src/FlagshipContext.js +6 -4
- package/dist/src/FlagshipHooks.js +79 -53
- package/dist/src/FlagshipProvider.js +150 -101
- package/dist/src/hooks.d.ts +6 -0
- package/dist/src/hooks.js +42 -0
- package/dist/src/sdkVersion.d.ts +1 -1
- package/dist/src/sdkVersion.js +1 -1
- package/dist/src/type.d.ts +2 -0
- package/dist/src/type.local.d.ts +6 -0
- package/dist/src/type.local.js +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
[](https://github.com/flagship-io/flagship-ts-sdk/actions/workflows/ci_push.yml) [](https://codecov.io/gh/flagship-io/flagship-ts-sdk) [](https://badge.fury.io/js/@flagship.io%2Fjs-sdk)
|
|
2
2
|
|
|
3
3
|
## About Flagship
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
<img src="https://www.
|
|
6
|
+
<img src="https://www.abtasty.com/wp-content/uploads/2024/01/cropped-logo-abtasty-green.png" alt="drawing" width="150"/>
|
|
7
7
|
|
|
8
|
-
[Flagship by AB Tasty](https://www.
|
|
8
|
+
[Flagship by AB Tasty](https://www.abtasty.com) is a feature flagging platform for modern engineering and product teams. It eliminates the risks of future releases by separating code deployments from these releases :bulb: With Flagship, you have full control over the release process. You can:
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
- Switch features on or off through remote config.
|
|
@@ -14,14 +14,13 @@
|
|
|
14
14
|
- Segment users by granting access to a feature based on certain user attributes.
|
|
15
15
|
- Carry out A/B tests by easily assigning feature variations to groups of users.
|
|
16
16
|
|
|
17
|
-
<img src="https://www.flagship.io/wp-content/uploads/demo-setup.png" alt="drawing" width="600"/>
|
|
18
17
|
|
|
19
18
|
Flagship also allows you to choose whatever implementation method works for you from our many available SDKs or directly through a REST API. Additionally, our architecture is based on multi-cloud providers that offer high performance and highly-scalable managed services.
|
|
20
19
|
|
|
21
20
|
**To learn more:**
|
|
22
21
|
|
|
23
|
-
- [Solution overview](https://www.
|
|
24
|
-
- [Documentation](https://docs.
|
|
25
|
-
- [Sign up for a free trial](https://www.
|
|
26
|
-
- [Guide to feature flagging](https://
|
|
27
|
-
- [Blog](https://www.
|
|
22
|
+
- [Solution overview](https://www.abtasty.com/feature-experimentation/) - Discover how Flagship can help you manage your releases and run experiments in production
|
|
23
|
+
- [Documentation](https://docs.abtasty.com/server-side/sdks/sdk-overview) - Our dev portal with guides, how tos, API and SDK references
|
|
24
|
+
- [Sign up for a free trial](https://www.abtasty.com/get-a-demo/) - Try out Flagship for free and see how it can help you manage your releases and run experiments in production
|
|
25
|
+
- [Guide to feature flagging](https://docs.abtasty.com/feature-experimentation-and-rollout) - Everyhting you need to know about feature flag related use cases
|
|
26
|
+
- [Blog](https://www.abtasty.com/resources/) - Additional resources about release management
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import { createContext } from
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext } from "react";
|
|
3
|
+
import { FSSdkStatus } from "@flagship.io/js-sdk";
|
|
3
4
|
export const initStat = {
|
|
4
|
-
isInitializing: true
|
|
5
|
+
isInitializing: true,
|
|
6
|
+
sdkStatus: FSSdkStatus.SDK_NOT_INITIALIZED,
|
|
5
7
|
};
|
|
6
8
|
export const FlagshipContext = createContext({
|
|
7
|
-
state: { ...initStat }
|
|
9
|
+
state: { ...initStat },
|
|
8
10
|
});
|
|
@@ -5,6 +5,7 @@ import { noVisitorMessage } from './constants.js';
|
|
|
5
5
|
import { FlagshipContext } from './FlagshipContext.js';
|
|
6
6
|
import { FSFlag } from './FSFlag.js';
|
|
7
7
|
import { deepClone, hasContextChanged, logError, logWarn } from './utils.js';
|
|
8
|
+
import { useLatestRef } from './hooks.js';
|
|
8
9
|
/**
|
|
9
10
|
* This hook returns a flag object by its key. If no flag match the given key an empty flag will be returned.
|
|
10
11
|
* @param key
|
|
@@ -35,103 +36,115 @@ const handleContextChange = (param) => {
|
|
|
35
36
|
export const useFlagship = () => {
|
|
36
37
|
const { state } = useContext(FlagshipContext);
|
|
37
38
|
const { visitor, config } = state;
|
|
39
|
+
const visitorRef = useLatestRef(visitor);
|
|
40
|
+
const configRef = useLatestRef(config);
|
|
41
|
+
const stateRef = useLatestRef(state);
|
|
42
|
+
const visitorContext = useMemo(() => ({ ...visitor?.context }), [JSON.stringify(visitor?.context)]);
|
|
38
43
|
const fsUpdateContext = useCallback((context) => {
|
|
39
44
|
handleContextChange({
|
|
40
|
-
config,
|
|
41
|
-
visitor,
|
|
42
|
-
updateFunction: () =>
|
|
45
|
+
config: configRef.current,
|
|
46
|
+
visitor: visitorRef.current,
|
|
47
|
+
updateFunction: () => visitorRef.current?.updateContext(context),
|
|
43
48
|
functionName: 'updateContext'
|
|
44
49
|
});
|
|
45
|
-
}, [
|
|
50
|
+
}, []);
|
|
46
51
|
const fsClearContext = useCallback(() => {
|
|
47
52
|
handleContextChange({
|
|
48
|
-
config,
|
|
49
|
-
visitor,
|
|
50
|
-
updateFunction: () =>
|
|
53
|
+
config: configRef.current,
|
|
54
|
+
visitor: visitorRef.current,
|
|
55
|
+
updateFunction: () => visitorRef.current?.clearContext(),
|
|
51
56
|
functionName: 'cleanContext'
|
|
52
57
|
});
|
|
53
|
-
}, [
|
|
58
|
+
}, []);
|
|
54
59
|
const fsAuthenticate = useCallback((visitorId) => {
|
|
55
60
|
const functionName = 'authenticate';
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
const currentVisitor = visitorRef.current;
|
|
62
|
+
if (!currentVisitor) {
|
|
63
|
+
logError(configRef.current, noVisitorMessage, functionName);
|
|
58
64
|
return;
|
|
59
65
|
}
|
|
60
|
-
const originalVisitorId =
|
|
61
|
-
|
|
66
|
+
const originalVisitorId = currentVisitor.visitorId;
|
|
67
|
+
currentVisitor.authenticate(visitorId);
|
|
62
68
|
if (originalVisitorId !== visitorId) {
|
|
63
|
-
|
|
69
|
+
currentVisitor.fetchFlags();
|
|
64
70
|
}
|
|
65
|
-
}, [
|
|
71
|
+
}, []);
|
|
66
72
|
const fsUnauthenticate = useCallback(() => {
|
|
67
73
|
const functionName = 'unauthenticate';
|
|
68
|
-
|
|
69
|
-
|
|
74
|
+
const currentVisitor = visitorRef.current;
|
|
75
|
+
if (!currentVisitor) {
|
|
76
|
+
logError(configRef.current, noVisitorMessage, functionName);
|
|
70
77
|
return;
|
|
71
78
|
}
|
|
72
|
-
const originalVisitorId =
|
|
73
|
-
|
|
74
|
-
if (originalVisitorId !==
|
|
75
|
-
|
|
79
|
+
const originalVisitorId = currentVisitor.visitorId;
|
|
80
|
+
currentVisitor.unauthenticate();
|
|
81
|
+
if (originalVisitorId !== currentVisitor.visitorId) {
|
|
82
|
+
currentVisitor.fetchFlags();
|
|
76
83
|
}
|
|
77
|
-
}, [
|
|
84
|
+
}, []);
|
|
78
85
|
const fsSendHit = useCallback((hit) => {
|
|
79
86
|
const functionName = 'sendHit';
|
|
80
|
-
|
|
81
|
-
|
|
87
|
+
const currentVisitor = visitorRef.current;
|
|
88
|
+
if (!currentVisitor) {
|
|
89
|
+
logError(configRef.current, noVisitorMessage, functionName);
|
|
82
90
|
return Promise.resolve();
|
|
83
91
|
}
|
|
84
92
|
if (Array.isArray(hit)) {
|
|
85
|
-
return
|
|
93
|
+
return currentVisitor.sendHits(hit);
|
|
86
94
|
}
|
|
87
|
-
return
|
|
88
|
-
}, [
|
|
95
|
+
return currentVisitor.sendHit(hit);
|
|
96
|
+
}, []);
|
|
89
97
|
const getFlag = useCallback((key) => {
|
|
90
|
-
|
|
91
|
-
|
|
98
|
+
const currentVisitor = visitorRef.current;
|
|
99
|
+
if (!currentVisitor) {
|
|
100
|
+
return new FSFlag(key, stateRef.current);
|
|
92
101
|
}
|
|
93
|
-
return
|
|
94
|
-
}, [
|
|
102
|
+
return currentVisitor.getFlag(key);
|
|
103
|
+
}, []);
|
|
95
104
|
const fetchFlags = useCallback(async () => {
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
const currentVisitor = visitorRef.current;
|
|
106
|
+
if (!currentVisitor) {
|
|
107
|
+
logWarn(configRef.current, noVisitorMessage, 'fetchFlags');
|
|
98
108
|
return Promise.resolve();
|
|
99
109
|
}
|
|
100
|
-
return
|
|
101
|
-
}, [
|
|
110
|
+
return currentVisitor.fetchFlags();
|
|
111
|
+
}, []);
|
|
102
112
|
const setConsent = useCallback((hasConsented) => {
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
const currentVisitor = visitorRef.current;
|
|
114
|
+
if (!currentVisitor) {
|
|
115
|
+
logWarn(configRef.current, noVisitorMessage, 'setConsent');
|
|
105
116
|
return;
|
|
106
117
|
}
|
|
107
|
-
|
|
108
|
-
}, [
|
|
118
|
+
currentVisitor.setConsent(hasConsented);
|
|
119
|
+
}, []);
|
|
109
120
|
const close = useCallback(() => {
|
|
110
121
|
return Flagship.close();
|
|
111
122
|
}, []);
|
|
112
123
|
const getFlags = useCallback(() => {
|
|
113
|
-
|
|
124
|
+
const currentVisitor = visitorRef.current;
|
|
125
|
+
if (!currentVisitor) {
|
|
114
126
|
const flags = new Map();
|
|
115
|
-
|
|
116
|
-
flags.set(key, new FSFlag(key,
|
|
127
|
+
stateRef.current.flags?.forEach((flag, key) => {
|
|
128
|
+
flags.set(key, new FSFlag(key, stateRef.current));
|
|
117
129
|
});
|
|
118
130
|
return new FSFlagCollection({ flags });
|
|
119
131
|
}
|
|
120
|
-
return
|
|
121
|
-
}, [
|
|
132
|
+
return currentVisitor.getFlags();
|
|
133
|
+
}, []);
|
|
122
134
|
const collectEAIEventsAsync = useCallback(async (...args) => {
|
|
123
|
-
|
|
135
|
+
const currentVisitor = visitorRef.current;
|
|
136
|
+
if (!currentVisitor) {
|
|
124
137
|
return;
|
|
125
138
|
}
|
|
126
|
-
return
|
|
127
|
-
}, [
|
|
139
|
+
return currentVisitor.collectEAIEventsAsync(...args);
|
|
140
|
+
}, []);
|
|
128
141
|
return useMemo(() => ({
|
|
129
142
|
visitorId: visitor?.visitorId,
|
|
130
143
|
anonymousId: visitor?.anonymousId,
|
|
131
|
-
context:
|
|
144
|
+
context: visitorContext,
|
|
132
145
|
hasConsented: visitor?.hasConsented,
|
|
133
|
-
sdkStatus:
|
|
134
|
-
flagsStatus:
|
|
146
|
+
sdkStatus: state.sdkStatus,
|
|
147
|
+
flagsStatus: state.flagsStatus,
|
|
135
148
|
setConsent,
|
|
136
149
|
updateContext: fsUpdateContext,
|
|
137
150
|
clearContext: fsClearContext,
|
|
@@ -143,10 +156,23 @@ export const useFlagship = () => {
|
|
|
143
156
|
close,
|
|
144
157
|
getFlags,
|
|
145
158
|
collectEAIEventsAsync
|
|
146
|
-
}), [
|
|
159
|
+
}), [
|
|
160
|
+
visitor?.visitorId,
|
|
161
|
+
visitor?.anonymousId,
|
|
162
|
+
visitor?.hasConsented,
|
|
163
|
+
visitorContext,
|
|
164
|
+
state.sdkStatus,
|
|
165
|
+
state.flagsStatus,
|
|
166
|
+
setConsent,
|
|
147
167
|
fsUpdateContext,
|
|
148
|
-
fsClearContext,
|
|
149
|
-
|
|
168
|
+
fsClearContext,
|
|
169
|
+
fsAuthenticate,
|
|
170
|
+
fsUnauthenticate,
|
|
171
|
+
fsSendHit,
|
|
172
|
+
getFlag,
|
|
173
|
+
fetchFlags,
|
|
174
|
+
close,
|
|
150
175
|
getFlags,
|
|
151
|
-
collectEAIEventsAsync
|
|
176
|
+
collectEAIEventsAsync
|
|
177
|
+
]);
|
|
152
178
|
};
|
|
@@ -1,159 +1,208 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useRef,
|
|
4
|
-
import { Flagship, DecisionMode, FSSdkStatus } from "./deps.js";
|
|
3
|
+
import { useState, useEffect, useCallback, useMemo, useRef, } from "react";
|
|
4
|
+
import { Flagship, DecisionMode, FSSdkStatus, } from "./deps.js";
|
|
5
5
|
import { FlagshipContext, initStat } from "./FlagshipContext.js";
|
|
6
6
|
import { INTERNAL_EVENTS } from "./internalType.js";
|
|
7
7
|
import { version as SDK_VERSION } from "./sdkVersion.js";
|
|
8
8
|
import { useNonInitialEffect, logError, extractFlagsMap } from "./utils.js";
|
|
9
|
+
import { useLatestRef, shouldRecreateVisitor, updateVisitorData, } from "./hooks.js";
|
|
9
10
|
export function FlagshipProvider({ children, envId, apiKey, decisionMode = DecisionMode.DECISION_API, visitorData, loadingComponent, onSdkStatusChanged, onBucketingUpdated, initialCampaigns, initialFlagsData, fetchFlagsOnBucketingUpdated, hitDeduplicationTime = 2, fetchNow = true, language = 1, sdkVersion = SDK_VERSION, onFlagsStatusChanged, shouldSaveInstance, ...props }) {
|
|
10
|
-
const flags = extractFlagsMap(initialFlagsData, initialCampaigns);
|
|
11
|
-
const [
|
|
11
|
+
const flags = useMemo(() => extractFlagsMap(initialFlagsData, initialCampaigns), [initialFlagsData, initialCampaigns]);
|
|
12
|
+
const [flagshipState, setFlagshipState] = useState({
|
|
12
13
|
...initStat,
|
|
13
14
|
flags,
|
|
14
15
|
hasVisitorData: !!visitorData,
|
|
15
16
|
});
|
|
16
17
|
const [lastModified, setLastModified] = useState();
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
const flagshipVisitorRef = useRef();
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
flagshipVisitorRef.current = flagshipState.visitor;
|
|
21
|
+
}, [flagshipState.visitor]);
|
|
22
|
+
const propsRef = useLatestRef(props);
|
|
23
|
+
const configRef = useLatestRef({
|
|
24
|
+
fetchNow,
|
|
25
|
+
hitDeduplicationTime,
|
|
26
|
+
language,
|
|
27
|
+
sdkVersion,
|
|
28
|
+
});
|
|
29
|
+
const callbacksRef = useLatestRef({
|
|
30
|
+
onSdkStatusChanged,
|
|
31
|
+
onBucketingUpdated,
|
|
32
|
+
onFlagsStatusChanged,
|
|
33
|
+
});
|
|
34
|
+
const visitorDataMemo = useMemo(() => visitorData, [
|
|
35
|
+
visitorData?.id,
|
|
36
|
+
visitorData?.isAuthenticated,
|
|
37
|
+
visitorData?.hasConsented,
|
|
38
|
+
JSON.stringify(visitorData?.context),
|
|
39
|
+
]);
|
|
20
40
|
// #region functions
|
|
21
|
-
const
|
|
22
|
-
if (onBucketingUpdated) {
|
|
23
|
-
onBucketingUpdated(lastUpdate);
|
|
41
|
+
const handleBucketingUpdate = useCallback((lastUpdate) => {
|
|
42
|
+
if (callbacksRef.current.onBucketingUpdated) {
|
|
43
|
+
callbacksRef.current.onBucketingUpdated(lastUpdate);
|
|
24
44
|
}
|
|
25
45
|
setLastModified(lastUpdate);
|
|
26
|
-
};
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
case FSSdkStatus.SDK_NOT_INITIALIZED:
|
|
37
|
-
setState((prev) => ({
|
|
38
|
-
...prev,
|
|
39
|
-
config: Flagship.getConfig(),
|
|
40
|
-
isInitializing: false,
|
|
41
|
-
}));
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
const initSdk = () => {
|
|
46
|
-
Flagship.start(envId, apiKey, {
|
|
47
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
|
-
decisionMode: decisionMode,
|
|
49
|
-
fetchNow,
|
|
50
|
-
onSdkStatusChanged: statusChanged,
|
|
51
|
-
onBucketingUpdated: onBucketingLastModified,
|
|
52
|
-
hitDeduplicationTime,
|
|
53
|
-
language,
|
|
54
|
-
sdkVersion,
|
|
55
|
-
...props,
|
|
56
|
-
});
|
|
57
|
-
};
|
|
58
|
-
function initializeState(param) {
|
|
59
|
-
setState((currentState) => ({
|
|
46
|
+
}, []);
|
|
47
|
+
const handleFlagsStatusChange = useCallback(({ status, reason }) => {
|
|
48
|
+
callbacksRef.current.onFlagsStatusChanged?.({ status, reason });
|
|
49
|
+
setFlagshipState((currentState) => ({
|
|
50
|
+
...currentState,
|
|
51
|
+
flagsStatus: { status, reason },
|
|
52
|
+
}));
|
|
53
|
+
}, []);
|
|
54
|
+
const initializeVisitorState = useCallback((param) => {
|
|
55
|
+
setFlagshipState((currentState) => ({
|
|
60
56
|
...currentState,
|
|
61
57
|
visitor: param.fsVisitor,
|
|
62
58
|
config: Flagship.getConfig(),
|
|
63
59
|
isInitializing: false,
|
|
64
|
-
hasVisitorData: !!
|
|
60
|
+
hasVisitorData: !!visitorDataMemo,
|
|
61
|
+
sdkStatus: param.sdkStatus,
|
|
65
62
|
}));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const onVisitorReady = (fsVisitor, error) => {
|
|
63
|
+
}, [!!visitorDataMemo]);
|
|
64
|
+
const handleVisitorReady = useCallback((fsVisitor, error, sdkStatus) => {
|
|
69
65
|
if (error) {
|
|
70
66
|
logError(Flagship.getConfig(), error.message || error, "onReady");
|
|
71
67
|
}
|
|
72
|
-
|
|
73
|
-
};
|
|
74
|
-
const
|
|
75
|
-
if (!
|
|
68
|
+
initializeVisitorState({ fsVisitor, sdkStatus });
|
|
69
|
+
}, [initializeVisitorState]);
|
|
70
|
+
const createFlagshipVisitor = useCallback((sdkStatus) => {
|
|
71
|
+
if (!visitorDataMemo) {
|
|
76
72
|
return;
|
|
77
73
|
}
|
|
78
74
|
const fsVisitor = Flagship.newVisitor({
|
|
79
|
-
visitorId:
|
|
80
|
-
context:
|
|
81
|
-
isAuthenticated:
|
|
82
|
-
hasConsented:
|
|
75
|
+
visitorId: visitorDataMemo.id,
|
|
76
|
+
context: visitorDataMemo.context,
|
|
77
|
+
isAuthenticated: visitorDataMemo.isAuthenticated,
|
|
78
|
+
hasConsented: visitorDataMemo.hasConsented,
|
|
83
79
|
initialCampaigns,
|
|
84
80
|
initialFlagsData,
|
|
85
|
-
onFlagsStatusChanged,
|
|
81
|
+
onFlagsStatusChanged: handleFlagsStatusChange,
|
|
86
82
|
shouldSaveInstance,
|
|
87
83
|
});
|
|
88
84
|
fsVisitor?.on("ready", (error) => {
|
|
89
|
-
|
|
85
|
+
handleVisitorReady(fsVisitor, error, sdkStatus);
|
|
90
86
|
});
|
|
91
87
|
if (!fetchNow) {
|
|
92
|
-
|
|
88
|
+
initializeVisitorState({ fsVisitor, sdkStatus });
|
|
93
89
|
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
90
|
+
}, [
|
|
91
|
+
initialCampaigns,
|
|
92
|
+
initialFlagsData,
|
|
93
|
+
handleFlagsStatusChange,
|
|
94
|
+
shouldSaveInstance,
|
|
95
|
+
fetchNow,
|
|
96
|
+
handleVisitorReady,
|
|
97
|
+
initializeVisitorState,
|
|
98
|
+
visitorDataMemo,
|
|
99
|
+
]);
|
|
100
|
+
const handleSdkStatusChange = useCallback((sdkStatus) => {
|
|
101
|
+
if (callbacksRef.current.onSdkStatusChanged) {
|
|
102
|
+
callbacksRef.current.onSdkStatusChanged(sdkStatus);
|
|
103
|
+
}
|
|
104
|
+
switch (sdkStatus) {
|
|
105
|
+
case FSSdkStatus.SDK_PANIC:
|
|
106
|
+
case FSSdkStatus.SDK_INITIALIZED:
|
|
107
|
+
createFlagshipVisitor(sdkStatus);
|
|
108
|
+
break;
|
|
109
|
+
case FSSdkStatus.SDK_NOT_INITIALIZED:
|
|
110
|
+
setFlagshipState((prev) => ({
|
|
111
|
+
...prev,
|
|
112
|
+
config: Flagship.getConfig(),
|
|
113
|
+
isInitializing: false,
|
|
114
|
+
sdkStatus,
|
|
115
|
+
}));
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}, [createFlagshipVisitor]);
|
|
119
|
+
const handleSdkStatusChangeRef = useLatestRef(handleSdkStatusChange);
|
|
120
|
+
const handleBucketingUpdateRef = useLatestRef(handleBucketingUpdate);
|
|
121
|
+
const initializeFlagshipSdk = useCallback(() => {
|
|
122
|
+
Flagship.start(envId, apiKey, {
|
|
123
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
|
+
decisionMode: decisionMode,
|
|
125
|
+
fetchNow: configRef.current.fetchNow,
|
|
126
|
+
onSdkStatusChanged: (...args) => handleSdkStatusChangeRef.current(...args),
|
|
127
|
+
onBucketingUpdated: (...args) => handleBucketingUpdateRef.current(...args),
|
|
128
|
+
hitDeduplicationTime: configRef.current.hitDeduplicationTime,
|
|
129
|
+
language: configRef.current.language,
|
|
130
|
+
sdkVersion: configRef.current.sdkVersion,
|
|
131
|
+
...propsRef.current,
|
|
132
|
+
});
|
|
133
|
+
}, [envId, apiKey, decisionMode, props.isQAModeEnabled]);
|
|
134
|
+
const updateFlagshipVisitor = useCallback(() => {
|
|
135
|
+
if (!visitorDataMemo ||
|
|
97
136
|
Flagship.getStatus() !== FSSdkStatus.SDK_INITIALIZED) {
|
|
98
137
|
return;
|
|
99
138
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
state.visitor.anonymousId)))) {
|
|
105
|
-
state.visitor?.cleanup();
|
|
106
|
-
createVisitor();
|
|
139
|
+
const currentVisitor = flagshipVisitorRef.current;
|
|
140
|
+
if (shouldRecreateVisitor(currentVisitor, visitorDataMemo.id, visitorDataMemo.isAuthenticated)) {
|
|
141
|
+
currentVisitor?.cleanup();
|
|
142
|
+
createFlagshipVisitor(Flagship.getStatus());
|
|
107
143
|
return;
|
|
108
144
|
}
|
|
109
|
-
|
|
110
|
-
|
|
145
|
+
// Update existing visitor
|
|
146
|
+
if (currentVisitor) {
|
|
147
|
+
updateVisitorData(currentVisitor, visitorDataMemo.id, visitorDataMemo.context, visitorDataMemo.hasConsented, visitorDataMemo.isAuthenticated);
|
|
111
148
|
}
|
|
112
|
-
|
|
113
|
-
state.visitor.updateContext(visitorDataRef.current.context);
|
|
114
|
-
if (!state.visitor.anonymousId && visitorDataRef.current.isAuthenticated) {
|
|
115
|
-
state.visitor.authenticate(visitorDataRef.current.id);
|
|
116
|
-
}
|
|
117
|
-
if (state.visitor.anonymousId && !visitorDataRef.current.isAuthenticated) {
|
|
118
|
-
state.visitor.unauthenticate();
|
|
119
|
-
}
|
|
120
|
-
state.visitor.fetchFlags();
|
|
121
|
-
}
|
|
149
|
+
}, [createFlagshipVisitor, visitorDataMemo]);
|
|
122
150
|
// #endregion
|
|
123
151
|
useNonInitialEffect(() => {
|
|
124
152
|
if (fetchFlagsOnBucketingUpdated) {
|
|
125
|
-
|
|
153
|
+
flagshipVisitorRef.current?.fetchFlags();
|
|
126
154
|
}
|
|
127
|
-
}, [lastModified]);
|
|
155
|
+
}, [lastModified, fetchFlagsOnBucketingUpdated]);
|
|
128
156
|
useNonInitialEffect(() => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
157
|
+
updateFlagshipVisitor();
|
|
158
|
+
}, [updateFlagshipVisitor]);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
initializeFlagshipSdk();
|
|
161
|
+
}, [initializeFlagshipSdk]);
|
|
132
162
|
useEffect(() => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
163
|
+
return () => {
|
|
164
|
+
flagshipVisitorRef.current?.cleanup();
|
|
165
|
+
Flagship.close();
|
|
166
|
+
};
|
|
167
|
+
}, []);
|
|
168
|
+
const handleDisplay = useMemo(() => {
|
|
169
|
+
const isFirstInit = !flagshipState.visitor;
|
|
170
|
+
if (flagshipState.isInitializing &&
|
|
171
|
+
loadingComponent &&
|
|
172
|
+
isFirstInit &&
|
|
173
|
+
fetchNow) {
|
|
138
174
|
return _jsx(_Fragment, { children: loadingComponent });
|
|
139
175
|
}
|
|
140
176
|
return _jsx(_Fragment, { children: children });
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
177
|
+
}, [
|
|
178
|
+
flagshipState.isInitializing,
|
|
179
|
+
flagshipState.visitor,
|
|
180
|
+
loadingComponent,
|
|
181
|
+
fetchNow,
|
|
182
|
+
children,
|
|
183
|
+
]);
|
|
184
|
+
const handleForcedVariations = useCallback((e) => {
|
|
147
185
|
const { detail } = e;
|
|
148
186
|
if (detail.forcedReFetchFlags) {
|
|
149
|
-
|
|
187
|
+
flagshipVisitorRef.current?.fetchFlags();
|
|
150
188
|
}
|
|
151
189
|
else {
|
|
152
|
-
|
|
190
|
+
setFlagshipState((state) => ({
|
|
153
191
|
...state,
|
|
154
192
|
toggleForcedVariations: !state.toggleForcedVariations,
|
|
155
193
|
}));
|
|
156
194
|
}
|
|
157
|
-
};
|
|
158
|
-
|
|
195
|
+
}, []);
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
window?.addEventListener?.(INTERNAL_EVENTS.FsTriggerRendering, handleForcedVariations);
|
|
198
|
+
globalThis.__abTastyOnTriggerRender__ = (arg) => {
|
|
199
|
+
const event = {
|
|
200
|
+
detail: arg,
|
|
201
|
+
};
|
|
202
|
+
handleForcedVariations(event);
|
|
203
|
+
};
|
|
204
|
+
return () => window?.removeEventListener?.(INTERNAL_EVENTS.FsTriggerRendering, handleForcedVariations);
|
|
205
|
+
}, [handleForcedVariations]);
|
|
206
|
+
const contextValue = useMemo(() => ({ state: flagshipState, setState: setFlagshipState }), [flagshipState]);
|
|
207
|
+
return (_jsx(FlagshipContext.Provider, { value: contextValue, children: handleDisplay }));
|
|
159
208
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useRef, useEffect } from "react";
|
|
3
|
+
export function useLatestRef(value) {
|
|
4
|
+
const ref = useRef(value);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
ref.current = value;
|
|
7
|
+
}, [value]);
|
|
8
|
+
return ref;
|
|
9
|
+
}
|
|
10
|
+
export function shouldRecreateVisitor(currentVisitor, visitorId, isAuthenticated) {
|
|
11
|
+
if (!currentVisitor) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
const hasIdChanged = currentVisitor.visitorId !== visitorId;
|
|
15
|
+
return hasIdChanged && (!isAuthenticated || (isAuthenticated && !!currentVisitor.anonymousId));
|
|
16
|
+
}
|
|
17
|
+
export function shouldUpdateConsent(currentVisitor, hasConsented) {
|
|
18
|
+
return currentVisitor.hasConsented !== hasConsented;
|
|
19
|
+
}
|
|
20
|
+
export function getAuthenticationAction(currentVisitor, isAuthenticated) {
|
|
21
|
+
if (!currentVisitor.anonymousId && isAuthenticated) {
|
|
22
|
+
return "authenticate";
|
|
23
|
+
}
|
|
24
|
+
if (currentVisitor.anonymousId && !isAuthenticated) {
|
|
25
|
+
return "unauthenticate";
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export function updateVisitorData(visitor, visitorId, context, hasConsented, isAuthenticated) {
|
|
30
|
+
if (shouldUpdateConsent(visitor, hasConsented)) {
|
|
31
|
+
visitor.setConsent(hasConsented ?? true);
|
|
32
|
+
}
|
|
33
|
+
visitor.updateContext(context);
|
|
34
|
+
const authAction = getAuthenticationAction(visitor, isAuthenticated);
|
|
35
|
+
if (authAction === "authenticate") {
|
|
36
|
+
visitor.authenticate(visitorId);
|
|
37
|
+
}
|
|
38
|
+
else if (authAction === "unauthenticate") {
|
|
39
|
+
visitor.unauthenticate();
|
|
40
|
+
}
|
|
41
|
+
visitor.fetchFlags();
|
|
42
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '5.2.
|
|
2
|
+
export const version = '5.2.3';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|