@ops-ai/feature-flags-toggly 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/CHANGELOG.md +13 -0
- package/LICENSE +11 -0
- package/README.md +134 -0
- package/example/demo-app-screenshot.png +0 -0
- package/example/index.html +65 -0
- package/example/toggly-logo.png +0 -0
- package/jest.config.js +5 -0
- package/lib/models/feature-requirement.ts +4 -0
- package/lib/models/index.ts +4 -0
- package/lib/models/storage-keys.ts +4 -0
- package/lib/models/toggly-config.ts +11 -0
- package/lib/models/toggly-init-response.ts +14 -0
- package/lib/toggly.ts +150 -0
- package/package.json +39 -0
- package/spec/toggly.spec.ts +33 -0
- package/tsconfig.json +8 -0
- package/webpack.config.js +23 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
## 0.0.1
|
|
2
|
+
|
|
3
|
+
2022-11-21 (Date of Last Commit)
|
|
4
|
+
|
|
5
|
+
* Toggly classe & models
|
|
6
|
+
* Allow usage without Toggly service (by providing flagDefaults)
|
|
7
|
+
* Allow usage with Toggly service (by providing your App Key & Environment name)
|
|
8
|
+
* Feature evaluation methods unit tests
|
|
9
|
+
* Documentation
|
|
10
|
+
* License
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Copyright 2022 opsAI LLC
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
4
|
+
|
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
6
|
+
|
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
8
|
+
|
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
10
|
+
|
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Lightweight package that provides feature flags support for javascript applications allowing you to check feature status and enable/disable them easily.
|
|
2
|
+
|
|
3
|
+
Can be used *WITH* or *WITHOUT* [Toggly.io](https://toggly.io).
|
|
4
|
+
|
|
5
|
+
## What is a Feature Flag
|
|
6
|
+
|
|
7
|
+
A feature flag (or toggle) in software development provides an alternative to maintaining multiple feature branches in source code. A condition within the code enables or disables a feature during runtime.
|
|
8
|
+
|
|
9
|
+
In agile settings the feature flag is used in production, to switch on the feature on demand, for some or all the users. Thus, feature flags make it easier to release often. Advanced roll out strategies such as canary roll out and A/B testing are easier to handle.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Simply embed our latest bundle from the following CDN.
|
|
14
|
+
|
|
15
|
+
```html
|
|
16
|
+
<script src="..."></script>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Alternatively, you can use NPM to manually build the bundled *.js file.
|
|
20
|
+
|
|
21
|
+
```shell
|
|
22
|
+
$ npm install @ops-ai/feature-flags-toggly
|
|
23
|
+
$ cd node_modules/@ops-ai/feature-flags-toggly && npm run build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
And then grab the generated bundled file from the */dist directory.
|
|
27
|
+
|
|
28
|
+
## Basic Usage (with Toggly.io)
|
|
29
|
+
|
|
30
|
+
Initialize Toggly by running the Toggly.init method and by providing your App Key from your [Toggly application page](https://app.toggly.io)
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
var featureFlagsDefaults = {
|
|
34
|
+
"SignUpButton": true,
|
|
35
|
+
"DemoScreenshot": true
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
Toggly.init({
|
|
39
|
+
appKey: '<YOUR_APP_KEY>',
|
|
40
|
+
environment: '<YOUR_APP_ENVIRONMENT>'
|
|
41
|
+
})
|
|
42
|
+
.then(function () {
|
|
43
|
+
// Now you can check if a feature (or more) is Enabled/Disabled
|
|
44
|
+
|
|
45
|
+
if (Toggly.isFeatureOn('SignUpButton')) {
|
|
46
|
+
// SignUpButton is ON
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (Toggly.isFeatureOff('DemoScreenshot')) {
|
|
50
|
+
// DemoScreenshot is OFF
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
You can also check multiple feature keys and make use of the *requirement* (FeatureRequirement.all, FeatureRequirement.any) and *negate* (bool) options.
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
if (Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.all)) {
|
|
59
|
+
// ALL the provided feature keys are TRUE
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
if (Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.any)) {
|
|
65
|
+
// AT LEAST ONE the provided feature keys is TRUE
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
if (Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.all, true)) {
|
|
71
|
+
// ALL the provided feature keys are FALSE
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Lastly, you can set how often you would like to synchronize (re-fetch from Toggly) the feature flags values by setting the *.featureFlagsRefreshInterval when runnint *.init.
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
Toggly.init({
|
|
79
|
+
appKey: '<YOUR_APP_KEY>',
|
|
80
|
+
environment: '<YOUR_APP_ENVIRONMENT>',
|
|
81
|
+
featureFlagsRefreshInterval: 3 * 60 * 1000
|
|
82
|
+
})
|
|
83
|
+
.then(function () {
|
|
84
|
+
// Now you can check if a feature (or more) is Enabled/Disabled ...
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Basic Usage (without Toggly.io)
|
|
89
|
+
|
|
90
|
+
Initialize Toggly by running the Toggly.init method
|
|
91
|
+
|
|
92
|
+
```js
|
|
93
|
+
var featureFlagsDefaults = {
|
|
94
|
+
"SignUpButton": true,
|
|
95
|
+
"DemoScreenshot": true
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
Toggly.init({ flagDefaults: featureFlagsDefaults }).then(function () {
|
|
99
|
+
|
|
100
|
+
// Now you can check if a feature (or more) is Enabled/Disabled
|
|
101
|
+
|
|
102
|
+
if (Toggly.isFeatureOn('SignUpButton')) {
|
|
103
|
+
// SignUpButton is ON
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (Toggly.isFeatureOff('DemoScreenshot')) {
|
|
107
|
+
// DemoScreenshot is OFF
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
You can also check multiple feature keys and make use of the *requirement* (FeatureRequirement.all, FeatureRequirement.any) and *negate* (bool) options.
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
if (Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.all)) {
|
|
116
|
+
// ALL the provided feature keys are TRUE
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
if (Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.any)) {
|
|
122
|
+
// AT LEAST ONE the provided feature keys is TRUE
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
if (Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.all, true)) {
|
|
128
|
+
// ALL the provided feature keys are FALSE
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Find out more about Toggly.io
|
|
133
|
+
|
|
134
|
+
Visit [our official website](https://toggly.io) or [check out a video overview of our product](https://docs.toggly.io/).
|
|
Binary file
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
8
|
+
<title>Feature Flags Example</title>
|
|
9
|
+
|
|
10
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
|
11
|
+
integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
|
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"
|
|
13
|
+
integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3"
|
|
14
|
+
crossorigin="anonymous"></script>
|
|
15
|
+
</head>
|
|
16
|
+
|
|
17
|
+
<body>
|
|
18
|
+
<div class="px-4 pt-5 my-5 text-center border-bottom">
|
|
19
|
+
<h1 class="display-5 fw-bold mb-5">What is a Feature Flag?</h1>
|
|
20
|
+
<div class="col-lg-6 mx-auto">
|
|
21
|
+
<p class="lead mb-4">A feature flag (or toggle) in software development provides an alternative to maintaining
|
|
22
|
+
multiple feature branches in source code. A condition within the code enables or disables a feature during
|
|
23
|
+
runtime.</p>
|
|
24
|
+
<p class="lead mb-4">In agile settings the feature flag is used in production, to switch on the feature on demand,
|
|
25
|
+
for some or all the users. Thus, feature flags make it easier to release often. Advanced roll out strategies
|
|
26
|
+
such as canary roll out and A/B testing are easier to handle.</p>
|
|
27
|
+
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center">
|
|
28
|
+
<a href="https://app.toggly.io/register" id="signUpButton" type="button"
|
|
29
|
+
class="d-none btn btn-primary btn-lg px-4 gap-3" style="background: #556ee6; border: #556ee6;">Sign up for
|
|
30
|
+
FREE</a>
|
|
31
|
+
<a href="https://toggly.io" type="button" class="btn btn-outline-secondary btn-lg px-4">Find out more on
|
|
32
|
+
Toggly.io</a>
|
|
33
|
+
</div>
|
|
34
|
+
<small class="d-block text-muted my-3">Note: You can use this library <strong>WITH</strong> or
|
|
35
|
+
<strong>WITHOUT</strong> Toggly.io</small>
|
|
36
|
+
</div>
|
|
37
|
+
<div id="demoScreenshot" class="overflow-hidden" style="max-height: 30vh;">
|
|
38
|
+
<div class="container px-5">
|
|
39
|
+
<img src="./demo-app-screenshot.png" class="img-fluid border rounded-3 shadow-lg mb-4" alt="Example image"
|
|
40
|
+
width="700" height="500" loading="lazy">
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<script src="../dist/feature-flags-toggly.bundle.js"></script>
|
|
46
|
+
<script>
|
|
47
|
+
var featureFlagsDefaults = {
|
|
48
|
+
"SignUpButton": true,
|
|
49
|
+
"DemoScreenshot": true
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
Toggly.init({ flagDefaults: featureFlagsDefaults }).then(function () {
|
|
53
|
+
|
|
54
|
+
// Now you can check if a feature (or more) is Enabled/Disabled
|
|
55
|
+
if (Toggly.isFeatureOn('SignUpButton')) {
|
|
56
|
+
document.querySelector('#signUpButton').classList.remove('d-none');
|
|
57
|
+
}
|
|
58
|
+
if (Toggly.isFeatureOff('DemoScreenshot')) {
|
|
59
|
+
document.querySelector('#demoScreenshot').classList.add('d-none');
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
</script>
|
|
63
|
+
</body>
|
|
64
|
+
|
|
65
|
+
</html>
|
|
Binary file
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TogglyConfig {
|
|
2
|
+
baseURI?: string;
|
|
3
|
+
reloadOnFeatureFlagValidation?: boolean;
|
|
4
|
+
connectTimeout?: number;
|
|
5
|
+
featureFlagsRefreshInterval?: number;
|
|
6
|
+
isDebug?: boolean;
|
|
7
|
+
|
|
8
|
+
appKey?: string;
|
|
9
|
+
environment?: string;
|
|
10
|
+
flagDefaults?: { [key: string]: boolean };
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export enum TogglyLoadFeatureFlagsResponse {
|
|
2
|
+
fetched,
|
|
3
|
+
cached,
|
|
4
|
+
defaults,
|
|
5
|
+
error,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class TogglyInitResponse {
|
|
9
|
+
status: TogglyLoadFeatureFlagsResponse;
|
|
10
|
+
|
|
11
|
+
constructor(status: TogglyLoadFeatureFlagsResponse) {
|
|
12
|
+
this.status = status;
|
|
13
|
+
}
|
|
14
|
+
}
|
package/lib/toggly.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { FeatureRequirement, StorageKeys, TogglyConfig } from './models';
|
|
3
|
+
|
|
4
|
+
export class Toggly {
|
|
5
|
+
private static _config: TogglyConfig;
|
|
6
|
+
private static _refreshInterval: number | undefined;
|
|
7
|
+
|
|
8
|
+
static init(config: TogglyConfig = {} as TogglyConfig): Promise<{ [key: string]: boolean }> {
|
|
9
|
+
Toggly._config = Object.assign({
|
|
10
|
+
baseURI: 'https://client.toggly.io',
|
|
11
|
+
reloadOnFeatureFlagValidation: false,
|
|
12
|
+
connectTimeout: 5 * 1000,
|
|
13
|
+
featureFlagsRefreshInterval: 3 * 60 * 1000,
|
|
14
|
+
isDebug: false,
|
|
15
|
+
environment: 'Production',
|
|
16
|
+
flagDefaults: {}
|
|
17
|
+
}, config);
|
|
18
|
+
|
|
19
|
+
if (!Toggly.identity) {
|
|
20
|
+
Toggly.identity = uuidv4();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Toggly.clearFeatureFlagsCache();
|
|
24
|
+
Toggly.startRefreshInterval();
|
|
25
|
+
|
|
26
|
+
return Toggly.refresh();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static get featureFlagsValue(): { [key: string]: boolean } {
|
|
30
|
+
var cachedFlags = JSON.parse(localStorage.getItem(StorageKeys.togglyFeatureFlagsKey.toString()) ?? null);
|
|
31
|
+
return Toggly._config?.appKey && cachedFlags ? cachedFlags : Toggly._config?.flagDefaults ?? {};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static get identity(): string {
|
|
35
|
+
return localStorage.getItem(StorageKeys.togglyIdentityKey.toString());
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static set identity(v: string) {
|
|
39
|
+
localStorage.setItem(StorageKeys.togglyIdentityKey.toString(), v);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static clearIdentity() {
|
|
43
|
+
localStorage.removeItem(StorageKeys.togglyIdentityKey.toString());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private static get _cachedFeatureFlags(): { [key: string]: boolean } {
|
|
47
|
+
return JSON.parse(localStorage.getItem(StorageKeys.togglyFeatureFlagsKey.toString()) ?? null);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static cacheFeatureFlags(flags: { [key: string]: boolean }) {
|
|
51
|
+
localStorage.setItem(StorageKeys.togglyFeatureFlagsKey.toString(), JSON.stringify(flags));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static clearFeatureFlagsCache() {
|
|
55
|
+
localStorage.removeItem(StorageKeys.togglyFeatureFlagsKey.toString());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static fetchFeatureFlags(): Promise<{ [key: string]: boolean }> {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
var url = `${Toggly._config.baseURI}/${Toggly._config.appKey}-${Toggly._config.environment}/defs`;
|
|
61
|
+
|
|
62
|
+
if (Toggly.identity) {
|
|
63
|
+
url += `?u=${Toggly.identity}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fetch(url)
|
|
67
|
+
.then((response) => response.json())
|
|
68
|
+
.then((flags) => {
|
|
69
|
+
// Cache flags on successful response
|
|
70
|
+
Toggly.cacheFeatureFlags(flags);
|
|
71
|
+
resolve(flags);
|
|
72
|
+
|
|
73
|
+
if (Toggly._config.isDebug) { console.log(`Toggly.fetchFeatureFlags - ${JSON.stringify(flags)}`); }
|
|
74
|
+
})
|
|
75
|
+
.catch((error) => {
|
|
76
|
+
// Try to use flags from cache, otherwise use provided default flags
|
|
77
|
+
var flags = Toggly._cachedFeatureFlags ?? Toggly._config.flagDefaults;
|
|
78
|
+
resolve(flags);
|
|
79
|
+
|
|
80
|
+
if (Toggly._config.isDebug) { console.log(`Toggly.loadedFromCache - ${JSON.stringify(flags)}`); }
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
static refresh(): Promise<{ [key: string]: boolean }> {
|
|
86
|
+
if (Toggly._config.isDebug) { console.log('Toggly.refresh'); }
|
|
87
|
+
|
|
88
|
+
// In case there is no API key provided, only the flag defaults shall be used
|
|
89
|
+
if (!Toggly._config.appKey) {
|
|
90
|
+
if (Toggly._config.isDebug) { console.log(`Toggly.usedFlagDefaults - ${JSON.stringify(Toggly._config.flagDefaults)}`); }
|
|
91
|
+
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
resolve(Toggly._config.flagDefaults);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Try to fetch flags from the API
|
|
98
|
+
return Toggly.fetchFeatureFlags();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private static _evaluateFeatureGate(flags: { [key: string]: boolean } = {}, featureGate: string[], requirement: FeatureRequirement = FeatureRequirement.all, negate: boolean = false) {
|
|
102
|
+
var isEnabled: boolean;
|
|
103
|
+
|
|
104
|
+
if (requirement === FeatureRequirement.any) {
|
|
105
|
+
isEnabled = featureGate.reduce((isEnabled, featureKey) => {
|
|
106
|
+
return isEnabled ||
|
|
107
|
+
(flags[featureKey] && flags[featureKey] === true);
|
|
108
|
+
}, false);
|
|
109
|
+
} else {
|
|
110
|
+
isEnabled = featureGate.reduce((isEnabled, featureKey) => {
|
|
111
|
+
return isEnabled &&
|
|
112
|
+
(flags[featureKey] && flags[featureKey] === true);
|
|
113
|
+
}, true);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (Toggly._config.isDebug) { console.log(`Toggly._evaluateFeatureGate - ${JSON.stringify(featureGate)}`); }
|
|
117
|
+
|
|
118
|
+
isEnabled = negate ? !isEnabled : isEnabled;
|
|
119
|
+
|
|
120
|
+
return isEnabled;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
static evaluateFeatureGate(featureGate: string[], requirement: FeatureRequirement = FeatureRequirement.all, negate: boolean = false): boolean {
|
|
124
|
+
return Toggly._evaluateFeatureGate(Toggly.featureFlagsValue, featureGate, requirement, negate);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static isFeatureOn(featureKey: string): boolean {
|
|
128
|
+
return Toggly._evaluateFeatureGate(Toggly.featureFlagsValue, [featureKey]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static isFeatureOff(featureKey: string): boolean {
|
|
132
|
+
return Toggly._evaluateFeatureGate(Toggly.featureFlagsValue, [featureKey], FeatureRequirement.all, true);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static cancelRefreshInterval() {
|
|
136
|
+
window.clearInterval(Toggly._refreshInterval);
|
|
137
|
+
Toggly._refreshInterval = undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
static startRefreshInterval() {
|
|
141
|
+
Toggly.cancelRefreshInterval();
|
|
142
|
+
|
|
143
|
+
if (Toggly._config.appKey && Toggly._config.featureFlagsRefreshInterval > 0) {
|
|
144
|
+
Toggly._refreshInterval = window.setInterval(() => Toggly.refresh(), Toggly._config.featureFlagsRefreshInterval);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
(window as any).Toggly = Toggly;
|
|
150
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ops-ai/feature-flags-toggly",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Provides feature flags support for Javascript applications allowing you to enable and disable features easily. Can be used with or without Toggly.io.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "webpack",
|
|
8
|
+
"dev": "npx webpack -w",
|
|
9
|
+
"test": "npx jest"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/ops-ai/Toggly.FeatureManagement.git#develop"
|
|
14
|
+
},
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Cosmin Atomei",
|
|
17
|
+
"email": "cosmin.atomei@gmail.com"
|
|
18
|
+
},
|
|
19
|
+
"license": "BSD-3-Clause",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/ops-ai/Toggly.FeatureManagement/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/ops-ai/Toggly.FeatureManagement/tree/develop#readme",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"rxjs": "^7.5.7",
|
|
26
|
+
"uuid": "^9.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/jest": "^29.2.3",
|
|
30
|
+
"@types/uuid": "^8.3.4",
|
|
31
|
+
"jest": "^29.3.1",
|
|
32
|
+
"jest-environment-jsdom": "^29.3.1",
|
|
33
|
+
"ts-jest": "^29.0.3",
|
|
34
|
+
"ts-loader": "^9.4.1",
|
|
35
|
+
"typescript": "^4.9.3",
|
|
36
|
+
"webpack": "^5.75.0",
|
|
37
|
+
"webpack-cli": "^5.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FeatureRequirement } from '../lib/models';
|
|
2
|
+
import { Toggly } from '../lib/toggly';
|
|
3
|
+
|
|
4
|
+
beforeAll(() => {
|
|
5
|
+
return Toggly.init({
|
|
6
|
+
flagDefaults: {
|
|
7
|
+
"ExampleFeatureKey1": true,
|
|
8
|
+
"ExampleFeatureKey2": false
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test('Check (isFeatureOn) result based on provided *.flagDefaults', () => {
|
|
14
|
+
expect(Toggly.isFeatureOn('ExampleFeatureKey1')).toBe(true);
|
|
15
|
+
expect(Toggly.isFeatureOn('ExampleFeatureKey2')).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('Check (isFeatureOff) result based on provided *.flagDefaults', () => {
|
|
19
|
+
expect(Toggly.isFeatureOff('ExampleFeatureKey1')).toBe(false);
|
|
20
|
+
expect(Toggly.isFeatureOff('ExampleFeatureKey2')).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('Check (evaluateFeatureGate, requirement: All) result based on provided *.flagDefaults', () => {
|
|
24
|
+
return expect(Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.all)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('Check (evaluateFeatureGate, requirement: Any) result based on provided *.flagDefaults', () => {
|
|
28
|
+
return expect(Toggly.evaluateFeatureGate(['ExampleFeatureKey1', 'ExampleFeatureKey2'], FeatureRequirement.any)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('Check (evaluateFeatureGate, negate) result based on provided *.flagDefaults', () => {
|
|
32
|
+
return expect(Toggly.evaluateFeatureGate(['ExampleFeatureKey1'], FeatureRequirement.all, true)).toBe(false);
|
|
33
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
mode: "production",
|
|
5
|
+
entry: {
|
|
6
|
+
main: "./lib/toggly.ts",
|
|
7
|
+
},
|
|
8
|
+
output: {
|
|
9
|
+
path: path.resolve(__dirname, './dist'),
|
|
10
|
+
filename: "feature-flags-toggly.bundle.js"
|
|
11
|
+
},
|
|
12
|
+
resolve: {
|
|
13
|
+
extensions: [".ts", ".tsx", ".js"],
|
|
14
|
+
},
|
|
15
|
+
module: {
|
|
16
|
+
rules: [
|
|
17
|
+
{
|
|
18
|
+
test: /\.tsx?$/,
|
|
19
|
+
loader: "ts-loader"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
};
|