├── README.md └── featureflagTS ├── wm-feature-flag-client ├── tsconfig.json ├── README.md ├── package.json ├── config │ └── app-a.json ├── lib │ ├── index.d.ts │ └── index.js ├── src │ └── index.ts └── package-lock.json └── testapp ├── package.json ├── package-lock.json └── index.js /README.md: -------------------------------------------------------------------------------- 1 | # ff-poc-ts -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "outDir": "./lib", 7 | "strict": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "**/__tests__/*"] 11 | } -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/README.md: -------------------------------------------------------------------------------- 1 | This library contains feature flagging functionality for determining feature availability across various platforms fo specific users. 2 | 3 | ## Installing 4 | To add the latset published version of this package to your application: 5 | 6 | ```bash 7 | npm install feature-flag-client 8 | ``` 9 | 10 | ## Setup 11 | TODO -------------------------------------------------------------------------------- /featureflagTS/testapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testapp", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "dependencies": { 10 | "wm-feature-flag-client": "file:../wm-feature-flag-client" 11 | }, 12 | "author": "", 13 | "license": "ISC" 14 | } 15 | -------------------------------------------------------------------------------- /featureflagTS/testapp/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testapp", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "wm-feature-flag-client": { 8 | "version": "file:../wm-feature-flag-client", 9 | "requires": { 10 | "@types/node": "^13.9.2", 11 | "@types/uuid": "^7.0.2", 12 | "uuid": "^7.0.2", 13 | "winston": "^3.2.1" 14 | } 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wm-feature-flag-client", 3 | "author": "Warner Media", 4 | "version": "1.0.0", 5 | "description": "A client library for determining the availability of certain platform features for a given user.", 6 | "license": "UNLICENSED", 7 | "private": true, 8 | "main": "./lib/index.js", 9 | "scripts": { 10 | "build": "tsc", 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "dependencies": { 14 | "@types/node": "^13.9.2", 15 | "@types/uuid": "^7.0.2", 16 | "uuid": "^7.0.2", 17 | "winston": "^3.2.1" 18 | }, 19 | "devDependencies": { 20 | "typescript": "^3.8.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/config/app-a.json: -------------------------------------------------------------------------------- 1 | { 2 | "appId": "app-A", 3 | "features": [ 4 | { 5 | "name": "feature-A", 6 | "featureId": "5159095521", 7 | "saltKey": "7302928630", 8 | "rolloutRate": "20" 9 | }, 10 | { 11 | "name": "feature-B", 12 | "featureId": "3735236558", 13 | "saltKey": "0961869093", 14 | "rolloutRate": "70" 15 | }, 16 | { 17 | "name": "feature-C", 18 | "featureId": "0961869093", 19 | "saltKey": "3735236558", 20 | "rolloutRate": "50" 21 | }, 22 | { 23 | "name": "feature-D", 24 | "featureId": "7302928630", 25 | "saltKey": "5159095521", 26 | "rolloutRate": "100" 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /featureflagTS/testapp/index.js: -------------------------------------------------------------------------------- 1 | const ffmodule = require('wm-feature-flag-client') 2 | 3 | const init = async () => { 4 | const userId = '123' 5 | const appId = 'app-a' 6 | 7 | // initiate feature flag client 8 | const client = new ffmodule.FeatureFlagClient(userId, appId) 9 | await client.init() 10 | 11 | // check user access to two features 12 | // const featureA = await client.queryFeatureFlag('feature-A') 13 | // const featureB = await client.queryFeatureFlag('feature-B') 14 | 15 | const featuresA = await client.queryAllFeatureFlags() 16 | 17 | // log results 18 | console.log('\nResults') 19 | // console.log(`Feature A is enabled: ${featureA.enabled}`) 20 | // console.log(`Feature B is enabled: ${featureB.enabled}`) 21 | console.log(featuresA) 22 | } 23 | 24 | init() 25 | 26 | -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/lib/index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module wm-feature-flag-client 3 | */ 4 | /** 5 | * Copyright (c) Warner Media. All rights reserved. 6 | */ 7 | interface IQueryFeatureResult { 8 | featureName: string; 9 | enabled: boolean; 10 | userId: string; 11 | } 12 | interface IFeatureFlagClient { 13 | appId: string; 14 | config: any; 15 | userId: string; 16 | createHash(userId: string, salt: string): string; 17 | createUserId(): string; 18 | getUserFeatureIndex(hash: string): string; 19 | init(): void; 20 | loadConfig(): void; 21 | queryFeature(featureName: string): any; 22 | queryAllFeatures(): any; 23 | } 24 | export declare class FeatureFlagClient implements IFeatureFlagClient { 25 | appId: string; 26 | config: any; 27 | userId: string; 28 | constructor(userId: string, appId: string, config: any); 29 | createHash(userId: string, salt: string): string; 30 | createUserId(): string; 31 | getUserFeatureIndex(hash: string): string; 32 | init(): Promise; 33 | loadConfig(): Promise; 34 | queryFeature(featureName: string): IQueryFeatureResult; 35 | queryAllFeatures(): any; 36 | } 37 | export {}; 38 | -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module wm-feature-flag-client 3 | */ 4 | /** 5 | * Copyright (c) Warner Media. All rights reserved. 6 | */ 7 | 8 | 'use strict' 9 | import * as crypto from 'crypto' 10 | import { v4 as uuidv4 } from 'uuid' 11 | import * as winston from 'winston' 12 | 13 | const logger = winston.createLogger({ 14 | transports: [ new winston.transports.Console() ] 15 | }) 16 | 17 | interface IQueryFeatureResult { 18 | featureName: string 19 | enabled: boolean 20 | userId: string 21 | } 22 | 23 | interface IFeatureFlagClient { 24 | appId: string 25 | config: any 26 | userId: string 27 | createHash(userId: string, salt: string): string; 28 | createUserId(): string; 29 | getUserFeatureIndex(hash: string): string 30 | init(): void 31 | loadConfig(): void 32 | queryFeatureFlag(featureName: string): any 33 | queryAllFeatureFlags(): any 34 | } 35 | 36 | export class FeatureFlagClient implements IFeatureFlagClient { 37 | appId: string 38 | config: any 39 | userId: string 40 | 41 | constructor(userId: string, appId: string, config: any) { 42 | this.appId = appId 43 | this.config = config 44 | this.userId = userId 45 | } 46 | 47 | createHash(userId: string, salt: string) { 48 | const hash = crypto.createHmac('sha256', salt) 49 | hash.update(userId) 50 | const hashValue = hash.digest('hex') 51 | return hashValue 52 | } 53 | 54 | createUserId() { 55 | const userId = uuidv4() 56 | return userId 57 | } 58 | 59 | getUserFeatureIndex(hash: string) { 60 | const hashSegment = parseInt(hash.slice(-2), 16) 61 | return hashSegment.toString().slice(-2) 62 | } 63 | 64 | async init() { 65 | this.config = this.config ? this.config : await this.loadConfig() 66 | this.userId = this.userId ? this.userId : this.createUserId() 67 | } 68 | 69 | async loadConfig() { 70 | // placeholder for fetching of the app's feature config file from AWS S3 71 | return new Promise((resolve, reject) => { 72 | const testConfig = require('../config/app-a.json') 73 | setTimeout(() => resolve(testConfig), 300) 74 | }) 75 | } 76 | 77 | queryFeatureFlag(featureName: string): IQueryFeatureResult { 78 | const featureConfig = this.config.features.filter((feature: any) => feature.name === featureName) 79 | const saltKey = featureConfig[0].saltKey 80 | const hash = this.createHash(this.userId, saltKey) 81 | const userFeatureIndex = this.getUserFeatureIndex(hash) 82 | const enabled = (parseInt(userFeatureIndex, 10) < featureConfig[0].rolloutRate) ? true : false 83 | 84 | // logging for demo only 85 | console.log(`\n${featureConfig[0].name}`) 86 | console.log(`rollout rate: ${featureConfig[0].rolloutRate}`) 87 | console.log(`user feature index: ${userFeatureIndex}`) 88 | 89 | return { 90 | featureName: featureConfig[0].name, 91 | enabled: enabled, 92 | userId: this.userId 93 | } 94 | } 95 | 96 | queryAllFeatureFlags() { 97 | const featureFlagResultsMap: any = [] 98 | this.config.features.map((feature: any) => { 99 | const featureFlagData = this.queryFeatureFlag(feature.name) 100 | featureFlagResultsMap.push(featureFlagData) 101 | }) 102 | return featureFlagResultsMap 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module wm-feature-flag-client 3 | */ 4 | /** 5 | * Copyright (c) Warner Media. All rights reserved. 6 | */ 7 | 'use strict'; 8 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 9 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 10 | return new (P || (P = Promise))(function (resolve, reject) { 11 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 12 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 13 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 14 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 15 | }); 16 | }; 17 | var __generator = (this && this.__generator) || function (thisArg, body) { 18 | var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; 19 | return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; 20 | function verb(n) { return function (v) { return step([n, v]); }; } 21 | function step(op) { 22 | if (f) throw new TypeError("Generator is already executing."); 23 | while (_) try { 24 | if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; 25 | if (y = 0, t) op = [op[0] & 2, t.value]; 26 | switch (op[0]) { 27 | case 0: case 1: t = op; break; 28 | case 4: _.label++; return { value: op[1], done: false }; 29 | case 5: _.label++; y = op[1]; op = [0]; continue; 30 | case 7: op = _.ops.pop(); _.trys.pop(); continue; 31 | default: 32 | if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } 33 | if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } 34 | if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } 35 | if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } 36 | if (t[2]) _.ops.pop(); 37 | _.trys.pop(); continue; 38 | } 39 | op = body.call(thisArg, _); 40 | } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } 41 | if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; 42 | } 43 | }; 44 | Object.defineProperty(exports, "__esModule", { value: true }); 45 | var crypto = require("crypto"); 46 | var uuid_1 = require("uuid"); 47 | var winston = require("winston"); 48 | var logger = winston.createLogger({ 49 | transports: [new winston.transports.Console()] 50 | }); 51 | var FeatureFlagClient = /** @class */ (function () { 52 | function FeatureFlagClient(userId, appId, config) { 53 | this.appId = appId; 54 | this.config = config; 55 | this.userId = userId; 56 | } 57 | FeatureFlagClient.prototype.createHash = function (userId, salt) { 58 | var hash = crypto.createHmac('sha256', salt); 59 | hash.update(userId); 60 | var hashValue = hash.digest('hex'); 61 | return hashValue; 62 | }; 63 | FeatureFlagClient.prototype.createUserId = function () { 64 | var userId = uuid_1.v4(); 65 | return userId; 66 | }; 67 | FeatureFlagClient.prototype.getUserFeatureIndex = function (hash) { 68 | var hashSegment = parseInt(hash.slice(-2), 16); 69 | return hashSegment.toString().slice(-2); 70 | }; 71 | FeatureFlagClient.prototype.init = function () { 72 | return __awaiter(this, void 0, void 0, function () { 73 | var _a, _b; 74 | return __generator(this, function (_c) { 75 | switch (_c.label) { 76 | case 0: 77 | _a = this; 78 | if (!this.config) return [3 /*break*/, 1]; 79 | _b = this.config; 80 | return [3 /*break*/, 3]; 81 | case 1: return [4 /*yield*/, this.loadConfig()]; 82 | case 2: 83 | _b = _c.sent(); 84 | _c.label = 3; 85 | case 3: 86 | _a.config = _b; 87 | this.userId = this.userId ? this.userId : this.createUserId(); 88 | return [2 /*return*/]; 89 | } 90 | }); 91 | }); 92 | }; 93 | FeatureFlagClient.prototype.loadConfig = function () { 94 | return __awaiter(this, void 0, void 0, function () { 95 | return __generator(this, function (_a) { 96 | // placeholder for fetching of the app's feature config file from AWS S3 97 | return [2 /*return*/, new Promise(function (resolve, reject) { 98 | var testConfig = require('../config/app-a.json'); 99 | setTimeout(function () { return resolve(testConfig); }, 300); 100 | })]; 101 | }); 102 | }); 103 | }; 104 | FeatureFlagClient.prototype.queryFeature = function (featureName) { 105 | var featureConfig = this.config.features.filter(function (feature) { return feature.name === featureName; }); 106 | var saltKey = featureConfig[0].saltKey; 107 | var hash = this.createHash(this.userId, saltKey); 108 | var userFeatureIndex = this.getUserFeatureIndex(hash); 109 | var enabled = (parseInt(userFeatureIndex, 10) < featureConfig[0].rolloutRate) ? true : false; 110 | // logging for demo only 111 | console.log("\n" + featureConfig[0].name); 112 | console.log("rollout rate: " + featureConfig[0].rolloutRate); 113 | console.log("user feature index: " + userFeatureIndex); 114 | return { 115 | featureName: featureConfig[0].name, 116 | enabled: enabled, 117 | userId: this.userId 118 | }; 119 | }; 120 | FeatureFlagClient.prototype.queryAllFeatures = function () { 121 | var _this = this; 122 | var featureFlagResultsMap = []; 123 | this.config.features.map(function (feature) { 124 | var featureFlagData = _this.queryFeature(feature.name); 125 | featureFlagResultsMap.push(featureFlagData); 126 | }); 127 | return featureFlagResultsMap; 128 | }; 129 | return FeatureFlagClient; 130 | }()); 131 | exports.FeatureFlagClient = FeatureFlagClient; 132 | -------------------------------------------------------------------------------- /featureflagTS/wm-feature-flag-client/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wm-feature-flag-client", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "13.9.2", 9 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/@types/node/-/@types/node-13.9.2.tgz", 10 | "integrity": "sha1-rOGIDANZTMPoAgbZaEcVfY5/o0k=" 11 | }, 12 | "@types/uuid": { 13 | "version": "7.0.2", 14 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/@types/uuid/-/@types/uuid-7.0.2.tgz", 15 | "integrity": "sha1-1oCpxZbvhKv1xMB6Mv/WbVglJvg=" 16 | }, 17 | "async": { 18 | "version": "2.6.3", 19 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/async/-/async-2.6.3.tgz", 20 | "integrity": "sha1-1yYl4jRKNlbjo61Pp0n6gymdgv8=", 21 | "requires": { 22 | "lodash": "^4.17.14" 23 | } 24 | }, 25 | "color": { 26 | "version": "3.0.0", 27 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/color/-/color-3.0.0.tgz", 28 | "integrity": "sha1-2SC0Mo1TSjrIKV1o971LpsQnvpo=", 29 | "requires": { 30 | "color-convert": "^1.9.1", 31 | "color-string": "^1.5.2" 32 | } 33 | }, 34 | "color-convert": { 35 | "version": "1.9.3", 36 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/color-convert/-/color-convert-1.9.3.tgz", 37 | "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", 38 | "requires": { 39 | "color-name": "1.1.3" 40 | } 41 | }, 42 | "color-name": { 43 | "version": "1.1.3", 44 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/color-name/-/color-name-1.1.3.tgz", 45 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" 46 | }, 47 | "color-string": { 48 | "version": "1.5.3", 49 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/color-string/-/color-string-1.5.3.tgz", 50 | "integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=", 51 | "requires": { 52 | "color-name": "^1.0.0", 53 | "simple-swizzle": "^0.2.2" 54 | } 55 | }, 56 | "colornames": { 57 | "version": "1.1.1", 58 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/colornames/-/colornames-1.1.1.tgz", 59 | "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" 60 | }, 61 | "colors": { 62 | "version": "1.4.0", 63 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/colors/-/colors-1.4.0.tgz", 64 | "integrity": "sha1-xQSRR51MG9rtLJztMs98fcI2D3g=" 65 | }, 66 | "colorspace": { 67 | "version": "1.1.2", 68 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/colorspace/-/colorspace-1.1.2.tgz", 69 | "integrity": "sha1-4BKJUNCCuGohaFgHlqCqXWxo2MU=", 70 | "requires": { 71 | "color": "3.0.x", 72 | "text-hex": "1.0.x" 73 | } 74 | }, 75 | "core-util-is": { 76 | "version": "1.0.2", 77 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/core-util-is/-/core-util-is-1.0.2.tgz", 78 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 79 | }, 80 | "diagnostics": { 81 | "version": "1.1.1", 82 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/diagnostics/-/diagnostics-1.1.1.tgz", 83 | "integrity": "sha1-yrasM99wydmnJ0kK5DrJladpsio=", 84 | "requires": { 85 | "colorspace": "1.1.x", 86 | "enabled": "1.0.x", 87 | "kuler": "1.0.x" 88 | } 89 | }, 90 | "enabled": { 91 | "version": "1.0.2", 92 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/enabled/-/enabled-1.0.2.tgz", 93 | "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", 94 | "requires": { 95 | "env-variable": "0.0.x" 96 | } 97 | }, 98 | "env-variable": { 99 | "version": "0.0.6", 100 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/env-variable/-/env-variable-0.0.6.tgz", 101 | "integrity": "sha1-dKsgs3hsVFtitKSBOrjPInJsmAg=" 102 | }, 103 | "fast-safe-stringify": { 104 | "version": "2.0.7", 105 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", 106 | "integrity": "sha1-EkqohYmSYfaK7bQqfAgN6dpgh0M=" 107 | }, 108 | "fecha": { 109 | "version": "2.3.3", 110 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/fecha/-/fecha-2.3.3.tgz", 111 | "integrity": "sha1-lI50FX3xoy/RsSw6PDzctuydls0=" 112 | }, 113 | "inherits": { 114 | "version": "2.0.4", 115 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/inherits/-/inherits-2.0.4.tgz", 116 | "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" 117 | }, 118 | "is-arrayish": { 119 | "version": "0.3.2", 120 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/is-arrayish/-/is-arrayish-0.3.2.tgz", 121 | "integrity": "sha1-RXSirlb3qyBolvtDHq7tBm/fjwM=" 122 | }, 123 | "is-stream": { 124 | "version": "1.1.0", 125 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/is-stream/-/is-stream-1.1.0.tgz", 126 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" 127 | }, 128 | "isarray": { 129 | "version": "1.0.0", 130 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/isarray/-/isarray-1.0.0.tgz", 131 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" 132 | }, 133 | "kuler": { 134 | "version": "1.0.1", 135 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/kuler/-/kuler-1.0.1.tgz", 136 | "integrity": "sha1-73x4TzbJ+24W3TFQ0VJneysCKKY=", 137 | "requires": { 138 | "colornames": "^1.1.1" 139 | } 140 | }, 141 | "lodash": { 142 | "version": "4.17.15", 143 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/lodash/-/lodash-4.17.15.tgz", 144 | "integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=" 145 | }, 146 | "logform": { 147 | "version": "2.1.2", 148 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/logform/-/logform-2.1.2.tgz", 149 | "integrity": "sha1-lXFV6+tnoTFkBpglzmfdtbst02A=", 150 | "requires": { 151 | "colors": "^1.2.1", 152 | "fast-safe-stringify": "^2.0.4", 153 | "fecha": "^2.3.3", 154 | "ms": "^2.1.1", 155 | "triple-beam": "^1.3.0" 156 | } 157 | }, 158 | "ms": { 159 | "version": "2.1.2", 160 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/ms/-/ms-2.1.2.tgz", 161 | "integrity": "sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=" 162 | }, 163 | "one-time": { 164 | "version": "0.0.4", 165 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/one-time/-/one-time-0.0.4.tgz", 166 | "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" 167 | }, 168 | "process-nextick-args": { 169 | "version": "2.0.1", 170 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 171 | "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=" 172 | }, 173 | "readable-stream": { 174 | "version": "3.6.0", 175 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/readable-stream/-/readable-stream-3.6.0.tgz", 176 | "integrity": "sha1-M3u9o63AcGvT4CRCaihtS0sskZg=", 177 | "requires": { 178 | "inherits": "^2.0.3", 179 | "string_decoder": "^1.1.1", 180 | "util-deprecate": "^1.0.1" 181 | } 182 | }, 183 | "safe-buffer": { 184 | "version": "5.2.0", 185 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/safe-buffer/-/safe-buffer-5.2.0.tgz", 186 | "integrity": "sha1-t02uxJsRSPiMZLaNSbHoFcHy9Rk=" 187 | }, 188 | "simple-swizzle": { 189 | "version": "0.2.2", 190 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 191 | "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", 192 | "requires": { 193 | "is-arrayish": "^0.3.1" 194 | } 195 | }, 196 | "stack-trace": { 197 | "version": "0.0.10", 198 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/stack-trace/-/stack-trace-0.0.10.tgz", 199 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" 200 | }, 201 | "string_decoder": { 202 | "version": "1.3.0", 203 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/string_decoder/-/string_decoder-1.3.0.tgz", 204 | "integrity": "sha1-QvEUWUpGzxqOMLCoT1bHjD7awh4=", 205 | "requires": { 206 | "safe-buffer": "~5.2.0" 207 | } 208 | }, 209 | "text-hex": { 210 | "version": "1.0.0", 211 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/text-hex/-/text-hex-1.0.0.tgz", 212 | "integrity": "sha1-adycGxdEbueakr9biEu0uRJ1BvU=" 213 | }, 214 | "triple-beam": { 215 | "version": "1.3.0", 216 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/triple-beam/-/triple-beam-1.3.0.tgz", 217 | "integrity": "sha1-pZUhTHKY24M57u7gg+TRC9jLjdk=" 218 | }, 219 | "typescript": { 220 | "version": "3.8.3", 221 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/typescript/-/typescript-3.8.3.tgz", 222 | "integrity": "sha1-QJ64VE6gM1cRIFhp7EWKsQnuEGE=", 223 | "dev": true 224 | }, 225 | "util-deprecate": { 226 | "version": "1.0.2", 227 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/util-deprecate/-/util-deprecate-1.0.2.tgz", 228 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" 229 | }, 230 | "uuid": { 231 | "version": "7.0.2", 232 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/uuid/-/uuid-7.0.2.tgz", 233 | "integrity": "sha1-f/XCA0Z+kfXg2Fz8uq99LrvKm+Y=" 234 | }, 235 | "winston": { 236 | "version": "3.2.1", 237 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/winston/-/winston-3.2.1.tgz", 238 | "integrity": "sha1-YwYTd5dsc1hAKL4kkKGEYFX3fwc=", 239 | "requires": { 240 | "async": "^2.6.1", 241 | "diagnostics": "^1.1.1", 242 | "is-stream": "^1.1.0", 243 | "logform": "^2.1.1", 244 | "one-time": "0.0.4", 245 | "readable-stream": "^3.1.1", 246 | "stack-trace": "0.0.x", 247 | "triple-beam": "^1.3.0", 248 | "winston-transport": "^4.3.0" 249 | } 250 | }, 251 | "winston-transport": { 252 | "version": "4.3.0", 253 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/winston-transport/-/winston-transport-4.3.0.tgz", 254 | "integrity": "sha1-32jAwgJILESNm0cxPAcwTC18LGY=", 255 | "requires": { 256 | "readable-stream": "^2.3.6", 257 | "triple-beam": "^1.2.0" 258 | }, 259 | "dependencies": { 260 | "readable-stream": { 261 | "version": "2.3.7", 262 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/readable-stream/-/readable-stream-2.3.7.tgz", 263 | "integrity": "sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c=", 264 | "requires": { 265 | "core-util-is": "~1.0.0", 266 | "inherits": "~2.0.3", 267 | "isarray": "~1.0.0", 268 | "process-nextick-args": "~2.0.0", 269 | "safe-buffer": "~5.1.1", 270 | "string_decoder": "~1.1.1", 271 | "util-deprecate": "~1.0.1" 272 | } 273 | }, 274 | "safe-buffer": { 275 | "version": "5.1.2", 276 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/safe-buffer/-/safe-buffer-5.1.2.tgz", 277 | "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=" 278 | }, 279 | "string_decoder": { 280 | "version": "1.1.1", 281 | "resolved": "https://botbuilder.myget.org/F/botbuilder-v3-js-daily/npm/string_decoder/-/string_decoder-1.1.1.tgz", 282 | "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", 283 | "requires": { 284 | "safe-buffer": "~5.1.0" 285 | } 286 | } 287 | } 288 | } 289 | } 290 | } 291 | --------------------------------------------------------------------------------