diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-01-17 00:06:51 -0500 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-01-17 00:07:54 -0500 |
commit | d9c5b399c6ac052936f6f7b6d3002e86d0fcea32 (patch) | |
tree | 4e146643e7843ba8cbfa44a3a6bbe343c9609f7e | |
parent | ae5c35f264320d90711238b8ec0fd43056ec98ed (diff) |
Add donation calculator
-rw-r--r-- | Dockerfile | 15 | ||||
-rw-r--r-- | _config.yml | 2 | ||||
-rw-r--r-- | _posts/2018-01-16-Fees-on-donation-platforms.md | 41 | ||||
-rw-r--r-- | backers.html | 6 | ||||
-rw-r--r-- | donation-calc/.babelrc | 3 | ||||
-rw-r--r-- | donation-calc/.gitignore | 1 | ||||
-rw-r--r-- | donation-calc/index.js | 579 | ||||
-rw-r--r-- | donation-calc/package-lock.json | 3605 | ||||
-rw-r--r-- | donation-calc/package.json | 29 | ||||
-rw-r--r-- | donation-calc/webpack.config.js | 18 | ||||
-rw-r--r-- | js/donation-calc.js | 23 | ||||
-rw-r--r-- | js/donation-calc.js.map | 1 |
12 files changed, 4323 insertions, 0 deletions
diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b640155 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,15 @@ +FROM ruby:latest + +RUN gem install \ + github-pages \ + jekyll \ + jekyll-redirect-from \ + kramdown \ + rdiscount \ + rouge + +VOLUME /src +EXPOSE 4000 + +WORKDIR /src +ENTRYPOINT ["jekyll"] diff --git a/_config.yml b/_config.yml index ae31b3d..1ace779 100644 --- a/_config.yml +++ b/_config.yml @@ -8,3 +8,5 @@ paginate_path: "/archive/page:num/" twitter: username: sircmpwn logo: /avatar.png +exclude: + - donation-calc diff --git a/_posts/2018-01-16-Fees-on-donation-platforms.md b/_posts/2018-01-16-Fees-on-donation-platforms.md new file mode 100644 index 0000000..d5a98e4 --- /dev/null +++ b/_posts/2018-01-16-Fees-on-donation-platforms.md @@ -0,0 +1,41 @@ +--- +layout: post +title: Fee breakdown for various donation platforms +--- + +Understanding fees are a really confusing part of supporting creators of things +you like. I provide a few ways for people to support my work, and my supporters +can struggle to understand the differences between them. It comes down to fees, +of which there are several kinds (note: I just made these terms up): + +- **Transaction fees** are charged by the payment processor (the company that + takes down your card number and runs the transaction with your bank). These + are typically in the form of a percentage of the transaction plus a few cents. +- **Platform fees** are charged by the platform (e.g. Patreon) to run their + operation, typically in the form of a fixed percentage of the transaction. +- **Withdrawl fees** are charged to move money from the platform to the + creator's bank account. These vary depending on the withdrawl processor. +- **Taxes** are also implicated, depending on how much the creator makes. + +All of this adds up to a very confusing picture. I've made a calculator to help +you sort it out. + +<noscript>Sorry, the calculator requires JavaScript.</noscript> +<div id="react-root"></div> +<script src="/js/donation-calc.js"></script> + +### Sources + +**fosspay** + +Only the typical [Stripe fee](https://stripe.com/us/pricing) is applied. + +**Patreon** + +[How do you calculate fees?](https://patreon.zendesk.com/hc/en-us/articles/204606125-How-do-you-calculate-fees-) + +[What are my options to receive payout?](https://patreon.zendesk.com/hc/en-us/articles/203913489-What-are-my-options-to-receive-payout-) + +**Liberapay** + +[FAQ](https://liberapay.com/about/faq) diff --git a/backers.html b/backers.html index 65f4094..2a1a293 100644 --- a/backers.html +++ b/backers.html @@ -26,6 +26,12 @@ title: Donations and early access to some projects are available to supporters on Patreon and fosspay! </p> +<p> + I have also written a + <a href="/2018/01/16/Fees-on-donation-platforms.html"> + blog post + </a> comparing how fees work across each of these platforms. +</p> <h2>Backers</h2> <p> <strong> diff --git a/donation-calc/.babelrc b/donation-calc/.babelrc new file mode 100644 index 0000000..626ad90 --- /dev/null +++ b/donation-calc/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["env", "stage-0", "react", "es2015"] +} diff --git a/donation-calc/.gitignore b/donation-calc/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/donation-calc/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/donation-calc/index.js b/donation-calc/index.js new file mode 100644 index 0000000..fd1f053 --- /dev/null +++ b/donation-calc/index.js @@ -0,0 +1,579 @@ +// I sincerely apologise for the garbage you're about to see + +// This file is public domain, I guess. If you use this I would appreciate +// it if you linked back to my blog, but I ain't a cop. + +import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; + +class Calculator extends Component { + constructor() { + super(); + this.state = { + provider: "fosspay", + amount: "5.00", + inMethod: "card-usd", + inAmount: "100.00", + frequency: "month", + outMethod: "usd", + patreonCreators: [ + { "id": 0, "username": "SirCmpwn", "amount": "5.00" } + ] + }; + this.renderFosspay = this.renderFosspay.bind(this); + this.renderLiberapay = this.renderLiberapay.bind(this); + this.renderPatreon = this.renderPatreon.bind(this); + } + + renderFosspay() { + const { amount } = this.state; + let amt = parseFloat(amount); + const stripe = amt * 0.029 + 0.30; + const creator = amt - stripe; + return ( + <div> + <div class="form-group"> + <label for="amount">Amount</label> + <div class="input-group"> + <span class="input-group-addon">$</span> + <input + type="text" + class="form-control" + aria-label="Amount" + onChange={e => this.setState({ amount: e.target.value })} + value={amount} + /> + <span class="input-group-addon">per month</span> + </div> + </div> + <table class="table"> + <thead> + <tr> + <th style={{ width: "12rem" }}>You pay</th> + <th></th> + <th + style={{ textAlign: "right" }} + >${amt.toFixed(2)}</th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>Stripe fee</td> + <td>2.9% + 30c</td> + <td + style={{ textAlign: "right" }} + >${stripe.toFixed(2)}</td> + <td></td> + </tr> + <tr class="active"> + <th>Creator earns (pre-tax)</th> + <th></th> + <th + style={{ textAlign: "right" }} + >${creator.toFixed(2)}</th> + <td>per month</td> + </tr> + <tr> + <th></th> + <th></th> + <th + style={{ textAlign: "right" }} + >{(creator / amt * 100).toFixed(0)}%</th> + <td>of your donation</td> + </tr> + </tbody> + </table> + </div> + ); + } + + renderLiberapay() { + const { amount, inMethod, frequency, outMethod } = this.state; + let amt = parseFloat(this.state.amount); + let inAmount = parseFloat(this.state.inAmount); + let inFee; + switch (inMethod) { + case "card-usd": + inFee = inAmount * 0.02925 + 0.35; + break; + case "card-euro": + inFee = inAmount * 0.02106 + 0.26; + break; + case "wire": + inFee = inAmount * 0.00585; + break; + case "debit": + inFee = 0.72; + break; + } + let lpBalance = inAmount - inFee; + const creator = amt; + const outFee = ({ + "sepa": 0, + "euro": 2.93, + "usd": 3.51 + })[outMethod]; + const creatorOut = lpBalance - outFee; + const duration = Math.floor(lpBalance / amt); + const inTypes = { + "card-usd": "Card", + "card-euro": "Card", + "wire": "Wire", + "debit": "Debit", + }; + const humanInFee = { + "card-usd": "2.925%+35c", + "card-euro": "2.106%+€0.21", + "wire": "0.585%", + "debit": "€0.59", + }; + return ( + <div> + <p> + If you transfer this amount into your Liberapay wallet: + </p> + <div class="form-group"> + <label for="amount">Amount</label> + <div class="input-group"> + <span class="input-group-addon">$</span> + <input + type="text" + class="form-control" + aria-label="Amount" + onChange={e => this.setState({ inAmount: e.target.value })} + value={this.state.inAmount} + /> + </div> + <div style={{marginTop: "1rem"}}> + <label>Transfer method</label> + </div> + <fieldset class="form-group"> + <label class="radio-inline"> + <input + type="radio" + name="inMethod" + checked={inMethod === "card-usd"} + onChange={e => this.setState({ inMethod: "card-usd" })} + /> + Card - USD (2.925%+35c) + </label> + <label class="radio-inline"> + <input + type="radio" + name="inMethod" + checked={inMethod === "card-euro"} + onChange={e => this.setState({ inMethod: "card-euro" })} + /> + Card - Euro (2.106%+€0.21) + </label> + </fieldset> + <fieldset class="form-group"> + <label class="radio-inline"> + <input + type="radio" + name="inMethod" + checked={inMethod === "wire"} + onChange={e => this.setState({ inMethod: "wire" })} + /> + Wire transfer (0.585%) + </label> + <label class="radio-inline"> + <input + type="radio" + name="inMethod" + checked={inMethod === "debit"} + onChange={e => this.setState({ inMethod: "debit" })} + /> + Direct debit - Euro (€0.59) + </label> + </fieldset> + </div> + <p>Your <strong>transaction fees</strong> will be:</p> + <table class="table"> + <thead> + <tr> + <th style={{ width: "12rem" }}>You pay</th> + <th></th> + <th>${inAmount.toFixed(2)}</th> + </tr> + </thead> + <tbody> + <tr class="active"> + <td>{inTypes[inMethod]} fee</td> + <td>{humanInFee[inMethod]}</td> + <td>(${inFee.toFixed(2)})</td> + </tr> + <tr> + <td>Liberapay balance</td> + <td></td> + <td>${lpBalance.toFixed(2)}</td> + </tr> + </tbody> + </table> + <p>If you support your creators for this amount:</p> + <div class="form-group"> + <label for="amount">Amount</label> + <div class="input-group"> + <span class="input-group-addon">$</span> + <input + type="text" + class="form-control" + aria-label="Amount" + onChange={e => this.setState({ amount: e.target.value })} + value={amount} + /> + </div> + </div> + <fieldset class="form-group" style={{ marginTop: "1rem" }}> + <label class="radio-inline"> + <input + type="radio" + name="frequency" + checked={frequency === "week"} + onChange={e => this.setState({ frequency: "week" })} + /> Per week + </label> + <label class="radio-inline"> + <input + type="radio" + name="frequency" + checked={frequency === "month"} + onChange={e => this.setState({ frequency: "month" })} + /> Per month + </label> + <label class="radio-inline"> + <input + type="radio" + name="frequency" + checked={frequency === "year"} + onChange={e => this.setState({ frequency: "year" })} + /> Per year + </label> + </fieldset> + <p> + Your creators earn <strong>${ + creator.toFixed(2)}</strong> per { + frequency} for <strong>{duration} {frequency}(s)</strong>. + If they wait until the end of this timeframe to withdraw their funds, + their <strong>withdrawl fees</strong> will look like this: + </p> + <fieldset class="form-group" style={{ marginTop: "1rem" }}> + <label class="radio-inline"> + <input + type="radio" + name="outMethod" + checked={outMethod === "sepa"} + onChange={e => this.setState({ outMethod: "sepa" })} + /> + <a href="https://en.wikipedia.org/wiki/Single_Euro_Payments_Area"> + SEPA + </a> (Europe): Free + </label> + <label class="radio-inline"> + <input + type="radio" + name="outMethod" + checked={outMethod === "euro"} + onChange={e => this.setState({ outMethod: "euro" })} + /> Other Europe: €2.93 + </label> + <label class="radio-inline"> + <input + type="radio" + name="outMethod" + checked={outMethod === "usd"} + onChange={e => this.setState({ outMethod: "usd" })} + /> USD: $3.51 + </label> + </fieldset> + <table class="table"> + <thead> + <tr> + <th style={{ width: "12rem" }}>You pay</th> + <th></th> + <th + style={{ textAlign: "right" }} + >${lpBalance.toFixed(2)}</th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>Withdrawl fee</td> + <td></td> + <td + style={{ textAlign: "right" }} + >(${outFee.toFixed(2)})</td> + <th></th> + </tr> + <tr class="active"> + <th>Creator earns (pre-tax)</th> + <th></th> + <th + style={{ textAlign: "right" }} + >${creatorOut.toFixed(2)}</th> + <td> + over {duration} {frequency}(s) + </td> + </tr> + <tr> + <th></th> + <th></th> + <th + style={{ textAlign: "right" }} + >{(creatorOut / inAmount * 100).toFixed(0)}%</th> + <td>of your donation</td> + </tr> + </tbody> + </table> + <div class="alert alert-info"> + Fees in euros are for reference only. All amounts should be input in + USD and all computed amounts are shown in USD based on the (probably + dated) assumption that €1 EUR = $1.22 USD. Sorry. + </div> + </div> + ); + } + + renderPatreon() { + const creators = this.state.patreonCreators.sort(c => c.id); + const total = creators.map(c => parseFloat(c.amount)) + .reduce((i, acc) => acc + i, 0); + const tx_fee = total * 0.029 + 0.30; + return ( + <div> + {creators.map(creator => + <div key={creator.id}> + <div class="form-group"> + <label for={`user-${creator.id}`}>Creator</label> + <input + id={`user-${creator.id}`} + type="text" + class="form-control" + aria-label="Username" + onChange={e => this.setState({ + patreonCreators: [ + { ...creator, username: e.target.value }, + ...creators.filter(c => c.id != creator.id) + ] + })} + value={creator.username} + /> + </div> + <div class="form-group"> + <label for="amount">Amount</label> + <div class="input-group"> + <span class="input-group-addon">$</span> + <input + type="text" + class="form-control" + aria-label="Amount" + onChange={e => this.setState({ + patreonCreators: [ + { ...creator, amount: e.target.value }, + ...creators.filter(c => c.id != creator.id) + ] + })} + value={creator.amount} + /> + <span class="input-group-addon">per month</span> + </div> + </div> + </div> + )} + <div class="form-group"> + <button + class="btn btn-default" + onClick={e => { + e.preventDefault(); + this.setState({ + patreonCreators: [ + ...creators, + { + "id": Math.max(...creators.map(c => c.id)) + 1, + "username": "", + "amount": "5.00", + } + ] + }); + }} + >Add another creator</button> + {creators.length > 1 && <button + class="btn btn-default" + style={{marginLeft: "1rem"}} + onClick={e => { + e.preventDefault(); + this.setState({ + patreonCreators: creators.slice(0, -1) + }); + }} + >Remove a creator</button>} + </div> + <p> + You only pay one <strong>transaction fee</strong> for all of your + creators: + </p> + <table class="table"> + <thead> + <tr> + <th style={{ width: "12rem" }}>You pay</th> + <th></th> + <th + style={{ textAlign: "right" }} + >${total.toFixed(2)}</th> + <th></th> + </tr> + </thead> + <tbody> + <tr> + <td>Transaction fee</td> + <td>2.9% + 30c</td> + <td + style={{ textAlign: "right" }} + >(${tx_fee.toFixed(2)})</td> + <td></td> + </tr> + <tr> + <th></th> + <th style={{textAlign: "right"}}>=</th> + <th + style={{ textAlign: "right" }} + >${(total - tx_fee).toFixed(2)}</th> + <th></th> + </tr> + </tbody> + </table> + <p> + Your total minus the transaction fee is distributed to your creators + like so: + </p> + <table class="table"> + <tbody> + {creators.map(c => { + const amt = parseFloat(c.amount); + const share = (amt / total) * (total - tx_fee); + const patreon_fee = share * 0.05; + const after_patreon = share - patreon_fee; + const clamp = (value, min, max) => Math.min(Math.max(value)); + const withdrawl_fee = clamp(after_patreon * 0.01, 0.25, 20); + const creator_total = after_patreon - withdrawl_fee; + return [ + <tr class="active"> + <th>{c.username}</th> + <td></td> + <td></td> + <td></td> + </tr>, + <tr> + <th>You pay</th> + <td></td> + <td + style={{textAlign: "right"}} + >${amt.toFixed(2)}</td> + <td></td> + </tr>, + <tr> + <td>Transaction fee</td> + <td>(see above)</td> + <td + style={{textAlign: "right"}} + >(${(amt - share).toFixed(2)})</td> + <td></td> + </tr>, + <tr> + <td>Patreon fee</td> + <td>5%</td> + <td + style={{ textAlign: "right" }} + >(${patreon_fee.toFixed(2)})</td> + <td></td> + </tr>, + <tr> + <td>Withdrawl fee*</td> + <td>1% (25c min, $20 max)</td> + <td + style={{ textAlign: "right" }} + >(${withdrawl_fee.toFixed(2)})</td> + <td></td> + </tr>, + <tr> + <th>Creator earns (pre-tax)</th> + <th></th> + <th + style={{ textAlign: "right" }} + >${creator_total.toFixed(2)}</th> + <td>per month</td> + </tr>, + <tr> + <th></th> + <th></th> + <th + style={{ textAlign: "right" }} + >{(creator_total / amt * 100).toFixed(0)}%</th> + <td>of your donation</td> + </tr> + ]; + })} + </tbody> + </table> + <small> + * Withdrawl fee assumes payout via PayPal. Numbers for payout via + Stripe are not available, and numbers for Payoneer are different. <a + href="https://patreon.zendesk.com/hc/en-us/articles/203913489-What-are-my-options-to-receive-payout-">Details here</a>. + </small> + </div> + ); + } + + render() { + const { amount, provider } = this.state; + return ( + <form class="calculator panel panel-default"> + <div class="panel-heading"> + Fee Calculator (based on 2018-01-16 fees) + </div> + <div class="panel-body"> + <div> + {/* I know this is supposed to be a legend inside the fieldset */} + <label>Platform</label> + </div> + <fieldset class="form-group"> + <label class="radio-inline"> + <input + type="radio" + name="platform" + checked={provider === "fosspay"} + onChange={e => this.setState({ provider: "fosspay" })} + /> + fosspay + </label> + <label class="radio-inline"> + <input + type="radio" + name="platform" + checked={provider === "liberapay"} + onChange={e => this.setState({ provider: "liberapay" })} + /> + Liberapay + </label> + <label class="radio-inline"> + <input + type="radio" + name="platform" + checked={provider === "patreon"} + onChange={e => this.setState({ provider: "patreon" })} + /> + Patreon + </label> + </fieldset> + {provider === "fosspay" && this.renderFosspay()} + {provider === "liberapay" && this.renderLiberapay()} + {provider === "patreon" && this.renderPatreon()} + </div> + </form> + ); + } +} + +ReactDOM.render(<Calculator />, document.getElementById("react-root")); diff --git a/donation-calc/package-lock.json b/donation-calc/package-lock.json new file mode 100644 index 0000000..68e9c03 --- /dev/null +++ b/donation-calc/package-lock.json @@ -0,0 +1,3605 @@ +{ + "name": "donation-calculator", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "acorn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", + "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==", + "dev": true + }, + "acorn-dynamic-import": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", + "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", + "dev": true, + "requires": { + "acorn": "4.0.13" + }, + "dependencies": { + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", + "dev": true + } + } + }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "anymatch": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "dev": true, + "requires": { + "micromatch": "2.3.11", + "normalize-path": "2.1.1" + } + }, + "arr-diff": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", + "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "dev": true, + "requires": { + "arr-flatten": "1.1.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "array-unique": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", + "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true + }, + "asn1.js": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", + "integrity": "sha512-b/OsSjvWEo8Pi8H0zsDd2P6Uqo2TK2pH8gNLSJtNLM2Db0v2QaAZ0pBQJXVjAn4gBuugeVDr7s63ZogpUIwWDg==", + "dev": true, + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "dev": true, + "requires": { + "util": "0.10.3" + } + }, + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "async-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", + "dev": true + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "babel-core": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", + "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "babel-generator": "6.26.0", + "babel-helpers": "6.24.1", + "babel-messages": "6.23.0", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "convert-source-map": "1.5.1", + "debug": "2.6.9", + "json5": "0.5.1", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "path-is-absolute": "1.0.1", + "private": "0.1.8", + "slash": "1.0.0", + "source-map": "0.5.7" + } + }, + "babel-generator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", + "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", + "dev": true, + "requires": { + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.4", + "source-map": "0.5.7", + "trim-right": "1.0.1" + } + }, + "babel-helper-bindify-decorators": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz", + "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "dev": true, + "requires": { + "babel-helper-explode-assignable-expression": "6.24.1", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0" + } + }, + "babel-helper-builder-react-jsx": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz", + "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "esutils": "2.0.2" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "dev": true, + "requires": { + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": " |