@configdirector/react-native-sdk 0.2.0 → 0.3.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 +4 -113
- package/dist/configdirector-react-native.d.mts +18 -9
- package/dist/configdirector-react-native.mjs +112 -43
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,121 +1,12 @@
|
|
|
1
1
|
# ConfigDirector React Native SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This is the React Native SDK for [ConfigDirector](https://www.configdirector.com).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Documentation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Refer to the [official documentation for the React Native SDK](https://docs.configdirector.com/sdks/mobile/react-native).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
npm install --save @configdirector/react-native-sdk
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
### 2. Initialize the provider using your client SDK key
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
import { View } from "react-native";
|
|
17
|
-
import AwesomeApp from "./AwesomeApp";
|
|
18
|
-
import { ConfigDirectorProvider } from "@configdirector/react-native-sdk";
|
|
19
|
-
|
|
20
|
-
export default function App() {
|
|
21
|
-
return (
|
|
22
|
-
<ConfigDirectorProvider
|
|
23
|
-
sdkKey={"YOUR-CLIENT-SDK-KEY"}
|
|
24
|
-
appName="MyAwesomeApp"
|
|
25
|
-
appVersion="1.0.0">
|
|
26
|
-
<View>
|
|
27
|
-
<AwesomeApp />
|
|
28
|
-
</View>
|
|
29
|
-
</ConfigDirectorProvider>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### 3. Retrieve config values
|
|
35
|
-
|
|
36
|
-
Retrieve a config value with the `useConfigValue` hook:
|
|
37
|
-
|
|
38
|
-
```tsx
|
|
39
|
-
import { useConfigValue } from "@configdirector/react-native-sdk";
|
|
40
|
-
|
|
41
|
-
function MyComponent() {
|
|
42
|
-
const { value } = useConfigValue<string>("my-config", "default value");
|
|
43
|
-
|
|
44
|
-
return <p>My config value: {value}</p>;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export default MyComponent;
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
You can also determine if the client is still initializing, so rather than transition from the default value to the evaluated value, you can show a loading state until the client is ready and config values are evaluated:
|
|
51
|
-
|
|
52
|
-
```tsx
|
|
53
|
-
import { useConfigValue } from "@configdirector/react-native-sdk";
|
|
54
|
-
|
|
55
|
-
function MyComponent() {
|
|
56
|
-
const { value, loading } = useConfigValue<string>(
|
|
57
|
-
"my-config",
|
|
58
|
-
"default value",
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
if (loading) {
|
|
62
|
-
return <p>Loading...</p>;
|
|
63
|
-
} else {
|
|
64
|
-
return <p>My config value: {value}</p>;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export default MyComponent;
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### 4. Updating the user context
|
|
72
|
-
|
|
73
|
-
Update the user context with the `useContext` hook:
|
|
74
|
-
|
|
75
|
-
```tsx
|
|
76
|
-
import {
|
|
77
|
-
useConfigValue,
|
|
78
|
-
useContext,
|
|
79
|
-
} from "@configdirector/react-native-sdk";
|
|
80
|
-
|
|
81
|
-
function MyComponent() {
|
|
82
|
-
const { value: myConfigValue } = useConfigValue<string>(
|
|
83
|
-
"my-config",
|
|
84
|
-
"default value",
|
|
85
|
-
);
|
|
86
|
-
const { updateContext } = useContext();
|
|
87
|
-
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
updateContext({
|
|
90
|
-
id: "54321",
|
|
91
|
-
name: "Another User",
|
|
92
|
-
traits: { customProperty: "customValue" },
|
|
93
|
-
});
|
|
94
|
-
}, []);
|
|
95
|
-
|
|
96
|
-
return <p>My config value: {myConfigValue}</p>;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export default MyComponent;
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## Requirements
|
|
103
|
-
|
|
104
|
-
- React Native **0.71 or later** (requires `ReadableStream` support)
|
|
105
|
-
- **Hermes** is strongly recommended. JavaScriptCore (JSC) is supported via a built-in UTF-8 fallback, but Hermes is the default engine in RN 0.70+ and provides better performance.
|
|
106
|
-
|
|
107
|
-
## Expo
|
|
108
|
-
|
|
109
|
-
This SDK is compatible with both **Expo managed workflow** and **bare workflow**.
|
|
110
|
-
|
|
111
|
-
- In the **managed workflow**, Hermes is the default engine — no additional setup needed.
|
|
112
|
-
- In the **bare workflow**, ensure Hermes is enabled in your `Podfile` (iOS) and `gradle.properties` (Android), which is the default in Expo SDK 47+.
|
|
113
|
-
|
|
114
|
-
Expo SDK 48 (RN 0.71) or later is required.
|
|
115
|
-
|
|
116
|
-
## License
|
|
117
|
-
|
|
118
|
-
MIT
|
|
9
|
+
There is also [a quickstart guide for ConfigDirector and any of our SDKs](https://docs.configdirector.com/getting-started/quickstart).
|
|
119
10
|
|
|
120
11
|
## Getting Help
|
|
121
12
|
|
|
@@ -61,6 +61,7 @@ type ConfigDirectorMetaContext = {
|
|
|
61
61
|
appVersion?: string;
|
|
62
62
|
appName?: string;
|
|
63
63
|
};
|
|
64
|
+
type ConnectionMode = "streaming" | "polling" | "one-time";
|
|
64
65
|
//#endregion
|
|
65
66
|
//#region ../js-client-core/src/types.d.ts
|
|
66
67
|
/**
|
|
@@ -76,18 +77,26 @@ type ConfigDirectorClientOptions = {
|
|
|
76
77
|
*/
|
|
77
78
|
connection?: {
|
|
78
79
|
/**
|
|
79
|
-
*
|
|
80
|
-
* If set to
|
|
80
|
+
* The connection mode to be used, one of `streaming` (default), `polling`, or `one-time`.
|
|
81
|
+
* If set to `streaming`, the connection will remain open and receive updates whenever
|
|
81
82
|
* config state is updated on the ConfigDirector dashboard.
|
|
82
|
-
* When set to
|
|
83
|
-
* initialization, and
|
|
84
|
-
*
|
|
83
|
+
* When set to `polling`, there will be an initial request to retrieve config state during
|
|
84
|
+
* initialization, and additional requests on a `pollingInterval`.
|
|
85
|
+
* The `one-time` connection mode will only retrieve config state during initialization and
|
|
86
|
+
* context updates. It will not poll for regular updates.
|
|
85
87
|
*
|
|
86
|
-
* Defaults to
|
|
88
|
+
* Defaults to `streaming`
|
|
87
89
|
*/
|
|
88
|
-
|
|
90
|
+
mode?: ConnectionMode;
|
|
89
91
|
/**
|
|
90
|
-
* The
|
|
92
|
+
* The polling interval in _seconds_ when the `mode` is set to `polling`. This option has no
|
|
93
|
+
* effect when the `mode` is set to `streaming` or `one-time`.
|
|
94
|
+
*
|
|
95
|
+
* Defaults to 60 seconds
|
|
96
|
+
*/
|
|
97
|
+
pollingInterval?: number;
|
|
98
|
+
/**
|
|
99
|
+
* The timeout, in _milliseconds_, to be used in initialization and when updating the context.
|
|
91
100
|
* If streaming is enabled, the operation (initialization or context update) may still succeed
|
|
92
101
|
* after it times out if no unrecoverable errors are encountered (like an invalid SDK key).
|
|
93
102
|
* If streaming is disabled, if the operation times out, it will not be retried.
|
|
@@ -246,7 +255,7 @@ declare class DefaultConsoleLogger implements ConfigDirectorLogger {
|
|
|
246
255
|
info(message: string, ...args: any): void;
|
|
247
256
|
warn(message: string, ...args: any): void;
|
|
248
257
|
error(message: string, ...args: any): void;
|
|
249
|
-
|
|
258
|
+
protected log(loggerFunction: (...args: any) => void, level: ConfigDirectorLoggingLevel, message: string, ...args: any): void;
|
|
250
259
|
}
|
|
251
260
|
//#endregion
|
|
252
261
|
//#region src/types.d.ts
|
|
@@ -679,6 +679,61 @@ const createDefaultLogger = (level, messageDecorator) => {
|
|
|
679
679
|
return new DefaultConsoleLogger(level !== null && level !== void 0 ? level : "warn", messageDecorator !== null && messageDecorator !== void 0 ? messageDecorator : new LogMessageDecorator());
|
|
680
680
|
};
|
|
681
681
|
//#endregion
|
|
682
|
+
//#region ../shared/src/transport/AbstractPollingTransport.ts
|
|
683
|
+
var AbstractPollingTransport = class {
|
|
684
|
+
constructor() {
|
|
685
|
+
_defineProperty(this, "fatalError", false);
|
|
686
|
+
_defineProperty(this, "pollingInterval", void 0);
|
|
687
|
+
_defineProperty(this, "pollingIntervalSeconds", 60);
|
|
688
|
+
}
|
|
689
|
+
clearPollingInterval() {
|
|
690
|
+
clearInterval(this.pollingInterval);
|
|
691
|
+
}
|
|
692
|
+
schedulePollingInterval(callback) {
|
|
693
|
+
clearInterval(this.pollingInterval);
|
|
694
|
+
if (this.pollingIntervalSeconds <= 0) return;
|
|
695
|
+
this.pollingInterval = setInterval(callback, this.pollingIntervalSeconds * 1e3);
|
|
696
|
+
}
|
|
697
|
+
async handleNonOkResponse(response) {
|
|
698
|
+
if (!response.ok) if (this.isStatusFatal(response.status)) {
|
|
699
|
+
this.fatalError = true;
|
|
700
|
+
throw this.prepareFatalResponseStatusError(response.status, await response.text());
|
|
701
|
+
} else throw new ConfigDirectorConnectionError(`Connection failed with status: ${response.status}`, response.status);
|
|
702
|
+
}
|
|
703
|
+
handleFetchError(fetchError) {
|
|
704
|
+
if (this.isFetchErrorFatal(fetchError)) {
|
|
705
|
+
this.fatalError = true;
|
|
706
|
+
this.close();
|
|
707
|
+
throw new ConfigDirectorConnectionError(`Connection failed with fatal error: ${fetchError}. This is an unrecoverable error, retry attempts will be ignored.`);
|
|
708
|
+
} else if (fetchError instanceof SyntaxError) throw new ConfigDirectorConnectionError(`Failed to parse the response from the server: ${fetchError}`);
|
|
709
|
+
else throw new ConfigDirectorConnectionError(`Connection failed with error: ${fetchError}.`);
|
|
710
|
+
}
|
|
711
|
+
prepareFatalResponseStatusError(responseStatus, errorBody) {
|
|
712
|
+
var _errorBody$trim$lengt, _errorBody$trim;
|
|
713
|
+
const status = responseStatus !== null && responseStatus !== void 0 ? responseStatus : 0;
|
|
714
|
+
return new ConfigDirectorConnectionError(`${`Connection failed with status: ${responseStatus !== null && responseStatus !== void 0 ? responseStatus : "unknown"}`}${((_errorBody$trim$lengt = errorBody === null || errorBody === void 0 || (_errorBody$trim = errorBody.trim()) === null || _errorBody$trim === void 0 ? void 0 : _errorBody$trim.length) !== null && _errorBody$trim$lengt !== void 0 ? _errorBody$trim$lengt : 0) > 0 ? ` (${errorBody})` : ""}. This is an unrecoverable error, retry attempts will be ignored.`, status);
|
|
715
|
+
}
|
|
716
|
+
isStatusFatal(status) {
|
|
717
|
+
return !!status && status >= 400 && status < 500;
|
|
718
|
+
}
|
|
719
|
+
isFetchErrorFatal(fetchError) {
|
|
720
|
+
if ((fetchError === null || fetchError === void 0 ? void 0 : fetchError.name) === "NotAllowedError") return true;
|
|
721
|
+
else if (fetchError instanceof TypeError) return true;
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
get isConnected() {
|
|
725
|
+
return this.pollingInterval !== void 0;
|
|
726
|
+
}
|
|
727
|
+
close() {
|
|
728
|
+
clearInterval(this.pollingInterval);
|
|
729
|
+
this.pollingInterval = void 0;
|
|
730
|
+
}
|
|
731
|
+
dispose() {
|
|
732
|
+
this.close();
|
|
733
|
+
this.clear();
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
//#endregion
|
|
682
737
|
//#region ../shared/src/fetchWithTimeout.ts
|
|
683
738
|
const fetchWithTimeout = async (timeout, resource, options, logger) => {
|
|
684
739
|
const abortController = new AbortController();
|
|
@@ -700,22 +755,35 @@ const fetchWithTimeout = async (timeout, resource, options, logger) => {
|
|
|
700
755
|
}
|
|
701
756
|
};
|
|
702
757
|
//#endregion
|
|
703
|
-
//#region ../js-client-core/src/
|
|
704
|
-
var
|
|
758
|
+
//#region ../js-client-core/src/PollingTransport.ts
|
|
759
|
+
var PollingTransport = class extends AbstractPollingTransport {
|
|
705
760
|
constructor(options) {
|
|
761
|
+
var _options$pollingInter;
|
|
762
|
+
super();
|
|
706
763
|
this.options = options;
|
|
707
764
|
_defineProperty(this, "logger", void 0);
|
|
708
765
|
_defineProperty(this, "eventEmitter", new Emitter());
|
|
709
766
|
_defineProperty(this, "url", void 0);
|
|
710
|
-
_defineProperty(this, "
|
|
767
|
+
_defineProperty(this, "lastUpdateTimestamp", void 0);
|
|
711
768
|
this.options = options;
|
|
712
769
|
this.logger = options.logger;
|
|
713
|
-
this.url = options.resolveUrl("client/
|
|
770
|
+
this.url = options.resolveUrl("client/polling/v1", options.baseUrl);
|
|
771
|
+
this.pollingIntervalSeconds = (_options$pollingInter = options.pollingInterval) !== null && _options$pollingInter !== void 0 ? _options$pollingInter : 60;
|
|
714
772
|
}
|
|
715
773
|
async connect(context, timeout) {
|
|
774
|
+
this.clearPollingInterval();
|
|
775
|
+
await this.fetchConfigs(context, timeout);
|
|
776
|
+
this.schedulePollingInterval(() => {
|
|
777
|
+
this.fetchConfigs(context, timeout).catch((error) => {
|
|
778
|
+
this.logger.error("[PollingTransport] Error during polling:", error);
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
return this;
|
|
782
|
+
}
|
|
783
|
+
async fetchConfigs(context, timeout) {
|
|
716
784
|
if (this.fatalError) {
|
|
717
|
-
this.logger.warn("[
|
|
718
|
-
return
|
|
785
|
+
this.logger.warn("[PollingTransport] There was a prior unrecoverable error. Ignoring attempt to reconnect.");
|
|
786
|
+
return;
|
|
719
787
|
}
|
|
720
788
|
try {
|
|
721
789
|
const response = await fetchWithTimeout(timeout, this.url, {
|
|
@@ -724,32 +792,20 @@ var PullTransport = class {
|
|
|
724
792
|
body: JSON.stringify({
|
|
725
793
|
givenContext: context,
|
|
726
794
|
metaContext: this.options.metaContext,
|
|
727
|
-
clientSdkKey: this.options.clientSdkKey
|
|
795
|
+
clientSdkKey: this.options.clientSdkKey,
|
|
796
|
+
lastUpdateTimestamp: this.lastUpdateTimestamp
|
|
728
797
|
})
|
|
729
798
|
}, this.logger);
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
return this;
|
|
799
|
+
await this.handleNonOkResponse(response);
|
|
800
|
+
if (response.status == 200) {
|
|
801
|
+
const json = JSON.parse(await response.text());
|
|
802
|
+
this.lastUpdateTimestamp = json.timestamp;
|
|
803
|
+
this.eventEmitter.emit("configSetReceived", json);
|
|
804
|
+
}
|
|
737
805
|
} catch (fetchError) {
|
|
738
|
-
|
|
739
|
-
this.fatalError = true;
|
|
740
|
-
throw new ConfigDirectorConnectionError(`Connection failed with fatal error: ${fetchError}. This is an unrecoverable error, retry attempts will be ignored.`);
|
|
741
|
-
} else if (fetchError instanceof SyntaxError) throw new ConfigDirectorConnectionError(`Failed to parse the response from the server: ${fetchError}`);
|
|
742
|
-
else throw new ConfigDirectorConnectionError(`Connection failed with error: ${fetchError}.`);
|
|
806
|
+
this.handleFetchError(fetchError);
|
|
743
807
|
}
|
|
744
808
|
}
|
|
745
|
-
prepareFatalResponseStatusError(responseStatus, errorBody) {
|
|
746
|
-
var _errorBody$trim$lengt, _errorBody$trim;
|
|
747
|
-
const status = responseStatus !== null && responseStatus !== void 0 ? responseStatus : 0;
|
|
748
|
-
return new ConfigDirectorConnectionError(`${`Connection failed with status: ${responseStatus !== null && responseStatus !== void 0 ? responseStatus : "unknown"}`}${((_errorBody$trim$lengt = errorBody === null || errorBody === void 0 || (_errorBody$trim = errorBody.trim()) === null || _errorBody$trim === void 0 ? void 0 : _errorBody$trim.length) !== null && _errorBody$trim$lengt !== void 0 ? _errorBody$trim$lengt : 0) > 0 ? ` (${errorBody})` : ""}. This is an unrecoverable error, retry attempts will be ignored.`, status);
|
|
749
|
-
}
|
|
750
|
-
isStatusFatal(status) {
|
|
751
|
-
return !!status && status >= 400 && status < 500;
|
|
752
|
-
}
|
|
753
809
|
on(name, handler) {
|
|
754
810
|
this.eventEmitter.on(name, handler);
|
|
755
811
|
}
|
|
@@ -759,10 +815,13 @@ var PullTransport = class {
|
|
|
759
815
|
clear() {
|
|
760
816
|
this.eventEmitter.clear();
|
|
761
817
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
818
|
+
};
|
|
819
|
+
//#endregion
|
|
820
|
+
//#region ../js-client-core/src/OneTimeTransport.ts
|
|
821
|
+
var OneTimeTransport = class extends PollingTransport {
|
|
822
|
+
constructor(options) {
|
|
823
|
+
super(options);
|
|
824
|
+
this.pollingIntervalSeconds = 0;
|
|
766
825
|
}
|
|
767
826
|
};
|
|
768
827
|
//#endregion
|
|
@@ -776,7 +835,7 @@ const CLIENT_BASE_URL = "https://client-sdk-api.configdirector.com";
|
|
|
776
835
|
const MAX_EXPONENTIAL_DELAY = 9;
|
|
777
836
|
var DefaultConfigDirectorClient = class {
|
|
778
837
|
constructor(telemetryClient, clientSdkKey, sdkOptions, clientOptions, internalClientOptions) {
|
|
779
|
-
var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _this$parseUrl, _clientOptions$connec3, _clientOptions$connec4, _navigator, _internalClientOption2;
|
|
838
|
+
var _clientOptions$logger, _clientOptions$connec, _clientOptions$connec2, _internalClientOption, _this$parseUrl, _clientOptions$connec3, _clientOptions$connec4, _clientOptions$connec5, _navigator, _internalClientOption2, _clientOptions$connec6;
|
|
780
839
|
_defineProperty(this, "logger", void 0);
|
|
781
840
|
_defineProperty(this, "telemetryClient", void 0);
|
|
782
841
|
_defineProperty(this, "configSet", void 0);
|
|
@@ -790,13 +849,13 @@ var DefaultConfigDirectorClient = class {
|
|
|
790
849
|
_defineProperty(this, "readyPromise", void 0);
|
|
791
850
|
_defineProperty(this, "readyResolve", void 0);
|
|
792
851
|
_defineProperty(this, "currentContext", void 0);
|
|
793
|
-
_defineProperty(this, "
|
|
852
|
+
_defineProperty(this, "connectionMode", void 0);
|
|
794
853
|
this.logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createDefaultLogger();
|
|
795
854
|
this.timeout = (_clientOptions$connec = clientOptions === null || clientOptions === void 0 || (_clientOptions$connec2 = clientOptions.connection) === null || _clientOptions$connec2 === void 0 ? void 0 : _clientOptions$connec2.timeout) !== null && _clientOptions$connec !== void 0 ? _clientOptions$connec : 3e3;
|
|
796
855
|
const urlFactory = (_internalClientOption = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.urlFactory) !== null && _internalClientOption !== void 0 ? _internalClientOption : defaultUrlFactory;
|
|
797
856
|
const baseUrl = (_this$parseUrl = this.parseUrl(clientOptions === null || clientOptions === void 0 || (_clientOptions$connec3 = clientOptions.connection) === null || _clientOptions$connec3 === void 0 ? void 0 : _clientOptions$connec3.url, urlFactory)) !== null && _this$parseUrl !== void 0 ? _this$parseUrl : CLIENT_BASE_URL;
|
|
798
|
-
this.
|
|
799
|
-
const transportConstructor = this.
|
|
857
|
+
this.connectionMode = (_clientOptions$connec4 = clientOptions === null || clientOptions === void 0 || (_clientOptions$connec5 = clientOptions.connection) === null || _clientOptions$connec5 === void 0 ? void 0 : _clientOptions$connec5.mode) !== null && _clientOptions$connec4 !== void 0 ? _clientOptions$connec4 : "streaming";
|
|
858
|
+
const transportConstructor = this.getTransportConstructor(this.connectionMode);
|
|
800
859
|
this.telemetryClient = telemetryClient;
|
|
801
860
|
this.transport = new transportConstructor({
|
|
802
861
|
clientSdkKey,
|
|
@@ -812,7 +871,8 @@ var DefaultConfigDirectorClient = class {
|
|
|
812
871
|
fetch: internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.fetch,
|
|
813
872
|
connectionRetryDelay: (_internalClientOption2 = internalClientOptions === null || internalClientOptions === void 0 ? void 0 : internalClientOptions.connectionRetryDelay) !== null && _internalClientOption2 !== void 0 ? _internalClientOption2 : ((attempt) => {
|
|
814
873
|
return Math.pow(2, Math.min(attempt, MAX_EXPONENTIAL_DELAY)) * 1e3;
|
|
815
|
-
})
|
|
874
|
+
}),
|
|
875
|
+
pollingInterval: clientOptions === null || clientOptions === void 0 || (_clientOptions$connec6 = clientOptions.connection) === null || _clientOptions$connec6 === void 0 ? void 0 : _clientOptions$connec6.pollingInterval
|
|
816
876
|
});
|
|
817
877
|
this.transport.on("configSetReceived", (configSet) => {
|
|
818
878
|
var _this$readyResolve;
|
|
@@ -833,6 +893,13 @@ var DefaultConfigDirectorClient = class {
|
|
|
833
893
|
this.logger.debug("[ConfigDirectorClient] ConfigSet updated from server:", { keys: configKeys });
|
|
834
894
|
});
|
|
835
895
|
}
|
|
896
|
+
getTransportConstructor(connectionMode) {
|
|
897
|
+
switch (connectionMode) {
|
|
898
|
+
case "one-time": return OneTimeTransport;
|
|
899
|
+
case "polling": return PollingTransport;
|
|
900
|
+
default: return StreamingTransport;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
836
903
|
async initialize(context) {
|
|
837
904
|
this.initializing = true;
|
|
838
905
|
await this.connectToTransport(context, "initialization");
|
|
@@ -866,10 +933,7 @@ var DefaultConfigDirectorClient = class {
|
|
|
866
933
|
if (remainingTimeout > 0) await Promise.race([this.readyPromise, new Promise((resolve) => {
|
|
867
934
|
this.timeoutTimer = setTimeout(() => resolve(), remainingTimeout);
|
|
868
935
|
})]).finally(() => clearTimeout(this.timeoutTimer));
|
|
869
|
-
if (!this.ready) {
|
|
870
|
-
const warningDetails = this.streaming ? "The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds." : "Since the client was configured without streaming, configs may not update and always return the default value.";
|
|
871
|
-
this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms. ${warningDetails}`);
|
|
872
|
-
}
|
|
936
|
+
if (!this.ready) this.logger.warn(`[ConfigDirectorClient] Timed out waiting for ${caller} after ${this.timeout}ms. The client will continue to retry since there were no fatal errors detected. Configs will return the default value until the connection succeeds.`);
|
|
873
937
|
} catch (error) {
|
|
874
938
|
this.logger.error(`[ConfigDirectorClient] An error occurred during ${caller}: `, error);
|
|
875
939
|
}
|
|
@@ -1303,6 +1367,11 @@ const encodeManually = (str) => {
|
|
|
1303
1367
|
};
|
|
1304
1368
|
//#endregion
|
|
1305
1369
|
//#region src/reactNativeStreamingFetch.ts
|
|
1370
|
+
const abortError = (message) => {
|
|
1371
|
+
const err = new Error(message);
|
|
1372
|
+
err.name = "AbortError";
|
|
1373
|
+
return err;
|
|
1374
|
+
};
|
|
1306
1375
|
/**
|
|
1307
1376
|
* A fetch-compatible function for React Native that uses XMLHttpRequest to
|
|
1308
1377
|
* support streaming SSE responses. React Native's built-in fetch accumulates
|
|
@@ -1312,7 +1381,7 @@ const encodeManually = (str) => {
|
|
|
1312
1381
|
*/
|
|
1313
1382
|
const reactNativeStreamingFetch = (url, init) => {
|
|
1314
1383
|
var _init$signal;
|
|
1315
|
-
if ((_init$signal = init.signal) === null || _init$signal === void 0 ? void 0 : _init$signal.aborted) return Promise.reject(
|
|
1384
|
+
if ((_init$signal = init.signal) === null || _init$signal === void 0 ? void 0 : _init$signal.aborted) return Promise.reject(abortError("Request was aborted before it was opened"));
|
|
1316
1385
|
return new Promise((resolve, reject) => {
|
|
1317
1386
|
var _init$method, _ref;
|
|
1318
1387
|
const xhr = new XMLHttpRequest();
|
|
@@ -1356,7 +1425,7 @@ const reactNativeStreamingFetch = (url, init) => {
|
|
|
1356
1425
|
};
|
|
1357
1426
|
if (init.signal) init.signal.addEventListener("abort", () => {
|
|
1358
1427
|
xhr.abort();
|
|
1359
|
-
if (!responseResolved) reject(
|
|
1428
|
+
if (!responseResolved) reject(abortError("Request was aborted"));
|
|
1360
1429
|
else try {
|
|
1361
1430
|
streamController.close();
|
|
1362
1431
|
} catch {}
|
|
@@ -1423,7 +1492,7 @@ const createClient = (clientSdkKey, clientOptions) => {
|
|
|
1423
1492
|
const logger = (_clientOptions$logger = clientOptions === null || clientOptions === void 0 ? void 0 : clientOptions.logger) !== null && _clientOptions$logger !== void 0 ? _clientOptions$logger : createConsoleLogger("warn");
|
|
1424
1493
|
return new DefaultConfigDirectorClient(new ReactNativeTelemetryClient(clientSdkKey, (clientOptions === null || clientOptions === void 0 || (_clientOptions$connec = clientOptions.connection) === null || _clientOptions$connec === void 0 ? void 0 : _clientOptions$connec.url) ? urlFactory(clientOptions.connection.url) : CLIENT_BASE_URL, logger, urlFactory), clientSdkKey, {
|
|
1425
1494
|
sdkName: "react-native-sdk",
|
|
1426
|
-
sdkVersion: "0.
|
|
1495
|
+
sdkVersion: "0.3.0"
|
|
1427
1496
|
}, {
|
|
1428
1497
|
...clientOptions,
|
|
1429
1498
|
logger
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configdirector/react-native-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "React Native client SDK for ConfigDirector. ConfigDirector is a remote configuration and feature flag service.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ConfigDirector",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"homepage": "https://www.configdirector.com",
|
|
22
22
|
"repository": {
|
|
23
23
|
"type": "git",
|
|
24
|
-
"url": "https://github.com/ConfigDirector/js-sdks"
|
|
24
|
+
"url": "git+https://github.com/ConfigDirector/js-sdks.git"
|
|
25
25
|
},
|
|
26
26
|
"main": "./dist/configdirector-react-native.mjs",
|
|
27
27
|
"types": "./dist/configdirector-react-native.d.mts",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
],
|
|
40
40
|
"scripts": {
|
|
41
41
|
"example": "yarn workspace @configdirector/react-native-sdk-example",
|
|
42
|
-
"clean": "
|
|
42
|
+
"clean": "rm -fr dist",
|
|
43
43
|
"build": "tsdown",
|
|
44
44
|
"prepare": "yarn build",
|
|
45
45
|
"typecheck": "tsc",
|
|
@@ -78,9 +78,9 @@
|
|
|
78
78
|
"wtfnode": "^0.10.1"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"expo": ">=
|
|
81
|
+
"expo": ">=52",
|
|
82
82
|
"react": "*",
|
|
83
|
-
"react-native": ">=0.
|
|
83
|
+
"react-native": ">=0.75"
|
|
84
84
|
},
|
|
85
85
|
"peerDependenciesMeta": {
|
|
86
86
|
"expo": {
|