@featbit/react-client-sdk 1.0.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/LICENSE +201 -0
- package/README.md +435 -0
- package/dist/asyncWithFbProvider.d.ts +29 -0
- package/dist/asyncWithFbProvider.js +129 -0
- package/dist/asyncWithFbProvider.js.map +1 -0
- package/dist/context.d.ts +15 -0
- package/dist/context.js +6 -0
- package/dist/context.js.map +1 -0
- package/dist/getFlagsProxy.d.ts +6 -0
- package/dist/getFlagsProxy.js +56 -0
- package/dist/getFlagsProxy.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/initClient.d.ts +12 -0
- package/dist/initClient.js +84 -0
- package/dist/initClient.js.map +1 -0
- package/dist/provider.d.ts +39 -0
- package/dist/provider.js +201 -0
- package/dist/provider.js.map +1 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/useFbClient.d.ts +11 -0
- package/dist/useFbClient.js +18 -0
- package/dist/useFbClient.js.map +1 -0
- package/dist/useFlags.d.ts +11 -0
- package/dist/useFlags.js +16 -0
- package/dist/useFlags.js.map +1 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +78 -0
- package/dist/utils.js.map +1 -0
- package/dist/withFbConsumer.d.ts +38 -0
- package/dist/withFbConsumer.js +36 -0
- package/dist/withFbConsumer.js.map +1 -0
- package/dist/withFbProvider.d.ts +25 -0
- package/dist/withFbProvider.js +50 -0
- package/dist/withFbProvider.js.map +1 -0
- package/package.json +57 -0
- package/src/asyncWithFbProvider.tsx +72 -0
- package/src/context.ts +24 -0
- package/src/getFlagsProxy.ts +82 -0
- package/src/index.ts +21 -0
- package/src/initClient.ts +26 -0
- package/src/provider.tsx +136 -0
- package/src/types.ts +103 -0
- package/src/useFbClient.ts +21 -0
- package/src/useFlags.ts +19 -0
- package/src/utils.ts +39 -0
- package/src/withFbConsumer.tsx +58 -0
- package/src/withFbProvider.tsx +49 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { IFbClient, IOptions, FlagValue } from '@featbit/js-client-sdk';
|
|
3
|
+
export interface IFlagSet {
|
|
4
|
+
[key: string]: FlagValue;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Initialization options for the FeatBit React SDK. These are in addition to the options exposed
|
|
8
|
+
* by [[IOption]] which are common to both the JavaScript and React SDKs.
|
|
9
|
+
*/
|
|
10
|
+
export interface FbReactOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Whether the React SDK should transform flag keys into camel-cased format.
|
|
13
|
+
* Using camel-cased flag keys allow for easier use as prop values, however,
|
|
14
|
+
* these keys won't directly match the flag keys as known to LaunchDarkly.
|
|
15
|
+
* Consequently, flag key collisions may be possible and the Code References feature
|
|
16
|
+
* will not function properly.
|
|
17
|
+
*
|
|
18
|
+
* This is false by default, if set to true, keys will automatically be converted to camel-case.
|
|
19
|
+
*/
|
|
20
|
+
useCamelCaseFlagKeys?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Whether to send flag evaluation events when a flag is read from the `flags` object
|
|
23
|
+
* returned by the `useFlags` hook. This is true by default, meaning flag evaluation
|
|
24
|
+
* events will be sent by default.
|
|
25
|
+
*/
|
|
26
|
+
sendEventsOnFlagRead?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Contains default values for the `reactOptions` object.
|
|
30
|
+
*/
|
|
31
|
+
export declare const defaultReactOptions: {
|
|
32
|
+
useCamelCaseFlagKeys: boolean;
|
|
33
|
+
sendEventsOnFlagRead: boolean;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Configuration object used to initialise FeatBit's JS client.
|
|
37
|
+
*/
|
|
38
|
+
export interface ProviderConfig {
|
|
39
|
+
/**
|
|
40
|
+
* If set to true, the FeatBit will not be initialized until the option prop has been defined.
|
|
41
|
+
*/
|
|
42
|
+
deferInitialization?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* FeatBit initialization options. These options are common between FeatBit's JavaScript and React SDKs.
|
|
45
|
+
*/
|
|
46
|
+
options?: IOptions;
|
|
47
|
+
/**
|
|
48
|
+
* Additional initialization options specific to the React SDK.
|
|
49
|
+
*
|
|
50
|
+
* @see options
|
|
51
|
+
*/
|
|
52
|
+
reactOptions?: FbReactOptions;
|
|
53
|
+
/**
|
|
54
|
+
* Optionally, the FB can be initialised outside the provider
|
|
55
|
+
* and passed in, instead of being initialised by the provider.
|
|
56
|
+
* Note: it should only be passed in when it has emitted the 'ready'
|
|
57
|
+
* event, to ensure that the flags are properly set.
|
|
58
|
+
*/
|
|
59
|
+
fbClient?: IFbClient;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The return type of withFbProvider HOC. Exported for testing purposes only.
|
|
63
|
+
*
|
|
64
|
+
* @ignore
|
|
65
|
+
*/
|
|
66
|
+
export interface EnhancedComponent extends React.Component {
|
|
67
|
+
subscribeToChanges(fbClient: IFbClient): void;
|
|
68
|
+
componentDidMount(): Promise<void>;
|
|
69
|
+
componentDidUpdate(prevProps: ProviderConfig): Promise<void>;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Return type of `initClient`.
|
|
73
|
+
*/
|
|
74
|
+
export interface AllFlagsFbClient {
|
|
75
|
+
/**
|
|
76
|
+
* Contains all flags from FeatBit.
|
|
77
|
+
*/
|
|
78
|
+
flags: IFlagSet;
|
|
79
|
+
/**
|
|
80
|
+
* An instance of `FB` from the FeatBit JS SDK.
|
|
81
|
+
*/
|
|
82
|
+
fbClient: IFbClient;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Map of camelized flag keys to original unmodified flag keys.
|
|
86
|
+
*/
|
|
87
|
+
export interface FlagKeyMap {
|
|
88
|
+
[camelCasedKey: string]: string;
|
|
89
|
+
}
|
|
90
|
+
export * from '@featbit/js-client-sdk';
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"../src/","sources":["types.ts"],"names":[],"mappings":"AA8BA;;GAEG;AACH,MAAM,CAAC,IAAM,mBAAmB,GAAG,EAAC,oBAAoB,EAAE,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAC,CAAC;AAqE7F,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IFbClient } from "@featbit/js-client-sdk";
|
|
2
|
+
/**
|
|
3
|
+
* `useFbClient` is a custom hook which returns the underlying [FeatBit JavaScript SDK client object](https://github.com/featbit/featbit-js-client-sdk).
|
|
4
|
+
* Like the `useFlags` custom hook, `useFbClient` also uses the `useContext` primitive to access the FeatBit
|
|
5
|
+
* context set up by `withFbProvider`. You will still need to use the `withFbProvider` HOC
|
|
6
|
+
* to initialise the react sdk to use this custom hook.
|
|
7
|
+
*
|
|
8
|
+
* @return The `@featbit/js-client-sdk` `FB` object
|
|
9
|
+
*/
|
|
10
|
+
declare const useFbClient: () => IFbClient;
|
|
11
|
+
export default useFbClient;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import context from './context';
|
|
3
|
+
// tslint:disable:max-line-length
|
|
4
|
+
/**
|
|
5
|
+
* `useFbClient` is a custom hook which returns the underlying [FeatBit JavaScript SDK client object](https://github.com/featbit/featbit-js-client-sdk).
|
|
6
|
+
* Like the `useFlags` custom hook, `useFbClient` also uses the `useContext` primitive to access the FeatBit
|
|
7
|
+
* context set up by `withFbProvider`. You will still need to use the `withFbProvider` HOC
|
|
8
|
+
* to initialise the react sdk to use this custom hook.
|
|
9
|
+
*
|
|
10
|
+
* @return The `@featbit/js-client-sdk` `FB` object
|
|
11
|
+
*/
|
|
12
|
+
// tslint:enable:max-line-length
|
|
13
|
+
var useFbClient = function () {
|
|
14
|
+
var fbClient = useContext(context).fbClient;
|
|
15
|
+
return fbClient;
|
|
16
|
+
};
|
|
17
|
+
export default useFbClient;
|
|
18
|
+
//# sourceMappingURL=useFbClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFbClient.js","sourceRoot":"../src/","sources":["useFbClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,OAAsB,MAAM,WAAW,CAAC;AAG/C,iCAAiC;AACjC;;;;;;;GAOG;AACH,gCAAgC;AAChC,IAAM,WAAW,GAAG;IACV,IAAA,QAAQ,GAAI,UAAU,CAAY,OAAO,CAAC,SAAlC,CAAmC;IAEnD,OAAO,QAAS,CAAC;AACnB,CAAC,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IFlagSet } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* `useFlags` is a custom hook which returns all feature flags. It uses the `useContext` primitive
|
|
4
|
+
* to access the FeatBit context set up by `withFbProvider`. As such you will still need to
|
|
5
|
+
* use the `withFbProvider` HOC at the root of your app to initialize the React SDK and populate the
|
|
6
|
+
* context with `fbClient` and your flags.
|
|
7
|
+
*
|
|
8
|
+
* @return All the feature flags configured in FeatBit
|
|
9
|
+
*/
|
|
10
|
+
declare const useFlags: <T extends IFlagSet = IFlagSet>() => T;
|
|
11
|
+
export default useFlags;
|
package/dist/useFlags.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import context from './context';
|
|
3
|
+
/**
|
|
4
|
+
* `useFlags` is a custom hook which returns all feature flags. It uses the `useContext` primitive
|
|
5
|
+
* to access the FeatBit context set up by `withFbProvider`. As such you will still need to
|
|
6
|
+
* use the `withFbProvider` HOC at the root of your app to initialize the React SDK and populate the
|
|
7
|
+
* context with `fbClient` and your flags.
|
|
8
|
+
*
|
|
9
|
+
* @return All the feature flags configured in FeatBit
|
|
10
|
+
*/
|
|
11
|
+
var useFlags = function () {
|
|
12
|
+
var flags = useContext(context).flags;
|
|
13
|
+
return flags;
|
|
14
|
+
};
|
|
15
|
+
export default useFlags;
|
|
16
|
+
//# sourceMappingURL=useFlags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFlags.js","sourceRoot":"../src/","sources":["useFlags.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,OAAsB,MAAM,WAAW,CAAC;AAG/C;;;;;;;GAOG;AACH,IAAM,QAAQ,GAAG;IACR,IAAA,KAAK,GAAI,UAAU,CAAY,OAAO,CAAC,MAAlC,CAAmC;IAE/C,OAAO,KAAU,CAAC;AACpB,CAAC,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { IFbClient } from '@featbit/js-client-sdk';
|
|
2
|
+
import { IFlagSet } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Transforms a set of flags so that their keys are camelCased. This function ignores
|
|
5
|
+
* flag keys which start with `$`.
|
|
6
|
+
*
|
|
7
|
+
* @param rawFlags A mapping of flag keys and their values
|
|
8
|
+
* @return A transformed `IFeatureFlagSet` with camelCased flag keys
|
|
9
|
+
*/
|
|
10
|
+
export declare const camelCaseKeys: (rawFlags: IFlagSet) => IFlagSet;
|
|
11
|
+
/**
|
|
12
|
+
* Retrieves flag values.
|
|
13
|
+
*
|
|
14
|
+
* @param fbClient FeatBit client
|
|
15
|
+
*
|
|
16
|
+
* @returns an `IFeatureFlagSet` with the current flag values from FeatBit
|
|
17
|
+
*/
|
|
18
|
+
export declare const fetchFlags: (fbClient: IFbClient) => Promise<{
|
|
19
|
+
[x: string]: string | undefined;
|
|
20
|
+
}[]>;
|
|
21
|
+
declare const _default: {
|
|
22
|
+
camelCaseKeys: (rawFlags: IFlagSet) => IFlagSet;
|
|
23
|
+
fetchFlags: (fbClient: IFbClient) => Promise<{
|
|
24
|
+
[x: string]: string | undefined;
|
|
25
|
+
}[]>;
|
|
26
|
+
};
|
|
27
|
+
export default _default;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
12
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import camelCase from 'lodash.camelcase';
|
|
38
|
+
/**
|
|
39
|
+
* Transforms a set of flags so that their keys are camelCased. This function ignores
|
|
40
|
+
* flag keys which start with `$`.
|
|
41
|
+
*
|
|
42
|
+
* @param rawFlags A mapping of flag keys and their values
|
|
43
|
+
* @return A transformed `IFeatureFlagSet` with camelCased flag keys
|
|
44
|
+
*/
|
|
45
|
+
export var camelCaseKeys = function (rawFlags) {
|
|
46
|
+
var flags = {};
|
|
47
|
+
for (var rawFlag in rawFlags) {
|
|
48
|
+
// Exclude system keys
|
|
49
|
+
if (rawFlag.indexOf('$') !== 0) {
|
|
50
|
+
flags[camelCase(rawFlag)] = rawFlags[rawFlag]; // tslint:disable-line:no-unsafe-any
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return flags;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Retrieves flag values.
|
|
57
|
+
*
|
|
58
|
+
* @param fbClient FeatBit client
|
|
59
|
+
*
|
|
60
|
+
* @returns an `IFeatureFlagSet` with the current flag values from FeatBit
|
|
61
|
+
*/
|
|
62
|
+
export var fetchFlags = function (fbClient) { return __awaiter(void 0, void 0, void 0, function () {
|
|
63
|
+
var evalDetails;
|
|
64
|
+
return __generator(this, function (_a) {
|
|
65
|
+
switch (_a.label) {
|
|
66
|
+
case 0: return [4 /*yield*/, fbClient.getAllVariations()];
|
|
67
|
+
case 1:
|
|
68
|
+
evalDetails = _a.sent();
|
|
69
|
+
return [2 /*return*/, evalDetails.map(function (_a) {
|
|
70
|
+
var _b;
|
|
71
|
+
var flagKey = _a.flagKey, value = _a.value;
|
|
72
|
+
return (_b = {}, _b[flagKey] = value, _b);
|
|
73
|
+
})];
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}); };
|
|
77
|
+
export default { camelCaseKeys: camelCaseKeys, fetchFlags: fetchFlags };
|
|
78
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"../src/","sources":["utils.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,OAAO,SAAS,MAAM,kBAAkB,CAAC;AAGzC;;;;;;GAMG;AACH,MAAM,CAAC,IAAM,aAAa,GAAG,UAAC,QAAkB;IAC9C,IAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,IAAM,OAAO,IAAI,QAAQ,EAAE;QAC9B,sBAAsB;QACtB,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YAC9B,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,oCAAoC;SACpF;KACF;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,IAAM,UAAU,GAAG,UACxB,QAAmB;;;;oBAEwB,qBAAM,QAAQ,CAAC,gBAAgB,EAAE,EAAA;;gBAAtE,WAAW,GAA0B,SAAiC;gBAE5E,sBAAO,WAAW,CAAC,GAAG,CAAC,UAAC,EAAgB;;4BAAf,OAAO,aAAA,EAAE,KAAK,WAAA;wBAAM,OAAA,UAAE,GAAC,OAAO,IAAG,KAAK,KAAE;oBAApB,CAAoB,CAAC,EAAC;;;KACpE,CAAC;AAEF,eAAe,EAAC,aAAa,eAAA,EAAE,UAAU,YAAA,EAAC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { IFbClient } from '@featbit/js-client-sdk';
|
|
3
|
+
import { IFlagSet } from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* Controls the props the wrapped component receives from the `FbConsumer` HOC.
|
|
6
|
+
*/
|
|
7
|
+
export interface ConsumerOptions {
|
|
8
|
+
/**
|
|
9
|
+
* If true then the wrapped component only receives the `fbClient` instance
|
|
10
|
+
* and nothing else.
|
|
11
|
+
*/
|
|
12
|
+
clientOnly: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The possible props the wrapped component can receive from the `FbConsumer` HOC.
|
|
16
|
+
*/
|
|
17
|
+
export interface FbProps {
|
|
18
|
+
/**
|
|
19
|
+
* A map of feature flags from their keys to their values.
|
|
20
|
+
* Keys are camelCased using `lodash.camelcase`.
|
|
21
|
+
*/
|
|
22
|
+
flags?: IFlagSet;
|
|
23
|
+
/**
|
|
24
|
+
* An instance of `FB` from the FeatBit JS Client SDK (`@featbit/js-client-sdk`)
|
|
25
|
+
*/
|
|
26
|
+
fbClient?: IFbClient;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* withFbConsumer is a function which accepts an optional options object and returns a function
|
|
30
|
+
* which accepts your React component. This function returns a HOC with flags
|
|
31
|
+
* and the FB instance injected via props.
|
|
32
|
+
*
|
|
33
|
+
* @param options - If you need only the `fbClient` instance and not flags, then set `{ clientOnly: true }`
|
|
34
|
+
* to only pass the fbClient prop to your component. Defaults to `{ clientOnly: false }`.
|
|
35
|
+
* @return A HOC with flags and the `fbClient` instance injected via props
|
|
36
|
+
*/
|
|
37
|
+
declare function withFbConsumer(options?: ConsumerOptions): <P>(WrappedComponent: React.ComponentType<P & FbProps>) => (props: P) => React.JSX.Element;
|
|
38
|
+
export default withFbConsumer;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import * as React from 'react';
|
|
13
|
+
import { Consumer } from './context';
|
|
14
|
+
/**
|
|
15
|
+
* withFbConsumer is a function which accepts an optional options object and returns a function
|
|
16
|
+
* which accepts your React component. This function returns a HOC with flags
|
|
17
|
+
* and the FB instance injected via props.
|
|
18
|
+
*
|
|
19
|
+
* @param options - If you need only the `fbClient` instance and not flags, then set `{ clientOnly: true }`
|
|
20
|
+
* to only pass the fbClient prop to your component. Defaults to `{ clientOnly: false }`.
|
|
21
|
+
* @return A HOC with flags and the `fbClient` instance injected via props
|
|
22
|
+
*/
|
|
23
|
+
function withFbConsumer(options) {
|
|
24
|
+
if (options === void 0) { options = { clientOnly: false }; }
|
|
25
|
+
return function withFbConsumerHoc(WrappedComponent) {
|
|
26
|
+
return function (props) { return (React.createElement(Consumer, null, function (_a) {
|
|
27
|
+
var flags = _a.flags, fbClient = _a.fbClient;
|
|
28
|
+
if (options.clientOnly) {
|
|
29
|
+
return React.createElement(WrappedComponent, __assign({ fbClient: fbClient }, props));
|
|
30
|
+
}
|
|
31
|
+
return React.createElement(WrappedComponent, __assign({ flags: flags, fbClient: fbClient }, props));
|
|
32
|
+
})); };
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export default withFbConsumer;
|
|
36
|
+
//# sourceMappingURL=withFbConsumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withFbConsumer.js","sourceRoot":"../src/","sources":["withFbConsumer.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,QAAQ,EAAa,MAAM,WAAW,CAAC;AA+BhD;;;;;;;;GAQG;AACH,SAAS,cAAc,CAAC,OAA8C;IAA9C,wBAAA,EAAA,YAA4B,UAAU,EAAE,KAAK,EAAC;IACpE,OAAO,SAAS,iBAAiB,CAAI,gBAAkD;QACrF,OAAO,UAAC,KAAQ,IAAK,OAAA,CACnB,oBAAC,QAAQ,QACL,UAAC,EAA4B;gBAA3B,KAAK,WAAA,EAAE,QAAQ,cAAA;YACjB,IAAI,OAAO,CAAC,UAAU,EAAE;gBACtB,OAAO,oBAAC,gBAAgB,aAAC,QAAQ,EAAG,QAAQ,IAAQ,KAAK,EAAK,CAAC;aAChE;YAED,OAAO,oBAAC,gBAAgB,aAAC,KAAK,EAAG,KAAK,EAAG,QAAQ,EAAG,QAAQ,IAAQ,KAAK,EAAK,CAAC;QACjF,CAAC,CACQ,CACZ,EAVoB,CAUpB,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { ProviderConfig } from './types';
|
|
3
|
+
import IntrinsicAttributes = React.JSX.IntrinsicAttributes;
|
|
4
|
+
/**
|
|
5
|
+
* `withFbProvider` is a function which accepts a config object which is used to
|
|
6
|
+
* initialize `@featbit/js-client-sdk`.
|
|
7
|
+
*
|
|
8
|
+
* This HOC handles passing configuration to the `FbProvider`, which does the following:
|
|
9
|
+
* - It initializes the fbClient instance by calling `@featbit/js-client-sdk` init on `componentDidMount`
|
|
10
|
+
* - It saves all flags and the fbClient instance in the context API
|
|
11
|
+
* - It subscribes to flag changes and propagate them through the context API
|
|
12
|
+
*
|
|
13
|
+
* The difference between `withFbProvider` and `asyncWithFbProvider` is that `withFbProvider` initializes
|
|
14
|
+
* `@featbit/js-client-sdk` at `componentDidMount`. This means your flags and the fbClient are only available after
|
|
15
|
+
* your app has mounted. This can result in a flicker due to flag changes at startup time.
|
|
16
|
+
*
|
|
17
|
+
* `asyncWithFbProvider` initializes `@featbit/js-client-sdk` at the entry point of your app prior to render.
|
|
18
|
+
* This means that your flags and the fbClient are ready at the beginning of your app. This ensures your app does not
|
|
19
|
+
* flicker due to flag changes at startup time.
|
|
20
|
+
*
|
|
21
|
+
* @param config - The configuration used to initialize FeatBit JS Client SDK
|
|
22
|
+
* @return A function which accepts your root React component and returns a HOC
|
|
23
|
+
*/
|
|
24
|
+
export declare function withFbProvider<T extends IntrinsicAttributes = {}>(config: ProviderConfig): (WrappedComponent: React.ComponentType<T>) => any;
|
|
25
|
+
export default withFbProvider;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
import * as React from 'react';
|
|
13
|
+
import { defaultReactOptions } from './types';
|
|
14
|
+
import FbProvider from './provider';
|
|
15
|
+
import hoistNonReactStatics from 'hoist-non-react-statics';
|
|
16
|
+
/**
|
|
17
|
+
* `withFbProvider` is a function which accepts a config object which is used to
|
|
18
|
+
* initialize `@featbit/js-client-sdk`.
|
|
19
|
+
*
|
|
20
|
+
* This HOC handles passing configuration to the `FbProvider`, which does the following:
|
|
21
|
+
* - It initializes the fbClient instance by calling `@featbit/js-client-sdk` init on `componentDidMount`
|
|
22
|
+
* - It saves all flags and the fbClient instance in the context API
|
|
23
|
+
* - It subscribes to flag changes and propagate them through the context API
|
|
24
|
+
*
|
|
25
|
+
* The difference between `withFbProvider` and `asyncWithFbProvider` is that `withFbProvider` initializes
|
|
26
|
+
* `@featbit/js-client-sdk` at `componentDidMount`. This means your flags and the fbClient are only available after
|
|
27
|
+
* your app has mounted. This can result in a flicker due to flag changes at startup time.
|
|
28
|
+
*
|
|
29
|
+
* `asyncWithFbProvider` initializes `@featbit/js-client-sdk` at the entry point of your app prior to render.
|
|
30
|
+
* This means that your flags and the fbClient are ready at the beginning of your app. This ensures your app does not
|
|
31
|
+
* flicker due to flag changes at startup time.
|
|
32
|
+
*
|
|
33
|
+
* @param config - The configuration used to initialize FeatBit JS Client SDK
|
|
34
|
+
* @return A function which accepts your root React component and returns a HOC
|
|
35
|
+
*/
|
|
36
|
+
export function withFbProvider(config) {
|
|
37
|
+
return function withFbProviderHoc(WrappedComponent) {
|
|
38
|
+
var userReactOptions = config.reactOptions;
|
|
39
|
+
var reactOptions = __assign(__assign({}, defaultReactOptions), userReactOptions);
|
|
40
|
+
var providerProps = __assign(__assign({}, config), { reactOptions: reactOptions });
|
|
41
|
+
function HoistedComponent(props) {
|
|
42
|
+
return (React.createElement(FbProvider, __assign({}, providerProps),
|
|
43
|
+
React.createElement(WrappedComponent, __assign({}, props))));
|
|
44
|
+
}
|
|
45
|
+
hoistNonReactStatics(HoistedComponent, WrappedComponent);
|
|
46
|
+
return HoistedComponent;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export default withFbProvider;
|
|
50
|
+
//# sourceMappingURL=withFbProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"withFbProvider.js","sourceRoot":"../src/","sources":["withFbProvider.tsx"],"names":[],"mappings":";;;;;;;;;;;AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAkB,MAAM,SAAS,CAAC;AAC9D,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,oBAAoB,MAAM,yBAAyB,CAAC;AAG3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAsB;IAEtB,OAAO,SAAS,iBAAiB,CAAC,gBAAwC;QACjE,IAAc,gBAAgB,GAAI,MAAM,aAAV,CAAW;QAChD,IAAM,YAAY,yBAAO,mBAAmB,GAAK,gBAAgB,CAAC,CAAC;QACnE,IAAM,aAAa,yBAAO,MAAM,KAAE,YAAY,cAAA,GAAC,CAAC;QAEhD,SAAS,gBAAgB,CAAC,KAAQ;YAChC,OAAO,CACL,oBAAC,UAAU,eAAM,aAAa;gBAC5B,oBAAC,gBAAgB,eAAM,KAAK,EAAK,CACtB,CACd,CAAC;QACJ,CAAC;QAED,oBAAoB,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC;QAEzD,OAAO,gBAAgB,CAAC;IAC1B,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,cAAc,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@featbit/react-client-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "FeatBit client SDK for React",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"FeatBit",
|
|
7
|
+
"React",
|
|
8
|
+
"feature flags"
|
|
9
|
+
],
|
|
10
|
+
"author": "FeatBit",
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/featbit/featbit-react-client-sdk"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/featbit/featbit-react-client-sdk",
|
|
17
|
+
"main": "dist/index.js",
|
|
18
|
+
"types": "dist/index.d.ts",
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"src"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "rimraf dist && tsc",
|
|
25
|
+
"lint": "tslint -p tsconfig.json 'src/**/*.ts*'",
|
|
26
|
+
"lint:all": "npm run lint",
|
|
27
|
+
"check-typescript": "tsc",
|
|
28
|
+
"prepublishOnly": "npm run build",
|
|
29
|
+
"prettier": "prettier --write 'src/*.@(js|ts|tsx|json|css)'"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/hoist-non-react-statics": "^3.3.1",
|
|
33
|
+
"@types/lodash.camelcase": "^4.3.6",
|
|
34
|
+
"@types/prop-types": "^15.7.4",
|
|
35
|
+
"@types/react": "^17.0.37",
|
|
36
|
+
"@types/react-dom": "^17.0.11",
|
|
37
|
+
"prettier": "^1.18.2",
|
|
38
|
+
"prop-types": "^15.7.2",
|
|
39
|
+
"react": "^18.0.0",
|
|
40
|
+
"react-dom": "^18.0.0",
|
|
41
|
+
"react-test-renderer": "^18.0.0",
|
|
42
|
+
"rimraf": "^3.0.0",
|
|
43
|
+
"tslint": "^6.1.3",
|
|
44
|
+
"tslint-config-prettier": "^1.18.0",
|
|
45
|
+
"tslint-plugin-prettier": "^2.3.0",
|
|
46
|
+
"typescript": "^4.5.3"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@featbit/js-client-sdk": "^3.0.0",
|
|
50
|
+
"hoist-non-react-statics": "^3.3.2",
|
|
51
|
+
"lodash.camelcase": "^4.3.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"react": "^16.6.3 || ^17.0.0 || ^18.0.0",
|
|
55
|
+
"react-dom": "^16.8.4 || ^17.0.0 || ^18.0.0"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useState, useEffect, ReactNode } from 'react';
|
|
2
|
+
import { ProviderConfig, defaultReactOptions, IFlagSet } from './types';
|
|
3
|
+
import { Provider } from './context';
|
|
4
|
+
import { initClient } from './initClient';
|
|
5
|
+
import getFlagsProxy from "./getFlagsProxy";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* This is an async function which initializes feature-flags.co's JS SDK (`@featbit/js-client-sdk`)
|
|
9
|
+
* and awaits it so all flags and the fbClient are ready before the consumer app is rendered.
|
|
10
|
+
*
|
|
11
|
+
* The difference between `withFbProvider` and `asyncWithFbProvider` is that `withFbProvider` initializes
|
|
12
|
+
* `@featbit/js-client-sdk` at componentDidMount. This means your flags and the fbClient are only available after
|
|
13
|
+
* your app has mounted. This can result in a flicker due to flag changes at startup time.
|
|
14
|
+
*
|
|
15
|
+
* `asyncWithFbProvider` initializes `@featbit/js-client-sdk` at the entry point of your app prior to render.
|
|
16
|
+
* This means that your flags and the fbClient are ready at the beginning of your app. This ensures your app does not
|
|
17
|
+
* flicker due to flag changes at startup time.
|
|
18
|
+
*
|
|
19
|
+
* `asyncWithFbProvider` accepts a config object which is used to initialize `@featbit/js-client-sdk`.
|
|
20
|
+
*
|
|
21
|
+
* `asyncWithFbProvider` does not support the `deferInitialization` config option because `asyncWithFbProvider` needs
|
|
22
|
+
* to be initialized at the entry point prior to render to ensure your flags and the fbClient are ready at the beginning
|
|
23
|
+
* of your app.
|
|
24
|
+
*
|
|
25
|
+
* It returns a provider which is a React FunctionComponent which:
|
|
26
|
+
* - saves all flags and the ldClient instance in the context API
|
|
27
|
+
* - subscribes to flag changes and propagate them through the context API
|
|
28
|
+
*
|
|
29
|
+
* @param config - The configuration used to initialize FeatBit's JS SDK
|
|
30
|
+
*/
|
|
31
|
+
export default async function asyncWithFbProvider(config: ProviderConfig) {
|
|
32
|
+
const {options, reactOptions: userReactOptions} = config;
|
|
33
|
+
const reactOptions = {...defaultReactOptions, ...userReactOptions};
|
|
34
|
+
const { flags: fetchedFlags, fbClient} = await initClient(reactOptions, options);
|
|
35
|
+
|
|
36
|
+
const bootstrapFlags = (options?.bootstrap || []).reduce((acc: {[key: string]: string}, flag: any) => {
|
|
37
|
+
acc[flag.id] = flag.variation;
|
|
38
|
+
return acc;
|
|
39
|
+
}, {} as {[key: string]: string});
|
|
40
|
+
|
|
41
|
+
const FbProvider = ({children}: { children: ReactNode }) => {
|
|
42
|
+
const [state, setState] = useState(() => ({
|
|
43
|
+
unproxiedFlags: fetchedFlags,
|
|
44
|
+
...getFlagsProxy(fbClient, bootstrapFlags, fetchedFlags, reactOptions)
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
fbClient.on('update', (changedKeys: string[]) => {
|
|
49
|
+
const updates: IFlagSet = changedKeys.reduce(async (acc, key) => {
|
|
50
|
+
acc[key] = await fbClient.variation(key, '');
|
|
51
|
+
return acc;
|
|
52
|
+
}, {} as IFlagSet);
|
|
53
|
+
|
|
54
|
+
if (Object.keys(updates).length > 0) {
|
|
55
|
+
setState(({ unproxiedFlags }) => {
|
|
56
|
+
const updatedUnproxiedFlags = { ...unproxiedFlags, ...updates };
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
unproxiedFlags: updatedUnproxiedFlags,
|
|
60
|
+
...getFlagsProxy(fbClient, bootstrapFlags, updatedUnproxiedFlags, reactOptions),
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
67
|
+
const { flags, flagKeyMap } = state;
|
|
68
|
+
return <Provider value={{ flags, flagKeyMap, fbClient }}>{ children }</Provider>;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
return FbProvider;
|
|
72
|
+
}
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IFbClient } from '@featbit/js-client-sdk';
|
|
2
|
+
import { createContext } from 'react';
|
|
3
|
+
import { FlagKeyMap, IFlagSet } from "./types";
|
|
4
|
+
|
|
5
|
+
interface FbContext {
|
|
6
|
+
flags: IFlagSet;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Map of camelized flag keys to their original unmodified form. Empty if useCamelCaseFlagKeys option is false.
|
|
10
|
+
*/
|
|
11
|
+
flagKeyMap: FlagKeyMap;
|
|
12
|
+
|
|
13
|
+
fbClient?: IFbClient
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const context = createContext<FbContext>({flags: {}, flagKeyMap: {}, fbClient: undefined});
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
Provider,
|
|
20
|
+
Consumer,
|
|
21
|
+
} = context;
|
|
22
|
+
|
|
23
|
+
export { Provider, Consumer, FbContext };
|
|
24
|
+
export default context;
|