@ops-ai/feature-flags-toggly 1.0.1 → 1.0.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 +3 -3
- package/dist/feature-flags-toggly.bundle.js +1 -0
- package/package.json +5 -1
- package/CHANGELOG.md +0 -13
- package/example/demo-app-screenshot.png +0 -0
- package/example/index.html +0 -65
- package/example/toggly-logo.png +0 -0
- package/jest.config.js +0 -5
- package/lib/models/feature-requirement.ts +0 -4
- package/lib/models/index.ts +0 -4
- package/lib/models/storage-keys.ts +0 -4
- package/lib/models/toggly-config.ts +0 -11
- package/lib/models/toggly-init-response.ts +0 -14
- package/lib/toggly.ts +0 -150
- package/spec/toggly.spec.ts +0 -33
- package/tsconfig.json +0 -8
- package/webpack.config.js +0 -23
package/README.md
CHANGED
|
@@ -13,17 +13,17 @@ In agile settings the feature flag is used in production, to switch on the featu
|
|
|
13
13
|
Simply embed our latest bundle from the following CDN.
|
|
14
14
|
|
|
15
15
|
```html
|
|
16
|
-
<script src="
|
|
16
|
+
<script src="https://cdn.jsdelivr.net/npm/@ops-ai/feature-flags-toggly@1.0.2/dist/feature-flags-toggly.bundle.js"></script>
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
Alternatively, you can use NPM to manually build the bundled *.js file.
|
|
20
20
|
|
|
21
21
|
```shell
|
|
22
|
-
$ npm
|
|
22
|
+
$ npm install @ops-ai/feature-flags-toggly
|
|
23
23
|
$ cd node_modules/@ops-ai/feature-flags-toggly && npm run build
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
And then grab the generated bundled file from the
|
|
26
|
+
And then grab the generated bundled file from the ./dist directory.
|
|
27
27
|
|
|
28
28
|
## Basic Usage (with Toggly.io)
|
|
29
29
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{"use strict";var e,t,r,n={46:(e,t)=>{var r;Object.defineProperty(t,"__esModule",{value:!0}),t.FeatureRequirement=void 0,(r=t.FeatureRequirement||(t.FeatureRequirement={}))[r.all=0]="all",r[r.any=1]="any"},898:function(e,t,r){var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r);var o=Object.getOwnPropertyDescriptor(t,r);o&&!("get"in o?!t.__esModule:o.writable||o.configurable)||(o={enumerable:!0,get:function(){return t[r]}}),Object.defineProperty(e,n,o)}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),o=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),o(r(286),t),o(r(389),t),o(r(46),t),o(r(433),t)},433:(e,t)=>{var r;Object.defineProperty(t,"__esModule",{value:!0}),t.StorageKeys=void 0,(r=t.StorageKeys||(t.StorageKeys={}))[r.togglyIdentityKey=0]="togglyIdentityKey",r[r.togglyFeatureFlagsKey=1]="togglyFeatureFlagsKey"},286:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0})},389:(e,t)=>{var r;Object.defineProperty(t,"__esModule",{value:!0}),t.TogglyInitResponse=t.TogglyLoadFeatureFlagsResponse=void 0,(r=t.TogglyLoadFeatureFlagsResponse||(t.TogglyLoadFeatureFlagsResponse={}))[r.fetched=0]="fetched",r[r.cached=1]="cached",r[r.defaults=2]="defaults",r[r.error=3]="error";t.TogglyInitResponse=function(e){this.status=e}},429:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),Object.defineProperty(t,"NIL",{enumerable:!0,get:function(){return l.default}}),Object.defineProperty(t,"parse",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(t,"stringify",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(t,"v1",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(t,"v3",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"v4",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(t,"v5",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"validate",{enumerable:!0,get:function(){return f.default}}),Object.defineProperty(t,"version",{enumerable:!0,get:function(){return i.default}});var n=d(r(990)),o=d(r(237)),a=d(r(355)),u=d(r(764)),l=d(r(314)),i=d(r(464)),f=d(r(435)),c=d(r(8)),s=d(r(222));function d(e){return e&&e.__esModule?e:{default:e}}},163:(e,t)=>{function r(e){return 14+(e+64>>>9<<4)+1}function n(e,t){const r=(65535&e)+(65535&t);return(e>>16)+(t>>16)+(r>>16)<<16|65535&r}function o(e,t,r,o,a,u){return n((l=n(n(t,e),n(o,u)))<<(i=a)|l>>>32-i,r);var l,i}function a(e,t,r,n,a,u,l){return o(t&r|~t&n,e,t,a,u,l)}function u(e,t,r,n,a,u,l){return o(t&n|r&~n,e,t,a,u,l)}function l(e,t,r,n,a,u,l){return o(t^r^n,e,t,a,u,l)}function i(e,t,r,n,a,u,l){return o(r^(t|~n),e,t,a,u,l)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default=function(e){if("string"==typeof e){const t=unescape(encodeURIComponent(e));e=new Uint8Array(t.length);for(let r=0;r<t.length;++r)e[r]=t.charCodeAt(r)}return function(e){const t=[],r=32*e.length,n="0123456789abcdef";for(let o=0;o<r;o+=8){const r=e[o>>5]>>>o%32&255,a=parseInt(n.charAt(r>>>4&15)+n.charAt(15&r),16);t.push(a)}return t}(function(e,t){e[t>>5]|=128<<t%32,e[r(t)-1]=t;let o=1732584193,f=-271733879,c=-1732584194,s=271733878;for(let t=0;t<e.length;t+=16){const r=o,d=f,g=c,y=s;o=a(o,f,c,s,e[t],7,-680876936),s=a(s,o,f,c,e[t+1],12,-389564586),c=a(c,s,o,f,e[t+2],17,606105819),f=a(f,c,s,o,e[t+3],22,-1044525330),o=a(o,f,c,s,e[t+4],7,-176418897),s=a(s,o,f,c,e[t+5],12,1200080426),c=a(c,s,o,f,e[t+6],17,-1473231341),f=a(f,c,s,o,e[t+7],22,-45705983),o=a(o,f,c,s,e[t+8],7,1770035416),s=a(s,o,f,c,e[t+9],12,-1958414417),c=a(c,s,o,f,e[t+10],17,-42063),f=a(f,c,s,o,e[t+11],22,-1990404162),o=a(o,f,c,s,e[t+12],7,1804603682),s=a(s,o,f,c,e[t+13],12,-40341101),c=a(c,s,o,f,e[t+14],17,-1502002290),f=a(f,c,s,o,e[t+15],22,1236535329),o=u(o,f,c,s,e[t+1],5,-165796510),s=u(s,o,f,c,e[t+6],9,-1069501632),c=u(c,s,o,f,e[t+11],14,643717713),f=u(f,c,s,o,e[t],20,-373897302),o=u(o,f,c,s,e[t+5],5,-701558691),s=u(s,o,f,c,e[t+10],9,38016083),c=u(c,s,o,f,e[t+15],14,-660478335),f=u(f,c,s,o,e[t+4],20,-405537848),o=u(o,f,c,s,e[t+9],5,568446438),s=u(s,o,f,c,e[t+14],9,-1019803690),c=u(c,s,o,f,e[t+3],14,-187363961),f=u(f,c,s,o,e[t+8],20,1163531501),o=u(o,f,c,s,e[t+13],5,-1444681467),s=u(s,o,f,c,e[t+2],9,-51403784),c=u(c,s,o,f,e[t+7],14,1735328473),f=u(f,c,s,o,e[t+12],20,-1926607734),o=l(o,f,c,s,e[t+5],4,-378558),s=l(s,o,f,c,e[t+8],11,-2022574463),c=l(c,s,o,f,e[t+11],16,1839030562),f=l(f,c,s,o,e[t+14],23,-35309556),o=l(o,f,c,s,e[t+1],4,-1530992060),s=l(s,o,f,c,e[t+4],11,1272893353),c=l(c,s,o,f,e[t+7],16,-155497632),f=l(f,c,s,o,e[t+10],23,-1094730640),o=l(o,f,c,s,e[t+13],4,681279174),s=l(s,o,f,c,e[t],11,-358537222),c=l(c,s,o,f,e[t+3],16,-722521979),f=l(f,c,s,o,e[t+6],23,76029189),o=l(o,f,c,s,e[t+9],4,-640364487),s=l(s,o,f,c,e[t+12],11,-421815835),c=l(c,s,o,f,e[t+15],16,530742520),f=l(f,c,s,o,e[t+2],23,-995338651),o=i(o,f,c,s,e[t],6,-198630844),s=i(s,o,f,c,e[t+7],10,1126891415),c=i(c,s,o,f,e[t+14],15,-1416354905),f=i(f,c,s,o,e[t+5],21,-57434055),o=i(o,f,c,s,e[t+12],6,1700485571),s=i(s,o,f,c,e[t+3],10,-1894986606),c=i(c,s,o,f,e[t+10],15,-1051523),f=i(f,c,s,o,e[t+1],21,-2054922799),o=i(o,f,c,s,e[t+8],6,1873313359),s=i(s,o,f,c,e[t+15],10,-30611744),c=i(c,s,o,f,e[t+6],15,-1560198380),f=i(f,c,s,o,e[t+13],21,1309151649),o=i(o,f,c,s,e[t+4],6,-145523070),s=i(s,o,f,c,e[t+11],10,-1120210379),c=i(c,s,o,f,e[t+2],15,718787259),f=i(f,c,s,o,e[t+9],21,-343485551),o=n(o,r),f=n(f,d),c=n(c,g),s=n(s,y)}return[o,f,c,s]}(function(e){if(0===e.length)return[];const t=8*e.length,n=new Uint32Array(r(t));for(let r=0;r<t;r+=8)n[r>>5]|=(255&e[r/8])<<r%32;return n}(e),8*e.length))}},790:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};t.default=r},314:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,t.default="00000000-0000-0000-0000-000000000000"},222:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,o=(n=r(435))&&n.__esModule?n:{default:n};t.default=function(e){if(!(0,o.default)(e))throw TypeError("Invalid UUID");let t;const r=new Uint8Array(16);return r[0]=(t=parseInt(e.slice(0,8),16))>>>24,r[1]=t>>>16&255,r[2]=t>>>8&255,r[3]=255&t,r[4]=(t=parseInt(e.slice(9,13),16))>>>8,r[5]=255&t,r[6]=(t=parseInt(e.slice(14,18),16))>>>8,r[7]=255&t,r[8]=(t=parseInt(e.slice(19,23),16))>>>8,r[9]=255&t,r[10]=(t=parseInt(e.slice(24,36),16))/1099511627776&255,r[11]=t/4294967296&255,r[12]=t>>>24&255,r[13]=t>>>16&255,r[14]=t>>>8&255,r[15]=255&t,r}},58:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,t.default=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i},319:(e,t)=>{let r;Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){if(!r&&(r="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!r))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return r(n)};const n=new Uint8Array(16)},757:(e,t)=>{function r(e,t,r,n){switch(e){case 0:return t&r^~t&n;case 1:case 3:return t^r^n;case 2:return t&r^t&n^r&n}}function n(e,t){return e<<t|e>>>32-t}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default=function(e){const t=[1518500249,1859775393,2400959708,3395469782],o=[1732584193,4023233417,2562383102,271733878,3285377520];if("string"==typeof e){const t=unescape(encodeURIComponent(e));e=[];for(let r=0;r<t.length;++r)e.push(t.charCodeAt(r))}else Array.isArray(e)||(e=Array.prototype.slice.call(e));e.push(128);const a=e.length/4+2,u=Math.ceil(a/16),l=new Array(u);for(let t=0;t<u;++t){const r=new Uint32Array(16);for(let n=0;n<16;++n)r[n]=e[64*t+4*n]<<24|e[64*t+4*n+1]<<16|e[64*t+4*n+2]<<8|e[64*t+4*n+3];l[t]=r}l[u-1][14]=8*(e.length-1)/Math.pow(2,32),l[u-1][14]=Math.floor(l[u-1][14]),l[u-1][15]=8*(e.length-1)&4294967295;for(let e=0;e<u;++e){const a=new Uint32Array(80);for(let t=0;t<16;++t)a[t]=l[e][t];for(let e=16;e<80;++e)a[e]=n(a[e-3]^a[e-8]^a[e-14]^a[e-16],1);let u=o[0],i=o[1],f=o[2],c=o[3],s=o[4];for(let e=0;e<80;++e){const o=Math.floor(e/20),l=n(u,5)+r(o,i,f,c)+s+t[o]+a[e]>>>0;s=c,c=f,f=n(i,30)>>>0,i=u,u=l}o[0]=o[0]+u>>>0,o[1]=o[1]+i>>>0,o[2]=o[2]+f>>>0,o[3]=o[3]+c>>>0,o[4]=o[4]+s>>>0}return[o[0]>>24&255,o[0]>>16&255,o[0]>>8&255,255&o[0],o[1]>>24&255,o[1]>>16&255,o[1]>>8&255,255&o[1],o[2]>>24&255,o[2]>>16&255,o[2]>>8&255,255&o[2],o[3]>>24&255,o[3]>>16&255,o[3]>>8&255,255&o[3],o[4]>>24&255,o[4]>>16&255,o[4]>>8&255,255&o[4]]}},8:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0,t.unsafeStringify=u;var n,o=(n=r(435))&&n.__esModule?n:{default:n};const a=[];for(let e=0;e<256;++e)a.push((e+256).toString(16).slice(1));function u(e,t=0){return(a[e[t+0]]+a[e[t+1]]+a[e[t+2]]+a[e[t+3]]+"-"+a[e[t+4]]+a[e[t+5]]+"-"+a[e[t+6]]+a[e[t+7]]+"-"+a[e[t+8]]+a[e[t+9]]+"-"+a[e[t+10]]+a[e[t+11]]+a[e[t+12]]+a[e[t+13]]+a[e[t+14]]+a[e[t+15]]).toLowerCase()}t.default=function(e,t=0){const r=u(e,t);if(!(0,o.default)(r))throw TypeError("Stringified UUID is invalid");return r}},990:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,o=(n=r(319))&&n.__esModule?n:{default:n},a=r(8);let u,l,i=0,f=0;t.default=function(e,t,r){let n=t&&r||0;const c=t||new Array(16);let s=(e=e||{}).node||u,d=void 0!==e.clockseq?e.clockseq:l;if(null==s||null==d){const t=e.random||(e.rng||o.default)();null==s&&(s=u=[1|t[0],t[1],t[2],t[3],t[4],t[5]]),null==d&&(d=l=16383&(t[6]<<8|t[7]))}let g=void 0!==e.msecs?e.msecs:Date.now(),y=void 0!==e.nsecs?e.nsecs:f+1;const v=g-i+(y-f)/1e4;if(v<0&&void 0===e.clockseq&&(d=d+1&16383),(v<0||g>i)&&void 0===e.nsecs&&(y=0),y>=1e4)throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");i=g,f=y,l=d,g+=122192928e5;const p=(1e4*(268435455&g)+y)%4294967296;c[n++]=p>>>24&255,c[n++]=p>>>16&255,c[n++]=p>>>8&255,c[n++]=255&p;const _=g/4294967296*1e4&268435455;c[n++]=_>>>8&255,c[n++]=255&_,c[n++]=_>>>24&15|16,c[n++]=_>>>16&255,c[n++]=d>>>8|128,c[n++]=255&d;for(let e=0;e<6;++e)c[n+e]=s[e];return t||(0,a.unsafeStringify)(c)}},237:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=a(r(925)),o=a(r(163));function a(e){return e&&e.__esModule?e:{default:e}}var u=(0,n.default)("v3",48,o.default);t.default=u},925:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.URL=t.DNS=void 0,t.default=function(e,t,r){function n(e,n,u,l){var i;if("string"==typeof e&&(e=function(e){e=unescape(encodeURIComponent(e));const t=[];for(let r=0;r<e.length;++r)t.push(e.charCodeAt(r));return t}(e)),"string"==typeof n&&(n=(0,a.default)(n)),16!==(null===(i=n)||void 0===i?void 0:i.length))throw TypeError("Namespace must be array-like (16 iterable integer values, 0-255)");let f=new Uint8Array(16+e.length);if(f.set(n),f.set(e,n.length),f=r(f),f[6]=15&f[6]|t,f[8]=63&f[8]|128,u){l=l||0;for(let e=0;e<16;++e)u[l+e]=f[e];return u}return(0,o.unsafeStringify)(f)}try{n.name=e}catch(e){}return n.DNS=u,n.URL=l,n};var n,o=r(8),a=(n=r(222))&&n.__esModule?n:{default:n};const u="6ba7b810-9dad-11d1-80b4-00c04fd430c8";t.DNS=u;const l="6ba7b811-9dad-11d1-80b4-00c04fd430c8";t.URL=l},355:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=u(r(790)),o=u(r(319)),a=r(8);function u(e){return e&&e.__esModule?e:{default:e}}t.default=function(e,t,r){if(n.default.randomUUID&&!t&&!e)return n.default.randomUUID();const u=(e=e||{}).random||(e.rng||o.default)();if(u[6]=15&u[6]|64,u[8]=63&u[8]|128,t){r=r||0;for(let e=0;e<16;++e)t[r+e]=u[e];return t}return(0,a.unsafeStringify)(u)}},764:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n=a(r(925)),o=a(r(757));function a(e){return e&&e.__esModule?e:{default:e}}var u=(0,n.default)("v5",80,o.default);t.default=u},435:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,o=(n=r(58))&&n.__esModule?n:{default:n};t.default=function(e){return"string"==typeof e&&o.default.test(e)}},464:(e,t,r)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var n,o=(n=r(435))&&n.__esModule?n:{default:n};t.default=function(e){if(!(0,o.default)(e))throw TypeError("Invalid UUID");return parseInt(e.slice(14,15),16)}}},o={};function a(e){var t=o[e];if(void 0!==t)return t.exports;var r=o[e]={exports:{}};return n[e].call(r.exports,r,r.exports,a),r.exports}e=a(429),t=a(898),r=function(){function r(){}return r.init=function(t){return void 0===t&&(t={}),r._config=Object.assign({baseURI:"https://client.toggly.io",reloadOnFeatureFlagValidation:!1,connectTimeout:5e3,featureFlagsRefreshInterval:18e4,isDebug:!1,environment:"Production",flagDefaults:{}},t),r.identity||(r.identity=(0,e.v4)()),r.clearFeatureFlagsCache(),r.startRefreshInterval(),r.refresh()},Object.defineProperty(r,"featureFlagsValue",{get:function(){var e,n,o,a,u=JSON.parse(null!==(e=localStorage.getItem(t.StorageKeys.togglyFeatureFlagsKey.toString()))&&void 0!==e?e:null);return(null===(n=r._config)||void 0===n?void 0:n.appKey)&&u?u:null!==(a=null===(o=r._config)||void 0===o?void 0:o.flagDefaults)&&void 0!==a?a:{}},enumerable:!1,configurable:!0}),Object.defineProperty(r,"identity",{get:function(){return localStorage.getItem(t.StorageKeys.togglyIdentityKey.toString())},set:function(e){localStorage.setItem(t.StorageKeys.togglyIdentityKey.toString(),e)},enumerable:!1,configurable:!0}),r.clearIdentity=function(){localStorage.removeItem(t.StorageKeys.togglyIdentityKey.toString())},Object.defineProperty(r,"_cachedFeatureFlags",{get:function(){var e;return JSON.parse(null!==(e=localStorage.getItem(t.StorageKeys.togglyFeatureFlagsKey.toString()))&&void 0!==e?e:null)},enumerable:!1,configurable:!0}),r.cacheFeatureFlags=function(e){localStorage.setItem(t.StorageKeys.togglyFeatureFlagsKey.toString(),JSON.stringify(e))},r.clearFeatureFlagsCache=function(){localStorage.removeItem(t.StorageKeys.togglyFeatureFlagsKey.toString())},r.fetchFeatureFlags=function(){return new Promise((function(e,t){var n="".concat(r._config.baseURI,"/").concat(r._config.appKey,"-").concat(r._config.environment,"/defs");r.identity&&(n+="?u=".concat(r.identity)),fetch(n).then((function(e){return e.json()})).then((function(t){r.cacheFeatureFlags(t),e(t),r._config.isDebug&&console.log("Toggly.fetchFeatureFlags - ".concat(JSON.stringify(t)))})).catch((function(t){var n,o=null!==(n=r._cachedFeatureFlags)&&void 0!==n?n:r._config.flagDefaults;e(o),r._config.isDebug&&console.log("Toggly.loadedFromCache - ".concat(JSON.stringify(o)))}))}))},r.refresh=function(){return r._config.isDebug&&console.log("Toggly.refresh"),r._config.appKey?r.fetchFeatureFlags():(r._config.isDebug&&console.log("Toggly.usedFlagDefaults - ".concat(JSON.stringify(r._config.flagDefaults))),new Promise((function(e,t){e(r._config.flagDefaults)})))},r._evaluateFeatureGate=function(e,n,o,a){var u;return void 0===e&&(e={}),void 0===o&&(o=t.FeatureRequirement.all),void 0===a&&(a=!1),u=o===t.FeatureRequirement.any?n.reduce((function(t,r){return t||e[r]&&!0===e[r]}),!1):n.reduce((function(t,r){return t&&e[r]&&!0===e[r]}),!0),r._config.isDebug&&console.log("Toggly._evaluateFeatureGate - ".concat(JSON.stringify(n))),a?!u:u},r.evaluateFeatureGate=function(e,n,o){return void 0===n&&(n=t.FeatureRequirement.all),void 0===o&&(o=!1),r._evaluateFeatureGate(r.featureFlagsValue,e,n,o)},r.isFeatureOn=function(e){return r._evaluateFeatureGate(r.featureFlagsValue,[e])},r.isFeatureOff=function(e){return r._evaluateFeatureGate(r.featureFlagsValue,[e],t.FeatureRequirement.all,!0)},r.cancelRefreshInterval=function(){window.clearInterval(r._refreshInterval),r._refreshInterval=void 0},r.startRefreshInterval=function(){r.cancelRefreshInterval(),r._config.appKey&&r._config.featureFlagsRefreshInterval>0&&(r._refreshInterval=window.setInterval((function(){return r.refresh()}),r._config.featureFlagsRefreshInterval))},r}(),window.Toggly=r})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ops-ai/feature-flags-toggly",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
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
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
"url": "https://github.com/ops-ai/Toggly.FeatureManagement/issues"
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/ops-ai/Toggly.FeatureManagement/tree/develop#readme",
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
24
27
|
"dependencies": {
|
|
28
|
+
"rxjs": "^7.5.7",
|
|
25
29
|
"uuid": "^9.0.0"
|
|
26
30
|
},
|
|
27
31
|
"devDependencies": {
|
package/CHANGELOG.md
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
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
|
-
|
|
Binary file
|
package/example/index.html
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
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>
|
package/example/toggly-logo.png
DELETED
|
Binary file
|
package/jest.config.js
DELETED
package/lib/models/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
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
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
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/spec/toggly.spec.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
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
DELETED
package/webpack.config.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
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
|
-
};
|