├── tslint.json ├── .gitignore ├── src ├── domain │ ├── entities │ │ ├── common │ │ │ ├── Action.ts │ │ │ └── UpdateEvent.ts │ │ ├── errors │ │ │ └── DomainError.ts │ │ ├── carrier │ │ │ ├── errors │ │ │ │ └── CourierOverloadedError.ts │ │ │ ├── AbstractCarrier.ts │ │ │ ├── Truck.ts │ │ │ └── Courier.ts │ │ ├── warehouseman │ │ │ └── Warehouseman.ts │ │ └── pack │ │ │ └── Pack.ts │ ├── shared │ │ ├── IEntity.ts │ │ ├── IValueObject.ts │ │ ├── IObserver.ts │ │ └── IObservable.ts │ └── service │ │ ├── IWorkdayService.ts │ │ └── IPackingStrategy.ts └── infrastructure │ ├── WorkdayService.ts │ └── GreedyPackingStrategy.ts ├── .editorconfig ├── tsconfig.json ├── test ├── domain │ ├── entities │ │ ├── carrier │ │ │ ├── Truck-spec.ts │ │ │ └── Courier-spec.ts │ │ ├── pack │ │ │ └── Pack-spec.ts │ │ └── warehouseman │ │ │ └── Warehouseman-spec.ts │ └── service │ │ └── GreedyPackingStrategy-spec.ts └── infrastructure │ └── WorkdayService-spec.ts ├── LICENSE ├── README.md ├── package.json ├── integration-test └── workday.ts └── npm-shrinkwrap.json /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:latest" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | coverage/ 3 | node_modules/ 4 | npm-debug.log 5 | lib/* 6 | -------------------------------------------------------------------------------- /src/domain/entities/common/Action.ts: -------------------------------------------------------------------------------- 1 | export enum Action { 2 | Unloaded, 3 | Packed, 4 | } 5 | -------------------------------------------------------------------------------- /src/domain/entities/errors/DomainError.ts: -------------------------------------------------------------------------------- 1 | export abstract class DomainError extends Error { 2 | 3 | } 4 | -------------------------------------------------------------------------------- /src/domain/shared/IEntity.ts: -------------------------------------------------------------------------------- 1 | export interface IEntity { 2 | sameIdentityAs(other: IEntity): boolean; 3 | } 4 | -------------------------------------------------------------------------------- /src/domain/shared/IValueObject.ts: -------------------------------------------------------------------------------- 1 | export interface IValueObject { 2 | sameValueAs(other: IValueObject): boolean; 3 | } 4 | -------------------------------------------------------------------------------- /src/domain/shared/IObserver.ts: -------------------------------------------------------------------------------- 1 | import { UpdateEvent } from "../entities/common/UpdateEvent"; 2 | 3 | export interface IObserver { 4 | onUpdate(event: UpdateEvent): void; 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 4 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /src/domain/service/IWorkdayService.ts: -------------------------------------------------------------------------------- 1 | import { Truck } from "../entities/carrier/Truck"; 2 | import { Warehouseman } from "../entities/warehouseman/Warehouseman"; 3 | 4 | export interface IWorkdayService { 5 | begin(truck: Truck, warehouseman: Warehouseman, courierGenerator: any): void; 6 | } 7 | -------------------------------------------------------------------------------- /src/domain/entities/carrier/errors/CourierOverloadedError.ts: -------------------------------------------------------------------------------- 1 | import { DomainError } from "../../errors/DomainError"; 2 | 3 | export class CourierOverloadedError extends DomainError { 4 | constructor(message: string) { 5 | super(message); 6 | 7 | (Object as any).setPrototypeOf(this, CourierOverloadedError.prototype); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "noImplicitAny": true, 7 | "outDir": "./lib", 8 | "preserveConstEnums": true, 9 | "removeComments": true, 10 | "target": "es6" 11 | }, 12 | "include": [ 13 | "src/**/*" 14 | ], 15 | "exclude": [ 16 | "node_modules", 17 | "**/*-spec.ts" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/domain/service/IPackingStrategy.ts: -------------------------------------------------------------------------------- 1 | import { Courier } from "../../domain/entities/carrier/Courier"; 2 | import { Pack } from "../../domain/entities/pack/Pack"; 3 | 4 | export interface IPackingStrategy { 5 | /** 6 | * pack 7 | * @param packs {Pack[]} packs to be packed into Courier's cars 8 | * @param courierGenerator {any} generator of next Courier object's 9 | */ 10 | pack(packs: Pack[], courierGenerator: any): void; 11 | } 12 | -------------------------------------------------------------------------------- /src/domain/entities/carrier/AbstractCarrier.ts: -------------------------------------------------------------------------------- 1 | import { IEntity } from "../../shared/IEntity"; 2 | 3 | export abstract class AbstractCarrier implements IEntity { 4 | protected name: string; 5 | 6 | /** 7 | * AbstractCarrier constructor. 8 | * @param name {string} name of carrier. Identity of carrier. 9 | */ 10 | constructor(name: string) { 11 | this.name = name; 12 | } 13 | 14 | public getName() { 15 | return this.name; 16 | } 17 | 18 | public sameIdentityAs(other: AbstractCarrier) { 19 | return other.getName() === this.getName(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/domain/shared/IObservable.ts: -------------------------------------------------------------------------------- 1 | import { UpdateEvent } from "../entities/common/UpdateEvent"; 2 | import { IObserver } from "./IObserver"; 3 | 4 | export interface IObservable { 5 | /** 6 | * Adds new observer of an IObservable. 7 | * @param observer {IObserver} 8 | */ 9 | addObserver(observer: IObserver): void; 10 | 11 | /** 12 | * Removes observer from list of observables. 13 | * @param observer {IObserver} observer to be removed. 14 | */ 15 | removeObserver(observer: IObserver): void; 16 | 17 | /** 18 | * Inform observers about changes. 19 | * @param event {UpdateEvent} 20 | */ 21 | informObservers(event: UpdateEvent): void; 22 | } 23 | -------------------------------------------------------------------------------- /src/infrastructure/WorkdayService.ts: -------------------------------------------------------------------------------- 1 | import { Truck } from "../domain/entities/carrier/Truck"; 2 | import { Warehouseman } from "../domain/entities/warehouseman/Warehouseman"; 3 | import { IWorkdayService } from "../domain/service/IWorkdayService"; 4 | 5 | export class WorkdayService implements IWorkdayService { 6 | /** 7 | * begin 8 | * @param truck {Truck} - a truck arriving in the morning. 9 | * @param warehouseman {Warehouseman} a warehouseman to repack the Truck and store events. 10 | * @param courierGenerator {any} generator of next Courier object's 11 | */ 12 | public begin(truck: Truck, warehouseman: Warehouseman, courierGenerator: any) { 13 | const packs = warehouseman.unload(truck); 14 | warehouseman.pack(packs, courierGenerator); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/domain/entities/carrier/Truck-spec.ts: -------------------------------------------------------------------------------- 1 | import * as chai from "chai"; 2 | import * as Uuid from "uuid"; 3 | import { Truck } from "../../../../src/domain/entities/carrier/Truck"; 4 | import { Pack } from "../../../../src/domain/entities/pack/Pack"; 5 | 6 | const expect = chai.expect; 7 | 8 | describe("Truck", () => { 9 | it("should pack the truck with packages", () => { 10 | const truck = new Truck("test truck"); 11 | const packs = [new Pack(Uuid.v4(), 10)]; 12 | truck.load(packs); 13 | expect(truck.showLoad()).to.equal(packs); 14 | }); 15 | it("should unload the truck", () => { 16 | const truck = new Truck("test truck"); 17 | const packs = [new Pack(Uuid.v4(), 10)]; 18 | truck.load(packs); 19 | const unloadedPacks = truck.unload(); 20 | expect(truck.showLoad()).to.deep.equal([]); 21 | expect(unloadedPacks).to.equal(packs); 22 | }); 23 | });; 24 | -------------------------------------------------------------------------------- /src/domain/entities/carrier/Truck.ts: -------------------------------------------------------------------------------- 1 | import { Pack } from "../pack/Pack"; 2 | import { AbstractCarrier } from "./AbstractCarrier"; 3 | 4 | /** 5 | * There are new packs every day in the truck. 6 | * The truck can come with unlimited number and weight of packs. 7 | */ 8 | export class Truck extends AbstractCarrier { 9 | private packs: Pack[]; 10 | 11 | /** 12 | * @inheritdoc 13 | */ 14 | constructor(name: string) { 15 | super(name); 16 | this.packs = []; 17 | } 18 | 19 | /** 20 | * Binds new packs to the truck. 21 | * @param packs {Pack[]} packs to be loaded into the truck. 22 | */ 23 | public load(packs: Pack[]) { 24 | this.packs = packs; 25 | } 26 | 27 | /** 28 | * The truck becomes empty. 29 | * @returns Pack[] 30 | */ 31 | public unload() { 32 | const packs = this.packs; 33 | this.packs = []; 34 | 35 | return packs; 36 | } 37 | 38 | /** 39 | * Preview of current load. 40 | * @returns Pack[] 41 | */ 42 | public showLoad() { 43 | return this.packs; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/domain/entities/common/UpdateEvent.ts: -------------------------------------------------------------------------------- 1 | import * as SprintfJs from "sprintf-js"; 2 | import { IEntity } from "../../shared/IEntity"; 3 | import { AbstractCarrier } from "../carrier/AbstractCarrier"; 4 | import { Pack } from "../pack/Pack"; 5 | import { Action } from "./Action"; 6 | 7 | export class UpdateEvent implements IEntity { 8 | private uuid: string; 9 | private action: Action; 10 | private carrier: AbstractCarrier; 11 | private pack: Pack; 12 | 13 | constructor(uuid: string, action: Action, carrier: AbstractCarrier, pack: Pack) { 14 | this.uuid = uuid; 15 | this.action = action; 16 | this.carrier = carrier; 17 | this.pack = pack; 18 | } 19 | 20 | /** 21 | * Returns human readable report from event. 22 | * @returns string 23 | */ 24 | public report() { 25 | return SprintfJs.sprintf( 26 | "%s: Action: %s, Carrier: %s, pack: %s.", 27 | this.uuid, 28 | Action[this.action], 29 | this.carrier.getName(), 30 | this.pack.getId(), 31 | ); 32 | } 33 | 34 | public sameIdentityAs(other: UpdateEvent) { 35 | return other.uuid === this.uuid; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TypeScript application created adhering to the rules of Domain-Driven Design. 2 | 3 | # What does this application do? 4 | It’s variation of bin packing problem solution. The truck with unlimited number and load of packs arrives daily in the morning. The warehouseman has to repack all packs to courier’s cars. The maximum load of single courier car is 200 kg. Application optimize the number of needed courier’s cars to minimum. All actions connected with packs and warehouseman are kept in memory during the object lifecycle and we have access to them. 5 | 6 | # What is it for? 7 | - to show that it’s possible to write code in TypeScript adhering to the rules of DDD. 8 | - simple to understand (there are no repositories, no unnecessary lines of code). 9 | - to show that it’s possible to implement IoC without using Dependency Injection Container. 10 | - I always wondered if it’s possible: [my question in stackoverflow](http://stackoverflow.com/questions/34027990/domain-driven-design-in-node-js-application). 11 | 12 | # How to test it? 13 | Unit tests: 14 | ``` 15 | npm test 16 | ``` 17 | 18 | Intergration tests: 19 | ``` 20 | npm run-script integration-test 21 | ``` 22 | 23 | # Usage 24 | [Usage example](https://github.com/dawiddominiak/ddd-typescript-bin-packing-problem-solution/blob/master/integration-test/workday.ts#L37) 25 | -------------------------------------------------------------------------------- /test/domain/entities/carrier/Courier-spec.ts: -------------------------------------------------------------------------------- 1 | import * as chai from "chai"; 2 | import * as spies from "chai-spies"; 3 | import * as Uuid from "uuid"; 4 | import { Courier } from "../../../../src/domain/entities/carrier/Courier"; 5 | import { Pack } from "../../../../src/domain/entities/pack/Pack"; 6 | import { Warehouseman } from "../../../../src/domain/entities/warehouseman/Warehouseman"; 7 | 8 | chai.use(spies); 9 | const expect = chai.expect; 10 | 11 | describe("Courier", () => { 12 | it("should load pack to courier's car and log action to warehouseman", () => { 13 | const courier = new Courier("test courier"); 14 | const pack = new Pack("94d4501f-2090-479f-8a5e-8c1494a648b8", 10); 15 | const updateStateSpy = chai.spy.on(pack, "updateState"); 16 | courier.loadPack(pack); 17 | 18 | expect(courier.showLoad().pop()).to.equal(pack); 19 | expect(updateStateSpy).to.have.been.called(); 20 | }); 21 | it("should count the load weight", () => { 22 | const courier = new Courier("test courier"); 23 | const packs = [ 24 | new Pack(Uuid.v4(), 10), 25 | new Pack(Uuid.v4(), 15), 26 | ]; 27 | 28 | packs.forEach((pack) => { 29 | courier.loadPack(pack); 30 | }); 31 | 32 | expect(courier.getLoadWeight()).to.equal(25); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /test/infrastructure/WorkdayService-spec.ts: -------------------------------------------------------------------------------- 1 | import * as chai from "chai"; 2 | import * as spies from "chai-spies"; 3 | import * as Uuid from "uuid"; 4 | import { Courier } from "../../src/domain/entities/carrier/Courier"; 5 | import { Truck } from "../../src/domain/entities/carrier/Truck"; 6 | import { Warehouseman } from "../../src/domain/entities/warehouseman/Warehouseman"; 7 | import { GreedyPackingStrategy } from "../../src/infrastructure/GreedyPackingStrategy"; 8 | import { WorkdayService } from "../../src/infrastructure/WorkdayService"; 9 | 10 | chai.use(spies); 11 | const expect = chai.expect; 12 | 13 | describe("WorkdayService", () => { 14 | it ("should should run warehouseman.unload and warehouseman.pack methods", () => { 15 | function* generator() { 16 | yield new Courier("1st courier"); 17 | } 18 | 19 | const workdayService = new WorkdayService(); 20 | const truck = new Truck("test truck"); 21 | const warehouseman = new Warehouseman("test warehouseman"); 22 | warehouseman.setPackingStrategy(new GreedyPackingStrategy()); 23 | 24 | const warehousemanUnloadSpy = chai.spy.on(warehouseman, "unload"); 25 | const warehousemanPackSpy = chai.spy.on(warehouseman, "pack"); 26 | 27 | workdayService.begin(truck, warehouseman, generator()); 28 | 29 | expect(warehousemanUnloadSpy).to.have.been.called(); 30 | expect(warehousemanPackSpy).to.have.been.called(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/domain/service/GreedyPackingStrategy-spec.ts: -------------------------------------------------------------------------------- 1 | import * as chai from "chai"; 2 | import * as Uuid from "uuid"; 3 | import { Courier } from "../../../src/domain/entities/carrier/Courier"; 4 | import { Pack } from "../../../src/domain/entities/pack/Pack"; 5 | import { GreedyPackingStrategy } from "../../../src/infrastructure/GreedyPackingStrategy"; 6 | 7 | const expect = chai.expect; 8 | 9 | describe("GreedyPackingStrategy", () => { 10 | it("should pack packages to couriers following the greedy rules", () => { 11 | const courier1 = new Courier("1st courier"); 12 | const courier2 = new Courier("2nd courier"); 13 | 14 | function* generator() { 15 | yield courier1; 16 | yield courier2; 17 | } 18 | 19 | const packingStrategy = new GreedyPackingStrategy(); 20 | const packs = [ 21 | new Pack(Uuid.v4(), 150), 22 | new Pack(Uuid.v4(), 60), 23 | new Pack(Uuid.v4(), 40), 24 | new Pack(Uuid.v4(), 20), 25 | new Pack(Uuid.v4(), 10), 26 | ]; 27 | 28 | const courierGenerator = generator(); 29 | 30 | packingStrategy.pack(packs, courierGenerator); 31 | 32 | expect(courier1.showLoad()).deep.equal([ 33 | packs[0], 34 | packs[2], 35 | packs[4], 36 | ]); 37 | expect(courier2.showLoad()).deep.equal([ 38 | packs[1], 39 | packs[3], 40 | ]); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ddd-bin-packing-problem-solution", 3 | "version": "0.1.0", 4 | "description": "ddd-bin-packing-problem-solution", 5 | "license": "MIT", 6 | "repository": "", 7 | "author": "", 8 | "keywords": [ 9 | "" 10 | ], 11 | "files": [ 12 | "lib" 13 | ], 14 | "main": "lib/index.js", 15 | "typings": "lib/index.d.ts", 16 | "scripts": { 17 | "clean": "rimraf lib", 18 | "lint": "tslint --force --format verbose \"src/**/*.ts\"", 19 | "build": "npm run clean && npm run lint && echo Using TypeScript && tsc --version && tsc --pretty", 20 | "test": "npm run build && mocha --compilers ts:ts-node/register --recursive test/**/*-spec.ts", 21 | "integration-test": "npm run build && mocha --compilers ts:ts-node/register --recursive integration-test/**/*.ts", 22 | "watch": "npm run build -- --watch", 23 | "watch:test": "npm run test -- --watch" 24 | }, 25 | "dependencies": { 26 | "lodash": "^4.17.4", 27 | "sprintf-js": "^1.0.3", 28 | "uuid": "^3.0.1" 29 | }, 30 | "devDependencies": { 31 | "@types/chai": "^3.0.0", 32 | "@types/chai-spies": "0.0.0", 33 | "@types/lodash": "^4.14.50", 34 | "@types/mocha": "^2.0.0", 35 | "@types/node": "6.0.31", 36 | "@types/sprintf-js": "0.0.27", 37 | "@types/uuid": "^2.0.29", 38 | "chai": "^3.0.0", 39 | "chai-spies": "^0.7.1", 40 | "mocha": "^3.0.0", 41 | "rimraf": "^2.0.0", 42 | "ts-node": "^1.0.0", 43 | "tslint": "^4.0.0", 44 | "typescript": "^2.0.0" 45 | }, 46 | "engines": { 47 | "node": ">=4.0.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/domain/entities/pack/Pack-spec.ts: -------------------------------------------------------------------------------- 1 | import * as chai from "chai"; 2 | import * as spies from "chai-spies"; 3 | import * as Uuid from "uuid"; 4 | import { Courier } from "../../../../src/domain/entities/carrier/Courier"; 5 | import { Action } from "../../../../src/domain/entities/common/Action"; 6 | import { Pack } from "../../../../src/domain/entities/pack/Pack"; 7 | import { Warehouseman } from "../../../../src/domain/entities/warehouseman/Warehouseman"; 8 | 9 | chai.use(spies); 10 | const expect = chai.expect; 11 | 12 | describe("Pack", () => { 13 | it("should add observer to pack", () => { 14 | const pack = new Pack(Uuid.v4(), 10); 15 | const courier = new Courier("test courier"); 16 | const warehouseman = new Warehouseman("test warehouseman"); 17 | 18 | const observerOnUpdateSpy = chai.spy.on(warehouseman, "onUpdate"); 19 | 20 | pack.addObserver(warehouseman); 21 | pack.updateState(Action.Packed, courier); 22 | 23 | expect(observerOnUpdateSpy).to.have.been.called(); 24 | }); 25 | it("should remove observer from pack", () => { 26 | const pack = new Pack(Uuid.v4(), 10); 27 | const courier = new Courier("test courier"); 28 | const warehouseman = new Warehouseman("test warehouseman"); 29 | 30 | const observerOnUpdateSpy = chai.spy.on(warehouseman, "onUpdate"); 31 | 32 | pack.addObserver(warehouseman); 33 | pack.removeObserver(warehouseman); 34 | pack.updateState(Action.Packed, courier); 35 | 36 | expect(observerOnUpdateSpy).to.not.have.been.called(); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/domain/entities/warehouseman/Warehouseman.ts: -------------------------------------------------------------------------------- 1 | import { IPackingStrategy } from "../../service/IPackingStrategy"; 2 | import { IEntity } from "../../shared/IEntity"; 3 | import { IObserver } from "../../shared/IObserver"; 4 | import { Truck } from "../carrier/Truck"; 5 | import { Action } from "../common/Action"; 6 | import { UpdateEvent } from "../common/UpdateEvent"; 7 | import { Pack } from "../pack/Pack"; 8 | 9 | export class Warehouseman implements IObserver, IEntity { 10 | private name: string; 11 | private events: UpdateEvent[]; 12 | private packingStrategy: IPackingStrategy; 13 | 14 | constructor(name: string) { 15 | this.name = name; 16 | this.events = []; 17 | } 18 | 19 | public onUpdate(event: UpdateEvent) { 20 | this.events.push(event); 21 | } 22 | 23 | public setPackingStrategy(packingStrategy: IPackingStrategy) { 24 | this.packingStrategy = packingStrategy; 25 | } 26 | 27 | public pack(packs: Pack[], courierGenerator: any) { 28 | // TODO: custom error is case of lack packingStrategy. 29 | this.packingStrategy.pack(packs, courierGenerator); 30 | } 31 | 32 | public unload(truck: Truck) { 33 | const packs = truck.unload(); 34 | 35 | packs.forEach((pack) => { 36 | pack.addObserver(this); 37 | pack.updateState(Action.Unloaded, truck); 38 | }); 39 | 40 | return packs; 41 | } 42 | 43 | public getObservedEvents() { 44 | return this.events; 45 | } 46 | 47 | public sameIdentityAs(other: Warehouseman) { 48 | return this.name === other.name; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /integration-test/workday.ts: -------------------------------------------------------------------------------- 1 | import * as chai from "chai"; 2 | import * as Uuid from "uuid"; 3 | import { Courier } from "../src/domain/entities/carrier/Courier"; 4 | import { Truck } from "../src/domain/entities/carrier/Truck"; 5 | import { Pack } from "../src/domain/entities/pack/Pack"; 6 | import { Warehouseman } from "../src/domain/entities/warehouseman/Warehouseman"; 7 | import { GreedyPackingStrategy } from "../src/infrastructure/GreedyPackingStrategy"; 8 | import { WorkdayService } from "../src/infrastructure/WorkdayService"; 9 | 10 | const expect = chai.expect; 11 | 12 | describe("WorkdayService", () => { 13 | it("should process the whole workday, packs should be loaded correctly to Courier's cars.", () => { 14 | const workdayService = new WorkdayService(); 15 | const warehouseman = new Warehouseman("test warehouseman"); 16 | warehouseman.setPackingStrategy(new GreedyPackingStrategy()); 17 | const truck = new Truck("test truck"); 18 | const packs = [ 19 | new Pack(Uuid.v4(), 100), 20 | new Pack(Uuid.v4(), 150), 21 | new Pack(Uuid.v4(), 110), 22 | new Pack(Uuid.v4(), 70), 23 | new Pack(Uuid.v4(), 40), 24 | ]; 25 | truck.load(packs); 26 | 27 | const courier1 = new Courier("1st courier"); 28 | const courier2 = new Courier("2nd courier"); 29 | const courier3 = new Courier("3rd courier"); 30 | 31 | function* generator() { 32 | yield courier1; 33 | yield courier2; 34 | yield courier3; 35 | } 36 | 37 | workdayService.begin(truck, warehouseman, generator()); 38 | 39 | expect(courier1.getLoadWeight()).to.equal(190); 40 | expect(courier2.getLoadWeight()).to.equal(180); 41 | expect(courier3.getLoadWeight()).to.equal(100); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/domain/entities/carrier/Courier.ts: -------------------------------------------------------------------------------- 1 | import * as SprintfJs from "sprintf-js"; 2 | import { Action } from "../common/Action"; 3 | import { Pack } from "../pack/Pack"; 4 | import { AbstractCarrier } from "./AbstractCarrier"; 5 | import { CourierOverloadedError } from "./errors/CourierOverloadedError"; 6 | 7 | /** 8 | * Courier can load only 200 kg to his mode of transportation. 9 | */ 10 | export class Courier extends AbstractCarrier { 11 | private maxLoad: number = 200; 12 | private packs: Pack[]; 13 | 14 | /** 15 | * @inheritdoc 16 | */ 17 | constructor(name: string) { 18 | super(name); 19 | this.packs = []; 20 | } 21 | 22 | /** 23 | * Loads single pack to courier. 24 | * @param pack {Pack} pack to be loaded. 25 | * @throws CourierOverloadedError 26 | */ 27 | public loadPack(pack: Pack) { 28 | const packWeight = pack.getWeight(); 29 | const currentLoadWeight = this.getLoadWeight(); 30 | 31 | if (this.maxLoad - currentLoadWeight < packWeight) { 32 | throw new CourierOverloadedError (SprintfJs.sprintf( 33 | "Courier could not carry additional %d kg, because current load is %d and max load is %d", 34 | packWeight, 35 | currentLoadWeight, 36 | this.maxLoad, 37 | )); 38 | } 39 | 40 | this.packs.push(pack); 41 | pack.updateState(Action.Packed, this); 42 | } 43 | 44 | /** 45 | * Counts current load weight of courier. 46 | * @returns number 47 | */ 48 | public getLoadWeight() { 49 | return this.packs.reduce((total, currentPack) => { 50 | return total + currentPack.getWeight(); 51 | }, 0); 52 | } 53 | 54 | /** 55 | * @returns Pack[] preview of loaded packs. 56 | */ 57 | public showLoad() { 58 | return this.packs; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/infrastructure/GreedyPackingStrategy.ts: -------------------------------------------------------------------------------- 1 | import { Courier } from "../domain/entities/carrier/Courier"; 2 | import { CourierOverloadedError } from "../domain/entities/carrier/errors/CourierOverloadedError"; 3 | import { Pack } from "../domain/entities/pack/Pack"; 4 | import { IPackingStrategy } from "../domain/service/IPackingStrategy"; 5 | 6 | export class GreedyPackingStrategy implements IPackingStrategy { 7 | /** 8 | * @inheritdoc 9 | */ 10 | public pack(packs: Pack[], courierGenerator: any) { 11 | this.sortPacksByWeightDesc(packs); 12 | 13 | const neededCouriers: Courier[] = [ 14 | courierGenerator.next().value, 15 | ]; 16 | 17 | packs.forEach((pack) => { 18 | let loaded = false; 19 | this.sortCouriersByLoadDesc(neededCouriers); 20 | 21 | for (const courier of neededCouriers) { 22 | if (this.tryLoadPack(courier, pack)) { 23 | loaded = true; 24 | break; 25 | } 26 | } 27 | 28 | if (!loaded) { 29 | const newCourier: Courier = courierGenerator.next().value; 30 | neededCouriers.push(newCourier); 31 | this.tryLoadPack(newCourier, pack); 32 | } 33 | }); 34 | } 35 | 36 | private tryLoadPack(courier: Courier, pack: Pack) { 37 | try { 38 | courier.loadPack(pack); 39 | return true; 40 | } catch (error) { 41 | if (!(error instanceof CourierOverloadedError)) { 42 | throw error; 43 | } 44 | 45 | return false; 46 | } 47 | } 48 | 49 | private sortCouriersByLoadDesc(couriers: Courier[]) { 50 | couriers.sort((courierA, courierB) => { 51 | return courierB.getLoadWeight() - courierA.getLoadWeight(); 52 | }); 53 | } 54 | 55 | private sortPacksByWeightDesc(packs: Pack[]) { 56 | packs.sort((packA, packB) => { 57 | return packB.getWeight() - packA.getWeight(); 58 | }); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/domain/entities/pack/Pack.ts: -------------------------------------------------------------------------------- 1 | import * as _ from "lodash"; 2 | import * as Uuid from "uuid"; 3 | import { IEntity } from "../../shared/IEntity"; 4 | import { IObservable } from "../../shared/IObservable"; 5 | import { IObserver } from "../../shared/IObserver"; 6 | import { AbstractCarrier } from "../carrier/AbstractCarrier"; 7 | import { Action } from "../common/Action"; 8 | import { UpdateEvent } from "../common/UpdateEvent"; 9 | 10 | export class Pack implements IObservable, IEntity { 11 | private packId: string; 12 | private weight: number; 13 | private observers: IObserver[]; 14 | 15 | /** 16 | * Pack constructor. 17 | * @param packId {string} identity of pack. 18 | * @param weight {number} weight of pack in kg. 19 | */ 20 | constructor(packId: string, weight: number) { 21 | this.packId = packId; 22 | this.weight = weight; 23 | this.observers = []; 24 | } 25 | 26 | /** 27 | * @inheritdoc 28 | */ 29 | public addObserver(observer: IObserver) { 30 | this.observers.push(observer); 31 | } 32 | 33 | /** 34 | * @inheritdoc 35 | */ 36 | public removeObserver(observer: IObserver) { 37 | _.pull(this.observers, observer); 38 | } 39 | 40 | /** 41 | * Updates state of pack. Informs observers about change. 42 | * @param action {Action} 43 | * @param carrier {AbstractCarrier} 44 | */ 45 | public updateState(action: Action, carrier: AbstractCarrier) { 46 | const event = new UpdateEvent(Uuid.v4(), action, carrier, this); 47 | this.informObservers(event); 48 | } 49 | 50 | /** 51 | * @inheritdoc 52 | */ 53 | public informObservers(event: UpdateEvent) { 54 | this.observers.forEach((observer) => { 55 | observer.onUpdate(event); 56 | }); 57 | } 58 | 59 | public getId() { 60 | return this.packId; 61 | } 62 | 63 | public getWeight() { 64 | return this.weight; 65 | } 66 | 67 | public sameIdentityAs(other: Pack) { 68 | return other.packId === this.packId; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /test/domain/entities/warehouseman/Warehouseman-spec.ts: -------------------------------------------------------------------------------- 1 | import * as chai from "chai"; 2 | import * as spies from "chai-spies"; 3 | import * as Uuid from "uuid"; 4 | import { Courier } from "../../../../src/domain/entities/carrier/Courier"; 5 | import { Truck } from "../../../../src/domain/entities/carrier/Truck"; 6 | import { Action } from "../../../../src/domain/entities/common/Action"; 7 | import { UpdateEvent } from "../../../../src/domain/entities/common/UpdateEvent"; 8 | import { Pack } from "../../../../src/domain/entities/pack/Pack"; 9 | import { Warehouseman } from "../../../../src/domain/entities/warehouseman/Warehouseman"; 10 | import { IPackingStrategy } from "../../../../src/domain/service/IPackingStrategy"; 11 | 12 | chai.use(spies); 13 | const expect = chai.expect; 14 | 15 | describe("Warehouseman", () => { 16 | it("should add event to warehouseman log", () => { 17 | const warehouseman = new Warehouseman("test warehouseman"); 18 | const carrier = new Courier("test carrier"); 19 | const pack = new Pack(Uuid.v4(), 10); 20 | const event = new UpdateEvent(Uuid.v4(), Action.Packed, carrier, pack); 21 | 22 | warehouseman.onUpdate(event); 23 | 24 | expect(warehouseman.getObservedEvents().pop()).to.equal(event); 25 | }); 26 | it("should run pack method from packing strategy", () => { 27 | class SamplePackingStrategy implements IPackingStrategy { 28 | // tslint:disable-next-line:no-empty 29 | public pack(packs: Pack[], courierGenerator: any) { 30 | 31 | } 32 | } 33 | 34 | function* generatorFunction() { 35 | yield new Courier("courrier 1"); 36 | } 37 | 38 | const courierGenerator = generatorFunction(); 39 | 40 | const warehouseman = new Warehouseman("test warehouseman"); 41 | const packingStrategy = new SamplePackingStrategy(); 42 | const packingStrategyPackSpy = chai.spy.on(packingStrategy, "pack"); 43 | 44 | warehouseman.setPackingStrategy(packingStrategy); 45 | warehouseman.pack([new Pack(Uuid.v4(), 10)], courierGenerator); 46 | 47 | expect(packingStrategyPackSpy).to.have.been.called(); 48 | }); 49 | it("should run truck.unload method, add observer to pack and update state of pack", () => { 50 | const warehouseman = new Warehouseman("test warehouseman"); 51 | const truck = new Truck("test truck"); 52 | const packs = [new Pack(Uuid.v4(), 10)]; 53 | truck.load(packs); 54 | 55 | const packAddObserverSpy = chai.spy.on(packs[0], "addObserver"); 56 | const packUpdateStateSpy = chai.spy.on(packs[0], "updateState"); 57 | 58 | warehouseman.unload(truck); 59 | 60 | expect(packAddObserverSpy).to.have.been.called(); 61 | expect(packUpdateStateSpy).to.have.been.called(); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /npm-shrinkwrap.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ddd-bin-packing-problem-solution", 3 | "version": "0.1.0", 4 | "dependencies": { 5 | "@types/chai": { 6 | "version": "3.4.34", 7 | "from": "@types/chai@>=3.0.0 <4.0.0", 8 | "resolved": "https://registry.npmjs.org/@types/chai/-/chai-3.4.34.tgz", 9 | "dev": true 10 | }, 11 | "@types/chai-spies": { 12 | "version": "0.0.0", 13 | "from": "@types/chai-spies@latest", 14 | "resolved": "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-0.0.0.tgz", 15 | "dev": true 16 | }, 17 | "@types/lodash": { 18 | "version": "4.14.50", 19 | "from": "@types/lodash@latest", 20 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.50.tgz", 21 | "dev": true 22 | }, 23 | "@types/mocha": { 24 | "version": "2.2.37", 25 | "from": "@types/mocha@>=2.0.0 <3.0.0", 26 | "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.37.tgz", 27 | "dev": true 28 | }, 29 | "@types/node": { 30 | "version": "6.0.31", 31 | "from": "@types/node@6.0.31", 32 | "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.31.tgz", 33 | "dev": true 34 | }, 35 | "@types/sprintf-js": { 36 | "version": "0.0.27", 37 | "from": "@types/sprintf-js@latest", 38 | "resolved": "https://registry.npmjs.org/@types/sprintf-js/-/sprintf-js-0.0.27.tgz", 39 | "dev": true 40 | }, 41 | "@types/uuid": { 42 | "version": "2.0.29", 43 | "from": "@types/uuid@latest", 44 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-2.0.29.tgz", 45 | "dev": true 46 | }, 47 | "ansi-align": { 48 | "version": "1.1.0", 49 | "from": "ansi-align@>=1.1.0 <2.0.0", 50 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-1.1.0.tgz", 51 | "dev": true 52 | }, 53 | "ansi-regex": { 54 | "version": "2.0.0", 55 | "from": "ansi-regex@>=2.0.0 <3.0.0", 56 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.0.0.tgz", 57 | "dev": true 58 | }, 59 | "ansi-styles": { 60 | "version": "2.2.1", 61 | "from": "ansi-styles@>=2.2.1 <3.0.0", 62 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", 63 | "dev": true 64 | }, 65 | "any-promise": { 66 | "version": "1.3.0", 67 | "from": "any-promise@>=1.3.0 <2.0.0", 68 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 69 | "dev": true 70 | }, 71 | "arrify": { 72 | "version": "1.0.1", 73 | "from": "arrify@>=1.0.0 <2.0.0", 74 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", 75 | "dev": true 76 | }, 77 | "assertion-error": { 78 | "version": "1.0.2", 79 | "from": "assertion-error@>=1.0.1 <2.0.0", 80 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", 81 | "dev": true 82 | }, 83 | "babel-code-frame": { 84 | "version": "6.20.0", 85 | "from": "babel-code-frame@>=6.20.0 <7.0.0", 86 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.20.0.tgz", 87 | "dev": true 88 | }, 89 | "balanced-match": { 90 | "version": "0.4.2", 91 | "from": "balanced-match@>=0.4.1 <0.5.0", 92 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", 93 | "dev": true 94 | }, 95 | "boxen": { 96 | "version": "0.6.0", 97 | "from": "boxen@>=0.6.0 <0.7.0", 98 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-0.6.0.tgz", 99 | "dev": true 100 | }, 101 | "brace-expansion": { 102 | "version": "1.1.6", 103 | "from": "brace-expansion@>=1.0.0 <2.0.0", 104 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.6.tgz", 105 | "dev": true 106 | }, 107 | "browser-stdout": { 108 | "version": "1.3.0", 109 | "from": "browser-stdout@1.3.0", 110 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 111 | "dev": true 112 | }, 113 | "buffer-shims": { 114 | "version": "1.0.0", 115 | "from": "buffer-shims@>=1.0.0 <2.0.0", 116 | "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", 117 | "dev": true 118 | }, 119 | "camelcase": { 120 | "version": "2.1.1", 121 | "from": "camelcase@>=2.1.0 <3.0.0", 122 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", 123 | "dev": true 124 | }, 125 | "capture-stack-trace": { 126 | "version": "1.0.0", 127 | "from": "capture-stack-trace@>=1.0.0 <2.0.0", 128 | "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", 129 | "dev": true 130 | }, 131 | "chai": { 132 | "version": "3.5.0", 133 | "from": "chai@>=3.0.0 <4.0.0", 134 | "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", 135 | "dev": true 136 | }, 137 | "chai-spies": { 138 | "version": "0.7.1", 139 | "from": "chai-spies@latest", 140 | "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-0.7.1.tgz", 141 | "dev": true 142 | }, 143 | "chalk": { 144 | "version": "1.1.3", 145 | "from": "chalk@>=1.1.1 <2.0.0", 146 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", 147 | "dev": true, 148 | "dependencies": { 149 | "supports-color": { 150 | "version": "2.0.0", 151 | "from": "supports-color@>=2.0.0 <3.0.0", 152 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", 153 | "dev": true 154 | } 155 | } 156 | }, 157 | "cli-boxes": { 158 | "version": "1.0.0", 159 | "from": "cli-boxes@>=1.0.0 <2.0.0", 160 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", 161 | "dev": true 162 | }, 163 | "code-point-at": { 164 | "version": "1.1.0", 165 | "from": "code-point-at@>=1.0.0 <2.0.0", 166 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 167 | "dev": true 168 | }, 169 | "colors": { 170 | "version": "1.1.2", 171 | "from": "colors@>=1.1.2 <2.0.0", 172 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", 173 | "dev": true 174 | }, 175 | "commander": { 176 | "version": "2.9.0", 177 | "from": "commander@2.9.0", 178 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 179 | "dev": true 180 | }, 181 | "concat-map": { 182 | "version": "0.0.1", 183 | "from": "concat-map@0.0.1", 184 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 185 | "dev": true 186 | }, 187 | "configstore": { 188 | "version": "2.1.0", 189 | "from": "configstore@>=2.0.0 <3.0.0", 190 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", 191 | "dev": true, 192 | "dependencies": { 193 | "uuid": { 194 | "version": "2.0.3", 195 | "from": "uuid@>=2.0.1 <3.0.0", 196 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", 197 | "dev": true 198 | } 199 | } 200 | }, 201 | "core-util-is": { 202 | "version": "1.0.2", 203 | "from": "core-util-is@>=1.0.0 <1.1.0", 204 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 205 | "dev": true 206 | }, 207 | "create-error-class": { 208 | "version": "3.0.2", 209 | "from": "create-error-class@>=3.0.1 <4.0.0", 210 | "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", 211 | "dev": true 212 | }, 213 | "debug": { 214 | "version": "2.2.0", 215 | "from": "debug@2.2.0", 216 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", 217 | "dev": true 218 | }, 219 | "deep-eql": { 220 | "version": "0.1.3", 221 | "from": "deep-eql@>=0.1.3 <0.2.0", 222 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", 223 | "dev": true, 224 | "dependencies": { 225 | "type-detect": { 226 | "version": "0.1.1", 227 | "from": "type-detect@0.1.1", 228 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", 229 | "dev": true 230 | } 231 | } 232 | }, 233 | "deep-extend": { 234 | "version": "0.4.1", 235 | "from": "deep-extend@>=0.4.0 <0.5.0", 236 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.1.tgz", 237 | "dev": true 238 | }, 239 | "diff": { 240 | "version": "1.4.0", 241 | "from": "diff@1.4.0", 242 | "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz", 243 | "dev": true 244 | }, 245 | "dot-prop": { 246 | "version": "3.0.0", 247 | "from": "dot-prop@>=3.0.0 <4.0.0", 248 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", 249 | "dev": true 250 | }, 251 | "duplexer2": { 252 | "version": "0.1.4", 253 | "from": "duplexer2@>=0.1.4 <0.2.0", 254 | "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", 255 | "dev": true 256 | }, 257 | "error-ex": { 258 | "version": "1.3.0", 259 | "from": "error-ex@>=1.2.0 <2.0.0", 260 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.0.tgz", 261 | "dev": true 262 | }, 263 | "escape-string-regexp": { 264 | "version": "1.0.5", 265 | "from": "escape-string-regexp@1.0.5", 266 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 267 | "dev": true 268 | }, 269 | "esutils": { 270 | "version": "2.0.2", 271 | "from": "esutils@>=2.0.2 <3.0.0", 272 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 273 | "dev": true 274 | }, 275 | "filled-array": { 276 | "version": "1.1.0", 277 | "from": "filled-array@>=1.0.0 <2.0.0", 278 | "resolved": "https://registry.npmjs.org/filled-array/-/filled-array-1.1.0.tgz", 279 | "dev": true 280 | }, 281 | "findup-sync": { 282 | "version": "0.3.0", 283 | "from": "findup-sync@>=0.3.0 <0.4.0", 284 | "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.3.0.tgz", 285 | "dev": true, 286 | "dependencies": { 287 | "glob": { 288 | "version": "5.0.15", 289 | "from": "glob@>=5.0.0 <5.1.0", 290 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", 291 | "dev": true 292 | } 293 | } 294 | }, 295 | "fs.realpath": { 296 | "version": "1.0.0", 297 | "from": "fs.realpath@>=1.0.0 <2.0.0", 298 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 299 | "dev": true 300 | }, 301 | "glob": { 302 | "version": "7.0.5", 303 | "from": "glob@7.0.5", 304 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.0.5.tgz", 305 | "dev": true 306 | }, 307 | "got": { 308 | "version": "5.7.1", 309 | "from": "got@>=5.0.0 <6.0.0", 310 | "resolved": "https://registry.npmjs.org/got/-/got-5.7.1.tgz", 311 | "dev": true 312 | }, 313 | "graceful-fs": { 314 | "version": "4.1.11", 315 | "from": "graceful-fs@>=4.1.2 <5.0.0", 316 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 317 | "dev": true 318 | }, 319 | "graceful-readlink": { 320 | "version": "1.0.1", 321 | "from": "graceful-readlink@>=1.0.0", 322 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 323 | "dev": true 324 | }, 325 | "growl": { 326 | "version": "1.9.2", 327 | "from": "growl@1.9.2", 328 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", 329 | "dev": true 330 | }, 331 | "has-ansi": { 332 | "version": "2.0.0", 333 | "from": "has-ansi@>=2.0.0 <3.0.0", 334 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", 335 | "dev": true 336 | }, 337 | "has-flag": { 338 | "version": "1.0.0", 339 | "from": "has-flag@>=1.0.0 <2.0.0", 340 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 341 | "dev": true 342 | }, 343 | "imurmurhash": { 344 | "version": "0.1.4", 345 | "from": "imurmurhash@>=0.1.4 <0.2.0", 346 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 347 | "dev": true 348 | }, 349 | "inflight": { 350 | "version": "1.0.6", 351 | "from": "inflight@>=1.0.4 <2.0.0", 352 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 353 | "dev": true 354 | }, 355 | "inherits": { 356 | "version": "2.0.3", 357 | "from": "inherits@>=2.0.0 <3.0.0", 358 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 359 | "dev": true 360 | }, 361 | "ini": { 362 | "version": "1.3.4", 363 | "from": "ini@>=1.3.0 <1.4.0", 364 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", 365 | "dev": true 366 | }, 367 | "is-arrayish": { 368 | "version": "0.2.1", 369 | "from": "is-arrayish@>=0.2.1 <0.3.0", 370 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 371 | "dev": true 372 | }, 373 | "is-finite": { 374 | "version": "1.0.2", 375 | "from": "is-finite@>=1.0.0 <2.0.0", 376 | "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", 377 | "dev": true 378 | }, 379 | "is-fullwidth-code-point": { 380 | "version": "1.0.0", 381 | "from": "is-fullwidth-code-point@>=1.0.0 <2.0.0", 382 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 383 | "dev": true 384 | }, 385 | "is-npm": { 386 | "version": "1.0.0", 387 | "from": "is-npm@>=1.0.0 <2.0.0", 388 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", 389 | "dev": true 390 | }, 391 | "is-obj": { 392 | "version": "1.0.1", 393 | "from": "is-obj@>=1.0.0 <2.0.0", 394 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", 395 | "dev": true 396 | }, 397 | "is-redirect": { 398 | "version": "1.0.0", 399 | "from": "is-redirect@>=1.0.0 <2.0.0", 400 | "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", 401 | "dev": true 402 | }, 403 | "is-retry-allowed": { 404 | "version": "1.1.0", 405 | "from": "is-retry-allowed@>=1.0.0 <2.0.0", 406 | "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", 407 | "dev": true 408 | }, 409 | "is-stream": { 410 | "version": "1.1.0", 411 | "from": "is-stream@>=1.0.0 <2.0.0", 412 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 413 | "dev": true 414 | }, 415 | "is-utf8": { 416 | "version": "0.2.1", 417 | "from": "is-utf8@>=0.2.0 <0.3.0", 418 | "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", 419 | "dev": true 420 | }, 421 | "isarray": { 422 | "version": "1.0.0", 423 | "from": "isarray@>=1.0.0 <1.1.0", 424 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 425 | "dev": true 426 | }, 427 | "js-tokens": { 428 | "version": "2.0.0", 429 | "from": "js-tokens@>=2.0.0 <3.0.0", 430 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-2.0.0.tgz", 431 | "dev": true 432 | }, 433 | "json3": { 434 | "version": "3.3.2", 435 | "from": "json3@3.3.2", 436 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", 437 | "dev": true 438 | }, 439 | "latest-version": { 440 | "version": "2.0.0", 441 | "from": "latest-version@>=2.0.0 <3.0.0", 442 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-2.0.0.tgz", 443 | "dev": true 444 | }, 445 | "lazy-req": { 446 | "version": "1.1.0", 447 | "from": "lazy-req@>=1.1.0 <2.0.0", 448 | "resolved": "https://registry.npmjs.org/lazy-req/-/lazy-req-1.1.0.tgz", 449 | "dev": true 450 | }, 451 | "lodash": { 452 | "version": "4.17.4", 453 | "from": "lodash@latest", 454 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz" 455 | }, 456 | "lodash._baseassign": { 457 | "version": "3.2.0", 458 | "from": "lodash._baseassign@>=3.0.0 <4.0.0", 459 | "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", 460 | "dev": true 461 | }, 462 | "lodash._basecopy": { 463 | "version": "3.0.1", 464 | "from": "lodash._basecopy@>=3.0.0 <4.0.0", 465 | "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", 466 | "dev": true 467 | }, 468 | "lodash._basecreate": { 469 | "version": "3.0.3", 470 | "from": "lodash._basecreate@>=3.0.0 <4.0.0", 471 | "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", 472 | "dev": true 473 | }, 474 | "lodash._getnative": { 475 | "version": "3.9.1", 476 | "from": "lodash._getnative@>=3.0.0 <4.0.0", 477 | "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", 478 | "dev": true 479 | }, 480 | "lodash._isiterateecall": { 481 | "version": "3.0.9", 482 | "from": "lodash._isiterateecall@>=3.0.0 <4.0.0", 483 | "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", 484 | "dev": true 485 | }, 486 | "lodash.create": { 487 | "version": "3.1.1", 488 | "from": "lodash.create@3.1.1", 489 | "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", 490 | "dev": true 491 | }, 492 | "lodash.isarguments": { 493 | "version": "3.1.0", 494 | "from": "lodash.isarguments@>=3.0.0 <4.0.0", 495 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", 496 | "dev": true 497 | }, 498 | "lodash.isarray": { 499 | "version": "3.0.4", 500 | "from": "lodash.isarray@>=3.0.0 <4.0.0", 501 | "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", 502 | "dev": true 503 | }, 504 | "lodash.keys": { 505 | "version": "3.1.2", 506 | "from": "lodash.keys@>=3.0.0 <4.0.0", 507 | "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", 508 | "dev": true 509 | }, 510 | "lowercase-keys": { 511 | "version": "1.0.0", 512 | "from": "lowercase-keys@>=1.0.0 <2.0.0", 513 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", 514 | "dev": true 515 | }, 516 | "make-error": { 517 | "version": "1.2.1", 518 | "from": "make-error@>=1.1.1 <2.0.0", 519 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.2.1.tgz", 520 | "dev": true 521 | }, 522 | "minimatch": { 523 | "version": "3.0.3", 524 | "from": "minimatch@>=3.0.2 <4.0.0", 525 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 526 | "dev": true 527 | }, 528 | "minimist": { 529 | "version": "0.0.8", 530 | "from": "minimist@0.0.8", 531 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 532 | "dev": true 533 | }, 534 | "mkdirp": { 535 | "version": "0.5.1", 536 | "from": "mkdirp@0.5.1", 537 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 538 | "dev": true 539 | }, 540 | "mocha": { 541 | "version": "3.2.0", 542 | "from": "mocha@>=3.0.0 <4.0.0", 543 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.2.0.tgz", 544 | "dev": true 545 | }, 546 | "ms": { 547 | "version": "0.7.1", 548 | "from": "ms@0.7.1", 549 | "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", 550 | "dev": true 551 | }, 552 | "node-status-codes": { 553 | "version": "1.0.0", 554 | "from": "node-status-codes@>=1.0.0 <2.0.0", 555 | "resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz", 556 | "dev": true 557 | }, 558 | "number-is-nan": { 559 | "version": "1.0.1", 560 | "from": "number-is-nan@>=1.0.0 <2.0.0", 561 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 562 | "dev": true 563 | }, 564 | "object-assign": { 565 | "version": "4.1.0", 566 | "from": "object-assign@>=4.0.1 <5.0.0", 567 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", 568 | "dev": true 569 | }, 570 | "once": { 571 | "version": "1.4.0", 572 | "from": "once@>=1.3.0 <2.0.0", 573 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 574 | "dev": true 575 | }, 576 | "optimist": { 577 | "version": "0.6.1", 578 | "from": "optimist@>=0.6.0 <0.7.0", 579 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 580 | "dev": true 581 | }, 582 | "os-homedir": { 583 | "version": "1.0.2", 584 | "from": "os-homedir@>=1.0.0 <2.0.0", 585 | "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", 586 | "dev": true 587 | }, 588 | "os-tmpdir": { 589 | "version": "1.0.2", 590 | "from": "os-tmpdir@>=1.0.0 <2.0.0", 591 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", 592 | "dev": true 593 | }, 594 | "osenv": { 595 | "version": "0.1.4", 596 | "from": "osenv@>=0.1.0 <0.2.0", 597 | "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", 598 | "dev": true 599 | }, 600 | "package-json": { 601 | "version": "2.4.0", 602 | "from": "package-json@>=2.0.0 <3.0.0", 603 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-2.4.0.tgz", 604 | "dev": true 605 | }, 606 | "parse-json": { 607 | "version": "2.2.0", 608 | "from": "parse-json@>=2.2.0 <3.0.0", 609 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", 610 | "dev": true 611 | }, 612 | "path-is-absolute": { 613 | "version": "1.0.1", 614 | "from": "path-is-absolute@>=1.0.0 <2.0.0", 615 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 616 | "dev": true 617 | }, 618 | "pinkie": { 619 | "version": "2.0.4", 620 | "from": "pinkie@>=2.0.4 <3.0.0", 621 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 622 | "dev": true 623 | }, 624 | "pinkie-promise": { 625 | "version": "2.0.1", 626 | "from": "pinkie-promise@>=2.0.0 <3.0.0", 627 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 628 | "dev": true 629 | }, 630 | "prepend-http": { 631 | "version": "1.0.4", 632 | "from": "prepend-http@>=1.0.1 <2.0.0", 633 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", 634 | "dev": true 635 | }, 636 | "process-nextick-args": { 637 | "version": "1.0.7", 638 | "from": "process-nextick-args@>=1.0.6 <1.1.0", 639 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 640 | "dev": true 641 | }, 642 | "rc": { 643 | "version": "1.1.6", 644 | "from": "rc@>=1.1.6 <2.0.0", 645 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz", 646 | "dev": true, 647 | "dependencies": { 648 | "minimist": { 649 | "version": "1.2.0", 650 | "from": "minimist@>=1.2.0 <2.0.0", 651 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 652 | "dev": true 653 | }, 654 | "strip-json-comments": { 655 | "version": "1.0.4", 656 | "from": "strip-json-comments@>=1.0.4 <1.1.0", 657 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", 658 | "dev": true 659 | } 660 | } 661 | }, 662 | "read-all-stream": { 663 | "version": "3.1.0", 664 | "from": "read-all-stream@>=3.0.0 <4.0.0", 665 | "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.1.0.tgz", 666 | "dev": true 667 | }, 668 | "readable-stream": { 669 | "version": "2.2.2", 670 | "from": "readable-stream@>=2.0.5 <3.0.0", 671 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.2.tgz", 672 | "dev": true 673 | }, 674 | "registry-auth-token": { 675 | "version": "3.1.0", 676 | "from": "registry-auth-token@>=3.0.1 <4.0.0", 677 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.1.0.tgz", 678 | "dev": true 679 | }, 680 | "registry-url": { 681 | "version": "3.1.0", 682 | "from": "registry-url@>=3.0.3 <4.0.0", 683 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 684 | "dev": true 685 | }, 686 | "repeating": { 687 | "version": "2.0.1", 688 | "from": "repeating@>=2.0.0 <3.0.0", 689 | "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", 690 | "dev": true 691 | }, 692 | "resolve": { 693 | "version": "1.2.0", 694 | "from": "resolve@>=1.1.7 <2.0.0", 695 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.2.0.tgz", 696 | "dev": true 697 | }, 698 | "rimraf": { 699 | "version": "2.5.4", 700 | "from": "rimraf@>=2.0.0 <3.0.0", 701 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.5.4.tgz", 702 | "dev": true 703 | }, 704 | "semver": { 705 | "version": "5.3.0", 706 | "from": "semver@>=5.1.0 <6.0.0", 707 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", 708 | "dev": true 709 | }, 710 | "semver-diff": { 711 | "version": "2.1.0", 712 | "from": "semver-diff@>=2.0.0 <3.0.0", 713 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", 714 | "dev": true 715 | }, 716 | "slide": { 717 | "version": "1.1.6", 718 | "from": "slide@>=1.1.5 <2.0.0", 719 | "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz", 720 | "dev": true 721 | }, 722 | "source-map": { 723 | "version": "0.5.6", 724 | "from": "source-map@>=0.5.3 <0.6.0", 725 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", 726 | "dev": true 727 | }, 728 | "source-map-support": { 729 | "version": "0.4.8", 730 | "from": "source-map-support@>=0.4.0 <0.5.0", 731 | "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.8.tgz", 732 | "dev": true 733 | }, 734 | "sprintf-js": { 735 | "version": "1.0.3", 736 | "from": "sprintf-js@latest", 737 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" 738 | }, 739 | "string_decoder": { 740 | "version": "0.10.31", 741 | "from": "string_decoder@>=0.10.0 <0.11.0", 742 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 743 | "dev": true 744 | }, 745 | "string-width": { 746 | "version": "1.0.2", 747 | "from": "string-width@>=1.0.1 <2.0.0", 748 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 749 | "dev": true 750 | }, 751 | "strip-ansi": { 752 | "version": "3.0.1", 753 | "from": "strip-ansi@>=3.0.0 <4.0.0", 754 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 755 | "dev": true 756 | }, 757 | "strip-bom": { 758 | "version": "2.0.0", 759 | "from": "strip-bom@>=2.0.0 <3.0.0", 760 | "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", 761 | "dev": true 762 | }, 763 | "strip-json-comments": { 764 | "version": "2.0.1", 765 | "from": "strip-json-comments@>=2.0.0 <3.0.0", 766 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 767 | "dev": true 768 | }, 769 | "supports-color": { 770 | "version": "3.1.2", 771 | "from": "supports-color@3.1.2", 772 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", 773 | "dev": true 774 | }, 775 | "timed-out": { 776 | "version": "3.1.3", 777 | "from": "timed-out@>=3.0.0 <4.0.0", 778 | "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", 779 | "dev": true 780 | }, 781 | "ts-node": { 782 | "version": "1.7.3", 783 | "from": "ts-node@>=1.0.0 <2.0.0", 784 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-1.7.3.tgz", 785 | "dev": true, 786 | "dependencies": { 787 | "diff": { 788 | "version": "3.2.0", 789 | "from": "diff@>=3.1.0 <4.0.0", 790 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", 791 | "dev": true 792 | }, 793 | "minimist": { 794 | "version": "1.2.0", 795 | "from": "minimist@>=1.2.0 <2.0.0", 796 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", 797 | "dev": true 798 | } 799 | } 800 | }, 801 | "tsconfig": { 802 | "version": "5.0.3", 803 | "from": "tsconfig@>=5.0.2 <6.0.0", 804 | "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz", 805 | "dev": true 806 | }, 807 | "tslint": { 808 | "version": "4.3.1", 809 | "from": "tslint@>=4.0.0 <5.0.0", 810 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-4.3.1.tgz", 811 | "dev": true, 812 | "dependencies": { 813 | "diff": { 814 | "version": "3.2.0", 815 | "from": "diff@>=3.0.1 <4.0.0", 816 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", 817 | "dev": true 818 | }, 819 | "glob": { 820 | "version": "7.1.1", 821 | "from": "glob@>=7.1.1 <8.0.0", 822 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 823 | "dev": true 824 | } 825 | } 826 | }, 827 | "type-detect": { 828 | "version": "1.0.0", 829 | "from": "type-detect@>=1.0.0 <2.0.0", 830 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", 831 | "dev": true 832 | }, 833 | "typescript": { 834 | "version": "2.1.5", 835 | "from": "typescript@>=2.0.0 <3.0.0", 836 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.1.5.tgz", 837 | "dev": true 838 | }, 839 | "underscore.string": { 840 | "version": "3.3.4", 841 | "from": "underscore.string@>=3.3.4 <4.0.0", 842 | "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz", 843 | "dev": true 844 | }, 845 | "unzip-response": { 846 | "version": "1.0.2", 847 | "from": "unzip-response@>=1.0.2 <2.0.0", 848 | "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-1.0.2.tgz", 849 | "dev": true 850 | }, 851 | "update-notifier": { 852 | "version": "1.0.3", 853 | "from": "update-notifier@>=1.0.2 <2.0.0", 854 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz", 855 | "dev": true 856 | }, 857 | "url-parse-lax": { 858 | "version": "1.0.0", 859 | "from": "url-parse-lax@>=1.0.0 <2.0.0", 860 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", 861 | "dev": true 862 | }, 863 | "user-home": { 864 | "version": "1.1.1", 865 | "from": "user-home@>=1.1.1 <2.0.0", 866 | "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz", 867 | "dev": true 868 | }, 869 | "util-deprecate": { 870 | "version": "1.0.2", 871 | "from": "util-deprecate@>=1.0.2 <2.0.0", 872 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 873 | "dev": true 874 | }, 875 | "uuid": { 876 | "version": "3.0.1", 877 | "from": "uuid@latest", 878 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz" 879 | }, 880 | "v8flags": { 881 | "version": "2.0.11", 882 | "from": "v8flags@>=2.0.11 <3.0.0", 883 | "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.0.11.tgz", 884 | "dev": true 885 | }, 886 | "widest-line": { 887 | "version": "1.0.0", 888 | "from": "widest-line@>=1.0.0 <2.0.0", 889 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-1.0.0.tgz", 890 | "dev": true 891 | }, 892 | "wordwrap": { 893 | "version": "0.0.3", 894 | "from": "wordwrap@>=0.0.2 <0.1.0", 895 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 896 | "dev": true 897 | }, 898 | "wrappy": { 899 | "version": "1.0.2", 900 | "from": "wrappy@>=1.0.0 <2.0.0", 901 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 902 | "dev": true 903 | }, 904 | "write-file-atomic": { 905 | "version": "1.3.1", 906 | "from": "write-file-atomic@>=1.1.2 <2.0.0", 907 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.1.tgz", 908 | "dev": true 909 | }, 910 | "xdg-basedir": { 911 | "version": "2.0.0", 912 | "from": "xdg-basedir@>=2.0.0 <3.0.0", 913 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", 914 | "dev": true 915 | }, 916 | "xtend": { 917 | "version": "4.0.1", 918 | "from": "xtend@>=4.0.0 <5.0.0", 919 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 920 | "dev": true 921 | }, 922 | "yn": { 923 | "version": "1.2.0", 924 | "from": "yn@>=1.2.0 <2.0.0", 925 | "resolved": "https://registry.npmjs.org/yn/-/yn-1.2.0.tgz", 926 | "dev": true 927 | } 928 | } 929 | } 930 | --------------------------------------------------------------------------------