@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 CHANGED
@@ -1,11 +1,11 @@
1
- [![Test & Code coverage](https://github.com/flagship-io/flagship-react-sdk/actions/workflows/ci-push.yml/badge.svg?branch=master&event=push)](https://github.com/flagship-io/flagship-react-sdk/actions/workflows/ci-push.yml) [![codecov](https://codecov.io/gh/flagship-io/flagship-react-sdk/branch/master/graph/badge.svg?token=jRh0h1XcT3)](https://codecov.io/gh/flagship-io/flagship-react-sdk) [![npm version](https://badge.fury.io/js/@flagship.io%2Freact-sdk.svg)](https://badge.fury.io/js/@flagship.io%2Freact-sdk)
1
+ [![Test](https://github.com/flagship-io/flagship-ts-sdk/actions/workflows/ci_push.yml/badge.svg)](https://github.com/flagship-io/flagship-ts-sdk/actions/workflows/ci_push.yml) [![codecov](https://codecov.io/gh/flagship-io/flagship-ts-sdk/branch/main/graph/badge.svg?token=IW0NWPTPSH)](https://codecov.io/gh/flagship-io/flagship-ts-sdk) [![npm version](https://badge.fury.io/js/@flagship.io%2Fjs-sdk.svg)](https://badge.fury.io/js/@flagship.io%2Fjs-sdk)
2
2
 
3
3
  ## About Flagship
4
4
 
5
5
 
6
- <img src="https://www.flagship.io/wp-content/uploads/Flagship-horizontal-black-wake-AB.png" alt="drawing" width="150"/>
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.flagship.io/) 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:
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.flagship.io/#showvideo) - A 5mn video demo :movie_camera:
24
- - [Documentation](https://docs.developers.flagship.io/) - Our dev portal with guides, how tos, API and SDK references
25
- - [Sign up for a free trial](https://www.flagship.io/sign-up/) - Create your free account
26
- - [Guide to feature flagging](https://www.flagship.io/feature-flags/) - Everyhting you need to know about feature flag related use cases
27
- - [Blog](https://www.flagship.io/blog/) - Additional resources about release management
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
- 'use client';
2
- import { createContext } from 'react';
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: () => visitor?.updateContext(context),
45
+ config: configRef.current,
46
+ visitor: visitorRef.current,
47
+ updateFunction: () => visitorRef.current?.updateContext(context),
43
48
  functionName: 'updateContext'
44
49
  });
45
- }, [visitor]);
50
+ }, []);
46
51
  const fsClearContext = useCallback(() => {
47
52
  handleContextChange({
48
- config,
49
- visitor,
50
- updateFunction: () => visitor?.clearContext(),
53
+ config: configRef.current,
54
+ visitor: visitorRef.current,
55
+ updateFunction: () => visitorRef.current?.clearContext(),
51
56
  functionName: 'cleanContext'
52
57
  });
53
- }, [visitor]);
58
+ }, []);
54
59
  const fsAuthenticate = useCallback((visitorId) => {
55
60
  const functionName = 'authenticate';
56
- if (!visitor) {
57
- logError(config, noVisitorMessage, functionName);
61
+ const currentVisitor = visitorRef.current;
62
+ if (!currentVisitor) {
63
+ logError(configRef.current, noVisitorMessage, functionName);
58
64
  return;
59
65
  }
60
- const originalVisitorId = visitor.visitorId;
61
- visitor.authenticate(visitorId);
66
+ const originalVisitorId = currentVisitor.visitorId;
67
+ currentVisitor.authenticate(visitorId);
62
68
  if (originalVisitorId !== visitorId) {
63
- visitor.fetchFlags();
69
+ currentVisitor.fetchFlags();
64
70
  }
65
- }, [visitor]);
71
+ }, []);
66
72
  const fsUnauthenticate = useCallback(() => {
67
73
  const functionName = 'unauthenticate';
68
- if (!visitor) {
69
- logError(config, noVisitorMessage, functionName);
74
+ const currentVisitor = visitorRef.current;
75
+ if (!currentVisitor) {
76
+ logError(configRef.current, noVisitorMessage, functionName);
70
77
  return;
71
78
  }
72
- const originalVisitorId = visitor.visitorId;
73
- visitor.unauthenticate();
74
- if (originalVisitorId !== visitor.visitorId) {
75
- visitor.fetchFlags();
79
+ const originalVisitorId = currentVisitor.visitorId;
80
+ currentVisitor.unauthenticate();
81
+ if (originalVisitorId !== currentVisitor.visitorId) {
82
+ currentVisitor.fetchFlags();
76
83
  }
77
- }, [visitor]);
84
+ }, []);
78
85
  const fsSendHit = useCallback((hit) => {
79
86
  const functionName = 'sendHit';
80
- if (!visitor) {
81
- logError(config, noVisitorMessage, functionName);
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 visitor.sendHits(hit);
93
+ return currentVisitor.sendHits(hit);
86
94
  }
87
- return visitor.sendHit(hit);
88
- }, [visitor]);
95
+ return currentVisitor.sendHit(hit);
96
+ }, []);
89
97
  const getFlag = useCallback((key) => {
90
- if (!visitor) {
91
- return new FSFlag(key, state);
98
+ const currentVisitor = visitorRef.current;
99
+ if (!currentVisitor) {
100
+ return new FSFlag(key, stateRef.current);
92
101
  }
93
- return visitor.getFlag(key);
94
- }, [visitor]);
102
+ return currentVisitor.getFlag(key);
103
+ }, []);
95
104
  const fetchFlags = useCallback(async () => {
96
- if (!visitor) {
97
- logWarn(config, noVisitorMessage, 'fetchFlags');
105
+ const currentVisitor = visitorRef.current;
106
+ if (!currentVisitor) {
107
+ logWarn(configRef.current, noVisitorMessage, 'fetchFlags');
98
108
  return Promise.resolve();
99
109
  }
100
- return visitor.fetchFlags();
101
- }, [visitor]);
110
+ return currentVisitor.fetchFlags();
111
+ }, []);
102
112
  const setConsent = useCallback((hasConsented) => {
103
- if (!visitor) {
104
- logWarn(config, noVisitorMessage, 'setConsent');
113
+ const currentVisitor = visitorRef.current;
114
+ if (!currentVisitor) {
115
+ logWarn(configRef.current, noVisitorMessage, 'setConsent');
105
116
  return;
106
117
  }
107
- visitor.setConsent(hasConsented);
108
- }, [visitor]);
118
+ currentVisitor.setConsent(hasConsented);
119
+ }, []);
109
120
  const close = useCallback(() => {
110
121
  return Flagship.close();
111
122
  }, []);
112
123
  const getFlags = useCallback(() => {
113
- if (!visitor) {
124
+ const currentVisitor = visitorRef.current;
125
+ if (!currentVisitor) {
114
126
  const flags = new Map();
115
- state.flags?.forEach((flag, key) => {
116
- flags.set(key, new FSFlag(key, state));
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 visitor.getFlags();
121
- }, [visitor]);
132
+ return currentVisitor.getFlags();
133
+ }, []);
122
134
  const collectEAIEventsAsync = useCallback(async (...args) => {
123
- if (!visitor) {
135
+ const currentVisitor = visitorRef.current;
136
+ if (!currentVisitor) {
124
137
  return;
125
138
  }
126
- return visitor.collectEAIEventsAsync(...args);
127
- }, [visitor]);
139
+ return currentVisitor.collectEAIEventsAsync(...args);
140
+ }, []);
128
141
  return useMemo(() => ({
129
142
  visitorId: visitor?.visitorId,
130
143
  anonymousId: visitor?.anonymousId,
131
- context: { ...visitor?.context },
144
+ context: visitorContext,
132
145
  hasConsented: visitor?.hasConsented,
133
- sdkStatus: Flagship.getStatus(),
134
- flagsStatus: visitor?.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
- }), [visitor, setConsent,
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, fsAuthenticate, fsUnauthenticate,
149
- fsSendHit, getFlag, fetchFlags, close,
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, useEffect } from "react";
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 [state, setState] = useState({
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 stateRef = useRef();
18
- stateRef.current = state;
19
- const visitorDataRef = useRef(visitorData);
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 onBucketingLastModified = (lastUpdate) => {
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 statusChanged = (status) => {
28
- if (onSdkStatusChanged) {
29
- onSdkStatusChanged(status);
30
- }
31
- switch (status) {
32
- case FSSdkStatus.SDK_PANIC:
33
- case FSSdkStatus.SDK_INITIALIZED:
34
- createVisitor();
35
- break;
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: !!visitorData,
60
+ hasVisitorData: !!visitorDataMemo,
61
+ sdkStatus: param.sdkStatus,
65
62
  }));
66
- }
67
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- initializeState({ fsVisitor });
73
- };
74
- const createVisitor = () => {
75
- if (!visitorDataRef.current) {
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: visitorDataRef.current.id,
80
- context: visitorDataRef.current.context,
81
- isAuthenticated: visitorDataRef.current.isAuthenticated,
82
- hasConsented: visitorDataRef.current.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
- onVisitorReady(fsVisitor, error);
85
+ handleVisitorReady(fsVisitor, error, sdkStatus);
90
86
  });
91
87
  if (!fetchNow) {
92
- initializeState({ fsVisitor });
88
+ initializeVisitorState({ fsVisitor, sdkStatus });
93
89
  }
94
- };
95
- function updateVisitor() {
96
- if (!visitorDataRef.current ||
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
- if (!state.visitor ||
101
- (state.visitor.visitorId !== visitorDataRef.current.id &&
102
- (!visitorDataRef.current.isAuthenticated ||
103
- (visitorDataRef.current.isAuthenticated &&
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
- if (visitorDataRef.current.hasConsented !== state.visitor.hasConsented) {
110
- state.visitor.setConsent(visitorDataRef.current.hasConsented ?? true);
145
+ // Update existing visitor
146
+ if (currentVisitor) {
147
+ updateVisitorData(currentVisitor, visitorDataMemo.id, visitorDataMemo.context, visitorDataMemo.hasConsented, visitorDataMemo.isAuthenticated);
111
148
  }
112
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- state.visitor?.fetchFlags();
153
+ flagshipVisitorRef.current?.fetchFlags();
126
154
  }
127
- }, [lastModified]);
155
+ }, [lastModified, fetchFlagsOnBucketingUpdated]);
128
156
  useNonInitialEffect(() => {
129
- visitorDataRef.current = visitorData;
130
- updateVisitor();
131
- }, [JSON.stringify(visitorData)]);
157
+ updateFlagshipVisitor();
158
+ }, [updateFlagshipVisitor]);
159
+ useEffect(() => {
160
+ initializeFlagshipSdk();
161
+ }, [initializeFlagshipSdk]);
132
162
  useEffect(() => {
133
- initSdk();
134
- }, [envId, apiKey, decisionMode]);
135
- const handleDisplay = () => {
136
- const isFirstInit = !state.visitor;
137
- if (state.isInitializing && loadingComponent && isFirstInit && fetchNow) {
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
- useEffect(() => {
143
- window?.addEventListener?.(INTERNAL_EVENTS.FsTriggerRendering, onVariationsForced);
144
- return () => window?.removeEventListener?.(INTERNAL_EVENTS.FsTriggerRendering, onVariationsForced);
145
- }, [state.config?.isQAModeEnabled]);
146
- const onVariationsForced = (e) => {
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
- stateRef.current?.visitor?.fetchFlags();
187
+ flagshipVisitorRef.current?.fetchFlags();
150
188
  }
151
189
  else {
152
- setState((state) => ({
190
+ setFlagshipState((state) => ({
153
191
  ...state,
154
192
  toggleForcedVariations: !state.toggleForcedVariations,
155
193
  }));
156
194
  }
157
- };
158
- return (_jsx(FlagshipContext.Provider, { value: { state, setState }, children: handleDisplay() }));
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.1';
2
+ export const version = '5.2.3';
@@ -0,0 +1 @@
1
+ export {};