<commit_before>/**
 * @fileOverview
 *
 * Implements the EdgeGrid authentication method.
 * Specification document: https://developer.akamai.com/legacy/introduction/Client_Auth.html
 * Sample impletentation by Akamai: https://github.com/akamai/AkamaiOPEN-edgegrid-node
 */

var _ = require('lodash'),
    uuid = require('uuid/v4'),
    crypto = require('crypto'),
    sdk = require('postman-collection'),
    RequestBody = sdk.RequestBody,
    querystring = require('querystring'),
    urlEncoder = require('postman-url-encoder'),
    bodyBuilder = require('../requester/core-body-builder'),

    EMPTY = '',
    COLON = ':',
    UTC_OFFSET = '+0000',
    ZERO = '0',
    DATE_TIME_SEPARATOR = 'T',
    TAB = '\t',
    SPACE = ' ',
    SLASH = '/',
    STRING = 'string',
    SIGNING_ALGORITHM = 'EG1-HMAC-SHA256 ',
    AUTHORIZATION = 'Authorization',

    /**
     * Returns current timestamp in the format described in EdgeGrid specification (yyyyMMddTHH:mm:ss+0000)
     *
     * @returns {String} UTC timestamp in format yyyyMMddTHH:mm:ss+0000
     */
    getTimestamp = function () {
        var date = new Date();

        return date.getUTCFullYear() +
                _.padStart(date.getUTCMonth() + 1, 2, ZERO) +
                _.padStart(date.getUTCDate(), 2, ZERO) +
                DATE_TIME_SEPARATOR +
                _.padStart(date.getUTCHours(), 2, ZERO) +
                COLON +
                _.padStart(date.getUTCMinutes(), 2, ZERO) +
                COLON +
                _.padStart(date.getUTCSeconds(), 2, ZERO) +
                UTC_OFFSET;
    },

    /**
     * Creates a String containing a tab delimited set of headers.
     *
     * @param {String[]} headersToSign Headers to include in signature
     * @param {Object} headers Request headers
     * @returns {String} Canonicalized headers
     */
    canonicalizeHeaders = function (headersToSign, headers) {
        var formattedHeaders = [],
            headerValue;

        headersToSign.forEach(function (headerName) {
            if (typeof headerName !== STRING) { return; }

            // trim the header name to remove extra spaces from user input
            headerName = headerName.trim().toLowerCase();
            headerValue = headers[headerName];

            // should not include empty headers as per the specification
            if (typeof headerValue !== STRING || headerValue === EMPTY) { return; }

            formattedHeaders.push(`${headerName}:${headerValue.trim().replace(/\s+/g, SPACE)}`);
        });

        return formattedHeaders.join(TAB);
    },

    /**
     * Returns base64 encoding of the SHA–256 HMAC of given data signed with given key
     *
     * @param {String} data Data to sign
     * @param {String} key Key to use while signing the data
     * @returns {String} Base64 encoded signature
     */
    base64HmacSha256 = function (data, key) {
        var encrypt = crypto.createHmac('sha256', key);

        encrypt.update(data);

        return encrypt.digest('base64');
    },

    /**
     * Encodes characters not encoded by querystring.stringify() according to RFC3986.
     * REFER: https://github.com/postmanlabs/postman-request/blob/master/lib/querystring.js#L42
     *
     * @param {String} str Partially encoded string by querystring.stringify()
     * @returns {String} Fully encoded string
     */
    rfc3986 = function (str) {
        return str.replace(/[!'()*]/g, function (c) {
            return '%' + c.charCodeAt(0).toString(16).toUpperCase();
        });
    },

    /**
     * Calculates body hash with given algorithm and digestEncoding.
     *
     * @param {RequestBody} body Request body
     * @param {String} algorithm Hash algorithm to use
     * @param {String} digestEncoding Encoding of the hash
     * @param {Function} callback Callback function that will be called with body hash
     */
    computeBodyHash = function (body, algorithm, digestEncoding, callback) {
        if (!(body && algorithm && digestEncoding) || body.isEmpty()) { return callback(); }

        var hash = crypto.createHash(algorithm),
            originalReadStream,
            rawBody,
            urlencodedBody,
            graphqlBody;

        if (body.mode === RequestBody.MODES.raw) {
            rawBody = bodyBuilder.raw(body.raw).body;
            hash.update(rawBody);

            return callback(hash.digest(digestEncoding));
        }

        if (body.mode === RequestBody.MODES.urlencoded) {
            urlencodedBody = bodyBuilder.urlencoded(body.urlencoded).form;
            urlencodedBody = querystring.stringify(urlencodedBody);
            urlencodedBody = rfc3986(urlencodedBody);
            hash.update(urlencodedBody);

            return callback(hash.digest(digestEncoding));
        }

        if (body.mode === RequestBody.MODES.file) {
            originalReadStream = _.get(body, 'file.content');

            if (!originalReadStream) {
                return callback();
            }

            return originalReadStream.cloneReadStream(function (err, clonedStream) {
                if (err) { return callback(); }

                clonedStream.on('data', function (chunk) {
                    hash.update(chunk);
                });

                clonedStream.on('end', function () {
                    callback(hash.digest(digestEncoding));
                });
            });
        }

        if (body.mode === RequestBody.MODES.graphql) {
            graphqlBody = bodyBuilder.graphql(body.graphql).body;
            hash.update(graphqlBody);

            return callback(hash.digest(digestEncoding));
        }

        // @todo: Figure out a way to calculate hash for formdata body type.

        // ensure that callback is called if body.mode doesn't match with any of the above modes
        return callback();
    };

/**
 * @implements {AuthHandlerInterface}
 */
module.exports = {
    /**
     * @property {AuthHandlerInterface~AuthManifest}
     */
    manifest: {
        info: {
            name: 'edgegrid',
            version: '1.0.0'
        },
        updates: [
            {
                property: 'Authorization',
                type: 'header'
            }
        ]
    },

    /**
     * Initializes a item (fetches all required parameters, etc) before the actual authorization step.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {Response} response Response of intermediate request (it any)
     * @param {AuthHandlerInterface~authInitHookCallback} done Callback function called with error as first argument
     */
    init: function (auth, response, done) {
        done(null);
    },

    /**
     * Checks the item, and fetches any parameters that are not already provided.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {AuthHandlerInterface~authPreHookCallback} done Callback function called with error, success and request
     */
    pre: function (auth, done) {
        // only check required auth params here
        done(null, Boolean(auth.get('accessToken') && auth.get('clientToken') && auth.get('clientSecret')));
    },

    /**
     * Verifies whether the request was successful after being sent.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {Requester} response Response of the request
     * @param {AuthHandlerInterface~authPostHookCallback} done Callback function called with error and success
     */
    post: function (auth, response, done) {
        done(null, true);
    },

    /**
     * Generates the signature, and returns the Authorization header.
     *
     * @param {Object} params Auth parameters to use in header calculation
     * @param {String} params.accessToken Access token provided by service provider
     * @param {String} params.clientToken Client token provided by service provider
     * @param {String} params.clientSecret Client secret provided by service provider
     * @param {String} params.nonce Nonce to include in authorization header
     * @param {String} params.timestamp Timestamp as defined in protocol specification
     * @param {String} [params.bodyHash] Base64-encoded SHA–256 hash of request body for POST request
     * @param {Object[]} params.headers Request headers
     * @param {String[]} params.headersToSign Ordered list of headers to include in signature
     * @param {String} params.method Request method
     * @param {Url} params.url Node's URL object
     * @returns {String} Authorization header
     */
    computeHeader: function (params) {
        var authHeader = SIGNING_ALGORITHM,
            signingKey = base64HmacSha256(params.timestamp, params.clientSecret),
            dataToSign;

        authHeader += `client_token=${params.clientToken};`;
        authHeader += `access_token=${params.accessToken};`;
        authHeader += `timestamp=${params.timestamp};`;
        authHeader += `nonce=${params.nonce};`;

        dataToSign = [
            params.method,

            // trim to convert 'http:' from Node's URL object to 'http'
            _.trimEnd(params.url.protocol, COLON),
            params.baseURL || params.url.host,
            params.url.path || SLASH,
            canonicalizeHeaders(params.headersToSign, params.headers),
            params.bodyHash || EMPTY,
            authHeader
        ].join(TAB);

        return authHeader + 'signature=' + base64HmacSha256(dataToSign, signingKey);
    },

    /**
     * Signs a request.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {Request} request Request to be sent
     * @param {AuthHandlerInterface~authSignHookCallback} done Callback function
     */
    sign: function (auth, request, done) {
        var params = auth.get([
                'accessToken',
                'clientToken',
                'clientSecret',
                'baseURL',
                'nonce',
                'timestamp',
                'headersToSign'
            ]),
            url = urlEncoder.toNodeUrl(request.url.toString(true)),
            self = this;

        request.removeHeader(AUTHORIZATION, {ignoreCase: true});

        // Extract host from provided baseURL.
        // @note: Here, instead of directly passing params.baseURL in urlEncoder.toNodeUrl() we first parse it using
        //        sdk.Url() and add protocol because urlEncoder.toNodeUrl() method doesn't work properly with URLs
        //        without protocol
        params.baseURL = params.baseURL && urlEncoder.toNodeUrl(new sdk.Url(params.baseURL).toString(true)).host;
        params.nonce = params.nonce || uuid();
        params.timestamp = params.timestamp || getTimestamp();
        params.url = url;
        params.method = request.method;

        // ensure that headers are case-insensitive as specified in the documentation
        params.headers = request.getHeaders({enabled: true, ignoreCase: true});

        if (typeof params.headersToSign === STRING) {
            params.headersToSign = params.headersToSign.split(',');
        }
        else if (!_.isArray(params.headersToSign)) {
            params.headersToSign = [];
        }

        // only calculate body hash for POST requests according to specification
        if (request.method === 'POST') {
            return computeBodyHash(request.body, 'sha256', 'base64', function (bodyHash) {
                params.bodyHash = bodyHash;

                request.addHeader({
                    key: AUTHORIZATION,
                    value: self.computeHeader(params),
                    system: true
                });

                return done();
            });
        }

        request.addHeader({
            key: AUTHORIZATION,
            value: self.computeHeader(params),
            system: true
        });

        return done();
    }
};
<commit_msg>Added sanity check for auth params in edgegrid auth<commit_after>/**
 * @fileOverview
 *
 * Implements the EdgeGrid authentication method.
 * Specification document: https://developer.akamai.com/legacy/introduction/Client_Auth.html
 * Sample impletentation by Akamai: https://github.com/akamai/AkamaiOPEN-edgegrid-node
 */

var _ = require('lodash'),
    uuid = require('uuid/v4'),
    crypto = require('crypto'),
    sdk = require('postman-collection'),
    RequestBody = sdk.RequestBody,
    querystring = require('querystring'),
    urlEncoder = require('postman-url-encoder'),
    bodyBuilder = require('../requester/core-body-builder'),

    EMPTY = '',
    COLON = ':',
    UTC_OFFSET = '+0000',
    ZERO = '0',
    DATE_TIME_SEPARATOR = 'T',
    TAB = '\t',
    SPACE = ' ',
    SLASH = '/',
    STRING = 'string',
    SIGNING_ALGORITHM = 'EG1-HMAC-SHA256 ',
    AUTHORIZATION = 'Authorization',

    /**
     * Returns current timestamp in the format described in EdgeGrid specification (yyyyMMddTHH:mm:ss+0000)
     *
     * @returns {String} UTC timestamp in format yyyyMMddTHH:mm:ss+0000
     */
    getTimestamp = function () {
        var date = new Date();

        return date.getUTCFullYear() +
                _.padStart(date.getUTCMonth() + 1, 2, ZERO) +
                _.padStart(date.getUTCDate(), 2, ZERO) +
                DATE_TIME_SEPARATOR +
                _.padStart(date.getUTCHours(), 2, ZERO) +
                COLON +
                _.padStart(date.getUTCMinutes(), 2, ZERO) +
                COLON +
                _.padStart(date.getUTCSeconds(), 2, ZERO) +
                UTC_OFFSET;
    },

    /**
     * Creates a String containing a tab delimited set of headers.
     *
     * @param {String[]} headersToSign Headers to include in signature
     * @param {Object} headers Request headers
     * @returns {String} Canonicalized headers
     */
    canonicalizeHeaders = function (headersToSign, headers) {
        var formattedHeaders = [],
            headerValue;

        headersToSign.forEach(function (headerName) {
            if (typeof headerName !== STRING) { return; }

            // trim the header name to remove extra spaces from user input
            headerName = headerName.trim().toLowerCase();
            headerValue = headers[headerName];

            // should not include empty headers as per the specification
            if (typeof headerValue !== STRING || headerValue === EMPTY) { return; }

            formattedHeaders.push(`${headerName}:${headerValue.trim().replace(/\s+/g, SPACE)}`);
        });

        return formattedHeaders.join(TAB);
    },

    /**
     * Returns base64 encoding of the SHA–256 HMAC of given data signed with given key
     *
     * @param {String} data Data to sign
     * @param {String} key Key to use while signing the data
     * @returns {String} Base64 encoded signature
     */
    base64HmacSha256 = function (data, key) {
        var encrypt = crypto.createHmac('sha256', key);

        encrypt.update(data);

        return encrypt.digest('base64');
    },

    /**
     * Encodes characters not encoded by querystring.stringify() according to RFC3986.
     * REFER: https://github.com/postmanlabs/postman-request/blob/master/lib/querystring.js#L42
     *
     * @param {String} str Partially encoded string by querystring.stringify()
     * @returns {String} Fully encoded string
     */
    rfc3986 = function (str) {
        return str.replace(/[!'()*]/g, function (c) {
            return '%' + c.charCodeAt(0).toString(16).toUpperCase();
        });
    },

    /**
     * Calculates body hash with given algorithm and digestEncoding.
     *
     * @param {RequestBody} body Request body
     * @param {String} algorithm Hash algorithm to use
     * @param {String} digestEncoding Encoding of the hash
     * @param {Function} callback Callback function that will be called with body hash
     */
    computeBodyHash = function (body, algorithm, digestEncoding, callback) {
        if (!(body && algorithm && digestEncoding) || body.isEmpty()) { return callback(); }

        var hash = crypto.createHash(algorithm),
            originalReadStream,
            rawBody,
            urlencodedBody,
            graphqlBody;

        if (body.mode === RequestBody.MODES.raw) {
            rawBody = bodyBuilder.raw(body.raw).body;
            hash.update(rawBody);

            return callback(hash.digest(digestEncoding));
        }

        if (body.mode === RequestBody.MODES.urlencoded) {
            urlencodedBody = bodyBuilder.urlencoded(body.urlencoded).form;
            urlencodedBody = querystring.stringify(urlencodedBody);
            urlencodedBody = rfc3986(urlencodedBody);
            hash.update(urlencodedBody);

            return callback(hash.digest(digestEncoding));
        }

        if (body.mode === RequestBody.MODES.file) {
            originalReadStream = _.get(body, 'file.content');

            if (!originalReadStream) {
                return callback();
            }

            return originalReadStream.cloneReadStream(function (err, clonedStream) {
                if (err) { return callback(); }

                clonedStream.on('data', function (chunk) {
                    hash.update(chunk);
                });

                clonedStream.on('end', function () {
                    callback(hash.digest(digestEncoding));
                });
            });
        }

        if (body.mode === RequestBody.MODES.graphql) {
            graphqlBody = bodyBuilder.graphql(body.graphql).body;
            hash.update(graphqlBody);

            return callback(hash.digest(digestEncoding));
        }

        // @todo: Figure out a way to calculate hash for formdata body type.

        // ensure that callback is called if body.mode doesn't match with any of the above modes
        return callback();
    };

/**
 * @implements {AuthHandlerInterface}
 */
module.exports = {
    /**
     * @property {AuthHandlerInterface~AuthManifest}
     */
    manifest: {
        info: {
            name: 'edgegrid',
            version: '1.0.0'
        },
        updates: [
            {
                property: 'Authorization',
                type: 'header'
            }
        ]
    },

    /**
     * Initializes a item (fetches all required parameters, etc) before the actual authorization step.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {Response} response Response of intermediate request (it any)
     * @param {AuthHandlerInterface~authInitHookCallback} done Callback function called with error as first argument
     */
    init: function (auth, response, done) {
        done(null);
    },

    /**
     * Checks the item, and fetches any parameters that are not already provided.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {AuthHandlerInterface~authPreHookCallback} done Callback function called with error, success and request
     */
    pre: function (auth, done) {
        // only check required auth params here
        done(null, Boolean(auth.get('accessToken') && auth.get('clientToken') && auth.get('clientSecret')));
    },

    /**
     * Verifies whether the request was successful after being sent.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {Requester} response Response of the request
     * @param {AuthHandlerInterface~authPostHookCallback} done Callback function called with error and success
     */
    post: function (auth, response, done) {
        done(null, true);
    },

    /**
     * Generates the signature, and returns the Authorization header.
     *
     * @param {Object} params Auth parameters to use in header calculation
     * @param {String} params.accessToken Access token provided by service provider
     * @param {String} params.clientToken Client token provided by service provider
     * @param {String} params.clientSecret Client secret provided by service provider
     * @param {String} params.nonce Nonce to include in authorization header
     * @param {String} params.timestamp Timestamp as defined in protocol specification
     * @param {String} [params.bodyHash] Base64-encoded SHA–256 hash of request body for POST request
     * @param {Object[]} params.headers Request headers
     * @param {String[]} params.headersToSign Ordered list of headers to include in signature
     * @param {String} params.method Request method
     * @param {Url} params.url Node's URL object
     * @returns {String} Authorization header
     */
    computeHeader: function (params) {
        var authHeader = SIGNING_ALGORITHM,
            signingKey = base64HmacSha256(params.timestamp, params.clientSecret),
            dataToSign;

        authHeader += `client_token=${params.clientToken};`;
        authHeader += `access_token=${params.accessToken};`;
        authHeader += `timestamp=${params.timestamp};`;
        authHeader += `nonce=${params.nonce};`;

        dataToSign = [
            params.method,

            // trim to convert 'http:' from Node's URL object to 'http'
            _.trimEnd(params.url.protocol, COLON),
            params.baseURL || params.url.host,
            params.url.path || SLASH,
            canonicalizeHeaders(params.headersToSign, params.headers),
            params.bodyHash || EMPTY,
            authHeader
        ].join(TAB);

        return authHeader + 'signature=' + base64HmacSha256(dataToSign, signingKey);
    },

    /**
     * Signs a request.
     *
     * @param {AuthInterface} auth AuthInterface instance created with request auth
     * @param {Request} request Request to be sent
     * @param {AuthHandlerInterface~authSignHookCallback} done Callback function
     */
    sign: function (auth, request, done) {
        var params = auth.get([
                'accessToken',
                'clientToken',
                'clientSecret',
                'baseURL',
                'nonce',
                'timestamp',
                'headersToSign'
            ]),
            url = urlEncoder.toNodeUrl(request.url.toString(true)),
            self = this;

        if (!(params.accessToken && params.clientToken && params.clientSecret)) {
            return done(); // Nothing to do if required parameters are not present.
        }

        request.removeHeader(AUTHORIZATION, {ignoreCase: true});

        // Extract host from provided baseURL.
        // @note: Here, instead of directly passing params.baseURL in urlEncoder.toNodeUrl() we first parse it using
        //        sdk.Url() and add protocol because urlEncoder.toNodeUrl() method doesn't work properly with URLs
        //        without protocol
        params.baseURL = params.baseURL && urlEncoder.toNodeUrl(new sdk.Url(params.baseURL).toString(true)).host;
        params.nonce = params.nonce || uuid();
        params.timestamp = params.timestamp || getTimestamp();
        params.url = url;
        params.method = request.method;

        // ensure that headers are case-insensitive as specified in the documentation
        params.headers = request.getHeaders({enabled: true, ignoreCase: true});

        if (typeof params.headersToSign === STRING) {
            params.headersToSign = params.headersToSign.split(',');
        }
        else if (!_.isArray(params.headersToSign)) {
            params.headersToSign = [];
        }

        // only calculate body hash for POST requests according to specification
        if (request.method === 'POST') {
            return computeBodyHash(request.body, 'sha256', 'base64', function (bodyHash) {
                params.bodyHash = bodyHash;

                request.addHeader({
                    key: AUTHORIZATION,
                    value: self.computeHeader(params),
                    system: true
                });

                return done();
            });
        }

        request.addHeader({
            key: AUTHORIZATION,
            value: self.computeHeader(params),
            system: true
        });

        return done();
    }
};
<|endoftext|>


<commit_before>'use strict';

var request = require('request');
var dbSession = require('../../src/backend/dbSession.js');
var resetDatabase = require('../resetDb.js');
var async = require('async');

describe('The API', function () {

    it('should response to a GET request at /api/keywords/', function (done) {

        var expected = {
            // _items contains the actual list of keywords. Percolator doesn't allow to return an array of things directly.
            "_items": [
                {'id': 1, 'value': 'Aubergine', 'categoryID': 1},
                {'id': 2, 'value': 'Onion', 'categoryID': 1},
                {'id': 3, 'value': 'Knife', 'categoryID': 2}
            ]
        };

        async.series(
            [
                function (callback) {
                    resetDatabase(dbSession, callback);
                },

                function (callback) {
                    dbSession.insert(
                        'keyword',
                        {'value': 'Aubergine', 'categoryID': 1},
                        function (err) {
                            callback(err);
                        });
                },

                function (callback) {
                    dbSession.insert(
                        'keyword',
                        {'value': 'Onion', 'categoryID': 1},
                        function (err) {
                            callback(err);
                        });
                },

                function (callback) {
                    dbSession.insert(
                        'keyword',
                        {'value': 'Knife', 'categoryID': 2},
                        function (err) {
                            callback(err);
                        });
                }
            ],

            function (err, results) {
                request.get(
                    {
                        'url': 'http://localhost:8585/api/keywords/',
                        'json': true
                    },
                    function (err, res, body) {
                        expect(res.statusCode).toBe(200);
                        expect(body).toEqual(expected);
                        done();
                    });
            });
    });

});
<commit_msg>let jasmine start and kill server by itself<commit_after>'use strict';

var request = require('request');
var dbSession = require('../../src/backend/dbSession.js');
var Server = require('../../src/backend/server.js').Server;
var resetDatabase = require('../resetDb.js');
var async = require('async');

describe('The API', function () {

    var server;
    var port = '8586';

    beforeEach(function (done) {
        server = Server(port);

        server.listen(
            function (err) {
                resetDatabase(dbSession,
                    function () {
                        done(err);
                    });
            });
    });

    afterEach(function (done) {
        server.close(
            function () {
                resetDatabase(dbSession,
                    function () {
                        done();
                    });
            });
    });

    it('should response to a GET request at /api/keywords/', function (done) {

        var expected = {
            // _items contains the actual list of keywords. Percolator doesn't allow to return an array of things directly.
            "_items": [
                {'id': 1, 'value': 'Aubergine', 'categoryID': 1},
                {'id': 2, 'value': 'Onion', 'categoryID': 1},
                {'id': 3, 'value': 'Knife', 'categoryID': 2}
            ]
        };

        async.series(
            [
                function (callback) {
                    dbSession.insert(
                        'keyword',
                        {'value': 'Aubergine', 'categoryID': 1},
                        function (err) {
                            callback(err);
                        });
                },

                function (callback) {
                    dbSession.insert(
                        'keyword',
                        {'value': 'Onion', 'categoryID': 1},
                        function (err) {
                            callback(err);
                        });
                },

                function (callback) {
                    dbSession.insert(
                        'keyword',
                        {'value': 'Knife', 'categoryID': 2},
                        function (err) {
                            callback(err);
                        });
                }
            ],

            function (err, results) {
                if (err) throw (err);
                request.get(
                    {
                        'url': 'http://localhost:' + port + '/api/keywords/',
                        'json': true
                    },
                    function (err, res, body) {
                        if (err) throw (err);
                        expect(res.statusCode).toBe(200);
                        expect(body).toEqual(expected);
                        done();
                    });
            });
    });

});
<|endoftext|>


<commit_before>  // support CommonJS, AMD & browser
  /* istanbul ignore next */
  if (typeof exports === T_OBJECT)
    module.exports = riot
  else if (typeof define === 'function' && define.amd)
    define(function() { return (window.riot = riot) })
  else
    window.riot = riot

})(typeof window != 'undefined' ? window : void 0);
<commit_msg>Remove UglifyJs warnings from webpack production shortcut (-p)<commit_after>  // support CommonJS, AMD & browser
  /* istanbul ignore next */
  if (typeof exports === T_OBJECT)
    module.exports = riot
  else if (typeof define === T_FUNCTION && typeof define.amd !== T_UNDEF)
    define(function() { return (window.riot = riot) })
  else
    window.riot = riot

})(typeof window != 'undefined' ? window : void 0);
<|endoftext|>


<commit_before>'use strict';

exports.LoaderPlugin = class {
  constructor(bundler, config) {
    this.bundler = bundler;
    this.config = config;
    this.name = config.name;
    this.stub = config.stub;
    this.test = config.test ? new RegExp(config.test) : regExpFromExtensions(config.extensions);
  }

  matches(filePath) {
    return this.test.test(filePath);
  }

  transform(moduleId, filePath, contents) {
    contents = `define(\'${this.createModuleId(moduleId)}\', [\'module\'], function(module) { module.exports = "` +
      contents
        .replace(/(["\\])/g, '\\$1')
        .replace(/[\f]/g, '\\f')
        .replace(/[\b]/g, '\\b')
        .replace(/[\n]/g, '\\n')
        .replace(/[\t]/g, '\\t')
        .replace(/[\r]/g, '\\r')
        .replace(/[\u2028]/g, '\\u2028')
        .replace(/[\u2029]/g, '\\u2029') +
      '"; });';

    return contents;
  }

  createModuleId(moduleId) {
    let loderConfigType = this.bundler.loaderOptions.type;

    switch (loderConfigType) {
    case 'require':
      return 'text!' + moduleId;
    case 'system':
      return moduleId + '!text';
    default:
      throw new Error(`Loader configuration style ${loderConfigType} is not supported.`);
    }
  }
};

function regExpFromExtensions(extensions) {
  return new RegExp('^.*(' + extensions.map(x => '\\' + x).join('|') + ')$');
}
<commit_msg>chore(bundler): simplify text escaping<commit_after>'use strict';

exports.LoaderPlugin = class {
  constructor(bundler, config) {
    this.bundler = bundler;
    this.config = config;
    this.name = config.name;
    this.stub = config.stub;
    this.test = config.test ? new RegExp(config.test) : regExpFromExtensions(config.extensions);
  }

  matches(filePath) {
    return this.test.test(filePath);
  }

  transform(moduleId, filePath, contents) {
    contents = `define(\'${this.createModuleId(moduleId)}\',[],function(){return ${JSON.stringify(contents)};});`;
    return contents;
  }

  createModuleId(moduleId) {
    let loderConfigType = this.bundler.loaderOptions.type;

    switch (loderConfigType) {
    case 'require':
      return 'text!' + moduleId;
    case 'system':
      return moduleId + '!text';
    default:
      throw new Error(`Loader configuration style ${loderConfigType} is not supported.`);
    }
  }
};

function regExpFromExtensions(extensions) {
  return new RegExp('^.*(' + extensions.map(x => '\\' + x).join('|') + ')$');
}
<|endoftext|>


<commit_before>"use strict";

class ColorContrastCalc {
  constructor(rgb, name = null) {
    const ownClass = this.constructor;
    this.rgb = ownClass.isString(rgb) ? ownClass.hexNotationToDecimal(rgb) : rgb;
    this.relativeLuminance = ownClass.relativeLuminance(this.rgb);
    this.name = name === null ? ownClass.decimalToHexNotation(this.rgb) : name;
    this.hexNotation = ownClass.decimalToHexNotation(this.rgb);
  }

  static tristimulusValue(primaryColor, base = 255) {
    const s = primaryColor / base;
    if (s <= 0.03928) {
      return s / 12.92;
    } else {
      return Math.pow((s + 0.055) / 1.055, 2.4);
    }
  }

  static relativeLuminance(rgb = [255, 255, 255]) {
    if (this.isString(rgb)) { rgb = this.hexNotationToDecimal(rgb); }

    const [r, g, b] = rgb.map(c => this.tristimulusValue(c));
    return r * 0.2126 + g * 0.7152 + b * 0.0722;
  }

  static contrastRatio(foreground, background) {
    const [l1, l2] = [foreground, background]
          .map(c => this.relativeLuminance(c))
          .sort((f, b) => b - f);
    return (l1 + 0.05) / (l2 + 0.05);
  }

  static hexNotationToDecimal(hexString) {
    const h = this.normalizeHexNotation(hexString);
    return [0, 2, 4].map(s => h.substr(s, 2))
      .map(primaryColor => Number.parseInt(primaryColor, 16));
  }

  static normalizeHexNotation(hexString) {
    const h = hexString.startsWith("#") ? hexString.replace("#", "") : hexString;
    if (h.length === 3) {
      return [0, 1, 2].map(s => h.substr(s, 1).repeat(2)).join("");
    } else {
      return h;
    }
  }

  static decimalToHexNotation(rgb) {
    return "#" + rgb.map(d => {
      const h = d.toString(16);
      return h.length === 1 ? "0" + h : h;
    }).join("");
  }

  static isString(str) {
    return typeof str === "string" || str instanceof String;
  }

  static loadColorKeywords() {
    this.NAMED_COLORS = [];
    this.NAME_TO_COLOR = new Map();
    this.HEX_TO_COLOR = new Map();
    /* https://www.w3.org/TR/SVG/types.html#ColorKeywords */
    require("./color-keywords.json").forEach(color => {
      const [name, hex] = color;
      const calc = new ColorContrastCalc(hex, name);
      this.NAMED_COLORS.push(calc);
      this.NAME_TO_COLOR.set(name, calc);
      this.HEX_TO_COLOR.set(hex, calc);
    });

    Object.freeze(this.NAMED_COLORS);
    this.assignColorConstants();
  }

  static assignColorConstants() {
    this.BLACK = this.HEX_TO_COLOR.get("#000000");
    this.WHITE = this.HEX_TO_COLOR.get("#ffffff");
    this.GRAY = this.NAME_TO_COLOR.get("gray");
    this.prototype.BLACK = this.BLACK;
    this.prototype.WHITE = this.WHITE;
    this.prototype.GRAY = this.GRAY;
    Object.freeze(this.BLACK);
    Object.freeze(this.WHITE);
    Object.freeze(this.GRAY);
    Object.freeze(this.prototype.BLACK);
    Object.freeze(this.prototype.WHITE);
    Object.freeze(this.prototype.GRAY);
  }

  contrastRatioAgainst(color) {
    if (!(color instanceof ColorContrastCalc)) {
      return this.constructor.contrastRatio(this.rgb, color);
    }

    const [l1, l2] = [this.relativeLuminance, color.relativeLuminance]
          .sort((s, o) => o - s);
    return (l1 + 0.05) / (l2 + 0.05);
  }

  colorsWithSufficientContrast(level = "AA") {
    const ratio = this.levelToContrastRatio(level);

    return this.constructor.NAMED_COLORS.filter(combinedColor => {
      return this.contrastRatioAgainst(combinedColor) >= ratio;
    });
  }

  newContrastColor(ratio, name = null) {
    const newRgb = this.rgb.map(c => this.calcNewContrast(c, ratio));
    return new ColorContrastCalc(newRgb, name);
  }

  newBrightnessColor(ratio, name = null) {
    const newRgb = this.rgb.map(c => this.calcNewBrightness(c, ratio));
    return new ColorContrastCalc(newRgb, name);
  }

  hasSufficientContrast(otherColor, level = "AA") {
    const ratio = this.levelToContrastRatio(level);
    return this.contrastRatioAgainst(otherColor) >= ratio;
  }

  isSameColor(otherColor) {
    return this.hexNotation === otherColor.hexNotation;
  }

  isMaxContrast() {
    const limits = [0, 255];
    return this.rgb.every(primaryColor => limits.includes(primaryColor));
  }

  isMinContrast() {
    return this.rgb.every((primaryColor, i) => {
      return this.GRAY.rgb[i] === primaryColor;
    });
  }

  toString(base = 16) {
    switch (base) {
    case 16:
      return this.hexNotation;
    case 10:
      return `rgb(${this.rgb.join(",")})`;
    default:
      return this.name || this.hexNotation;
    }
  }

  levelToContrastRatio(level) {
    if (level === "A" || level === 1) {
      return 3.0;
    } else if (level === "AA" || level === 2) {
      return 4.5;
    } else if (level === "AAA" || level === 3) {
      return 7.0;
    }
  }

  calcNewContrast(origColor, ratio = 100) {
    /*
       https://www.w3.org/TR/filter-effects/#funcdef-contrast
       https://www.w3.org/TR/SVG/filters.html#TransferFunctionElementAttributes
    */
    const newColor = Math.round((origColor * ratio + 255 * (50 - ratio / 2)) / 100);
    if (newColor > 255) {
      return 255;
    } else if (newColor < 0) {
      return 0;
    } else {
      return newColor;
    }
  }

  calcNewBrightness(origColor, ratio = 100) {
    /*
       https://www.w3.org/TR/filter-effects/#funcdef-brightness
       https://www.w3.org/TR/SVG/filters.html#TransferFunctionElementAttributes
    */
    return Math.min(255, Math.round(origColor * ratio / 100));
  }

  calcRatioLimit (otherColor) {
    if (this.relativeLuminance > otherColor.relativeLuminance) {
      return 100;
    }

    if (this.relativeLuminance === otherColor.relativeLuminance &&
        this.relativeLuminance > this.GRAY.relativeLuminance) {
      return 100;
    }

    const darkest = otherColor.rgb.sort((c1, c2) => c1 - c2).filter(c => c > 0)[0];
    return Math.ceil((255 / darkest) * 100);
  }
}

ColorContrastCalc.loadColorKeywords();

module.exports.ColorContrastCalc = ColorContrastCalc;
<commit_msg>refactoring of ColorContrastCalc.prototype.calcRatioLimit<commit_after>"use strict";

class ColorContrastCalc {
  constructor(rgb, name = null) {
    const ownClass = this.constructor;
    this.rgb = ownClass.isString(rgb) ? ownClass.hexNotationToDecimal(rgb) : rgb;
    this.relativeLuminance = ownClass.relativeLuminance(this.rgb);
    this.name = name === null ? ownClass.decimalToHexNotation(this.rgb) : name;
    this.hexNotation = ownClass.decimalToHexNotation(this.rgb);
  }

  static tristimulusValue(primaryColor, base = 255) {
    const s = primaryColor / base;
    if (s <= 0.03928) {
      return s / 12.92;
    } else {
      return Math.pow((s + 0.055) / 1.055, 2.4);
    }
  }

  static relativeLuminance(rgb = [255, 255, 255]) {
    if (this.isString(rgb)) { rgb = this.hexNotationToDecimal(rgb); }

    const [r, g, b] = rgb.map(c => this.tristimulusValue(c));
    return r * 0.2126 + g * 0.7152 + b * 0.0722;
  }

  static contrastRatio(foreground, background) {
    const [l1, l2] = [foreground, background]
          .map(c => this.relativeLuminance(c))
          .sort((f, b) => b - f);
    return (l1 + 0.05) / (l2 + 0.05);
  }

  static hexNotationToDecimal(hexString) {
    const h = this.normalizeHexNotation(hexString);
    return [0, 2, 4].map(s => h.substr(s, 2))
      .map(primaryColor => Number.parseInt(primaryColor, 16));
  }

  static normalizeHexNotation(hexString) {
    const h = hexString.startsWith("#") ? hexString.replace("#", "") : hexString;
    if (h.length === 3) {
      return [0, 1, 2].map(s => h.substr(s, 1).repeat(2)).join("");
    } else {
      return h;
    }
  }

  static decimalToHexNotation(rgb) {
    return "#" + rgb.map(d => {
      const h = d.toString(16);
      return h.length === 1 ? "0" + h : h;
    }).join("");
  }

  static isString(str) {
    return typeof str === "string" || str instanceof String;
  }

  static loadColorKeywords() {
    this.NAMED_COLORS = [];
    this.NAME_TO_COLOR = new Map();
    this.HEX_TO_COLOR = new Map();
    /* https://www.w3.org/TR/SVG/types.html#ColorKeywords */
    require("./color-keywords.json").forEach(color => {
      const [name, hex] = color;
      const calc = new ColorContrastCalc(hex, name);
      this.NAMED_COLORS.push(calc);
      this.NAME_TO_COLOR.set(name, calc);
      this.HEX_TO_COLOR.set(hex, calc);
    });

    Object.freeze(this.NAMED_COLORS);
    this.assignColorConstants();
  }

  static assignColorConstants() {
    this.BLACK = this.HEX_TO_COLOR.get("#000000");
    this.WHITE = this.HEX_TO_COLOR.get("#ffffff");
    this.GRAY = this.NAME_TO_COLOR.get("gray");
    this.prototype.BLACK = this.BLACK;
    this.prototype.WHITE = this.WHITE;
    this.prototype.GRAY = this.GRAY;
    Object.freeze(this.BLACK);
    Object.freeze(this.WHITE);
    Object.freeze(this.GRAY);
    Object.freeze(this.prototype.BLACK);
    Object.freeze(this.prototype.WHITE);
    Object.freeze(this.prototype.GRAY);
  }

  contrastRatioAgainst(color) {
    if (!(color instanceof ColorContrastCalc)) {
      return this.constructor.contrastRatio(this.rgb, color);
    }

    const [l1, l2] = [this.relativeLuminance, color.relativeLuminance]
          .sort((s, o) => o - s);
    return (l1 + 0.05) / (l2 + 0.05);
  }

  colorsWithSufficientContrast(level = "AA") {
    const ratio = this.levelToContrastRatio(level);

    return this.constructor.NAMED_COLORS.filter(combinedColor => {
      return this.contrastRatioAgainst(combinedColor) >= ratio;
    });
  }

  newContrastColor(ratio, name = null) {
    const newRgb = this.rgb.map(c => this.calcNewContrast(c, ratio));
    return new ColorContrastCalc(newRgb, name);
  }

  newBrightnessColor(ratio, name = null) {
    const newRgb = this.rgb.map(c => this.calcNewBrightness(c, ratio));
    return new ColorContrastCalc(newRgb, name);
  }

  hasSufficientContrast(otherColor, level = "AA") {
    const ratio = this.levelToContrastRatio(level);
    return this.contrastRatioAgainst(otherColor) >= ratio;
  }

  isSameColor(otherColor) {
    return this.hexNotation === otherColor.hexNotation;
  }

  isMaxContrast() {
    const limits = [0, 255];
    return this.rgb.every(primaryColor => limits.includes(primaryColor));
  }

  isMinContrast() {
    return this.rgb.every((primaryColor, i) => {
      return this.GRAY.rgb[i] === primaryColor;
    });
  }

  toString(base = 16) {
    switch (base) {
    case 16:
      return this.hexNotation;
    case 10:
      return `rgb(${this.rgb.join(",")})`;
    default:
      return this.name || this.hexNotation;
    }
  }

  levelToContrastRatio(level) {
    if (level === "A" || level === 1) {
      return 3.0;
    } else if (level === "AA" || level === 2) {
      return 4.5;
    } else if (level === "AAA" || level === 3) {
      return 7.0;
    }
  }

  calcNewContrast(origColor, ratio = 100) {
    /*
       https://www.w3.org/TR/filter-effects/#funcdef-contrast
       https://www.w3.org/TR/SVG/filters.html#TransferFunctionElementAttributes
    */
    const newColor = Math.round((origColor * ratio + 255 * (50 - ratio / 2)) / 100);
    if (newColor > 255) {
      return 255;
    } else if (newColor < 0) {
      return 0;
    } else {
      return newColor;
    }
  }

  calcNewBrightness(origColor, ratio = 100) {
    /*
       https://www.w3.org/TR/filter-effects/#funcdef-brightness
       https://www.w3.org/TR/SVG/filters.html#TransferFunctionElementAttributes
    */
    return Math.min(255, Math.round(origColor * ratio / 100));
  }

  calcRatioLimit (otherColor) {
    if (this.isBrighterThan(otherColor) ||
        (this.hasSameLuminance(otherColor) &&
         this.isBrighterThan(this.GRAY))) {
      return 100;
    }

    const darkest = otherColor.rgb.sort((c1, c2) => c1 - c2).filter(c => c > 0)[0];
    return Math.ceil((255 / darkest) * 100);
  }

  isBrighterThan(otherColor) {
    return this.relativeLuminance > otherColor.relativeLuminance;
  }

  hasSameLuminance(otherColor) {
    return this.relativeLuminance === otherColor.relativeLuminance;
  }
}

ColorContrastCalc.loadColorKeywords();

module.exports.ColorContrastCalc = ColorContrastCalc;
<|endoftext|>


<commit_before>var fs = require('graceful-fs')
var path = require('path')
var copyFileSync = require('./copy-file-sync')
var mkdir = require('../mkdirs')

function copySync (src, dest, options) {
  if (typeof options === 'function' || options instanceof RegExp) {
    options = {filter: options}
  }

  options = options || {}
  options.recursive = !!options.recursive

  // default to true for now
  options.clobber = 'clobber' in options ? !!options.clobber : true
  // overwrite falls back to clobber
  options.overwrite = 'overwrite' in options ? !!options.overwrite : options.clobber
  options.dereference = 'dereference' in options ? !!options.dereference : false
  options.preserveTimestamps = 'preserveTimestamps' in options ? !!options.preserveTimestamps : false

  options.filter = options.filter || function () { return true }

  // Warn about using preserveTimestamps on 32-bit node:
  if (options.preserveTimestamps && process.arch === 'ia32') {
    console.warn('fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n' +
    'see https://github.com/jprichardson/node-fs-extra/issues/269')
  }

  var stats = (options.recursive && !options.dereference) ? fs.lstatSync(src) : fs.statSync(src)
  var destFolder = path.dirname(dest)
  var destFolderExists = fs.existsSync(destFolder)
  var performCopy = false

  if (options.filter instanceof RegExp) {
    console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
    performCopy = options.filter.test(src)
  } else if (typeof options.filter === 'function') performCopy = options.filter(src)

  if (stats.isFile() && performCopy) {
    if (!destFolderExists) mkdir.mkdirsSync(destFolder)
    copyFileSync(src, dest, {
      overwrite: options.overwrite,
      errorOnExist: options.errorOnExist,
      preserveTimestamps: options.preserveTimestamps
    })
  } else if (stats.isDirectory() && performCopy) {
    if (!fs.existsSync(dest)) mkdir.mkdirsSync(dest)
    var contents = fs.readdirSync(src)
    contents.forEach(function (content) {
      var opts = options
      opts.recursive = true
      copySync(path.join(src, content), path.join(dest, content), opts)
    })
  } else if (options.recursive && stats.isSymbolicLink() && performCopy) {
    var srcPath = fs.readlinkSync(src)
    fs.symlinkSync(srcPath, dest)
  }
}

module.exports = copySync
<commit_msg>Refactor copy-sync<commit_after>'use strict'

const fs = require('graceful-fs')
const path = require('path')
const copyFileSync = require('./copy-file-sync')
const mkdir = require('../mkdirs')

function copySync (src, dest, options) {
  if (typeof options === 'function' || options instanceof RegExp) {
    options = {filter: options}
  }

  options = options || {}
  options.recursive = !!options.recursive

  // default to true for now
  options.clobber = 'clobber' in options ? !!options.clobber : true
  // overwrite falls back to clobber
  options.overwrite = 'overwrite' in options ? !!options.overwrite : options.clobber
  options.dereference = 'dereference' in options ? !!options.dereference : false
  options.preserveTimestamps = 'preserveTimestamps' in options ? !!options.preserveTimestamps : false

  options.filter = options.filter || function () { return true }

  // Warn about using preserveTimestamps on 32-bit node:
  if (options.preserveTimestamps && process.arch === 'ia32') {
    console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
    see https://github.com/jprichardson/node-fs-extra/issues/269`)
  }

  const stats = (options.recursive && !options.dereference) ? fs.lstatSync(src) : fs.statSync(src)
  const destFolder = path.dirname(dest)
  const destFolderExists = fs.existsSync(destFolder)
  let performCopy = false

  if (options.filter instanceof RegExp) {
    console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
    performCopy = options.filter.test(src)
  } else if (typeof options.filter === 'function') performCopy = options.filter(src)

  if (stats.isFile() && performCopy) {
    if (!destFolderExists) mkdir.mkdirsSync(destFolder)
    copyFileSync(src, dest, {
      overwrite: options.overwrite,
      errorOnExist: options.errorOnExist,
      preserveTimestamps: options.preserveTimestamps
    })
  } else if (stats.isDirectory() && performCopy) {
    if (!fs.existsSync(dest)) mkdir.mkdirsSync(dest)
    const contents = fs.readdirSync(src)
    contents.forEach(content => {
      const opts = options
      opts.recursive = true
      copySync(path.join(src, content), path.join(dest, content), opts)
    })
  } else if (options.recursive && stats.isSymbolicLink() && performCopy) {
    const srcPath = fs.readlinkSync(src)
    fs.symlinkSync(srcPath, dest)
  }
}

module.exports = copySync
<|endoftext|>


<commit_before>var profiles = require('../collections/profiles')
    uuid     = require('../helpers/uuid'),
		wrap	   = require('../helpers/wrapper'),
		time     = require('microtime')

function profile(fn) {
  var modul = arguments[arguments.length - 1]

  function __profile__ () {

  	/* START Retrieve file & line info */
  	var err  = new Error,
  	    data = [ ]

    Error.captureStackTrace(err, fn.callee)
    var lines = err.stack.split("\n")

    for (var i = 0; i < lines.length; i++) {
      if (lines[i].search(/.*Error|wrap|decorate|__profile__|Object\.\.js|Module|Function|require.*/) === -1) {
        var file = lines[i].split('(')

        if (file.length > 1) {
          data = file[1].substring(0, file[1].length - 2).split(':')
        } else {
          file = lines[i].split('/')
          var index = file.length - 1
          data = file[index].substring(0, file[index].length - 2).split(':')
        }

        break;
      }
    }

    // console.error("\data ", data)

  	modul.file = data[0]
  	modul.line = data[1]
  	/* END Retrieve file & line info */

    var u = uuid(fn, modul),
        prof = {
          id: u.id,
          invokation: u.invokations,
          initialized: time.now(),
          observers: [ ],
          anonymous: u.anonymous,
          file: modul.file,
          line: modul.line },
        last = profiles.mostRecent();
    if (last && last.anonymous) {
      prof.owner = last.id + '.' + last.invokation;
      last.observers.push(u.uid);
    }
    profiles.add(prof);
    function decorate(af) {
      var a = [ ];
      for (var k = 0; k < af.length; k++)
        if (typeof af[k] === 'function') {
          if (af[k].name !== '__profile__') {
            a.push(wrap(profile, 'OWNER:' + u.uid, af[k], modul));

            var afk = uuid(af[k], false, modul);

            prof.observers.push(afk.id + '.0');
          } else {
            a.push(af[k]);

            var afk = uuid(af[k].__fn__, modul);

            prof.observers.push(afk.uid);
          }
          if (k === af.length - 1)
            prof.callback = 1;
        } else if (af[k] instanceof String && af[k].indexOf('OWNER:') === 0) {
          prof.owner = af[k].substring(6);
        } else {
          a.push(af[k]);
        }
      return a;
    }
    prof.rawArgs = arguments;
    prof.args 	  = decorate(arguments);
    prof.start   = time.now();
    prof.result  = fn.apply(this, prof.args);
    prof.end     = time.now();
    return prof.result;
  }

  __profile__.__fn__ = fn;

  return __profile__;
}

module.exports = profile;<commit_msg>Profile functions are now eval'd<commit_after>var profiles = require('../collections/profiles'),
    uuid     = require('../helpers/uuid'),
    wrap	   = require('../helpers/wrapper'),
    time     = require('microtime'),

    body = ' () {' +

    	/* START Retrieve file & line info */
    	'var err  = new Error,' +
    	    'data = [ ];' +

      'Error.captureStackTrace(err, fn.callee);' +
      'var lines = err.stack.split("\\n");' +

      // 'console.log("\\nstack ", lines);' +

      'for (var i = 0; i < lines.length; i++) {' +
        'if (lines[i].search(/.*Error|wrap|decorate|__profile__|Object\.\.js|eval at profile|Module|Function|require.*/) === -1) {' +
          "var file = lines[i].split('(');" +

          'if (file.length > 1) {' +
            "data = file[1].substring(0, file[1].length - 2).split(':');" +
          '} else {' +
            "file = lines[i].split('/');" +
            'var index = file.length - 1;' +
            "data = file[index].substring(0, file[index].length - 2).split(':');" +
          '}' +

          'break;' +
        '}' +
      '}' +

    	'modul.file = data[0];' +
    	'modul.line = data[1];' +
    	/* END Retrieve file & line info */

      'var u    = uuid(fn, modul),' +
          'prof = {' +
            'id: u.id,' +
            'invokation: u.invokations,' +
            'initialized: initd,' +
            'observers: [ ],' +
            'anonymous: u.anonymous,' +
            'file: modul.file,' +
            'line: modul.line },' +
          'last = profiles.mostRecent();' +
      'if (last && last.anonymous) {' +
        "prof.owner = last.id + '.' + last.invokation;" +
        'last.observers.push(u.uid);' +
      '}' +
      'profiles.add(prof);' +
      'function decorate(af) {' +
        'var a = [ ];' +
        'for (var k = 0; k < af.length; k++)' +
          "if (typeof af[k] === 'function') {" +
            "if (af[k].name !== '__profile__') {" +
              "a.push(wrap(profile, 'OWNER:' + u.uid, af[k], modul));" +

              'var afk = uuid(af[k], false, modul);' +

              "prof.observers.push(afk.id + '.0');" +
            '} else {' +
              'a.push(af[k]);' +

              'var afk = uuid(af[k].__fn__, modul);' +

              'prof.observers.push(afk.uid);' +
            '}' +
          "} else if (af[k] instanceof String && af[k].indexOf('OWNER:') === 0) {" +
            'prof.owner = af[k].substring(6);' +
          '} else {' +
            'a.push(af[k]);' +
          '}' +
        'return a;' +
      '}' +
      'prof.rawArgs = arguments;' +
      'prof.args 	  = decorate(arguments);' +
      'prof.start   = time.now();' +
      'prof.result  = fn.apply(this, prof.args);' +
      'prof.end     = time.now();' +

      'return prof.result;' +
    '}'

function profile(fn) {
  var modul = arguments[arguments.length - 1],
      initd = time.now()

  return eval('(function ' + fn.name + body + ')')
}

module.exports = profile<|endoftext|>


<commit_before>var events = require('events'),
    qs = require('querystring'),
    util = require('util'),
    BaseRouter = require('../router').Router,
    responses = require('./responses');

//
// ### Expose all HTTP methods and responses
//
exports.methods   = require('./methods');
Object.keys(responses).forEach(function (name) {
  exports[name] = responses[name];
});

//
// ### function Router (routes)
// #### @routes {Object} **Optional** Routing table for this instance.
// Constuctor function for the HTTP Router object responsible for building
// and dispatching from a given routing table.
//
var Router = exports.Router = function (routes) {
  //
  // ### Extend the `Router` prototype with all of the RFC methods.
  //
  this.params   = {};
  this.routes   = {};
  this.methods  = ['on', 'after', 'before'];
  this.scope    = [];
  this._methods = {};
  this.recurse = 'forward';
  this._attach = [];

  this.extend(exports.methods.concat(['before', 'after']));
  this.configure();
  this.mount(routes || {});
};

//
// Inherit from `BaseRouter`.
//
util.inherits(Router, BaseRouter);

//
// ### function configure (options)
// #### @options {Object} **Optional** Options to configure this instance with
// Configures this instance with the specified `options`.
//
Router.prototype.configure = function (options) {
  options = options || {};

  // useful when using connect's bodyParser
  this.stream = options.stream || false;

  return BaseRouter.prototype.configure.call(this, options);
};

//
// ### function on (method, path, route)
// #### @method {string} **Optional** Method to use
// #### @path {string} Path to set this route on.
// #### @route {Array|function} Handler for the specified method and path.
// Adds a new `route` to this instance for the specified `method`
// and `path`.
//
Router.prototype.on = function (method, path) {
  var args = Array.prototype.slice.call(arguments, 2),
      route = args.pop(),
      options = args.pop(),
      accept;

  if (options) {
    if (options.stream) {
      route.stream = true;
    }

    if (options.accept) {
      this._hasAccepts = true;
      accept = options.accept;
      route.accept = (Array.isArray(accept) ? accept : [accept]).map(function (a) {
        return typeof a === 'string' ? RegExp(a) : a;
      });
    }
  }

  if (typeof path !== 'string' && !path.source) {
    path = '';
  }

  BaseRouter.prototype.on.call(this, method, path, route);
};

//
// ### function attach (func)
// ### @func {function} Function to execute on `this` before applying to router function
// Ask the router to attach objects or manipulate `this` object on which the
// function passed to the http router will get applied
Router.prototype.attach = function (func) {
  this._attach.push(func);
};

//
// ### function dispatch (method, path)
// #### @req {http.ServerRequest} Incoming request to dispatch.
// #### @res {http.ServerResponse} Outgoing response to dispatch.
// #### @callback {function} **Optional** Continuation to respond to for async scenarios.
// Finds a set of functions on the traversal towards
// `method` and `path` in the core routing table then
// invokes them based on settings in this instance.
//
Router.prototype.dispatch = function (req, res, callback) {
  //
  // Dispatch `HEAD` requests to `GET`
  //
  var method = req.method === 'HEAD' ? 'get' : req.method.toLowerCase(),
      thisArg = { req: req, res: res },
      self = this,
      contentType,
      runlist,
      stream,
      error,
      fns,
      url;

  //
  // Trap bad URLs from `decodeUri`
  //
  try { url = decodeURI(req.url.split('?', 1)[0]); }
  catch (ex) { url = null }

  if (url && this._hasAccepts) {
    contentType = req.headers['content-type'];
    fns = this.traverse(method, url, this.routes, '', function (route) {
      return !route.accept || route.accept.some(function (a) {
        return a.test(contentType);
      });
    });
  }
  else if (url) {
    fns = this.traverse(method, url, this.routes, '');
  }

  if (this._attach) {
    for (var i=0;i< this._attach.length; i++) {
      this._attach[i].call(thisArg);
    }
  }

  if (!fns || fns.length === 0) {
    error = new exports.NotFound('Could not find path: ' + req.url);
    if (typeof this.notfound === 'function') {
      this.notfound.call(thisArg, callback);
    }
    else if (callback) {
      callback.call(thisArg, error, req, res);
    }
    return false;
  }

  if (this.recurse === 'forward') {
    fns = fns.reverse();
  }

  runlist = this.runlist(fns);
  stream  = this.stream || runlist.some(function (fn) { return fn.stream === true; });

  function parseAndInvoke() {
    error = self.parse(req);
    if (error) {
      if (callback) {
        callback.call(thisArg, error, req, res);
      }
      return false;
    }

    self.invoke(runlist, thisArg, callback);
  }

  if (!stream) {
    //
    // If there is no streaming required on any of the functions on the
    // way to `path`, then attempt to parse the fully buffered request stream
    // once it has emitted the `end` event.
    //
    if (req.readable) {
      //
      // If the `http.ServerRequest` is still readable, then await
      // the end event and then continue
      //
      req.once('end', parseAndInvoke);
      // Streams2 requires us to start the stream if we're not explicitly
      // reading from it.
      req.resume();
    }
    else {
      //
      // Otherwise, just parse the body now.
      //
      parseAndInvoke();
    }
  }
  else {
    this.invoke(runlist, thisArg, callback);
  }

  return true;
};

//
// ### @parsers {Object}
// Lookup table of parsers to use when attempting to
// parse incoming responses.
//
Router.prototype.parsers = {
  'application/x-www-form-urlencoded': qs.parse,
  'application/json': JSON.parse
};

//
// ### function parse (req)
// #### @req {http.ServerResponse|BufferedStream} Incoming HTTP request to parse
// Attempts to parse `req.body` using the value found at `req.headers['content-type']`.
//
Router.prototype.parse = function (req) {
  function mime(req) {
    var str = req.headers['content-type'] || '';
    return str.split(';')[0];
  }

  var parser = this.parsers[mime(req)],
      body;

  if (parser) {
    req.body = req.body || '';

    if (req.chunks) {
      req.chunks.forEach(function (chunk) {
        req.body += chunk;
      });
    }

    if ('string' === typeof req.body) {
      try {
        req.body = req.body && req.body.length
          ? parser(req.body)
          : {};
      }
      catch (err) {
        return new exports.BadRequest('Malformed data');
      }
    }
  }
};

<commit_msg>[minor] style nit<commit_after>var events = require('events'),
    qs = require('querystring'),
    util = require('util'),
    BaseRouter = require('../router').Router,
    responses = require('./responses');

//
// ### Expose all HTTP methods and responses
//
exports.methods   = require('./methods');
Object.keys(responses).forEach(function (name) {
  exports[name] = responses[name];
});

//
// ### function Router (routes)
// #### @routes {Object} **Optional** Routing table for this instance.
// Constuctor function for the HTTP Router object responsible for building
// and dispatching from a given routing table.
//
var Router = exports.Router = function (routes) {
  //
  // ### Extend the `Router` prototype with all of the RFC methods.
  //
  this.params   = {};
  this.routes   = {};
  this.methods  = ['on', 'after', 'before'];
  this.scope    = [];
  this._methods = {};
  this.recurse = 'forward';
  this._attach = [];

  this.extend(exports.methods.concat(['before', 'after']));
  this.configure();
  this.mount(routes || {});
};

//
// Inherit from `BaseRouter`.
//
util.inherits(Router, BaseRouter);

//
// ### function configure (options)
// #### @options {Object} **Optional** Options to configure this instance with
// Configures this instance with the specified `options`.
//
Router.prototype.configure = function (options) {
  options = options || {};

  // useful when using connect's bodyParser
  this.stream = options.stream || false;

  return BaseRouter.prototype.configure.call(this, options);
};

//
// ### function on (method, path, route)
// #### @method {string} **Optional** Method to use
// #### @path {string} Path to set this route on.
// #### @route {Array|function} Handler for the specified method and path.
// Adds a new `route` to this instance for the specified `method`
// and `path`.
//
Router.prototype.on = function (method, path) {
  var args = Array.prototype.slice.call(arguments, 2),
      route = args.pop(),
      options = args.pop(),
      accept;

  if (options) {
    if (options.stream) {
      route.stream = true;
    }

    if (options.accept) {
      this._hasAccepts = true;
      accept = options.accept;
      route.accept = (Array.isArray(accept) ? accept : [accept]).map(function (a) {
        return typeof a === 'string' ? RegExp(a) : a;
      });
    }
  }

  if (typeof path !== 'string' && !path.source) {
    path = '';
  }

  BaseRouter.prototype.on.call(this, method, path, route);
};

//
// ### function attach (func)
// ### @func {function} Function to execute on `this` before applying to router function
// Ask the router to attach objects or manipulate `this` object on which the
// function passed to the http router will get applied
Router.prototype.attach = function (func) {
  this._attach.push(func);
};

//
// ### function dispatch (method, path)
// #### @req {http.ServerRequest} Incoming request to dispatch.
// #### @res {http.ServerResponse} Outgoing response to dispatch.
// #### @callback {function} **Optional** Continuation to respond to for async scenarios.
// Finds a set of functions on the traversal towards
// `method` and `path` in the core routing table then
// invokes them based on settings in this instance.
//
Router.prototype.dispatch = function (req, res, callback) {
  //
  // Dispatch `HEAD` requests to `GET`
  //
  var method = req.method === 'HEAD' ? 'get' : req.method.toLowerCase(),
      thisArg = { req: req, res: res },
      self = this,
      contentType,
      runlist,
      stream,
      error,
      fns,
      url;

  //
  // Trap bad URLs from `decodeUri`
  //
  try { url = decodeURI(req.url.split('?', 1)[0]); }
  catch (ex) { url = null }

  if (url && this._hasAccepts) {
    contentType = req.headers['content-type'];
    fns = this.traverse(method, url, this.routes, '', function (route) {
      return !route.accept || route.accept.some(function (a) {
        return a.test(contentType);
      });
    });
  }
  else if (url) {
    fns = this.traverse(method, url, this.routes, '');
  }

  if (this._attach) {
    for (var i = 0; i < this._attach.length; i++) {
      this._attach[i].call(thisArg);
    }
  }

  if (!fns || fns.length === 0) {
    error = new exports.NotFound('Could not find path: ' + req.url);
    if (typeof this.notfound === 'function') {
      this.notfound.call(thisArg, callback);
    }
    else if (callback) {
      callback.call(thisArg, error, req, res);
    }
    return false;
  }

  if (this.recurse === 'forward') {
    fns = fns.reverse();
  }

  runlist = this.runlist(fns);
  stream  = this.stream || runlist.some(function (fn) { return fn.stream === true; });

  function parseAndInvoke() {
    error = self.parse(req);
    if (error) {
      if (callback) {
        callback.call(thisArg, error, req, res);
      }
      return false;
    }

    self.invoke(runlist, thisArg, callback);
  }

  if (!stream) {
    //
    // If there is no streaming required on any of the functions on the
    // way to `path`, then attempt to parse the fully buffered request stream
    // once it has emitted the `end` event.
    //
    if (req.readable) {
      //
      // If the `http.ServerRequest` is still readable, then await
      // the end event and then continue
      //
      req.once('end', parseAndInvoke);
      // Streams2 requires us to start the stream if we're not explicitly
      // reading from it.
      req.resume();
    }
    else {
      //
      // Otherwise, just parse the body now.
      //
      parseAndInvoke();
    }
  }
  else {
    this.invoke(runlist, thisArg, callback);
  }

  return true;
};

//
// ### @parsers {Object}
// Lookup table of parsers to use when attempting to
// parse incoming responses.
//
Router.prototype.parsers = {
  'application/x-www-form-urlencoded': qs.parse,
  'application/json': JSON.parse
};

//
// ### function parse (req)
// #### @req {http.ServerResponse|BufferedStream} Incoming HTTP request to parse
// Attempts to parse `req.body` using the value found at `req.headers['content-type']`.
//
Router.prototype.parse = function (req) {
  function mime(req) {
    var str = req.headers['content-type'] || '';
    return str.split(';')[0];
  }

  var parser = this.parsers[mime(req)],
      body;

  if (parser) {
    req.body = req.body || '';

    if (req.chunks) {
      req.chunks.forEach(function (chunk) {
        req.body += chunk;
      });
    }

    if ('string' === typeof req.body) {
      try {
        req.body = req.body && req.body.length
          ? parser(req.body)
          : {};
      }
      catch (err) {
        return new exports.BadRequest('Malformed data');
      }
    }
  }
};

<|endoftext|>


<commit_before>'use strict';

var Fs = require('fs');
var Request = require('request');
var List = require('immutable').List;
var Promise = require('promise');
var Url = require('url');
var Mkdirp = require('mkdirp');

/**
 * @exports lib/document-downloader
 */

var DocumentDownloader = {};

DocumentDownloader.fetch = function (url) {
  return new Promise(function (resolve, reject) {
    Request.get(url, {
      timeout: 30000,
      encoding: null // If not specified, utf8 by default
    }, function (error, response, body) {
      if (error) {
        reject(new Error(
          'Fetching ' + url + ' triggered a network error: ' + error.message
        ));
      }
      else if (response.statusCode !== 200) {
        reject(new Error(
          'Fetching ' + url +
          ' triggered and HTTP error: code ' + response.statusCode
        ));
      }
      else resolve(body);
    });
  });
};

DocumentDownloader.fetchAll = function (urls) {
  return Promise.all(urls.toArray().map(this.fetch)).then(List);
};

DocumentDownloader.install = function (dest, content) {
  var path = require('path').dirname(dest);

  Mkdirp.sync(path);
  return Promise.denodeify(Fs.writeFile)(dest, content);
};

DocumentDownloader.installAll = function (destsContents) {
  // `e` is a tuple of [dest, content]
  return Promise.all(destsContents.toArray().map(function (e) {
    return DocumentDownloader.install(e[0], e[1]);
  }));
};

/**
 * Checks if a given filename is allowed to be published by the system.
 *
 * @param {string} filename - The filename to check against
 * @returns {boolean} `true` if the filename is allowed, `false` otherwise
 */
DocumentDownloader.isAllowed = function isAllowed(filename) {
  if (filename.toLowerCase().indexOf('.htaccess') !== -1) return false;
  else if (filename.toLowerCase().indexOf('.php') !== -1) return false;
  else if (filename.indexOf('CVS') !== -1) return false;
  else if (filename.indexOf('../') !== -1) return false;
  else if (filename.indexOf('://') !== -1) return false;
  else return true;
};

DocumentDownloader.fetchAndInstall = function (url, dest) {
  var _ = DocumentDownloader; // Class name shortener
  var mkdir = Promise.denodeify(Fs.mkdir);

  return new Promise(function (resolve) {
    Fs.exists(dest, function (exists) { resolve(exists); });
  }).then(function (pathExists) {
    if (!pathExists) return mkdir(dest);
  }).then(function () {
    return _.fetch(url).then(function (content) {
      var fileType = require('file-type');
      var type = fileType(content);

      if (type && type.mime !== 'application/x-tar') {
        throw new TypeError('Only tar, html and manifest are supported.');
      }
      else if (type && type.mime === 'application/x-tar') {
        return new Promise(function (resolve, reject) {
          var tar = require('tar-stream');
          var extract = tar.extract();
          var hasOverview = false;

          extract.on('entry', function (header, stream, callback) {
            stream.on('data', function (data) {
              if (_.isAllowed(header.name)) {
                if (header.name === 'Overview.html') {
                  hasOverview = true;
                }
                var path = require('path').dirname(dest + '/' + header.name);

                Mkdirp.sync(path);
                Fs.writeFileSync(dest + '/' + header.name, data);
              }
            });
            stream.on('end', function () {
              callback();
            });
          });
          extract.on('finish', function () {
            return (hasOverview) ? resolve() :
              reject(
                new Error('The tarball doesn\'t have an Overview.html file.')
              );
          });
          extract.end(content);
        });
      }
      else {
        var contentUtf8 = content.toString('utf8');

        if (contentUtf8.trim().charAt(0) !== '<') {
          var filenames = _.getFilenames(contentUtf8).filter(_.isAllowed);

          var dests = filenames
            .set(0, 'Overview.html')
            .map(function (filename) { return dest + '/' + filename; });

          var urls = filenames.map(function (filename) {
            // If an entry in the manifest had a space in it,
            // we assume it needs to be built from the spec-generator.
            // See https://github.com/w3c/spec-generator
            var specGeneratorComp = filename.split(' ');
            var absUrl = Url.resolve(url, specGeneratorComp[0]);

            if (specGeneratorComp.length === 2) {
              return global.SPEC_GENERATOR +
                '?type=' + encodeURIComponent(specGeneratorComp[1]) +
                '&url=' + encodeURIComponent(absUrl);
            }
            return absUrl;
          });

          return _.fetchAll(urls).then(function (contents) {
            return _.installAll(dests.zip(contents));
          });
        }
        else return _.install(dest + '/Overview.html', content);
      }
    });
  });
};

DocumentDownloader.getFilenames = function getFilenames(manifest) {
  return manifest.split('\n').reduce(function (acc, line) {
    var filename = line.split('#')[0].trim();

    if (filename !== '') return acc.push(filename);
    else return acc;
  }, new List());
};

module.exports = DocumentDownloader;
<commit_msg>fix escaping issue if the tar doesn't have an Overview.html<commit_after>'use strict';

var Fs = require('fs');
var Request = require('request');
var List = require('immutable').List;
var Promise = require('promise');
var Url = require('url');
var Mkdirp = require('mkdirp');

/**
 * @exports lib/document-downloader
 */

var DocumentDownloader = {};

DocumentDownloader.fetch = function (url) {
  return new Promise(function (resolve, reject) {
    Request.get(url, {
      timeout: 30000,
      encoding: null // If not specified, utf8 by default
    }, function (error, response, body) {
      if (error) {
        reject(new Error(
          'Fetching ' + url + ' triggered a network error: ' + error.message
        ));
      }
      else if (response.statusCode !== 200) {
        reject(new Error(
          'Fetching ' + url +
          ' triggered and HTTP error: code ' + response.statusCode
        ));
      }
      else resolve(body);
    });
  });
};

DocumentDownloader.fetchAll = function (urls) {
  return Promise.all(urls.toArray().map(this.fetch)).then(List);
};

DocumentDownloader.install = function (dest, content) {
  var path = require('path').dirname(dest);

  Mkdirp.sync(path);
  return Promise.denodeify(Fs.writeFile)(dest, content);
};

DocumentDownloader.installAll = function (destsContents) {
  // `e` is a tuple of [dest, content]
  return Promise.all(destsContents.toArray().map(function (e) {
    return DocumentDownloader.install(e[0], e[1]);
  }));
};

/**
 * Checks if a given filename is allowed to be published by the system.
 *
 * @param {string} filename - The filename to check against
 * @returns {boolean} `true` if the filename is allowed, `false` otherwise
 */
DocumentDownloader.isAllowed = function isAllowed(filename) {
  if (filename.toLowerCase().indexOf('.htaccess') !== -1) return false;
  else if (filename.toLowerCase().indexOf('.php') !== -1) return false;
  else if (filename.indexOf('CVS') !== -1) return false;
  else if (filename.indexOf('../') !== -1) return false;
  else if (filename.indexOf('://') !== -1) return false;
  else return true;
};

DocumentDownloader.fetchAndInstall = function (url, dest) {
  var _ = DocumentDownloader; // Class name shortener
  var mkdir = Promise.denodeify(Fs.mkdir);

  return new Promise(function (resolve) {
    Fs.exists(dest, function (exists) { resolve(exists); });
  }).then(function (pathExists) {
    if (!pathExists) return mkdir(dest);
  }).then(function () {
    return _.fetch(url).then(function (content) {
      var fileType = require('file-type');
      var type = fileType(content);

      if (type && type.mime !== 'application/x-tar') {
        throw new TypeError('Only tar, html and manifest are supported.');
      }
      else if (type && type.mime === 'application/x-tar') {
        return new Promise(function (resolve, reject) {
          var tar = require('tar-stream');
          var extract = tar.extract();
          var hasOverview = false;

          extract.on('entry', function (header, stream, callback) {
            stream.on('data', function (data) {
              if (_.isAllowed(header.name)) {
                if (header.name === 'Overview.html') {
                  hasOverview = true;
                }
                var path = require('path').dirname(dest + '/' + header.name);

                Mkdirp.sync(path);
                Fs.writeFileSync(dest + '/' + header.name, data);
              }
            });
            stream.on('end', function () {
              callback();
            });
          });
          extract.on('finish', function () {
            return (hasOverview) ? resolve() :
              reject(
                new Error('No Overview.html in the tarball.')
              );
          });
          extract.end(content);
        });
      }
      else {
        var contentUtf8 = content.toString('utf8');

        if (contentUtf8.trim().charAt(0) !== '<') {
          var filenames = _.getFilenames(contentUtf8).filter(_.isAllowed);

          var dests = filenames
            .set(0, 'Overview.html')
            .map(function (filename) { return dest + '/' + filename; });

          var urls = filenames.map(function (filename) {
            // If an entry in the manifest had a space in it,
            // we assume it needs to be built from the spec-generator.
            // See https://github.com/w3c/spec-generator
            var specGeneratorComp = filename.split(' ');
            var absUrl = Url.resolve(url, specGeneratorComp[0]);

            if (specGeneratorComp.length === 2) {
              return global.SPEC_GENERATOR +
                '?type=' + encodeURIComponent(specGeneratorComp[1]) +
                '&url=' + encodeURIComponent(absUrl);
            }
            return absUrl;
          });

          return _.fetchAll(urls).then(function (contents) {
            return _.installAll(dests.zip(contents));
          });
        }
        else return _.install(dest + '/Overview.html', content);
      }
    });
  });
};

DocumentDownloader.getFilenames = function getFilenames(manifest) {
  return manifest.split('\n').reduce(function (acc, line) {
    var filename = line.split('#')[0].trim();

    if (filename !== '') return acc.push(filename);
    else return acc;
  }, new List());
};

module.exports = DocumentDownloader;
<|endoftext|>


<commit_before>module.exports = extractDescription

// Extracts description from contents of a readme file in markdown format
function extractDescription (d) {
  if (!d) return;
  // the first block of text before the first heading
  // that isn't the first line heading
  d = d.trim().split('\n')
  for (var s = 0; d[s] && d[s].trim().match(/^(#|$)/); s ++);
  var l = d.length
  for (var e = s + 1; e < l && d[e].trim(); e ++);
  data.description = d.slice(s, e).join(' ').trim()
}
<commit_msg>Fix description-from-readme extraction<commit_after>module.exports = extractDescription

// Extracts description from contents of a readme file in markdown format
function extractDescription (d) {
  if (!d) return;
  // the first block of text before the first heading
  // that isn't the first line heading
  d = d.trim().split('\n')
  for (var s = 0; d[s] && d[s].trim().match(/^(#|$)/); s ++);
  var l = d.length
  for (var e = s + 1; e < l && d[e].trim(); e ++);
  return d.slice(s, e).join(' ').trim()
}
<|endoftext|>


<commit_before>/*
 * featureable-torrent
 * https://github.com/pwmckenna/featureable-torrent
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */

var express = require('express');
var Firebase = require('./firebase-node');
var app = express();
var util = require('./util');
var FirebaseTokenGenerator = require("./firebase-token-generator-node.js");

if(!process.env.FIREBASE_SECRET) {
	console.error('FIREBASE_SECRET not available in the current environment');
	process.exit(1);
}

var tokenGenerator = new FirebaseTokenGenerator(process.env.FIREBASE_SECRET);
var token = tokenGenerator.createToken({}, {
    admin: true
});
var firebase = new Firebase('https://featuredcontent.firebaseio.com/');

var onAnnounceRequest = function(req, res) {
	var query = req.query;

	if(!req.query.hasOwnProperty('info_hash')) {
		console.error('received an announce without an info hash');
		res.writeHead(400);
		res.end();
		return;
	}

	if(query.hasOwnProperty('event') && query['event'] === 'completed') {
		var info_hash = util.uri2Hex(req.query['info_hash']);
		var trackerId = req.params.trackerId;
		console.log('completed event');

		var tracker = firebase.child('trackers').child(trackerId);
		tracker.once('value', function(dataSnapshot) {
			var value = dataSnapshot.val();
			console.log('tracker value', value);
			if(value.info_hash.toUpperCase() !== info_hash.toUpperCase()) {
				console.error('mismatch info hashes', info_hash, value.info_hash);
				return;
			}

			var completed = value.completed + 1;
			var size = req.query['downloaded'];
			tracker.update({
				'completed': completed,
				'size': size
			});
		});
	} else {
		console.log('non completed tracker announce');
	}

	res.writeHead(200);
	res.end('d8:intervali60e5:peerslee');
};

var onScrapeRequest = function(req, res) {
	res.writeHead(200);
	res.end('de');
};

app.get('/:trackerId/announce', onAnnounceRequest);
app.get('/:trackerId/scrape', onScrapeRequest);

var port = process.env.PORT || 5000;
var onAuth = function(success) {
	if(success) {
		console.log('firebase login success');
		app.listen(port, function() {
		    console.log('Listening on ' + port);
		});
	} else {
		console.log('firebase login failure');
		process.exit(1);
	}
};
firebase.auth(token, onAuth);
<commit_msg>tracking total transferred, rather than size<commit_after>/*
 * featureable-torrent
 * https://github.com/pwmckenna/featureable-torrent
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */

var express = require('express');
var Firebase = require('./firebase-node');
var app = express();
var util = require('./util');
var FirebaseTokenGenerator = require("./firebase-token-generator-node.js");

if(!process.env.FIREBASE_SECRET) {
	console.error('FIREBASE_SECRET not available in the current environment');
	process.exit(1);
}

var tokenGenerator = new FirebaseTokenGenerator(process.env.FIREBASE_SECRET);
var token = tokenGenerator.createToken({}, {
    admin: true
});
var firebase = new Firebase('https://featuredcontent.firebaseio.com/');

var onAnnounceRequest = function(req, res) {
	var query = req.query;

	if(!req.query.hasOwnProperty('info_hash')) {
		console.error('received an announce without an info hash');
		res.writeHead(400);
		res.end();
		return;
	}

	if(query.hasOwnProperty('event') && query['event'] === 'completed') {
		var info_hash = util.uri2Hex(req.query['info_hash']);
		var trackerId = req.params.trackerId;
		console.log('completed event');

		var tracker = firebase.child('trackers').child(trackerId);
		tracker.once('value', function(dataSnapshot) {
			var value = dataSnapshot.val();
			console.log('tracker value', value);
			if(value.info_hash.toUpperCase() !== info_hash.toUpperCase()) {
				console.error('mismatch info hashes', info_hash, value.info_hash);
				return;
			}

			var completed = value.completed + 1;
			var transferred = value.transferred;
			var downloaded = req.query['downloaded'];

			var updates = {};
			updates.completed = completed;
			updates.transferred = transferred + downloaded;
			tracker.update(updates);
		});
	} else {
		console.log('non completed tracker announce');
	}

	res.writeHead(200);
	res.end('d8:intervali60e5:peerslee');
};

var onScrapeRequest = function(req, res) {
	res.writeHead(200);
	res.end('de');
};

app.get('/:trackerId/announce', onAnnounceRequest);
app.get('/:trackerId/scrape', onScrapeRequest);

var port = process.env.PORT || 5000;
var onAuth = function(success) {
	if(success) {
		console.log('firebase login success');
		app.listen(port, function() {
		    console.log('Listening on ' + port);
		});
	} else {
		console.log('firebase login failure');
		process.exit(1);
	}
};
firebase.auth(token, onAuth);
<|endoftext|>


<commit_before>/*
 * This script represents an operator over Gearman protocol.
 * {@see http://gearman.org/protocol}
 *
 * (C) 2013 Vaclav Sykora
 * Apache License, Version 2.0, http://www.apache.org/licenses/
 *
 */

var winston = require('winston'),
    util    = require('util');

// various constants used in protocol
var constants = exports.CONSTANTS = {
    TYPE_REQ: 1,
    TYPE_RESP: 2,
    // --
    HEADER_REQ: 0x00524551,
    HEADER_RESP: 0x00524553,
    HEADER_LEN: 12,
    // --
    UNKNOWN_OPTION: 'UNKNOWN_OPTION'
};
var logger = winston.loggers.get('protocol');

/** @enum */
exports.DEFINITION = {
    CAN_DO: [1, constants.TYPE_REQ],                // W->J: FUNC
    CANT_DO: [2, constants.TYPE_REQ],               // W->J: FUNC
    RESET_ABILITIES: [3, constants.TYPE_REQ],       // # W->J: --
    PRE_SLEEP: [4, constants.TYPE_REQ],             // W->J: --
    // 5 (unused)
    NOOP: [6, constants.TYPE_RESP, ''],             // J->W: --
    SUBMIT_JOB: [7, constants.TYPE_REQ],            // C->J: FUNC[0]UNIQ[0]ARGS
    JOB_CREATED: [8, constants.TYPE_RESP, 'L'],     // J->C: HANDLE
    GRAB_JOB: [9, constants.TYPE_REQ],              // W->J: --
    NO_JOB: [10, constants.TYPE_RESP, ''],          // J->W: --
    JOB_ASSIGN: [11, constants.TYPE_RESP, 'nnb'],   // J->W: HANDLE[0]FUNC[0]ARG
    WORK_STATUS: [12, constants.TYPE_REQ | constants.TYPE_RESP, 'nnL'],     // W->J/C: HANDLE[0]NUMERATOR[0]DENOMINATOR
    WORK_COMPLETE: [13, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],    // W->J/C: HANDLE[0]RES
    WORK_FAIL: [14, constants.TYPE_REQ | constants.TYPE_RESP, 'L'],         // W->J/C: HANDLE
    GET_STATUS: [15, constants.TYPE_REQ],           // C->J: HANDLE
    ECHO_REQ: [16, constants.TYPE_REQ],             // C/W->J: TEXT
    ECHO_RES: [17, constants.TYPE_RESP, 'L'],       // J->C/W: TEXT
    SUBMIT_JOB_BG: [18, constants.TYPE_REQ],        // C->J: FUNC[0]UNIQ[0]ARGS
    ERROR: [19, constants.TYPE_RESP, 'nL'],         // J->C/W: ERRCODE[0]ERR_TEXT
    STATUS_RES: [20, constants.TYPE_RESP, 'nnnnL'], // C->J: HANDLE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM
    SUBMIT_JOB_HIGH: [21, constants.TYPE_REQ],      // C->J: FUNC[0]UNIQ[0]ARGS
    SET_CLIENT_ID: [22, constants.TYPE_REQ],        // W->J: STRING_NO_WHITESPACE
    CAN_DO_TIMEOUT: [23, constants.TYPE_REQ],       // W->J: FUNC[0]TIMEOUT
    WORK_EXCEPTION: [25, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],   // W->J/C: HANDLE[0]ARG
    OPTION_REQ: [26, constants.TYPE_REQ],           // C->J: TEXT
    OPTION_RES: [27, constants.TYPE_RESP, 'L'],     // J->C: TEXT
    WORK_DATA: [28, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],        // W->J/C: HANDLE[0]RES
    WORK_WARNING: [29, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],     // W->J/C: HANDLE[0]MSG
    SUBMIT_JOB_HIGH_BG: [32, constants.TYPE_REQ],   // C->J: FUNC[0]UNIQ[0]ARGS
    SUBMIT_JOB_LOW: [33, constants.TYPE_REQ],       // C->J: FUNC[0]UNIQ[0]ARGS
    SUBMIT_JOB_LOW_BG: [34, constants.TYPE_REQ],    // C->J: FUNC[0]UNIQ[0]ARGS
};

// desc=>code - {CAN_DO: 1}
exports.PACKET_TYPES = {}
// code=>desc - {1: CAN_DO}
exports.PACKET_CODES = {};
// code=>format for RESP - {19: 'nL'}
exports.PACKET_RESP_FORMAT = {};

var def;
for (var i in exports.DEFINITION) {
    def = exports.DEFINITION[i];

    exports.PACKET_TYPES[i] = def[0];
    exports.PACKET_CODES[def[0]] = i;

    if (constants.TYPE_RESP === def[1]) {
        exports.PACKET_RESP_FORMAT[def[0]] = def[2];
    }
}

/**
 * Parses given buffer according to defined format.
 *
 * *format*
 *   *N* NULL byte terminated string (default encoding)
 *   *n* NULL byte terminated string (ASCII encoding)
 *   *L* last segment of buffer (default encoding)
 *   *b* last segment of buffer (as Buffer)
 *
 * return array
 *   *rslt[0]* number of processed bytes
 *   *rslt[1..]* chunks with order defined by format
 */
exports.parsePacket = function (buff, format) {
    var i, j, key;
    var rslt = [];
    var offset = constants.HEADER_LEN;
    var packetType = buff.readUInt32BE(4);
    var packetLength = offset + buff.readUInt32BE(8);

    format = format || '';

    for (i = 0; i < format.length; i ++) {
        key = format.charAt(i);

        if ('N' == key || 'n' == key) {
            // find next NULL
            for (j = offset; j < buff.length; j ++) {
                if (buff[j] == 0) {
                    break;
                }
            }
            rslt[i + 1] = buff.toString('n' == key ? 'ascii' : undefined, offset, j);
            offset = j + 1; // +1 == skip NULL
        } else if ('L' == key) { // LAST segment up to packetLength as String
            rslt[i + 1] = buff.toString(undefined, offset, packetLength);
            offset = packetLength;
        } else if ('b' == key) { // LAST segment up to packetLength as Buffer
            rslt[i + 1] = buff.slice(offset);
            offset = packetLength;
        } else {
            return new Error('unknow format: ' + format);
        }
    }

    rslt[0] = offset;
    if (logger.isLevelEnabled('verbose')) {
        logger.log('verbose', 'packet parsed, bytes=%d, type=%s', offset, exports.PACKET_CODES[packetType]);
    }
    return rslt;
}

exports.encodePacket = function (packetType, args) {
    var i, buff;
    var packetLength = 0;
    var offset = constants.HEADER_LEN;

    // default values
    args = args || [];

    // compute packet length
    for (i = 0; i < args.length; i ++) {
        if (args[i].constructor.name === 'String') {
            packetLength += Buffer.byteLength(args[i]);
        } else if (args[i].constructor.name === 'Buffer') {
            packetLength += args[i].length;
        } else {
            packetLength += Buffer.byteLength(args[i].toString());
        }
    }
    if (args.length > 0) {
        packetLength += args.length - 1; // NULL byte terminations
    }

    buff = new Buffer(constants.HEADER_LEN + packetLength);

    buff.writeUInt32BE(constants.HEADER_REQ, 0); // \0REQ
    buff.writeUInt32BE(packetType, 4);
    buff.writeUInt32BE(packetLength, 8);

    // construct packet
    for (i = 0; i < args.length; i ++) {
        if (args[i].constructor.name === 'String') {
            buff.write(args[i], offset);
            offset += Buffer.byteLength(args[i]);
        } else if (args[i].constructor.name === 'Buffer') {
            args[i].copy(buff, offset);
            offset += args[i].length;
        } else {
            buff.write(args[i].toString(), offset);
            offset += Buffer.byteLength(args[i].toString());
        }

        if (i < (args.length - 1)) {
            buff.writeUInt8(0, offset); // NULL byte terminated chunk
            offset ++;
        }
    }

    logger.log('debug', 'packet encoded, type=%s, buffer.size=%d',
        exports.PACKET_CODES[packetType], packetLength + constants.HEADER_LEN);
    return buff;
}
<commit_msg>cosmetic<commit_after>/*
 * This script represents an operator over Gearman protocol.
 * {@see http://gearman.org/protocol}
 *
 * (C) 2013 Vaclav Sykora
 * Apache License, Version 2.0, http://www.apache.org/licenses/
 *
 */

var winston = require('winston'),
    util    = require('util');

// various constants used in protocol
var constants = exports.CONSTANTS = {
    TYPE_REQ: 1,
    TYPE_RESP: 2,
    // --
    HEADER_REQ: 0x00524551,
    HEADER_RESP: 0x00524553,
    HEADER_LEN: 12,
    // --
    UNKNOWN_OPTION: 'UNKNOWN_OPTION'
};
var logger = winston.loggers.get('protocol');

/** @enum */
exports.DEFINITION = {
    CAN_DO: [1, constants.TYPE_REQ],                // W->J: FUNC
    CANT_DO: [2, constants.TYPE_REQ],               // W->J: FUNC
    RESET_ABILITIES: [3, constants.TYPE_REQ],       // # W->J: --
    PRE_SLEEP: [4, constants.TYPE_REQ],             // W->J: --
    // 5 (unused)
    NOOP: [6, constants.TYPE_RESP, ''],             // J->W: --
    SUBMIT_JOB: [7, constants.TYPE_REQ],            // C->J: FUNC[0]UNIQ[0]ARGS
    JOB_CREATED: [8, constants.TYPE_RESP, 'L'],     // J->C: HANDLE
    GRAB_JOB: [9, constants.TYPE_REQ],              // W->J: --
    NO_JOB: [10, constants.TYPE_RESP, ''],          // J->W: --
    JOB_ASSIGN: [11, constants.TYPE_RESP, 'nnb'],   // J->W: HANDLE[0]FUNC[0]ARG
    WORK_STATUS: [12, constants.TYPE_REQ | constants.TYPE_RESP, 'nnL'],     // W->J/C: HANDLE[0]NUMERATOR[0]DENOMINATOR
    WORK_COMPLETE: [13, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],    // W->J/C: HANDLE[0]RES
    WORK_FAIL: [14, constants.TYPE_REQ | constants.TYPE_RESP, 'L'],         // W->J/C: HANDLE
    GET_STATUS: [15, constants.TYPE_REQ],           // C->J: HANDLE
    ECHO_REQ: [16, constants.TYPE_REQ],             // C/W->J: TEXT
    ECHO_RES: [17, constants.TYPE_RESP, 'L'],       // J->C/W: TEXT
    SUBMIT_JOB_BG: [18, constants.TYPE_REQ],        // C->J: FUNC[0]UNIQ[0]ARGS
    ERROR: [19, constants.TYPE_RESP, 'nL'],         // J->C/W: ERRCODE[0]ERR_TEXT
    STATUS_RES: [20, constants.TYPE_RESP, 'nnnnL'], // C->J: HANDLE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM
    SUBMIT_JOB_HIGH: [21, constants.TYPE_REQ],      // C->J: FUNC[0]UNIQ[0]ARGS
    SET_CLIENT_ID: [22, constants.TYPE_REQ],        // W->J: STRING_NO_WHITESPACE
    CAN_DO_TIMEOUT: [23, constants.TYPE_REQ],       // W->J: FUNC[0]TIMEOUT
    // 24 ALL_YOURS (Not yet implemented)
    WORK_EXCEPTION: [25, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],   // W->J/C: HANDLE[0]ARG
    OPTION_REQ: [26, constants.TYPE_REQ],           // C->J: TEXT
    OPTION_RES: [27, constants.TYPE_RESP, 'L'],     // J->C: TEXT
    WORK_DATA: [28, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],        // W->J/C: HANDLE[0]RES
    WORK_WARNING: [29, constants.TYPE_REQ | constants.TYPE_RESP, 'nb'],     // W->J/C: HANDLE[0]MSG
    SUBMIT_JOB_HIGH_BG: [32, constants.TYPE_REQ],   // C->J: FUNC[0]UNIQ[0]ARGS
    SUBMIT_JOB_LOW: [33, constants.TYPE_REQ],       // C->J: FUNC[0]UNIQ[0]ARGS
    SUBMIT_JOB_LOW_BG: [34, constants.TYPE_REQ],    // C->J: FUNC[0]UNIQ[0]ARGS
};

// desc=>code - {CAN_DO: 1}
exports.PACKET_TYPES = {}
// code=>desc - {1: CAN_DO}
exports.PACKET_CODES = {};
// code=>format for RESP - {19: 'nL'}
exports.PACKET_RESP_FORMAT = {};

var def;
for (var i in exports.DEFINITION) {
    def = exports.DEFINITION[i];

    exports.PACKET_TYPES[i] = def[0];
    exports.PACKET_CODES[def[0]] = i;

    if (constants.TYPE_RESP === def[1]) {
        exports.PACKET_RESP_FORMAT[def[0]] = def[2];
    }
}

/**
 * Parses given buffer according to defined format.
 *
 * *format*
 *   *N* NULL byte terminated string (default encoding)
 *   *n* NULL byte terminated string (ASCII encoding)
 *   *L* last segment of buffer (default encoding)
 *   *b* last segment of buffer (as Buffer)
 *
 * return array
 *   *rslt[0]* number of processed bytes
 *   *rslt[1..]* chunks with order defined by format
 */
exports.parsePacket = function (buff, format) {
    var i, j, key;
    var rslt = [];
    var offset = constants.HEADER_LEN;
    var packetType = buff.readUInt32BE(4);
    var packetLength = offset + buff.readUInt32BE(8);

    format = format || '';

    for (i = 0; i < format.length; i ++) {
        key = format.charAt(i);

        if ('N' == key || 'n' == key) {
            // find next NULL
            for (j = offset; j < buff.length; j ++) {
                if (buff[j] == 0) {
                    break;
                }
            }
            rslt[i + 1] = buff.toString('n' == key ? 'ascii' : undefined, offset, j);
            offset = j + 1; // +1 == skip NULL
        } else if ('L' == key) { // LAST segment up to packetLength as String
            rslt[i + 1] = buff.toString(undefined, offset, packetLength);
            offset = packetLength;
        } else if ('b' == key) { // LAST segment up to packetLength as Buffer
            rslt[i + 1] = buff.slice(offset);
            offset = packetLength;
        } else {
            return new Error('unknow format: ' + format);
        }
    }

    rslt[0] = offset;
    if (logger.isLevelEnabled('verbose')) {
        logger.log('verbose', 'packet parsed, bytes=%d, type=%s', offset, exports.PACKET_CODES[packetType]);
    }
    return rslt;
}

exports.encodePacket = function (packetType, args) {
    var i, buff;
    var packetLength = 0;
    var offset = constants.HEADER_LEN;

    // default values
    args = args || [];

    // compute packet length
    for (i = 0; i < args.length; i ++) {
        if (args[i].constructor.name === 'String') {
            packetLength += Buffer.byteLength(args[i]);
        } else if (args[i].constructor.name === 'Buffer') {
            packetLength += args[i].length;
        } else {
            packetLength += Buffer.byteLength(args[i].toString());
        }
    }
    if (args.length > 0) {
        packetLength += args.length - 1; // NULL byte terminations
    }

    buff = new Buffer(constants.HEADER_LEN + packetLength);

    buff.writeUInt32BE(constants.HEADER_REQ, 0); // \0REQ
    buff.writeUInt32BE(packetType, 4);
    buff.writeUInt32BE(packetLength, 8);

    // construct packet
    for (i = 0; i < args.length; i ++) {
        if (args[i].constructor.name === 'String') {
            buff.write(args[i], offset);
            offset += Buffer.byteLength(args[i]);
        } else if (args[i].constructor.name === 'Buffer') {
            args[i].copy(buff, offset);
            offset += args[i].length;
        } else {
            buff.write(args[i].toString(), offset);
            offset += Buffer.byteLength(args[i].toString());
        }

        if (i < (args.length - 1)) {
            buff.writeUInt8(0, offset); // NULL byte terminated chunk
            offset ++;
        }
    }

    logger.log('debug', 'packet encoded, type=%s, buffer.size=%d',
        exports.PACKET_CODES[packetType], packetLength + constants.HEADER_LEN);
    return buff;
}
<|endoftext|>