diff --git a/.jshintrc b/.jshintrc index 4aedb3cf..f03eef65 100644 --- a/.jshintrc +++ b/.jshintrc @@ -87,6 +87,8 @@ "describe": true, "before": true, "after": true, + "beforeEach": true, + "afterEach": true, "it": true, "suite": true, "suiteSetup": true, diff --git a/Makefile b/Makefile index 11899d75..2bd19912 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ test: config/environments/test.js jshint: @echo "***jshint***" - @./node_modules/.bin/jshint lib/ + @./node_modules/.bin/jshint lib/ app.js test-all: jshint test diff --git a/NEWS.md b/NEWS.md index 9b2ea413..2d4f35ec 100644 --- a/NEWS.md +++ b/NEWS.md @@ -24,6 +24,8 @@ Announcements: - scale_factor * Affected tables for x-cache-channel will use direct connection to postgresql * Removes some metrics: authorized times ones + * Mapnik renderer configuration not part of the `renderer` root configuration + - All configuration must be moved into `renderer.mapnik`, see `config/environments/*.js.example` for reference - Removes rollbar as optional logger diff --git a/app.js b/app.js index 539043e9..19361566 100755 --- a/app.js +++ b/app.js @@ -7,17 +7,21 @@ * environments: [development, production] */ -var path = require('path'), - fs = require('fs'), - RedisPool = require('redis-mpool') -; +var path = require('path'); +var fs = require('fs'); +var RedisPool = require('redis-mpool'); +var _ = require('underscore'); +var ENV; +if ( process.argv[2] ) { + ENV = process.argv[2]; +} else if ( process.env.NODE_ENV ) { + ENV = process.env.NODE_ENV; +} else { + ENV = 'development'; +} -if ( process.argv[2] ) ENV = process.argv[2]; -else if ( process.env['NODE_ENV'] ) ENV = process.env['NODE_ENV']; -else ENV = 'development'; - -process.env['NODE_ENV'] = ENV; +process.env.NODE_ENV = ENV; // sanity check if (ENV != 'development' && ENV != 'production' && ENV != 'staging' ){ @@ -26,14 +30,12 @@ if (ENV != 'development' && ENV != 'production' && ENV != 'staging' ){ process.exit(1); } -var _ = require('underscore'); - // set environment specific variables global.environment = require(__dirname + '/config/environments/' + ENV); global.environment.api_hostname = require('os').hostname().split('.')[0]; global.log4js = require('log4js'); -log4js_config = { +var log4js_config = { appenders: [], replaceConsole:true }; @@ -60,18 +62,18 @@ if ( global.environment.log_filename ) { ); } -log4js.configure(log4js_config, { cwd: __dirname }); -global.logger = log4js.getLogger(); +global.log4js.configure(log4js_config, { cwd: __dirname }); +global.logger = global.log4js.getLogger(); var redisOpts = _.extend(global.environment.redis, { name: 'windshaft' }), redisPool = new RedisPool(redisOpts); // Include cartodb_windshaft only _after_ the "global" variable is set // See https://github.com/Vizzuality/Windshaft-cartodb/issues/28 -var CartodbWindshaft = require('./lib/cartodb/cartodb_windshaft'), +var cartodbWindshaft = require('./lib/cartodb/cartodb_windshaft'), serverOptions = require('./lib/cartodb/server_options')(redisPool); -ws = CartodbWindshaft(serverOptions); +var ws = cartodbWindshaft(serverOptions); if (global.statsClient) { redisPool.on('status', function(status) { @@ -93,19 +95,20 @@ ws.listen(global.environment.port, global.environment.host); var version = require("./package").version; ws.on('listening', function() { - console.log("Windshaft tileserver " + version + " started on " - + global.environment.host + ':' + global.environment.port - + " (" + ENV + ")"); + console.log( + "Windshaft tileserver %s started on %s:%s (%s)", + version, global.environment.host, global.environment.port, ENV + ); }); process.on('SIGHUP', function() { global.log4js.clearAndShutdownAppenders(function() { global.log4js.configure(log4js_config); - global.logger = log4js.getLogger(); + global.logger = global.log4js.getLogger(); console.log('Log files reloaded'); }); }); process.on('uncaughtException', function(err) { - logger.error('Uncaught exception: ' + err.stack); + global.logger.error('Uncaught exception: ' + err.stack); }); diff --git a/assets/render-timeout-fallback.png b/assets/render-timeout-fallback.png new file mode 100644 index 00000000..f80cdbf8 Binary files /dev/null and b/assets/render-timeout-fallback.png differ diff --git a/config/environments/development.js.example b/config/environments/development.js.example index d04ddf01..7b7866b1 100644 --- a/config/environments/development.js.example +++ b/config/environments/development.js.example @@ -84,9 +84,49 @@ var config = { ,renderer: { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, - metatile: 4, - bufferSize: 64, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mapnik: { + // The size of the pool of internal mapnik renderers + // Check the configuration of uv_threadpool_size to use suitable value + poolSize: 8, + + // Metatile is the number of tiles-per-side that are going + // to be rendered at once. If all of them will be requested + // we'd have saved time. If only one will be used, we'd have + // wasted time. + metatile: 2, + + // Buffer size is the tickness in pixel of a buffer + // around the rendered (meta?)tile. + // + // This is important for labels and other marker that overlap tile boundaries. + // Setting to 128 ensures no render artifacts. + // 64 may have artifacts but is faster. + // Less important if we can turn metatiling on. + bufferSize: 64, + + // SQL queries will be wrapped with ST_SnapToGrid + // Snapping all points of the geometry to a regular grid + snapToGrid: false, + + // SQL queries will be wrapped with ST_ClipByBox2D + // Returning the portion of a geometry falling within a rectangle + // It will only work if snapToGrid is enabled + clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5 + + limits: { + // Time in milliseconds a render request can take before it fails, some notes: + // - 0 means no render limit + // - it considers metatiling, naive implementation: (render timeout) * (number of tiles in metatile) + render: 0, + // As the render request will finish even if timed out, whether it should be placed in the internal + // cache or it should be fully discarded. When placed in the internal cache another attempt to retrieve + // the same tile will result in an immediate response, however that will use a lot of more application + // memory. If we want to enforce this behaviour we have to implement a cache eviction policy for the + // internal cache. + cacheOnTimeout: true + } + }, http: { timeout: 2000, // the timeout in ms for a http tile request proxy: undefined, // the url for a proxy server @@ -156,6 +196,8 @@ var config = { // Use this as a feature flags enabling/disabling mechanism ,enabledFeatures: { + // whether it should intercept tile render errors an act based on them, enabled by default. + onTileErrorStrategy: true, // whether the affected tables for a given SQL must query directly postgresql or use the SQL API cdbQueryTablesFromPostgres: true } diff --git a/config/environments/production.js.example b/config/environments/production.js.example index 85408685..72fae68b 100644 --- a/config/environments/production.js.example +++ b/config/environments/production.js.example @@ -78,9 +78,49 @@ var config = { ,renderer: { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, - metatile: 4, - bufferSize: 64, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mapnik: { + // The size of the pool of internal mapnik renderers + // Check the configuration of uv_threadpool_size to use suitable value + poolSize: 8, + + // Metatile is the number of tiles-per-side that are going + // to be rendered at once. If all of them will be requested + // we'd have saved time. If only one will be used, we'd have + // wasted time. + metatile: 2, + + // Buffer size is the tickness in pixel of a buffer + // around the rendered (meta?)tile. + // + // This is important for labels and other marker that overlap tile boundaries. + // Setting to 128 ensures no render artifacts. + // 64 may have artifacts but is faster. + // Less important if we can turn metatiling on. + bufferSize: 64, + + // SQL queries will be wrapped with ST_SnapToGrid + // Snapping all points of the geometry to a regular grid + snapToGrid: false, + + // SQL queries will be wrapped with ST_ClipByBox2D + // Returning the portion of a geometry falling within a rectangle + // It will only work if snapToGrid is enabled + clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5 + + limits: { + // Time in milliseconds a render request can take before it fails, some notes: + // - 0 means no render limit + // - it considers metatiling, naive implementation: (render timeout) * (number of tiles in metatile) + render: 0, + // As the render request will finish even if timed out, whether it should be placed in the internal + // cache or it should be fully discarded. When placed in the internal cache another attempt to retrieve + // the same tile will result in an immediate response, however that will use a lot of more application + // memory. If we want to enforce this behaviour we have to implement a cache eviction policy for the + // internal cache. + cacheOnTimeout: true + } + }, http: { timeout: 2000, // the timeout in ms for a http tile request proxy: undefined, // the url for a proxy server @@ -156,6 +196,8 @@ var config = { // Use this as a feature flags enabling/disabling mechanism ,enabledFeatures: { + // whether it should intercept tile render errors an act based on them, enabled by default. + onTileErrorStrategy: true, // whether the affected tables for a given SQL must query directly postgresql or use the SQL API cdbQueryTablesFromPostgres: true } diff --git a/config/environments/staging.js.example b/config/environments/staging.js.example index d8e186b8..242890f6 100644 --- a/config/environments/staging.js.example +++ b/config/environments/staging.js.example @@ -78,9 +78,49 @@ var config = { ,renderer: { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, - metatile: 4, - bufferSize: 64, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mapnik: { + // The size of the pool of internal mapnik renderers + // Check the configuration of uv_threadpool_size to use suitable value + poolSize: 8, + + // Metatile is the number of tiles-per-side that are going + // to be rendered at once. If all of them will be requested + // we'd have saved time. If only one will be used, we'd have + // wasted time. + metatile: 2, + + // Buffer size is the tickness in pixel of a buffer + // around the rendered (meta?)tile. + // + // This is important for labels and other marker that overlap tile boundaries. + // Setting to 128 ensures no render artifacts. + // 64 may have artifacts but is faster. + // Less important if we can turn metatiling on. + bufferSize: 64, + + // SQL queries will be wrapped with ST_SnapToGrid + // Snapping all points of the geometry to a regular grid + snapToGrid: false, + + // SQL queries will be wrapped with ST_ClipByBox2D + // Returning the portion of a geometry falling within a rectangle + // It will only work if snapToGrid is enabled + clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5 + + limits: { + // Time in milliseconds a render request can take before it fails, some notes: + // - 0 means no render limit + // - it considers metatiling, naive implementation: (render timeout) * (number of tiles in metatile) + render: 0, + // As the render request will finish even if timed out, whether it should be placed in the internal + // cache or it should be fully discarded. When placed in the internal cache another attempt to retrieve + // the same tile will result in an immediate response, however that will use a lot of more application + // memory. If we want to enforce this behaviour we have to implement a cache eviction policy for the + // internal cache. + cacheOnTimeout: true + } + }, http: { timeout: 2000, // the timeout in ms for a http tile request proxy: undefined, // the url for a proxy server @@ -156,6 +196,8 @@ var config = { // Use this as a feature flags enabling/disabling mechanism ,enabledFeatures: { + // whether it should intercept tile render errors an act based on them, enabled by default. + onTileErrorStrategy: true, // whether the affected tables for a given SQL must query directly postgresql or use the SQL API cdbQueryTablesFromPostgres: true } diff --git a/config/environments/test.js.example b/config/environments/test.js.example index 9032752e..de03dc7f 100644 --- a/config/environments/test.js.example +++ b/config/environments/test.js.example @@ -78,9 +78,49 @@ var config = { ,renderer: { // Milliseconds since last access before renderer cache item expires cache_ttl: 60000, - metatile: 4, - bufferSize: 64, statsInterval: 5000, // milliseconds between each report to statsd about number of renderers and mapnik pool status + mapnik: { + // The size of the pool of internal mapnik renderers + // Check the configuration of uv_threadpool_size to use suitable value + poolSize: 8, + + // Metatile is the number of tiles-per-side that are going + // to be rendered at once. If all of them will be requested + // we'd have saved time. If only one will be used, we'd have + // wasted time. + metatile: 2, + + // Buffer size is the tickness in pixel of a buffer + // around the rendered (meta?)tile. + // + // This is important for labels and other marker that overlap tile boundaries. + // Setting to 128 ensures no render artifacts. + // 64 may have artifacts but is faster. + // Less important if we can turn metatiling on. + bufferSize: 64, + + // SQL queries will be wrapped with ST_SnapToGrid + // Snapping all points of the geometry to a regular grid + snapToGrid: false, + + // SQL queries will be wrapped with ST_ClipByBox2D + // Returning the portion of a geometry falling within a rectangle + // It will only work if snapToGrid is enabled + clipByBox2d: false, // this requires postgis >=2.2 and geos >=3.5 + + limits: { + // Time in milliseconds a render request can take before it fails, some notes: + // - 0 means no render limit + // - it considers metatiling, naive implementation: (render timeout) * (number of tiles in metatile) + render: 0, + // As the render request will finish even if timed out, whether it should be placed in the internal + // cache or it should be fully discarded. When placed in the internal cache another attempt to retrieve + // the same tile will result in an immediate response, however that will use a lot of more application + // memory. If we want to enforce this behaviour we have to implement a cache eviction policy for the + // internal cache. + cacheOnTimeout: true + } + }, http: { timeout: 2000, // the timeout in ms for a http tile request proxy: undefined, // the url for a proxy server @@ -152,6 +192,8 @@ var config = { // Use this as a feature flags enabling/disabling mechanism ,enabledFeatures: { + // whether it should intercept tile render errors an act based on them, enabled by default. + onTileErrorStrategy: true, // whether the affected tables for a given SQL must query directly postgresql or use the SQL API cdbQueryTablesFromPostgres: true } diff --git a/lib/cartodb/controllers/template_maps.js b/lib/cartodb/controllers/template_maps.js index e071d8a0..df504d82 100644 --- a/lib/cartodb/controllers/template_maps.js +++ b/lib/cartodb/controllers/template_maps.js @@ -408,7 +408,8 @@ TemplateMapsController.prototype.instantiateTemplate = function(req, res, templa if ( req.profiler ) req.profiler.done('TemplateMaps_instance'); if ( err ) throw err; layergroup = instance; - fakereq = { query: {}, params: {}, headers: _.clone(req.headers), + fakereq = {query: {}, params: {}, headers: _.clone(req.headers), + context: _.clone(req.context), method: req.method, res: res, profiler: req.profiler diff --git a/lib/cartodb/server_options.js b/lib/cartodb/server_options.js index e7f8b855..188ba578 100644 --- a/lib/cartodb/server_options.js +++ b/lib/cartodb/server_options.js @@ -1,12 +1,17 @@ var _ = require('underscore'); var step = require('step'); +var LZMA = require('lzma').LZMA; +var assert = require('assert'); +var RedisPool = require('redis-mpool'); + var QueryTablesApi = require('./api/query_tables_api'); var PgConnection = require('./backends/pg_connection'); -var LZMA = require('lzma').LZMA; var TemplateMaps = require('./template_maps.js'); var MapConfigNamedLayersAdapter = require('./models/mapconfig_named_layers_adapter'); var CdbRequest = require('./models/cdb_request'); -var assert = require('assert'); + +var timeoutErrorTilePath = __dirname + '/../../assets/render-timeout-fallback.png'; +var timeoutErrorTile = require('fs').readFileSync(timeoutErrorTilePath, {encoding: null}); // Whitelist query parameters and attach format var REQUEST_QUERY_PARAMS_WHITELIST = [ @@ -18,8 +23,7 @@ var REQUEST_QUERY_PARAMS_WHITELIST = [ ]; module.exports = function(redisPool) { - redisPool = redisPool || - require('redis-mpool')(_.extend(global.environment.redis, {name: 'windshaft:server_options'})); + redisPool = redisPool || new RedisPool(_.extend(global.environment.redis, {name: 'windshaft:server_options'})); var cartoData = require('cartodb-redis')({ pool: redisPool }), lzmaWorker = new LZMA(), @@ -29,9 +33,16 @@ module.exports = function(redisPool) { var rendererConfig = _.defaults(global.environment.renderer || {}, { cache_ttl: 60000, // milliseconds - metatile: 4, - bufferSize: 64, - statsInterval: 60000 + statsInterval: 60000, + mapnik: { + poolSize: 8, + metatile: 2, + bufferSize: 64, + snapToGrid: false, + clipByBox2d: false, + limits: {} + }, + http: {} }); var me = { @@ -59,17 +70,13 @@ module.exports = function(redisPool) { mapnik_tile_format: global.environment.mapnik_tile_format || 'png', default_layergroup_ttl: global.environment.mapConfigTTL || 7200 }, - mapnik: { - poolSize: rendererConfig.poolSize, - metatile: rendererConfig.metatile, - bufferSize: rendererConfig.bufferSize - }, statsd: global.environment.statsd, renderCache: { ttl: rendererConfig.cache_ttl, statsInterval: rendererConfig.statsInterval }, renderer: { + mapnik: rendererConfig.mapnik, http: rendererConfig.http }, redis: global.environment.redis, @@ -240,6 +247,45 @@ module.exports = function(redisPool) { }); }; + + if (global.environment.enabledFeatures.onTileErrorStrategy !== false) { + me.renderer.onTileErrorStrategy = function(err, tile, headers, stats, format, callback) { + if (err && err.message === 'Render timed out' && format === 'png') { + return callback(null, timeoutErrorTile, { 'Content-Type': 'image/png' }, {}); + } else { + return callback(err, tile, headers, stats); + } + }; + } + + me.renderCache.beforeRendererCreate = function(req, callback) { + var user = cdbRequest.userByReq(req); + + var rendererOptions = {}; + + step( + function getLimits(err) { + assert.ifError(err); + cartoData.getTilerRenderLimit(user, this); + }, + function handleTilerLimits(err, renderLimit) { + assert.ifError(err); + rendererOptions.limits = { + cacheOnTimeout: rendererConfig.mapnik.limits.cacheOnTimeout || false, + render: renderLimit || rendererConfig.mapnik.limits.render || 0 + }; + return null; + }, + function finish(err) { + if (err) { + return callback(err); + } + + return callback(null, rendererOptions); + } + ); + }; + me.beforeLayergroupCreate = function(req, requestMapConfig, callback) { mapConfigNamedLayersAdapter.getLayers(cdbRequest.userByReq(req), requestMapConfig.layers, pgConnection, function(err, layers, datasource) { diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 00000000..676dd57a --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,2375 @@ +{ + "name": "windshaft-cartodb", + "version": "2.0.0", + "dependencies": { + "cartodb-psql": { + "version": "0.4.0", + "from": "cartodb-psql@~0.4.0", + "resolved": "https://registry.npmjs.org/cartodb-psql/-/cartodb-psql-0.4.0.tgz", + "dependencies": { + "pg": { + "version": "2.6.2-cdb1", + "from": "pg@git://github.com/CartoDB/node-postgres.git#2.6.2-cdb1", + "resolved": "git://github.com/CartoDB/node-postgres.git#836a2dc3131e873fc4ba20cd16e7fb69a7dca98a", + "dependencies": { + "generic-pool": { + "version": "2.0.3", + "from": "generic-pool@2.0.3", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.0.3.tgz" + }, + "buffer-writer": { + "version": "1.0.0", + "from": "buffer-writer@1.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.0.tgz" + } + } + } + } + }, + "cartodb-redis": { + "version": "0.12.1", + "from": "cartodb-redis@~0.12.1", + "dependencies": { + "redis-mpool": { + "version": "0.1.0", + "from": "redis-mpool@git://github.com/CartoDB/node-redis-mpool.git#0.1.0", + "resolved": "git://github.com/CartoDB/node-redis-mpool.git#47510b8d4525ee24aa2e5328976372274a1d144e", + "dependencies": { + "generic-pool": { + "version": "2.1.1", + "from": "generic-pool@~2.1.1", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz" + }, + "redis": { + "version": "0.12.1", + "from": "redis@~0.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + }, + "hiredis": { + "version": "0.1.17", + "from": "hiredis@~0.1.17", + "resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.1.17.tgz", + "dependencies": { + "bindings": { + "version": "1.2.1", + "from": "bindings@*", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "nan": { + "version": "1.1.2", + "from": "nan@~1.1.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.1.2.tgz" + } + } + } + } + } + } + }, + "dot": { + "version": "1.0.3", + "from": "dot@~1.0.2", + "resolved": "https://registry.npmjs.org/dot/-/dot-1.0.3.tgz" + }, + "log4js": { + "version": "0.6.21", + "from": "https://github.com/CartoDB/log4js-node/tarball/cdb", + "resolved": "https://github.com/CartoDB/log4js-node/tarball/cdb", + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@~0.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@~1.0.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "lzma": { + "version": "1.3.7", + "from": "lzma@~1.3.7", + "resolved": "https://registry.npmjs.org/lzma/-/lzma-1.3.7.tgz" + }, + "queue-async": { + "version": "1.0.7", + "from": "queue-async@~1.0.7", + "resolved": "https://registry.npmjs.org/queue-async/-/queue-async-1.0.7.tgz" + }, + "redis-mpool": { + "version": "0.3.0", + "from": "redis-mpool@~0.3.0", + "resolved": "https://registry.npmjs.org/redis-mpool/-/redis-mpool-0.3.0.tgz", + "dependencies": { + "generic-pool": { + "version": "2.1.1", + "from": "generic-pool@~2.1.1", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz" + }, + "redis": { + "version": "0.12.1", + "from": "redis@~0.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + }, + "hiredis": { + "version": "0.1.17", + "from": "hiredis@~0.1.17", + "resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.1.17.tgz", + "dependencies": { + "bindings": { + "version": "1.2.1", + "from": "bindings@*", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "nan": { + "version": "1.1.2", + "from": "nan@~1.1.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.1.2.tgz" + } + } + } + } + }, + "request": { + "version": "2.9.203", + "from": "request@~2.9.203", + "resolved": "https://registry.npmjs.org/request/-/request-2.9.203.tgz" + }, + "step": { + "version": "0.0.5", + "from": "step@~0.0.5", + "resolved": "https://registry.npmjs.org/step/-/step-0.0.5.tgz" + }, + "underscore": { + "version": "1.6.0", + "from": "underscore@~1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz" + }, + "windshaft": { + "version": "0.41.0", + "from": "windshaft@0.41.0", + "resolved": "https://registry.npmjs.org/windshaft/-/windshaft-0.41.0.tgz", + "dependencies": { + "chronograph": { + "version": "0.1.0", + "from": "chronograph@git://github.com/CartoDB/chronographjs.git#0.1.0", + "resolved": "git://github.com/CartoDB/chronographjs.git#0b8c35eee510cfa14a16be24d70533b38ecc1d2d" + }, + "grainstore": { + "version": "0.23.0", + "from": "grainstore@~0.23.0", + "resolved": "https://registry.npmjs.org/grainstore/-/grainstore-0.23.0.tgz", + "dependencies": { + "redis-mpool": { + "version": "0.1.0", + "from": "https://github.com/CartoDB/node-redis-mpool/tarball/0.1.0", + "resolved": "https://github.com/CartoDB/node-redis-mpool/tarball/0.1.0", + "dependencies": { + "generic-pool": { + "version": "2.1.1", + "from": "generic-pool@~2.1.1", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz" + }, + "redis": { + "version": "0.12.1", + "from": "redis@~0.12.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-0.12.1.tgz" + }, + "hiredis": { + "version": "0.1.17", + "from": "hiredis@~0.1.17", + "resolved": "https://registry.npmjs.org/hiredis/-/hiredis-0.1.17.tgz", + "dependencies": { + "bindings": { + "version": "1.2.1", + "from": "bindings@*", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "nan": { + "version": "1.1.2", + "from": "nan@~1.1.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.1.2.tgz" + } + } + } + } + }, + "carto": { + "version": "0.9.5-cdb2", + "from": "https://github.com/CartoDB/carto/tarball/0.9.5-cdb2", + "resolved": "https://github.com/CartoDB/carto/tarball/0.9.5-cdb2", + "dependencies": { + "underscore": { + "version": "1.4.4", + "from": "underscore@~1.4.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz" + }, + "mapnik-reference": { + "version": "5.0.9", + "from": "mapnik-reference@~5.0.7", + "resolved": "https://registry.npmjs.org/mapnik-reference/-/mapnik-reference-5.0.9.tgz" + }, + "xml2js": { + "version": "0.2.8", + "from": "xml2js@~0.2.4", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz", + "dependencies": { + "sax": { + "version": "0.5.8", + "from": "sax@0.5.x", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz" + } + } + }, + "optimist": { + "version": "0.6.1", + "from": "optimist@~0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "from": "wordwrap@~0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + }, + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + } + } + }, + "millstone": { + "version": "0.6.14", + "from": "millstone@0.6.14", + "resolved": "https://registry.npmjs.org/millstone/-/millstone-0.6.14.tgz", + "dependencies": { + "generic-pool": { + "version": "2.0.4", + "from": "generic-pool@~2.0.3", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.0.4.tgz" + }, + "request": { + "version": "2.34.0", + "from": "request@~2.34.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.34.0.tgz", + "dependencies": { + "qs": { + "version": "0.6.6", + "from": "qs@~0.6.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" + }, + "json-stringify-safe": { + "version": "5.0.0", + "from": "json-stringify-safe@~5.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@~0.5.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "node-uuid": { + "version": "1.4.3", + "from": "node-uuid@~1.4.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.3.tgz" + }, + "tough-cookie": { + "version": "0.12.1", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz", + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@>=0.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + } + } + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@~0.1.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dependencies": { + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@~0.0.4", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + }, + "async": { + "version": "0.9.0", + "from": "async@~0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.3.0", + "from": "tunnel-agent@~0.3.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz" + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@~0.10.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@^0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" + } + } + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@~0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" + }, + "hawk": { + "version": "1.0.0", + "from": "hawk@~1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.x", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.x", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.x", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.x", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@~0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + } + } + }, + "srs": { + "version": "0.4.6", + "from": "srs@~0.4.1", + "resolved": "https://registry.npmjs.org/srs/-/srs-0.4.6.tgz", + "dependencies": { + "nan": { + "version": "1.4.3", + "from": "nan@~1.4.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.4.3.tgz" + }, + "node-pre-gyp": { + "version": "0.6.1", + "from": "node-pre-gyp@~0.6.1", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.1.tgz", + "dependencies": { + "nopt": { + "version": "3.0.1", + "from": "nopt@~3.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "from": "abbrev@1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "npmlog": { + "version": "0.1.1", + "from": "npmlog@~0.1.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-0.1.1.tgz", + "dependencies": { + "ansi": { + "version": "0.3.0", + "from": "ansi@~0.3.0", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.0.tgz" + } + } + }, + "request": { + "version": "2.48.0", + "from": "request@2.x", + "resolved": "https://registry.npmjs.org/request/-/request-2.48.0.tgz", + "dependencies": { + "bl": { + "version": "0.9.3", + "from": "bl@~0.9.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.3.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "caseless": { + "version": "0.7.0", + "from": "caseless@~0.7.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.7.0.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@~0.5.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@~0.1.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dependencies": { + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "async": { + "version": "0.9.0", + "from": "async@~0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + } + } + }, + "json-stringify-safe": { + "version": "5.0.0", + "from": "json-stringify-safe@~5.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" + }, + "mime-types": { + "version": "1.0.2", + "from": "mime-types@~1.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz" + }, + "node-uuid": { + "version": "1.4.1", + "from": "node-uuid@~1.4.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.1.tgz" + }, + "qs": { + "version": "2.3.3", + "from": "qs@~2.3.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" + }, + "tunnel-agent": { + "version": "0.4.0", + "from": "tunnel-agent@~0.4.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz" + }, + "tough-cookie": { + "version": "0.12.1", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz", + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@>=0.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + } + } + }, + "http-signature": { + "version": "0.10.0", + "from": "http-signature@~0.10.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.0.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.2", + "from": "assert-plus@0.1.2", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.2", + "from": "ctype@0.5.2", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz" + } + } + }, + "oauth-sign": { + "version": "0.5.0", + "from": "oauth-sign@~0.5.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.5.0.tgz" + }, + "hawk": { + "version": "1.1.1", + "from": "hawk@1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.x", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.x", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.x", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.x", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@~0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + }, + "stringstream": { + "version": "0.0.4", + "from": "stringstream@~0.0.4", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz" + }, + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@~0.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + } + } + }, + "semver": { + "version": "4.1.0", + "from": "semver@~4.1.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.1.0.tgz" + }, + "tar": { + "version": "1.0.2", + "from": "tar@~1.0.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-1.0.2.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "fstream": { + "version": "1.0.2", + "from": "fstream@^1.0.2", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.2.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.4", + "from": "graceful-fs@3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar-pack": { + "version": "2.0.0", + "from": "tar-pack@~2.0.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-2.0.0.tgz", + "dependencies": { + "uid-number": { + "version": "0.0.3", + "from": "uid-number@0.0.3", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.3.tgz" + }, + "once": { + "version": "1.1.1", + "from": "once@~1.1.1", + "resolved": "https://registry.npmjs.org/once/-/once-1.1.1.tgz" + }, + "debug": { + "version": "0.7.4", + "from": "debug@~0.7.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "fstream": { + "version": "0.1.31", + "from": "fstream@~0.1.22", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.4", + "from": "graceful-fs@~3.0.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.4.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar": { + "version": "0.1.20", + "from": "tar@~0.1.17", + "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.20.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "fstream-ignore": { + "version": "0.0.7", + "from": "fstream-ignore@0.0.7", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-0.0.7.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@~0.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.5.0", + "from": "lru-cache@2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + }, + "sigmund": { + "version": "1.0.0", + "from": "sigmund@~1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@~1.0.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "graceful-fs": { + "version": "1.2.3", + "from": "graceful-fs@1.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + } + } + }, + "mkdirp": { + "version": "0.5.0", + "from": "mkdirp@~0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "rc": { + "version": "0.5.4", + "from": "rc@~0.5.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-0.5.4.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + }, + "deep-extend": { + "version": "0.2.11", + "from": "deep-extend@~0.2.5", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.2.11.tgz" + }, + "strip-json-comments": { + "version": "0.1.3", + "from": "strip-json-comments@0.1.x", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-0.1.3.tgz" + }, + "ini": { + "version": "1.1.0", + "from": "ini@~1.1.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.1.0.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "from": "rimraf@~2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + } + } + } + } + }, + "zipfile": { + "version": "0.5.7", + "from": "zipfile@~0.5.2", + "resolved": "https://registry.npmjs.org/zipfile/-/zipfile-0.5.7.tgz", + "dependencies": { + "nan": { + "version": "1.5.3", + "from": "nan@~1.5.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.5.3.tgz" + }, + "node-pre-gyp": { + "version": "0.6.2", + "from": "node-pre-gyp@>=0.6.2 <0.7.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.2.tgz", + "dependencies": { + "nopt": { + "version": "3.0.1", + "from": "nopt@~3.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "from": "abbrev@1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "npmlog": { + "version": "0.1.1", + "from": "npmlog@~0.1.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-0.1.1.tgz", + "dependencies": { + "ansi": { + "version": "0.3.0", + "from": "ansi@~0.3.0", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.0.tgz" + } + } + }, + "request": { + "version": "2.51.0", + "from": "request@2.x", + "resolved": "https://registry.npmjs.org/request/-/request-2.51.0.tgz", + "dependencies": { + "bl": { + "version": "0.9.3", + "from": "bl@~0.9.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.3.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "caseless": { + "version": "0.8.0", + "from": "caseless@~0.8.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.8.0.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@~0.5.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "form-data": { + "version": "0.2.0", + "from": "form-data@~0.2.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.2.0.tgz", + "dependencies": { + "async": { + "version": "0.9.0", + "from": "async@~0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + }, + "mime-types": { + "version": "2.0.7", + "from": "mime-types@~2.0.3", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.7.tgz", + "dependencies": { + "mime-db": { + "version": "1.5.0", + "from": "mime-db@~1.5.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.5.0.tgz" + } + } + } + } + }, + "json-stringify-safe": { + "version": "5.0.0", + "from": "json-stringify-safe@~5.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" + }, + "mime-types": { + "version": "1.0.2", + "from": "mime-types@~1.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz" + }, + "node-uuid": { + "version": "1.4.2", + "from": "node-uuid@~1.4.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.2.tgz" + }, + "qs": { + "version": "2.3.3", + "from": "qs@~2.3.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" + }, + "tunnel-agent": { + "version": "0.4.0", + "from": "tunnel-agent@~0.4.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz" + }, + "tough-cookie": { + "version": "0.12.1", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz", + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@>=0.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + } + } + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@~0.10.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@^0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" + } + } + }, + "oauth-sign": { + "version": "0.5.0", + "from": "oauth-sign@~0.5.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.5.0.tgz" + }, + "hawk": { + "version": "1.1.1", + "from": "hawk@1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.x", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.x", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.x", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.x", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@~0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + }, + "stringstream": { + "version": "0.0.4", + "from": "stringstream@~0.0.4", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz" + }, + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@~0.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + } + } + }, + "semver": { + "version": "4.2.0", + "from": "semver@~4.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.2.0.tgz" + }, + "tar": { + "version": "1.0.3", + "from": "tar@~1.0.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-1.0.3.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "fstream": { + "version": "1.0.3", + "from": "fstream@^1.0.2", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.3.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.5", + "from": "graceful-fs@3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.5.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar-pack": { + "version": "2.0.0", + "from": "tar-pack@~2.0.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-2.0.0.tgz", + "dependencies": { + "uid-number": { + "version": "0.0.3", + "from": "uid-number@0.0.3", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.3.tgz" + }, + "once": { + "version": "1.1.1", + "from": "once@~1.1.1", + "resolved": "https://registry.npmjs.org/once/-/once-1.1.1.tgz" + }, + "debug": { + "version": "0.7.4", + "from": "debug@~0.7.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "fstream": { + "version": "0.1.31", + "from": "fstream@~0.1.22", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.5", + "from": "graceful-fs@3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.5.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar": { + "version": "0.1.20", + "from": "tar@~0.1.17", + "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.20.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "fstream-ignore": { + "version": "0.0.7", + "from": "fstream-ignore@0.0.7", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-0.0.7.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@~0.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.5.0", + "from": "lru-cache@2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + }, + "sigmund": { + "version": "1.0.0", + "from": "sigmund@~1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@~1.0.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "graceful-fs": { + "version": "1.2.3", + "from": "graceful-fs@1.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + } + } + }, + "mkdirp": { + "version": "0.5.0", + "from": "mkdirp@~0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "rc": { + "version": "0.5.5", + "from": "rc@~0.5.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-0.5.5.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + }, + "deep-extend": { + "version": "0.2.11", + "from": "deep-extend@~0.2.5", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.2.11.tgz" + }, + "strip-json-comments": { + "version": "0.1.3", + "from": "strip-json-comments@0.1.x", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-0.1.3.tgz" + }, + "ini": { + "version": "1.3.2", + "from": "ini@~1.3.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.2.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "from": "rimraf@~2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + } + } + } + } + }, + "sqlite3": { + "version": "2.2.7", + "from": "sqlite3@2.x", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-2.2.7.tgz", + "dependencies": { + "nan": { + "version": "1.1.2", + "from": "nan@1.1.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.1.2.tgz" + }, + "node-pre-gyp": { + "version": "0.5.22", + "from": "node-pre-gyp@0.5.22", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.5.22.tgz", + "dependencies": { + "nopt": { + "version": "3.0.1", + "from": "nopt@~3.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "from": "abbrev@1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "npmlog": { + "version": "0.1.1", + "from": "npmlog@~0.1.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-0.1.1.tgz", + "dependencies": { + "ansi": { + "version": "0.3.0", + "from": "ansi@~0.3.0", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.0.tgz" + } + } + }, + "request": { + "version": "2.39.0", + "from": "request@2.x", + "resolved": "https://registry.npmjs.org/request/-/request-2.39.0.tgz", + "dependencies": { + "qs": { + "version": "0.6.6", + "from": "qs@~0.6.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz" + }, + "json-stringify-safe": { + "version": "5.0.0", + "from": "json-stringify-safe@~5.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" + }, + "mime-types": { + "version": "1.0.2", + "from": "mime-types@~1.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@~0.5.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "node-uuid": { + "version": "1.4.1", + "from": "node-uuid@~1.4.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.1.tgz" + }, + "tough-cookie": { + "version": "0.12.1", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz", + "dependencies": { + "punycode": { + "version": "1.3.0", + "from": "punycode@>=0.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.0.tgz" + } + } + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@~0.1.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dependencies": { + "combined-stream": { + "version": "0.0.5", + "from": "combined-stream@~0.0.4", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + }, + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "async": { + "version": "0.9.0", + "from": "async@~0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.4.0", + "from": "tunnel-agent@~0.4.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz" + }, + "http-signature": { + "version": "0.10.0", + "from": "http-signature@~0.10.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.0.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.2", + "from": "assert-plus@0.1.2", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.2", + "from": "ctype@0.5.2", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz" + } + } + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@~0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" + }, + "hawk": { + "version": "1.1.1", + "from": "hawk@1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.x", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.x", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.x", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.x", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@~0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + }, + "stringstream": { + "version": "0.0.4", + "from": "stringstream@~0.0.4", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz" + } + } + }, + "semver": { + "version": "3.0.1", + "from": "semver@~3.0.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-3.0.1.tgz" + }, + "tar": { + "version": "1.0.0", + "from": "tar@~1.0.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-1.0.0.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "fstream": { + "version": "1.0.1", + "from": "fstream@^1.0.0", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.1.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.2", + "from": "graceful-fs@3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.2.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar-pack": { + "version": "2.0.0", + "from": "tar-pack@~2.0.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-2.0.0.tgz", + "dependencies": { + "uid-number": { + "version": "0.0.3", + "from": "uid-number@0.0.3", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.3.tgz" + }, + "once": { + "version": "1.1.1", + "from": "once@~1.1.1", + "resolved": "https://registry.npmjs.org/once/-/once-1.1.1.tgz" + }, + "debug": { + "version": "0.7.4", + "from": "debug@~0.7.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "fstream": { + "version": "0.1.31", + "from": "fstream@~0.1.22", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.2", + "from": "graceful-fs@3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar": { + "version": "0.1.20", + "from": "tar@~0.1.17", + "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.20.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "fstream-ignore": { + "version": "0.0.7", + "from": "fstream-ignore@0.0.7", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-0.0.7.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@~0.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.5.0", + "from": "lru-cache@2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + }, + "sigmund": { + "version": "1.0.0", + "from": "sigmund@~1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "readable-stream": { + "version": "1.0.27-1", + "from": "readable-stream@~1.0.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.27-1.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.25-1", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.25-1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "graceful-fs": { + "version": "1.2.3", + "from": "graceful-fs@1.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + } + } + }, + "mkdirp": { + "version": "0.5.0", + "from": "mkdirp@~0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "rc": { + "version": "0.5.0", + "from": "rc@~0.5.0", + "resolved": "https://registry.npmjs.org/rc/-/rc-0.5.0.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + }, + "deep-extend": { + "version": "0.2.11", + "from": "deep-extend@~0.2.5", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.2.11.tgz" + }, + "strip-json-comments": { + "version": "0.1.3", + "from": "strip-json-comments@0.1.x", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-0.1.3.tgz" + }, + "ini": { + "version": "1.1.0", + "from": "ini@~1.1.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.1.0.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "from": "rimraf@~2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + } + } + }, + "set-immediate": { + "version": "0.1.1", + "from": "set-immediate@0.1.1", + "resolved": "https://registry.npmjs.org/set-immediate/-/set-immediate-0.1.1.tgz" + } + } + }, + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.9", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "mkdirp": { + "version": "0.3.5", + "from": "mkdirp@~0.3.3", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz" + }, + "optimist": { + "version": "0.6.1", + "from": "optimist@~0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "from": "wordwrap@~0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + }, + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + } + } + } + } + }, + "express": { + "version": "2.5.11", + "from": "express@~2.5.11", + "resolved": "https://registry.npmjs.org/express/-/express-2.5.11.tgz", + "dependencies": { + "connect": { + "version": "1.9.2", + "from": "connect@1.x", + "resolved": "https://registry.npmjs.org/connect/-/connect-1.9.2.tgz", + "dependencies": { + "formidable": { + "version": "1.0.17", + "from": "formidable@1.0.x", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.17.tgz" + } + } + }, + "mime": { + "version": "1.2.4", + "from": "mime@1.2.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.4.tgz" + }, + "qs": { + "version": "0.4.2", + "from": "qs@0.4.x", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.4.2.tgz" + }, + "mkdirp": { + "version": "0.3.0", + "from": "mkdirp@0.3.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" + } + } + }, + "tilelive": { + "version": "4.5.3", + "from": "tilelive@~4.5.3", + "resolved": "https://registry.npmjs.org/tilelive/-/tilelive-4.5.3.tgz", + "dependencies": { + "optimist": { + "version": "0.6.1", + "from": "optimist@~0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "from": "wordwrap@~0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + }, + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + } + } + }, + "tilelive-mapnik": { + "version": "0.6.15", + "from": "https://github.com/CartoDB/tilelive-mapnik/tarball/cdb", + "resolved": "https://github.com/CartoDB/tilelive-mapnik/tarball/cdb", + "dependencies": { + "generic-pool": { + "version": "2.1.1", + "from": "generic-pool@~2.1.1", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-2.1.1.tgz" + }, + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.9", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + } + } + }, + "mapnik": { + "version": "1.4.15-cdb1", + "from": "https://github.com/CartoDB/node-mapnik/tarball/1.4.15-cdb1", + "resolved": "https://github.com/CartoDB/node-mapnik/tarball/1.4.15-cdb1", + "dependencies": { + "nan": { + "version": "1.2.0", + "from": "nan@~1.2.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.2.0.tgz" + }, + "mapnik-vector-tile": { + "version": "0.5.5", + "from": "mapnik-vector-tile@0.5.5", + "resolved": "https://registry.npmjs.org/mapnik-vector-tile/-/mapnik-vector-tile-0.5.5.tgz" + }, + "node-pre-gyp": { + "version": "0.5.25", + "from": "node-pre-gyp@0.5.25", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.5.25.tgz", + "dependencies": { + "nopt": { + "version": "3.0.1", + "from": "nopt@~3.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.1.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.5", + "from": "abbrev@1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.5.tgz" + } + } + }, + "npmlog": { + "version": "0.1.1", + "from": "npmlog@~0.1.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-0.1.1.tgz", + "dependencies": { + "ansi": { + "version": "0.3.0", + "from": "ansi@~0.3.0", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.0.tgz" + } + } + }, + "request": { + "version": "2.40.0", + "from": "request@2.x", + "resolved": "https://registry.npmjs.org/request/-/request-2.40.0.tgz", + "dependencies": { + "qs": { + "version": "1.0.2", + "from": "qs@~1.0.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-1.0.2.tgz" + }, + "json-stringify-safe": { + "version": "5.0.0", + "from": "json-stringify-safe@~5.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" + }, + "mime-types": { + "version": "1.0.2", + "from": "mime-types@~1.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@~0.5.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "node-uuid": { + "version": "1.4.1", + "from": "node-uuid@~1.4.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.1.tgz" + }, + "tough-cookie": { + "version": "0.12.1", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz", + "dependencies": { + "punycode": { + "version": "1.3.1", + "from": "punycode@>=0.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.1.tgz" + } + } + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@~0.1.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dependencies": { + "combined-stream": { + "version": "0.0.5", + "from": "combined-stream@~0.0.4", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.5.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + }, + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "async": { + "version": "0.9.0", + "from": "async@~0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + } + } + }, + "tunnel-agent": { + "version": "0.4.0", + "from": "tunnel-agent@~0.4.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz" + }, + "http-signature": { + "version": "0.10.0", + "from": "http-signature@~0.10.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.0.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.2", + "from": "assert-plus@0.1.2", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.2.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.2", + "from": "ctype@0.5.2", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.2.tgz" + } + } + }, + "oauth-sign": { + "version": "0.3.0", + "from": "oauth-sign@~0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz" + }, + "hawk": { + "version": "1.1.1", + "from": "hawk@1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.x", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.x", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.x", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.x", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@~0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + }, + "stringstream": { + "version": "0.0.4", + "from": "stringstream@~0.0.4", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz" + } + } + }, + "semver": { + "version": "3.0.1", + "from": "semver@~3.0.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-3.0.1.tgz" + }, + "tar": { + "version": "1.0.1", + "from": "tar@~1.0.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-1.0.1.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "fstream": { + "version": "1.0.2", + "from": "fstream@^1.0.2", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.2.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.2", + "from": "graceful-fs@3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.2.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar-pack": { + "version": "2.0.0", + "from": "tar-pack@~2.0.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-2.0.0.tgz", + "dependencies": { + "uid-number": { + "version": "0.0.3", + "from": "uid-number@0.0.3", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.3.tgz" + }, + "once": { + "version": "1.1.1", + "from": "once@~1.1.1", + "resolved": "https://registry.npmjs.org/once/-/once-1.1.1.tgz" + }, + "debug": { + "version": "0.7.4", + "from": "debug@~0.7.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz" + }, + "fstream": { + "version": "0.1.31", + "from": "fstream@~0.1.22", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-0.1.31.tgz", + "dependencies": { + "graceful-fs": { + "version": "3.0.2", + "from": "graceful-fs@~3.0.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "tar": { + "version": "0.1.20", + "from": "tar@~0.1.17", + "resolved": "https://registry.npmjs.org/tar/-/tar-0.1.20.tgz", + "dependencies": { + "block-stream": { + "version": "0.0.7", + "from": "block-stream@*", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.7.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "fstream-ignore": { + "version": "0.0.7", + "from": "fstream-ignore@0.0.7", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-0.0.7.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@~0.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.5.0", + "from": "lru-cache@2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.0.tgz" + }, + "sigmund": { + "version": "1.0.0", + "from": "sigmund@~1.0.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.0.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@2", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "readable-stream": { + "version": "1.0.31", + "from": "readable-stream@~1.0.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "graceful-fs": { + "version": "1.2.3", + "from": "graceful-fs@1.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + } + } + }, + "mkdirp": { + "version": "0.5.0", + "from": "mkdirp@~0.5.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "rc": { + "version": "0.5.1", + "from": "rc@~0.5.0", + "resolved": "https://registry.npmjs.org/rc/-/rc-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + }, + "deep-extend": { + "version": "0.2.11", + "from": "deep-extend@~0.2.5", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.2.11.tgz" + }, + "strip-json-comments": { + "version": "0.1.3", + "from": "strip-json-comments@0.1.x", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-0.1.3.tgz" + }, + "ini": { + "version": "1.1.0", + "from": "ini@~1.1.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.1.0.tgz" + } + } + }, + "rimraf": { + "version": "2.2.8", + "from": "rimraf@~2.2.8", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz" + } + } + } + } + }, + "canvas": { + "version": "1.2.1", + "from": "canvas@1.2.1", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-1.2.1.tgz", + "dependencies": { + "nan": { + "version": "1.5.3", + "from": "nan@~1.5.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.5.3.tgz" + } + } + }, + "carto": { + "version": "0.15.1-cdb1", + "from": "https://github.com/CartoDB/carto/tarball/0.15.1-cdb1", + "resolved": "https://github.com/CartoDB/carto/tarball/0.15.1-cdb1", + "dependencies": { + "mapnik-reference": { + "version": "6.0.5", + "from": "mapnik-reference@~6.0.2", + "resolved": "https://registry.npmjs.org/mapnik-reference/-/mapnik-reference-6.0.5.tgz" + }, + "optimist": { + "version": "0.6.1", + "from": "optimist@~0.6.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.2", + "from": "wordwrap@~0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz" + }, + "minimist": { + "version": "0.0.10", + "from": "minimist@~0.0.1", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + } + } + }, + "step-profiler": { + "version": "0.2.1", + "from": "step-profiler@~0.2.1", + "resolved": "https://registry.npmjs.org/step-profiler/-/step-profiler-0.2.1.tgz" + }, + "torque.js": { + "version": "2.11.0", + "from": "torque.js@~2.11.0", + "resolved": "https://registry.npmjs.org/torque.js/-/torque.js-2.11.0.tgz" + }, + "request": { + "version": "2.48.0", + "from": "request@2.48.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.48.0.tgz", + "dependencies": { + "bl": { + "version": "0.9.4", + "from": "bl@~0.9.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.4.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@~1.0.26", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@~1.0.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@~0.10.x", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@~2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "caseless": { + "version": "0.7.0", + "from": "caseless@~0.7.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.7.0.tgz" + }, + "forever-agent": { + "version": "0.5.2", + "from": "forever-agent@~0.5.0", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz" + }, + "form-data": { + "version": "0.1.4", + "from": "form-data@~0.1.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "dependencies": { + "mime": { + "version": "1.2.11", + "from": "mime@~1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "async": { + "version": "0.9.0", + "from": "async@~0.9.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.0.tgz" + } + } + }, + "json-stringify-safe": { + "version": "5.0.0", + "from": "json-stringify-safe@~5.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz" + }, + "mime-types": { + "version": "1.0.2", + "from": "mime-types@~1.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz" + }, + "node-uuid": { + "version": "1.4.3", + "from": "node-uuid@~1.4.0", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.3.tgz" + }, + "qs": { + "version": "2.3.3", + "from": "qs@~2.3.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz" + }, + "tunnel-agent": { + "version": "0.4.0", + "from": "tunnel-agent@~0.4.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.0.tgz" + }, + "tough-cookie": { + "version": "0.12.1", + "from": "tough-cookie@>=0.12.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-0.12.1.tgz", + "dependencies": { + "punycode": { + "version": "1.3.2", + "from": "punycode@>=0.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + } + } + }, + "http-signature": { + "version": "0.10.1", + "from": "http-signature@~0.10.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "dependencies": { + "assert-plus": { + "version": "0.1.5", + "from": "assert-plus@^0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz" + }, + "asn1": { + "version": "0.1.11", + "from": "asn1@0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz" + }, + "ctype": { + "version": "0.5.3", + "from": "ctype@0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz" + } + } + }, + "oauth-sign": { + "version": "0.5.0", + "from": "oauth-sign@~0.5.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.5.0.tgz" + }, + "hawk": { + "version": "1.1.1", + "from": "hawk@1.1.1", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", + "dependencies": { + "hoek": { + "version": "0.9.1", + "from": "hoek@0.9.x", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz" + }, + "boom": { + "version": "0.4.2", + "from": "boom@0.4.x", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz" + }, + "cryptiles": { + "version": "0.2.2", + "from": "cryptiles@0.2.x", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz" + }, + "sntp": { + "version": "0.2.4", + "from": "sntp@0.2.x", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz" + } + } + }, + "aws-sign2": { + "version": "0.5.0", + "from": "aws-sign2@~0.5.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz" + }, + "stringstream": { + "version": "0.0.4", + "from": "stringstream@~0.0.4", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.4.tgz" + }, + "combined-stream": { + "version": "0.0.7", + "from": "combined-stream@~0.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "dependencies": { + "delayed-stream": { + "version": "0.0.5", + "from": "delayed-stream@0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz" + } + } + } + } + }, + "abaculus": { + "version": "1.1.0", + "from": "https://github.com/CartoDB/abaculus/tarball/cdb", + "resolved": "https://github.com/CartoDB/abaculus/tarball/cdb" + }, + "sphericalmercator": { + "version": "1.0.2", + "from": "sphericalmercator@1.0.2", + "resolved": "https://registry.npmjs.org/sphericalmercator/-/sphericalmercator-1.0.2.tgz" + }, + "node-statsd": { + "version": "0.0.7", + "from": "node-statsd@~0.0.7", + "resolved": "https://registry.npmjs.org/node-statsd/-/node-statsd-0.0.7.tgz" + } + } + } + } +} diff --git a/package.json b/package.json index da3344c3..fc6e5451 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,11 @@ "dependencies": { "underscore" : "~1.6.0", "dot": "~1.0.2", - "windshaft": "https://github.com/CartoDB/Windshaft/tarball/master", + "windshaft": "0.41.0", "step": "~0.0.5", "queue-async": "~1.0.7", "request": "~2.9.203", - "cartodb-redis": "~0.11.0", + "cartodb-redis": "~0.12.1", "cartodb-psql": "~0.4.0", "redis-mpool": "~0.3.0", "lzma": "~1.3.7", diff --git a/test/acceptance/limits.js b/test/acceptance/limits.js new file mode 100644 index 00000000..fbe37186 --- /dev/null +++ b/test/acceptance/limits.js @@ -0,0 +1,307 @@ +require('../support/test_helper'); + +var assert = require('../support/assert'); +var _ = require('underscore'); +var redis = require('redis'); + +var CartodbWindshaft = require('../../lib/cartodb/cartodb_windshaft'); +var serverOptions = require('../../lib/cartodb/server_options'); + +describe('render limits', function() { + + var layergroupUrl = '/api/v1/map'; + + var redisClient = redis.createClient(global.environment.redis.port); + after(function(done) { + redisClient.keys("map_style|*", function(err, matches) { + redisClient.del(matches, function() { + done(); + }); + }); + }); + + var server; + beforeEach(function() { + server = new CartodbWindshaft(serverOptions()); + server.setMaxListeners(0); + }); + + var keysToDelete = []; + afterEach(function(done) { + redisClient.DEL(keysToDelete, function() { + keysToDelete = []; + done(); + }); + }); + + var user = 'localhost'; + + var pointSleepSql = "SELECT pg_sleep(0.5)," + + " 'SRID=3857;POINT(0 0)'::geometry the_geom_webmercator, 1 cartodb_id"; + var pointCartoCss = '#layer { marker-fill:red; }'; + var polygonSleepSql = "SELECT pg_sleep(0.5)," + + " ST_Buffer('SRID=3857;POINT(0 0)'::geometry, 100000000) the_geom_webmercator, 1 cartodb_id"; + var polygonCartoCss = '#layer { polygon-fill:red; }'; + + function singleLayergroupConfig(sql, cartocss) { + return { + version: '1.0.0', + layers: [ + { + type: 'mapnik', + options: { + sql: sql, + cartocss: cartocss, + cartocss_version: '2.0.1' + } + } + ] + }; + } + + function createRequest(layergroup, userHost) { + return { + url: layergroupUrl, + method: 'POST', + headers: { + host: userHost, + 'Content-Type': 'application/json' + }, + data: JSON.stringify(layergroup) + }; + } + + function withRenderLimit(user, renderLimit, callback) { + redisClient.SELECT(5, function(err) { + if (err) { + return callback(err); + } + var userLimitsKey = 'limits:tiler:' + user; + redisClient.HSET(userLimitsKey, 'render', renderLimit, function(err) { + if (err) { + return callback(err); + } + keysToDelete.push(userLimitsKey); + return callback(); + }); + }); + + } + + describe('with onTileErrorStrategy DISABLED', function() { + var onTileErrorStrategyEnabled; + before(function() { + onTileErrorStrategyEnabled = global.environment.enabledFeatures.onTileErrorStrategy; + global.environment.enabledFeatures.onTileErrorStrategy = false; + }); + + after(function() { + global.environment.enabledFeatures.onTileErrorStrategy = onTileErrorStrategyEnabled; + }); + + it("layergroup creation fails if test tile is slow", function(done) { + withRenderLimit(user, 50, function(err) { + if (err) { + return done(err); + } + + var layergroup = singleLayergroupConfig(polygonSleepSql, polygonCartoCss); + assert.response(server, + createRequest(layergroup, user), + { + status: 400 + }, + function(res) { + var parsed = JSON.parse(res.body); + assert.deepEqual(parsed, { errors: [ 'Render timed out' ] }); + done(); + } + ); + }); + }); + + it("layergroup creation does not fail if user limit is high enough even if test tile is slow", function(done) { + withRenderLimit(user, 5000, function(err) { + if (err) { + return done(err); + } + + var layergroup = singleLayergroupConfig(polygonSleepSql, polygonCartoCss); + assert.response(server, + createRequest(layergroup, user), + { + status: 200 + }, + function(res) { + var parsed = JSON.parse(res.body); + assert.ok(parsed.layergroupid); + done(); + } + ); + }); + }); + + + it("layergroup creation works if test tile is fast but tile request fails if they are slow", function(done) { + withRenderLimit(user, 50, function(err) { + if (err) { + return done(err); + } + + var layergroup = singleLayergroupConfig(pointSleepSql, pointCartoCss); + assert.response(server, + createRequest(layergroup, user), + { + status: 200 + }, + function(res) { + assert.response(server, + { + url: layergroupUrl + _.template('/<%= layergroupId %>/<%= z %>/<%= x %>/<%= y %>.png', { + layergroupId: JSON.parse(res.body).layergroupid, + z: 0, + x: 0, + y: 0 + }), + method: 'GET', + headers: { + host: 'localhost' + }, + encoding: 'binary' + }, + { + status: 400 + }, + function(res) { + var parsed = JSON.parse(res.body); + assert.deepEqual(parsed, { error: 'Render timed out' }); + done(); + } + ); + + } + ); + }); + }); + + it("tile request does not fail if user limit is high enough", function(done) { + withRenderLimit(user, 5000, function(err) { + if (err) { + return done(err); + } + + var layergroup = singleLayergroupConfig(pointSleepSql, pointCartoCss); + assert.response(server, + createRequest(layergroup, user), + { + status: 200 + }, + function(res) { + assert.response(server, + { + url: layergroupUrl + _.template('/<%= layergroupId %>/<%= z %>/<%= x %>/<%= y %>.png', { + layergroupId: JSON.parse(res.body).layergroupid, + z: 0, + x: 0, + y: 0 + }), + method: 'GET', + headers: { + host: 'localhost' + }, + encoding: 'binary' + }, + { + status: 200, + headers: { + 'Content-Type': 'image/png' + } + }, + function(res, err) { + done(err); + } + ); + + } + ); + }); + }); + + }); + + describe('with onTileErrorStrategy', function() { + + it("layergroup creation works even if test tile is slow", function(done) { + withRenderLimit(user, 50, function(err) { + if (err) { + return done(err); + } + + var layergroup = singleLayergroupConfig(polygonSleepSql, polygonCartoCss); + assert.response(server, + createRequest(layergroup, user), + { + status: 200 + }, + function(res) { + var parsed = JSON.parse(res.body); + assert.ok(parsed.layergroupid); + done(); + } + ); + }); + }); + + it("layergroup creation and tile requests works even if they are slow but returns fallback", function(done) { + withRenderLimit(user, 50, function(err) { + if (err) { + return done(err); + } + + var layergroup = singleLayergroupConfig(pointSleepSql, pointCartoCss); + assert.response(server, + createRequest(layergroup, user), + { + status: 200 + }, + function(res) { + assert.response(server, + { + url: layergroupUrl + _.template('/<%= layergroupId %>/<%= z %>/<%= x %>/<%= y %>.png', { + layergroupId: JSON.parse(res.body).layergroupid, + z: 0, + x: 0, + y: 0 + }), + method: 'GET', + headers: { + host: 'localhost' + }, + encoding: 'binary' + }, + { + status: 200, + headers: { + 'Content-Type': 'image/png' + } + }, + function(res, err) { + if (err) { + done(err); + } + assert.imageEqualsFile(res.body, './test/fixtures/render-timeout-fallback.png', 25, + function(imgErr/*, similarity*/) { + done(imgErr); + } + ); + } + ); + + } + ); + }); + }); + + }); + +}); diff --git a/test/fixtures/render-timeout-fallback.png b/test/fixtures/render-timeout-fallback.png new file mode 100644 index 00000000..f80cdbf8 Binary files /dev/null and b/test/fixtures/render-timeout-fallback.png differ