diff --git a/.travis.yml b/.travis.yml index 236f617..583b095 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - "9.8" + - "11.3" script: - npx nyc npm test after_script: diff --git a/README.md b/README.md index 5d72b96..4d3a0f8 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ # Welcome to `Sane Flags` -`Sane flags` is a small, focused library to add feature flags to your JavaScript application. +`Sane flags` is a small, focused library to add feature flags to your JavaScript or TypeScript application. ## Be Explicit -_You should be able to see all the available feature flags._ +_You should be able to see all the available feature flags that are part of the app!._ Because there might be subtle interactions between multiple flags, its key see what is available at any time. This also allows you to better sunset flags. @@ -37,7 +37,7 @@ var features = saneFlags.wrap({ ### Globally turn a feature on or off When you want to ship a piece of code, without having it really running in production. -This makes super sense if you practice continuous integration. +This is an important technique if you practice continuous integration. Given the following file containing all your features flags: @@ -135,6 +135,9 @@ Every flag MUST have a `description` and an `enabled` key. To use the per-environment configuration of enabled, you MUST declare the available environments in the `environments` key. Failure to do so will throw an error when calling `wrap(config)` to avoid odd behaviour and enforce good practices as far as possible. +If you are using `sane-flags` in TypeScript, you will notice that it comes with proper type definitions that force this same principle: +The type system will not allow you to call `.isEnabled('some-flag')` or `.isDisabled('other-flag')` if those keys don't exist in the `flags` object in the config. + ## Insight diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..3387df2 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,62 @@ +type DirectlyEnabled = boolean +type EnabledEnvironments = { [k: string]: boolean } +export type Flag = { + enabled: DirectlyEnabled | EnabledEnvironments + description: string + name?: string +} + +type CurrentEnvironment = Readonly<{ current: string }> +type AvailableEnvironments = string[] +type Environments = Readonly< + { available: AvailableEnvironments } & CurrentEnvironment +> + +export interface Config { + flags: { [key: string]: Flag } + environments?: Environments + sources?: Source[] +} + +type SourceFn = (flag: Flag) => boolean +type SourceObject = Readonly<{ isEnabled: SourceFn }> +type Source = SourceFn | SourceObject + +type State = Flag[] + +interface TestBox { + enable: (flag: TFlagName) => void + disable: (flag: TFlagName) => void + reset: () => void +} + +type SyncTogglingClosure = ( + name: TFlagName, + closure: () => TResult +) => TResult +type AsyncTogglingClosure = ( + name: TFlagName, + closure: () => Promise +) => Promise + +export interface FeatureFlags< + TConfig extends Config, + TFlagName extends keyof TConfig['flags'] & string +> { + isEnabled: (key: TFlagName) => boolean + summary: () => string + state: () => State + enabling: SyncTogglingClosure + disabling: SyncTogglingClosure + enablingAsync: AsyncTogglingClosure + disablingAsync: AsyncTogglingClosure + testBox: () => TestBox +} +export declare function wrap< + TConfig extends Config, + TFlagName extends keyof TConfig['flags'] & string +>(config: TConfig): FeatureFlags + +export declare namespace sources { + export function processEnvSource(flag: any): boolean +} \ No newline at end of file diff --git a/index.js b/index.js index 9ea3a39..2bc656c 100644 --- a/index.js +++ b/index.js @@ -8,7 +8,7 @@ const errors = { unknown_env_enabled: (flagName, envInFlag, configuredEnvs) => `The feature flag ${flagName} is configured for ${envInFlag} that is not listed in environments.available: ${configuredEnvs}. Please check if this is an error.`, missing_environment: () => - 'You need to configure which environments are available to your application under config.environments.available as an array of strings. This allows us to check your config for consistency' + 'You need to configure which environments are available to your application under config.environments.available as an array of strings. This allows us to check your config for consistency', } const checkConsistency = (config) => { @@ -94,8 +94,8 @@ module.exports = { } return { - isEnabled: function(flagName) { - flag = flags[flagName] + isEnabled: function (flagName) { + const flag = flags[flagName] if (flag) { flag.name = flagName return ( @@ -108,33 +108,33 @@ module.exports = { } }, - state: function() { + state: function () { return Object.keys(flags).map((flagName) => { return { name: flagName, enabled: this.isEnabled(flagName), - description: flags[flagName].description + description: flags[flagName].description, } }) }, - enabling: function(flagName, closure) { + enabling: function (flagName, closure) { return toggle(this, flagName, { to: true, around: closure }) }, - disabling: function(flagName, closure) { + disabling: function (flagName, closure) { return toggle(this, flagName, { to: false, around: closure }) }, - enablingAsync: async function(flagName, closure) { + enablingAsync: async function (flagName, closure) { return toggleAsync(this, flagName, { to: true, around: closure }) }, - disablingAsync: async function(flagName, closure) { + disablingAsync: async function (flagName, closure) { return toggleAsync(this, flagName, { to: false, around: closure }) }, - testBox: function() { + testBox: function () { const features = this let changedFlags = [] @@ -142,7 +142,7 @@ module.exports = { const isEnabled = features.isEnabled(flagName) changedFlags = changedFlags.concat({ flag: flagName, - originalValue: isEnabled + originalValue: isEnabled, }) flags[flagName].enabled = value } @@ -158,15 +158,15 @@ module.exports = { ) changedFlags = [] - } + }, } - } + }, } }, sources: { processEnvSource: (flag) => { const value = process.env[flag.environment_flag] return value === '1' || value === 'true' - } - } + }, + }, } diff --git a/package-lock.json b/package-lock.json index bff2f8b..4172904 100644 --- a/package-lock.json +++ b/package-lock.json @@ -325,12 +325,39 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@types/chai": { + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.12.tgz", + "integrity": "sha512-aN5IAC8QNtSUdQzxu7lGBgYAOuU1tmRU4c9dIq5OKGf/SBVjXo+ffM2wEjudAWbgpOhy60nLoAGH1xm8fpCKFQ==", + "dev": true + }, + "@types/chai-as-promised": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.3.tgz", + "integrity": "sha512-FQnh1ohPXJELpKhzjuDkPLR2BZCAqed+a6xV4MI/T3XzHfd2FlarfUGUdZYgqYe8oxkYn0fchHEeHfHqdZ96sg==", + "dev": true, + "requires": { + "@types/chai": "*" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/mocha": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.0.1.tgz", + "integrity": "sha512-TBZ6YdX7IZz4U9/mBoB8zCMRN1vXw8QdihRcZxD3I0Cv/r8XF8RggZ8WiXFws4aj5atzRR5hJrYer7g8nXwpnQ==", + "dev": true + }, + "@types/node": { + "version": "14.0.27", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.27.tgz", + "integrity": "sha512-kVrqXhbclHNHGu9ztnAwSncIgJv/FaxmzXJvGXNdcCpV1b8u1/Mi6z6m0vwy0LzKeXFTPLH0NzwmoJ3fNCIq0g==", + "dev": true + }, "aggregate-error": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", @@ -399,6 +426,12 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -505,6 +538,12 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, "caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -520,7 +559,8 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true }, "caseless": { "version": "0.12.0", @@ -733,7 +773,8 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, "deep-eql": { "version": "3.0.1", @@ -904,12 +945,13 @@ } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, "flat": { @@ -1211,10 +1253,16 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-regex": { + "is-plain-obj": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "dev": true, "requires": { "has-symbols": "^1.0.1" @@ -1475,21 +1523,14 @@ "dev": true }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^4.1.0" } }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", - "dev": true - }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -1520,6 +1561,12 @@ "semver": "^6.0.0" } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", @@ -1551,9 +1598,9 @@ "dev": true }, "mocha": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.0.1.tgz", - "integrity": "sha512-vefaXfdYI8+Yo8nPZQQi0QO2o+5q9UIMX1jZ1XMmK3+4+CQjc7+B0hPdUeglXiTlr8IHMVRo63IhO9Mzt6fxOg==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz", + "integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==", "dev": true, "requires": { "ansi-colors": "4.1.1", @@ -1572,7 +1619,7 @@ "ms": "2.1.2", "object.assign": "4.1.0", "promise.allsettled": "1.0.2", - "serialize-javascript": "3.0.0", + "serialize-javascript": "4.0.0", "strip-json-comments": "3.0.1", "supports-color": "7.1.0", "which": "2.0.2", @@ -1580,95 +1627,21 @@ "workerpool": "6.0.0", "yargs": "13.3.2", "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" + "yargs-unparser": "1.6.1" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", @@ -1677,79 +1650,6 @@ "requires": { "has-flag": "^4.0.0" } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } } } }, @@ -2014,12 +1914,12 @@ } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^2.2.0" } }, "p-map": { @@ -2050,9 +1950,9 @@ } }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { @@ -2182,6 +2082,15 @@ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "dev": true }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "readdirp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", @@ -2283,10 +2192,13 @@ "dev": true }, "serialize-javascript": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.0.0.tgz", - "integrity": "sha512-skZcHYw2vEX4bw90nAr2iTTsz6x2SrHEnfxgKYmZlvJYBEZrvbKtobJWlQ20zczKb3bsHHXXTYt48zBA7ni9cw==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } }, "set-blocking": { "version": "2.0.0", @@ -2321,6 +2233,24 @@ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -2454,6 +2384,19 @@ "punycode": "^2.1.1" } }, + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2490,6 +2433,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "dev": true + }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -2610,9 +2559,9 @@ "dev": true }, "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { "cliui": "^5.0.0", @@ -2624,7 +2573,7 @@ "string-width": "^3.0.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" + "yargs-parser": "^13.1.2" }, "dependencies": { "ansi-regex": { @@ -2633,6 +2582,40 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -2659,21 +2642,121 @@ "version": "13.1.2", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, "requires": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" } }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.1.tgz", + "integrity": "sha512-qZV14lK9MWsGCmcr7u5oXGH0dbGqZAIxTDrWXZDo5zUr6b6iUmelNKO6x6R1dQT24AH3LgRxJpr8meWy2unolA==", "dev": true, "requires": { + "camelcase": "^5.3.1", + "decamelize": "^1.2.0", "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "is-plain-obj": "^1.1.0", + "yargs": "^14.2.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "yargs-parser": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.1.tgz", + "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index c092453..2299550 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,11 @@ "author": "Felipe Sere ", "license": "MIT", "main": "index.js", + "types": "index.d.ts", "scripts": { - "test": "mocha --require test/setup.js", - "prettier": "prettier --write test/*.js index.js" + "test": "mocha --require test/setup.js && npm run ts-test", + "ts-test": "mocha -r ts-node/register 'test/*.ts'", + "prettier": "prettier --write test/* index.js index.d.ts" }, "prettier": { "semi": false, @@ -22,12 +24,18 @@ }, "dependencies": {}, "devDependencies": { + "@types/chai": "^4.2.12", + "@types/chai-as-promised": "^7.1.3", + "@types/mocha": "^8.0.1", + "@types/node": "^14.0.27", "chai": "^4.2.0", "chai-as-promised": "^7.0.0", "coveralls": "^3.1.0", - "mocha": "^8.0.0", + "mocha": "^8.1.1", "mocha-lcov-reporter": "^1.3.0", "nyc": "^15.0.0", - "prettier": "^2.0.5" + "prettier": "^2.0.5", + "ts-node": "^8.10.2", + "typescript": "^3.4.1" } } diff --git a/test/process-env-source.spec.js b/test/process-env-source.spec.js index c97c501..a80cee8 100644 --- a/test/process-env-source.spec.js +++ b/test/process-env-source.spec.js @@ -7,10 +7,10 @@ describe('the process env source', () => { description: 'a feature which will be activated with a process variable', enabled: false, - environment_flag: 'THIS_IS_THE_FLAG' - } + environment_flag: 'THIS_IS_THE_FLAG', + }, }, - sources: [saneFlags.sources.processEnvSource] + sources: [saneFlags.sources.processEnvSource], } const features = saneFlags.wrap(config) @@ -19,7 +19,7 @@ describe('the process env source', () => { { value: 1, expect_to_be: 'enabled' }, { value: true, expect_to_be: 'enabled' }, { value: 0, expect_to_be: 'disabled' }, - { value: false, expect_to_be: 'disabled' } + { value: false, expect_to_be: 'disabled' }, ] for (const { value, expect_to_be } of settings) { diff --git a/test/sane-flags-typescript.spec.ts b/test/sane-flags-typescript.spec.ts new file mode 100644 index 0000000..c84f906 --- /dev/null +++ b/test/sane-flags-typescript.spec.ts @@ -0,0 +1,222 @@ +import * as saneFlags from '../index.js' +import { expect, use } from 'chai' +import * as chaiAsPromised from 'chai-as-promised' +import { Source } from '../index.js' + +use(chaiAsPromised) + +describe('the sane-flags in typescript', () => { + let config = { + flags: { + dynamic_contact_form: { + description: + 'The new form that fills in form contacts from the current account', + enabled: true, + }, + + disabled_feature: { + description: 'The feature we are working on but have disabled', + enabled: false, + }, + enabled_feature: { + description: 'This is on', + enabled: true, + }, + cool_feature: { + description: 'The feature we are working on but have disabled', + enabled: { + dev: true, + qa: false, + }, + }, + }, + environments: { + available: ['dev', 'qa'], + current: 'qa', + }, + } + it('can check if features are enabled', () => { + let features = saneFlags.wrap(config) + expect(features.isEnabled('dynamic_contact_form')).to.be.true + }) + + it('can check if features are disabled', () => { + let features = saneFlags.wrap(config) + expect(features.isEnabled('disabled_feature')).to.be.false + }) + + describe('supports external sources', () => { + const naiveSource: Source = (flag) => flag.name === 'from_the_naive_source' + + const complexSource: Source = { + isEnabled: (flag) => flag.name === 'from_the_complex_source', + } + + const smallerConfig = { + flags: { + from_the_naive_source: { + description: 'A flag that is enabled by a simple functipon', + enabled: false, + }, + + from_the_complex_source: { + description: 'A flag that is enabled by a complex object', + enabled: false, + }, + }, + sources: [naiveSource, complexSource], + } + + it('as simple functions', () => { + const featuresWithExtraSource = saneFlags.wrap(smallerConfig) + expect(featuresWithExtraSource.isEnabled('from_the_naive_source')).to.be + .true + }) + + it('as objects with an isEnabled function', () => { + const featuresWithExtraSource = saneFlags.wrap(smallerConfig) + expect(featuresWithExtraSource.isEnabled('from_the_complex_source')).to.be + .true + }) + }) + + describe('environments', () => { + const features = saneFlags.wrap({ + flags: { + enabled_in_dev: { + description: 'This feature should only be turned in in development', + enabled: { + dev: true, + }, + }, + enabled_in_qa: { + description: 'This is only on in QA', + enabled: { + dev: false, + qa: true, + }, + }, + always_on: { + description: 'Evergreen feature', + enabled: true, + }, + }, + environments: { + available: ['dev', 'qa'], + current: 'dev', + }, + }) + + it('allow you to have different settings for different environments', () => { + expect(features.isEnabled('enabled_in_dev')).to.be.true + expect(features.isEnabled('always_on')).to.be.true + expect(features.isEnabled('enabled_in_qa')).to.be.false + }) + }) + + it('presents the state of all available features', () => { + const features = saneFlags.wrap(config) + expect(features.state()).to.have.deep.members([ + { + name: 'dynamic_contact_form', + enabled: true, + description: + 'The new form that fills in form contacts from the current account', + }, + { + name: 'disabled_feature', + enabled: false, + description: 'The feature we are working on but have disabled', + }, + { name: 'enabled_feature', enabled: true, description: 'This is on' }, + { + name: 'cool_feature', + enabled: false, + description: 'The feature we are working on but have disabled', + }, + ]) + }) + + describe('supports tests by...', () => { + it('...temporariliy enabling features', () => { + const features = saneFlags.wrap(config) + features.enabling('disabled_feature', () => { + expect(features.isEnabled('disabled_feature')).to.eql(true) + }) + expect(features.isEnabled('disabled_feature')).to.eql(false) + }) + + it('...temporariliy disabling features', () => { + const features = saneFlags.wrap(config) + features.disabling('enabled_feature', () => { + expect(features.isEnabled('enabled_feature')).to.eql(false) + }) + expect(features.isEnabled('enabled_feature')).to.eql(true) + }) + + it('...resetting feature when exceptions happen', () => { + const features = saneFlags.wrap(config) + expect(() => + features.enabling('disabled_feature', () => { + throw Error('this should not have happend') + }) + ).to.throw() + expect(features.isEnabled('disabled_feature')).to.eql(false) + }) + + it('...resetting features when exceptions happen async code', async () => { + const features = saneFlags.wrap(config) + await expect( + features.enablingAsync('disabled_feature', () => { + throw new Error('this shoudl bubble up') + }) + ).to.eventually.be.rejected + + expect(features.isEnabled('disabled_feature')).to.eql(false) + }) + + it('...temporarily enabling features around async functions', async () => { + const features = saneFlags.wrap(config) + let someFunctionHere = async () => features.isEnabled('disabled_feature') + + const wasItEnabled = await expect( + features.enablingAsync('disabled_feature', async () => { + return await someFunctionHere() + }) + ).to.eventually.be.fulfilled + + expect(wasItEnabled).to.eql(true) + expect(features.isEnabled('disabled_feature')).to.eql(false) + }) + + it('...temporarily disabling features around async functions', async () => { + const features = saneFlags.wrap(config) + let someFunctionHere = async () => features.isEnabled('enabled_feature') + + const wasItEnabled = await features.disablingAsync( + 'enabled_feature', + async () => { + return await someFunctionHere() + } + ) + + expect(wasItEnabled).to.eql(false) + expect(features.isEnabled('enabled_feature')).to.eql(true) + }) + + it('...allowing you to enable and disable multiple features at once', () => { + const features = saneFlags.wrap(config) + const box = features.testBox() + box.enable('disabled_feature') + box.disable('enabled_feature') + + expect(features.isEnabled('disabled_feature')).to.eql(true) + expect(features.isEnabled('enabled_feature')).to.eql(false) + + box.reset() + + expect(features.isEnabled('disabled_feature')).to.eql(false) + expect(features.isEnabled('enabled_feature')).to.eql(true) + }) + }) +}) diff --git a/test/sane-flags.spec.js b/test/sane-flags.spec.js index 6e1c6e6..e152b37 100644 --- a/test/sane-flags.spec.js +++ b/test/sane-flags.spec.js @@ -9,29 +9,29 @@ describe('the sane flags', () => { dynamic_contact_form: { description: 'The new form that fills in form contacts from the current account', - enabled: true + enabled: true, }, disabled_feature: { description: 'The feature we are working on but have disabled', - enabled: false + enabled: false, }, enabled_feature: { description: 'This is on', - enabled: true + enabled: true, }, cool_feature: { description: 'The feature we are working on but have disabled', enabled: { dev: true, - qa: false - } - } + qa: false, + }, + }, }, environments: { available: ['dev', 'qa'], - current: 'qa' - } + current: 'qa', + }, }) }) @@ -57,7 +57,7 @@ describe('the sane flags', () => { const naiveSource = (flag) => flag.name === 'from_the_naive_source' const complexSource = { - isEnabled: (flag) => flag.name === 'from_the_complex_source' + isEnabled: (flag) => flag.name === 'from_the_complex_source', } beforeEach(() => { @@ -65,15 +65,15 @@ describe('the sane flags', () => { flags: { from_the_naive_source: { description: 'A flag that is enabled by a simple functipon', - enabled: false + enabled: false, }, from_the_complex_source: { description: 'A flag that is enabled by a complex object', - enabled: false - } + enabled: false, + }, }, - sources: [naiveSource, complexSource] + sources: [naiveSource, complexSource], }) }) @@ -94,25 +94,25 @@ describe('the sane flags', () => { enabled_in_dev: { description: 'This feature should only be turned in in development', enabled: { - dev: true - } + dev: true, + }, }, enabled_in_qa: { description: 'This is only on in QA', enabled: { dev: false, - qa: true - } + qa: true, + }, }, always_on: { description: 'Evergreen feature', - enabled: true - } + enabled: true, + }, }, environments: { available: ['dev', 'qa'], - current: 'dev' - } + current: 'dev', + }, }) it('allow you to have different settings for different environments', () => { @@ -127,9 +127,9 @@ describe('the sane flags', () => { const config = { flags: { has_no_description: { - enabled: true - } - } + enabled: true, + }, + }, } expect(() => saneFlags.wrap(config)).to.throw('has_no_description') @@ -139,9 +139,9 @@ describe('the sane flags', () => { const config = { flags: { is_it_enabled: { - description: 'Is this feature really on?' - } - } + description: 'Is this feature really on?', + }, + }, } expect(() => saneFlags.wrap(config)).to.throw('is_it_enabled') @@ -153,10 +153,10 @@ describe('the sane flags', () => { anything: { description: 'We dont know odd or dev yet', enabled: { - dev: true - } - } - } + dev: true, + }, + }, + }, } expect(() => saneFlags.wrap(config)).to.throw( @@ -170,13 +170,13 @@ describe('the sane flags', () => { anything: { description: 'We dont know odd or dev yet', enabled: { - odd: true - } - } + odd: true, + }, + }, }, environments: { - available: ['dev'] - } + available: ['dev'], + }, } expect(() => saneFlags.wrap(config)).to.throw('anything') @@ -189,19 +189,19 @@ describe('the sane flags', () => { name: 'dynamic_contact_form', enabled: true, description: - 'The new form that fills in form contacts from the current account' + 'The new form that fills in form contacts from the current account', }, { name: 'disabled_feature', enabled: false, - description: 'The feature we are working on but have disabled' + description: 'The feature we are working on but have disabled', }, { name: 'enabled_feature', enabled: true, description: 'This is on' }, { name: 'cool_feature', enabled: false, - description: 'The feature we are working on but have disabled' - } + description: 'The feature we are working on but have disabled', + }, ]) }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9ca3fe4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "alwaysStrict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noEmit": true, + "checkJs": false, + "allowJs": true + }, + "include": [ + "test/**/*.ts" + ] +}