Skip to content

Instantly share code, notes, and snippets.

@datonis
Created January 1, 2014 11:34
Show Gist options
  • Save datonis/8207164 to your computer and use it in GitHub Desktop.
Save datonis/8207164 to your computer and use it in GitHub Desktop.
Basic Gist Account
This gist exceeds the recommended number of files (~10). To access all files, please clone this gist.
<component name="ProjectDictionaryState">
<dictionary name="root">
<words>
<w>havan</w>
</words>
</dictionary>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Node.js v0.11.10-pre Core Modules" level="application" />
</component>
</module>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{Node.js v0.11.10-pre Core Modules}" />
<includedPredefinedLibrary name="Node.js Globals" />
</component>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" />
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/havanapp.iml" filepath="$PROJECT_DIR$/.idea/havanapp.iml" />
</modules>
</component>
</project>
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="app.js" type="NodeJSConfigurationType" factoryName="Node.js" path-to-node="/usr/local/bin/node" path-to-js-file="app.js" working-dir="$PROJECT_DIR$">
<browser start="true" url="http://localhost:3000" with-js-debugger="false" />
<RunnerSettings RunnerId="NodeJS.run" />
<ConfigurationWrapper RunnerId="NodeJS.run" />
<method />
</configuration>
</component>
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="" />
</component>
</project>
/**
* Module dependencies.
*/
var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(express.cookieParser('your secret here'));
app.use(express.session());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', routes.index);
app.get('/dashboard', routes.dashboard);
app.get('/ceremony', routes.ceremony);
app.get('/mantra', routes.mantra);
app.get('/havan', routes.havan);
app.get('/users', user.list);
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, version = pkg.version
, os = require('os')
, fs = require('fs');
// CLI
program
.version(version)
.usage('[options] [dir]')
.option('-s, --sessions', 'add session support')
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
.option('-H, --hogan', 'add hogan.js engine support')
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
.option('-f, --force', 'force on non-empty directory')
.parse(process.argv);
// Path
var path = program.args.shift() || '.';
// end-of-line code
var eol = os.EOL
// Template engine
program.template = 'jade';
if (program.ejs) program.template = 'ejs';
if (program.jshtml) program.template = 'jshtml';
if (program.hogan) program.template = 'hjs';
/**
* Routes index template.
*/
var index = [
''
, '/*'
, ' * GET home page.'
, ' */'
, ''
, 'exports.index = function(req, res){'
, ' res.render(\'index\', { title: \'Express\' });'
, '};'
].join(eol);
/**
* Routes users template.
*/
var users = [
''
, '/*'
, ' * GET users listing.'
, ' */'
, ''
, 'exports.list = function(req, res){'
, ' res.send("respond with a resource");'
, '};'
].join(eol);
/**
* Jade layout template.
*/
var jadeLayout = [
'doctype 5'
, 'html'
, ' head'
, ' title= title'
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
, ' body'
, ' block content'
].join(eol);
/**
* Jade index template.
*/
var jadeIndex = [
'extends layout'
, ''
, 'block content'
, ' h1= title'
, ' p Welcome to #{title}'
].join(eol);
/**
* EJS index template.
*/
var ejsIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title><%= title %></title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1><%= title %></h1>'
, ' <p>Welcome to <%= title %></p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML layout template.
*/
var jshtmlLayout = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title> @write(title) </title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' @write(body)'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML index template.
*/
var jshtmlIndex = [
'<h1>@write(title)</h1>'
, '<p>Welcome to @write(title)</p>'
].join(eol);
/**
* Hogan.js index template.
*/
var hoganIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title>{{ title }}</title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1>{{ title }}</h1>'
, ' <p>Welcome to {{ title }}</p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* Default css template.
*/
var css = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default less template.
*/
var less = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default stylus template.
*/
var stylus = [
'body'
, ' padding: 50px'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' color: #00B7FF'
].join(eol);
/**
* App template.
*/
var app = [
''
, '/**'
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\');'
, 'var routes = require(\'./routes\');'
, 'var user = require(\'./routes/user\');'
, 'var http = require(\'http\');'
, 'var path = require(\'path\');'
, ''
, 'var app = express();'
, ''
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', path.join(__dirname, \'views\'));'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
, 'app.use(express.json());'
, 'app.use(express.urlencoded());'
, 'app.use(express.methodOverride());{sess}'
, 'app.use(app.router);{css}'
, 'app.use(express.static(path.join(__dirname, \'public\')));'
, ''
, '// development only'
, 'if (\'development\' == app.get(\'env\')) {'
, ' app.use(express.errorHandler());'
, '}'
, ''
, 'app.get(\'/\', routes.index);'
, 'app.get(\'/users\', user.list);'
, ''
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
, '});'
, ''
].join(eol);
// Generate application
(function createApplication(path) {
emptyDirectory(path, function(empty){
if (empty || program.force) {
createApplicationAt(path);
} else {
program.confirm('destination is not empty, continue? ', function(ok){
if (ok) {
process.stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
}
});
}
});
})(path);
/**
* Create application at the given directory `path`.
*
* @param {String} path
*/
function createApplicationAt(path) {
console.log();
process.on('exit', function(){
console.log();
console.log(' install dependencies:');
console.log(' $ cd %s && npm install', path);
console.log();
console.log(' run the app:');
console.log(' $ node app');
console.log();
});
mkdir(path, function(){
mkdir(path + '/public');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
switch (program.css) {
case 'less':
write(path + '/public/stylesheets/style.less', less);
break;
case 'stylus':
write(path + '/public/stylesheets/style.styl', stylus);
break;
default:
write(path + '/public/stylesheets/style.css', css);
}
});
mkdir(path + '/routes', function(){
write(path + '/routes/index.js', index);
write(path + '/routes/user.js', users);
});
mkdir(path + '/views', function(){
switch (program.template) {
case 'ejs':
write(path + '/views/index.ejs', ejsIndex);
break;
case 'jade':
write(path + '/views/layout.jade', jadeLayout);
write(path + '/views/index.jade', jadeIndex);
break;
case 'jshtml':
write(path + '/views/layout.jshtml', jshtmlLayout);
write(path + '/views/index.jshtml', jshtmlIndex);
break;
case 'hjs':
write(path + '/views/index.hjs', hoganIndex);
break;
}
});
// CSS Engine support
switch (program.css) {
case 'less':
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));');
break;
case 'stylus':
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));');
break;
default:
app = app.replace('{css}', '');
}
// Session support
app = app.replace('{sess}', program.sessions
? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
: '');
// Template support
app = app.replace(':TEMPLATE', program.template);
// package.json
var pkg = {
name: 'application-name'
, version: '0.0.1'
, private: true
, scripts: { start: 'node app.js' }
, dependencies: {
express: version
}
}
if (program.template) pkg.dependencies[program.template] = '*';
// CSS Engine support
switch (program.css) {
case 'less':
pkg.dependencies['less-middleware'] = '*';
break;
default:
if (program.css) {
pkg.dependencies[program.css] = '*';
}
}
write(path + '/package.json', JSON.stringify(pkg, null, 2));
write(path + '/app.js', app);
});
}
/**
* Check if the given directory `path` is empty.
*
* @param {String} path
* @param {Function} fn
*/
function emptyDirectory(path, fn) {
fs.readdir(path, function(err, files){
if (err && 'ENOENT' != err.code) throw err;
fn(!files || !files.length);
});
}
/**
* echo str > path.
*
* @param {String} path
* @param {String} str
*/
function write(path, str) {
fs.writeFile(path, str);
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
}
/**
* Mkdir -p.
*
* @param {String} path
* @param {Function} fn
*/
function mkdir(path, fn) {
mkdirp(path, 0755, function(err){
if (err) throw err;
console.log(' \033[36mcreate\033[0m : ' + path);
fn && fn();
});
}
/**
* Exit with the given `str`.
*
* @param {String} str
*/
function abort(str) {
console.error(str);
process.exit(1);
}
#!/usr/bin/env node
/**
* Module dependencies.
*/
var fs = require('fs')
, program = require('commander')
, path = require('path')
, basename = path.basename
, dirname = path.dirname
, resolve = path.resolve
, exists = fs.existsSync || path.existsSync
, join = path.join
, monocle = require('monocle')()
, mkdirp = require('mkdirp')
, jade = require('../');
// jade options
var options = {};
// options
program
.version(require('../package.json').version)
.usage('[options] [dir|file ...]')
.option('-O, --obj <str>', 'javascript options object')
.option('-o, --out <dir>', 'output the compiled html to <dir>')
.option('-p, --path <path>', 'filename used to resolve includes')
.option('-P, --pretty', 'compile pretty html output')
.option('-c, --client', 'compile function for client-side runtime.js')
.option('-D, --no-debug', 'compile without debugging (smaller functions)')
.option('-w, --watch', 'watch files for changes and automatically re-render')
program.on('--help', function(){
console.log(' Examples:');
console.log('');
console.log(' # translate jade the templates dir');
console.log(' $ jade templates');
console.log('');
console.log(' # create {foo,bar}.html');
console.log(' $ jade {foo,bar}.jade');
console.log('');
console.log(' # jade over stdio');
console.log(' $ jade < my.jade > my.html');
console.log('');
console.log(' # jade over stdio');
console.log(' $ echo "h1 Jade!" | jade');
console.log('');
console.log(' # foo, bar dirs rendering to /tmp');
console.log(' $ jade foo bar --out /tmp ');
console.log('');
});
program.parse(process.argv);
// options given, parse them
if (program.obj) {
if (exists(program.obj)) {
options = JSON.parse(fs.readFileSync(program.obj));
} else {
options = eval('(' + program.obj + ')');
}
}
// --filename
if (program.path) options.filename = program.path;
// --no-debug
options.compileDebug = program.debug;
// --client
options.client = program.client;
// --pretty
options.pretty = program.pretty;
// --watch
options.watch = program.watch;
// left-over args are file paths
var files = program.args;
// compile files
if (files.length) {
console.log();
files.forEach(renderFile);
if (options.watch) {
monocle.watchFiles({
files: files,
listener: function(file) {
renderFile(file.absolutePath);
}
});
}
process.on('exit', function () {
console.log();
});
// stdio
} else {
stdin();
}
/**
* Compile from stdin.
*/
function stdin() {
var buf = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(chunk){ buf += chunk; });
process.stdin.on('end', function(){
var output;
if (options.client) {
output = jade.compileClient(buf, options);
} else {
var fn = jade.compile(buf, options);
var output = fn(options);
}
process.stdout.write(output);
}).resume();
}
/**
* Process the given path, compiling the jade files found.
* Always walk the subdirectories.
*/
function renderFile(path) {
var re = /\.jade$/;
fs.lstat(path, function(err, stat) {
if (err) throw err;
// Found jade file
if (stat.isFile() && re.test(path)) {
fs.readFile(path, 'utf8', function(err, str){
if (err) throw err;
options.filename = path;
var fn = options.client ? jade.compileClient(str, options) : jade.compile(str, options);
var extname = options.client ? '.js' : '.html';
path = path.replace(re, extname);
if (program.out) path = join(program.out, basename(path));
var dir = resolve(dirname(path));
mkdirp(dir, 0755, function(err){
if (err) throw err;
try {
var output = options.client ? fn : fn(options);
fs.writeFile(path, output, function(err){
if (err) throw err;
console.log(' \033[90mrendered \033[36m%s\033[0m', path);
});
} catch (e) {
if (options.watch) {
console.error(e.stack || e.message || e);
} else {
throw e
}
}
});
});
// Found directory
} else if (stat.isDirectory()) {
fs.readdir(path, function(err, files) {
if (err) throw err;
files.map(function(filename) {
return path + '/' + filename;
}).forEach(renderFile);
});
}
});
}
.git*
docs/
examples/
support/
test/
testing.js
.DS_Store
coverage.html
lib-cov
language: node_js
node_js:
- "0.8"
- "0.10"
#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, version = pkg.version
, os = require('os')
, fs = require('fs');
// CLI
program
.version(version)
.usage('[options] [dir]')
.option('-s, --sessions', 'add session support')
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
.option('-H, --hogan', 'add hogan.js engine support')
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
.option('-f, --force', 'force on non-empty directory')
.parse(process.argv);
// Path
var path = program.args.shift() || '.';
// end-of-line code
var eol = os.EOL
// Template engine
program.template = 'jade';
if (program.ejs) program.template = 'ejs';
if (program.jshtml) program.template = 'jshtml';
if (program.hogan) program.template = 'hjs';
/**
* Routes index template.
*/
var index = [
''
, '/*'
, ' * GET home page.'
, ' */'
, ''
, 'exports.index = function(req, res){'
, ' res.render(\'index\', { title: \'Express\' });'
, '};'
].join(eol);
/**
* Routes users template.
*/
var users = [
''
, '/*'
, ' * GET users listing.'
, ' */'
, ''
, 'exports.list = function(req, res){'
, ' res.send("respond with a resource");'
, '};'
].join(eol);
/**
* Jade layout template.
*/
var jadeLayout = [
'doctype 5'
, 'html'
, ' head'
, ' title= title'
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
, ' body'
, ' block content'
].join(eol);
/**
* Jade index template.
*/
var jadeIndex = [
'extends layout'
, ''
, 'block content'
, ' h1= title'
, ' p Welcome to #{title}'
].join(eol);
/**
* EJS index template.
*/
var ejsIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title><%= title %></title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1><%= title %></h1>'
, ' <p>Welcome to <%= title %></p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML layout template.
*/
var jshtmlLayout = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title> @write(title) </title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' @write(body)'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML index template.
*/
var jshtmlIndex = [
'<h1>@write(title)</h1>'
, '<p>Welcome to @write(title)</p>'
].join(eol);
/**
* Hogan.js index template.
*/
var hoganIndex = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title>{{ title }}</title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1>{{ title }}</h1>'
, ' <p>Welcome to {{ title }}</p>'
, ' </body>'
, '</html>'
].join(eol);
/**
* Default css template.
*/
var css = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default less template.
*/
var less = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
/**
* Default stylus template.
*/
var stylus = [
'body'
, ' padding: 50px'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' color: #00B7FF'
].join(eol);
/**
* App template.
*/
var app = [
''
, '/**'
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\');'
, 'var routes = require(\'./routes\');'
, 'var user = require(\'./routes/user\');'
, 'var http = require(\'http\');'
, 'var path = require(\'path\');'
, ''
, 'var app = express();'
, ''
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', path.join(__dirname, \'views\'));'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
, 'app.use(express.json());'
, 'app.use(express.urlencoded());'
, 'app.use(express.methodOverride());{sess}'
, 'app.use(app.router);{css}'
, 'app.use(express.static(path.join(__dirname, \'public\')));'
, ''
, '// development only'
, 'if (\'development\' == app.get(\'env\')) {'
, ' app.use(express.errorHandler());'
, '}'
, ''
, 'app.get(\'/\', routes.index);'
, 'app.get(\'/users\', user.list);'
, ''
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
, '});'
, ''
].join(eol);
// Generate application
(function createApplication(path) {
emptyDirectory(path, function(empty){
if (empty || program.force) {
createApplicationAt(path);
} else {
program.confirm('destination is not empty, continue? ', function(ok){
if (ok) {
process.stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
}
});
}
});
})(path);
/**
* Create application at the given directory `path`.
*
* @param {String} path
*/
function createApplicationAt(path) {
console.log();
process.on('exit', function(){
console.log();
console.log(' install dependencies:');
console.log(' $ cd %s && npm install', path);
console.log();
console.log(' run the app:');
console.log(' $ node app');
console.log();
});
mkdir(path, function(){
mkdir(path + '/public');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
switch (program.css) {
case 'less':
write(path + '/public/stylesheets/style.less', less);
break;
case 'stylus':
write(path + '/public/stylesheets/style.styl', stylus);
break;
default:
write(path + '/public/stylesheets/style.css', css);
}
});
mkdir(path + '/routes', function(){
write(path + '/routes/index.js', index);
write(path + '/routes/user.js', users);
});
mkdir(path + '/views', function(){
switch (program.template) {
case 'ejs':
write(path + '/views/index.ejs', ejsIndex);
break;
case 'jade':
write(path + '/views/layout.jade', jadeLayout);
write(path + '/views/index.jade', jadeIndex);
break;
case 'jshtml':
write(path + '/views/layout.jshtml', jshtmlLayout);
write(path + '/views/index.jshtml', jshtmlIndex);
break;
case 'hjs':
write(path + '/views/index.hjs', hoganIndex);
break;
}
});
// CSS Engine support
switch (program.css) {
case 'less':
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));');
break;
case 'stylus':
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));');
break;
default:
app = app.replace('{css}', '');
}
// Session support
app = app.replace('{sess}', program.sessions
? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
: '');
// Template support
app = app.replace(':TEMPLATE', program.template);
// package.json
var pkg = {
name: 'application-name'
, version: '0.0.1'
, private: true
, scripts: { start: 'node app.js' }
, dependencies: {
express: version
}
}
if (program.template) pkg.dependencies[program.template] = '*';
// CSS Engine support
switch (program.css) {
case 'less':
pkg.dependencies['less-middleware'] = '*';
break;
default:
if (program.css) {
pkg.dependencies[program.css] = '*';
}
}
write(path + '/package.json', JSON.stringify(pkg, null, 2));
write(path + '/app.js', app);
});
}
/**
* Check if the given directory `path` is empty.
*
* @param {String} path
* @param {Function} fn
*/
function emptyDirectory(path, fn) {
fs.readdir(path, function(err, files){
if (err && 'ENOENT' != err.code) throw err;
fn(!files || !files.length);
});
}
/**
* echo str > path.
*
* @param {String} path
* @param {String} str
*/
function write(path, str) {
fs.writeFile(path, str);
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
}
/**
* Mkdir -p.
*
* @param {String} path
* @param {Function} fn
*/
function mkdir(path, fn) {
mkdirp(path, 0755, function(err){
if (err) throw err;
console.log(' \033[36mcreate\033[0m : ' + path);
fn && fn();
});
}
/**
* Exit with the given `str`.
*
* @param {String} str
*/
function abort(str) {
console.error(str);
process.exit(1);
}

3.4.7 / 2013-12-10

  • update connect

3.4.6 / 2013-12-01

  • update connect (raw-body)

3.4.5 / 2013-11-27

  • update connect
  • res.location: remove leading ./ #1802 @kapouer
  • res.redirect: fix `res.redirect('toString') #1829 @michaelficarra
  • res.send: always send ETag when content-length > 0
  • router: add Router.all() method

3.4.4 / 2013-10-29

  • update connect
  • update supertest
  • update methods
  • express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04

3.4.3 / 2013-10-23

  • update connect

3.4.2 / 2013-10-18

  • update connect
  • downgrade commander

3.4.1 / 2013-10-15

  • update connect
  • update commander
  • jsonp: check if callback is a function
  • router: wrap encodeURIComponent in a try/catch #1735 (@lxe)
  • res.format: now includes chraset @1747 (@sorribas)
  • res.links: allow multiple calls @1746 (@sorribas)

3.4.0 / 2013-09-07

  • add res.vary(). Closes #1682
  • update connect

3.3.8 / 2013-09-02

  • update connect

3.3.7 / 2013-08-28

  • update connect

3.3.6 / 2013-08-27

  • Revert "remove charset from json responses. Closes #1631" (causes issues in some clients)
  • add: req.accepts take an argument list

3.3.4 / 2013-07-08

  • update send and connect

3.3.3 / 2013-07-04

  • update connect

3.3.2 / 2013-07-03

  • update connect
  • update send
  • remove .version export

3.3.1 / 2013-06-27

  • update connect

3.3.0 / 2013-06-26

  • update connect
  • add support for multiple X-Forwarded-Proto values. Closes #1646
  • change: remove charset from json responses. Closes #1631
  • change: return actual booleans from req.accept* functions
  • fix jsonp callback array throw

3.2.6 / 2013-06-02

  • update connect

3.2.5 / 2013-05-21

  • update connect
  • update node-cookie
  • add: throw a meaningful error when there is no default engine
  • change generation of ETags with res.send() to GET requests only. Closes #1619

3.2.4 / 2013-05-09

  • fix req.subdomains when no Host is present
  • fix req.host when no Host is present, return undefined

3.2.3 / 2013-05-07

  • update connect / qs

3.2.2 / 2013-05-03

  • update qs

3.2.1 / 2013-04-29

  • add app.VERB() paths array deprecation warning
  • update connect
  • update qs and remove all ~ semver crap
  • fix: accept number as value of Signed Cookie

3.2.0 / 2013-04-15

  • add "view" constructor setting to override view behaviour
  • add req.acceptsEncoding(name)
  • add req.acceptedEncodings
  • revert cookie signature change causing session race conditions
  • fix sorting of Accept values of the same quality

3.1.2 / 2013-04-12

  • add support for custom Accept parameters
  • update cookie-signature

3.1.1 / 2013-04-01

  • add X-Forwarded-Host support to req.host
  • fix relative redirects
  • update mkdirp
  • update buffer-crc32
  • remove legacy app.configure() method from app template.

3.1.0 / 2013-01-25

  • add support for leading "." in "view engine" setting
  • add array support to res.set()
  • add node 0.8.x to travis.yml
  • add "subdomain offset" setting for tweaking req.subdomains
  • add res.location(url) implementing res.redirect()-like setting of Location
  • use app.get() for x-powered-by setting for inheritance
  • fix colons in passwords for req.auth

3.0.6 / 2013-01-04

  • add http verb methods to Router
  • update connect
  • fix mangling of the res.cookie() options object
  • fix jsonp whitespace escape. Closes #1132

3.0.5 / 2012-12-19

  • add throwing when a non-function is passed to a route
  • fix: explicitly remove Transfer-Encoding header from 204 and 304 responses
  • revert "add 'etag' option"

3.0.4 / 2012-12-05

  • add 'etag' option to disable res.send() Etags
  • add escaping of urls in text/plain in res.redirect() for old browsers interpreting as html
  • change crc32 module for a more liberal license
  • update connect

3.0.3 / 2012-11-13

  • update connect
  • update cookie module
  • fix cookie max-age

3.0.2 / 2012-11-08

  • add OPTIONS to cors example. Closes #1398
  • fix route chaining regression. Closes #1397

3.0.1 / 2012-11-01

  • update connect

3.0.0 / 2012-10-23

  • add make clean
  • add "Basic" check to req.auth
  • add req.auth test coverage
  • add cb && cb(payload) to res.jsonp(). Closes #1374
  • add backwards compat for res.redirect() status. Closes #1336
  • add support for res.json() to retain previously defined Content-Types. Closes #1349
  • update connect
  • change res.redirect() to utilize a pathname-relative Location again. Closes #1382
  • remove non-primitive string support for res.send()
  • fix view-locals example. Closes #1370
  • fix route-separation example

3.0.0rc5 / 2012-09-18

  • update connect
  • add redis search example
  • add static-files example
  • add "x-powered-by" setting (app.disable('x-powered-by'))
  • add "application/octet-stream" redirect Accept test case. Closes #1317

3.0.0rc4 / 2012-08-30

  • add res.jsonp(). Closes #1307
  • add "verbose errors" option to error-pages example
  • add another route example to express(1) so people are not so confused
  • add redis online user activity tracking example
  • update connect dep
  • fix etag quoting. Closes #1310
  • fix error-pages 404 status
  • fix jsonp callback char restrictions
  • remove old OPTIONS default response

3.0.0rc3 / 2012-08-13

  • update connect dep
  • fix signed cookies to work with connect.cookieParser() ("s:" prefix was missing) [tnydwrds]
  • fix res.render() clobbering of "locals"

3.0.0rc2 / 2012-08-03

  • add CORS example
  • update connect dep
  • deprecate .createServer() & remove old stale examples
  • fix: escape res.redirect() link
  • fix vhost example

3.0.0rc1 / 2012-07-24

  • add more examples to view-locals
  • add scheme-relative redirects (res.redirect("//foo.com")) support
  • update cookie dep
  • update connect dep
  • update send dep
  • fix express(1) -h flag, use -H for hogan. Closes #1245
  • fix res.sendfile() socket error handling regression

3.0.0beta7 / 2012-07-16

  • update connect dep for send() root normalization regression

3.0.0beta6 / 2012-07-13

  • add err.view property for view errors. Closes #1226
  • add "jsonp callback name" setting
  • add support for "/foo/:bar*" non-greedy matches
  • change res.sendfile() to use send() module
  • change res.send to use "response-send" module
  • remove app.locals.use and res.locals.use, use regular middleware

3.0.0beta5 / 2012-07-03

  • add "make check" support
  • add route-map example
  • add res.json(obj, status) support back for BC
  • add "methods" dep, remove internal methods module
  • update connect dep
  • update auth example to utilize cores pbkdf2
  • updated tests to use "supertest"

3.0.0beta4 / 2012-06-25

  • Added req.auth
  • Added req.range(size)
  • Added res.links(obj)
  • Added res.send(body, status) support back for backwards compat
  • Added .default() support to res.format()
  • Added 2xx / 304 check to req.fresh
  • Revert "Added + support to the router"
  • Fixed res.send() freshness check, respect res.statusCode

3.0.0beta3 / 2012-06-15

  • Added hogan --hjs to express(1) [nullfirm]
  • Added another example to content-negotiation
  • Added fresh dep
  • Changed: res.send() always checks freshness
  • Fixed: expose connects mime module. Cloases #1165

3.0.0beta2 / 2012-06-06

  • Added + support to the router
  • Added req.host
  • Changed req.param() to check route first
  • Update connect dep

3.0.0beta1 / 2012-06-01

  • Added res.format() callback to override default 406 behaviour
  • Fixed res.redirect() 406. Closes #1154

3.0.0alpha5 / 2012-05-30

  • Added req.ip
  • Added { signed: true } option to res.cookie()
  • Removed res.signedCookie()
  • Changed: dont reverse req.ips
  • Fixed "trust proxy" setting check for req.ips

3.0.0alpha4 / 2012-05-09

  • Added: allow [] in jsonp callback. Closes #1128
  • Added PORT env var support in generated template. Closes #1118 [benatkin]
  • Updated: connect 2.2.2

3.0.0alpha3 / 2012-05-04

  • Added public app.routes. Closes #887
  • Added view-locals example
  • Added mvc example
  • Added res.locals.use(). Closes #1120
  • Added conditional-GET support to res.send()
  • Added: coerce res.set() values to strings
  • Changed: moved static() in generated apps below router
  • Changed: res.send() only set ETag when not previously set
  • Changed connect 2.2.1 dep
  • Changed: make test now runs unit / acceptance tests
  • Fixed req/res proto inheritance

3.0.0alpha2 / 2012-04-26

  • Added make benchmark back
  • Added res.send() support for String objects
  • Added client-side data exposing example
  • Added res.header() and req.header() aliases for BC
  • Added express.createServer() for BC
  • Perf: memoize parsed urls
  • Perf: connect 2.2.0 dep
  • Changed: make expressInit() middleware self-aware
  • Fixed: use app.get() for all core settings
  • Fixed redis session example
  • Fixed session example. Closes #1105
  • Fixed generated express dep. Closes #1078

3.0.0alpha1 / 2012-04-15

  • Added app.locals.use(callback)
  • Added app.locals object
  • Added app.locals(obj)
  • Added res.locals object
  • Added res.locals(obj)
  • Added res.format() for content-negotiation
  • Added app.engine()
  • Added res.cookie() JSON cookie support
  • Added "trust proxy" setting
  • Added req.subdomains
  • Added req.protocol
  • Added req.secure
  • Added req.path
  • Added req.ips
  • Added req.fresh
  • Added req.stale
  • Added comma-delmited / array support for req.accepts()
  • Added debug instrumentation
  • Added res.set(obj)
  • Added res.set(field, value)
  • Added res.get(field)
  • Added app.get(setting). Closes #842
  • Added req.acceptsLanguage()
  • Added req.acceptsCharset()
  • Added req.accepted
  • Added req.acceptedLanguages
  • Added req.acceptedCharsets
  • Added "json replacer" setting
  • Added "json spaces" setting
  • Added X-Forwarded-Proto support to res.redirect(). Closes #92
  • Added --less support to express(1)
  • Added express.response prototype
  • Added express.request prototype
  • Added express.application prototype
  • Added app.path()
  • Added app.render()
  • Added res.type() to replace res.contentType()
  • Changed: res.redirect() to add relative support
  • Changed: enable "jsonp callback" by default
  • Changed: renamed "case sensitive routes" to "case sensitive routing"
  • Rewrite of all tests with mocha
  • Removed "root" setting
  • Removed res.redirect('home') support
  • Removed req.notify()
  • Removed app.register()
  • Removed app.redirect()
  • Removed app.is()
  • Removed app.helpers()
  • Removed app.dynamicHelpers()
  • Fixed res.sendfile() with non-GET. Closes #723
  • Fixed express(1) public dir for windows. Closes #866

2.5.9/ 2012-04-02

  • Added support for PURGE request method [pbuyle]
  • Fixed express(1) generated app app.address() before listening [mmalecki]

2.5.8 / 2012-02-08

  • Update mkdirp dep. Closes #991

2.5.7 / 2012-02-06

  • Fixed app.all duplicate DELETE requests [mscdex]

2.5.6 / 2012-01-13

  • Updated hamljs dev dep. Closes #953

2.5.5 / 2012-01-08

  • Fixed: set filename on cached templates [matthewleon]

2.5.4 / 2012-01-02

  • Fixed express(1) eol on 0.4.x. Closes #947

2.5.3 / 2011-12-30

  • Fixed req.is() when a charset is present

2.5.2 / 2011-12-10

  • Fixed: express(1) LF -> CRLF for windows

2.5.1 / 2011-11-17

  • Changed: updated connect to 1.8.x
  • Removed sass.js support from express(1)

2.5.0 / 2011-10-24

  • Added ./routes dir for generated app by default
  • Added npm install reminder to express(1) app gen
  • Added 0.5.x support
  • Removed make test-cov since it wont work with node 0.5.x
  • Fixed express(1) public dir for windows. Closes #866

2.4.7 / 2011-10-05

  • Added mkdirp to express(1). Closes #795
  • Added simple json-config example
  • Added shorthand for the parsed request's pathname via req.path
  • Changed connect dep to 1.7.x to fix npm issue...
  • Fixed res.redirect() HEAD support. [reported by xerox]
  • Fixed req.flash(), only escape args
  • Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie]

2.4.6 / 2011-08-22

  • Fixed multiple param callback regression. Closes #824 [reported by TroyGoode]

2.4.5 / 2011-08-19

  • Added support for routes to handle errors. Closes #809
  • Added app.routes.all(). Closes #803
  • Added "basepath" setting to work in conjunction with reverse proxies etc.
  • Refactored Route to use a single array of callbacks
  • Added support for multiple callbacks for app.param(). Closes #801 Closes #805
  • Changed: removed .call(self) for route callbacks
  • Dependency: qs >= 0.3.1
  • Fixed res.redirect() on windows due to join() usage. Closes #808

2.4.4 / 2011-08-05

  • Fixed res.header() intention of a set, even when undefined
  • Fixed *, value no longer required
  • Fixed res.send(204) support. Closes #771

2.4.3 / 2011-07-14

  • Added docs for status option special-case. Closes #739
  • Fixed options.filename, exposing the view path to template engines

2.4.2. / 2011-07-06

  • Revert "removed jsonp stripping" for XSS

2.4.1 / 2011-07-06

  • Added res.json() JSONP support. Closes #737
  • Added extending-templates example. Closes #730
  • Added "strict routing" setting for trailing slashes
  • Added support for multiple envs in app.configure() calls. Closes #735
  • Changed: res.send() using res.json()
  • Changed: when cookie path === null don't default it
  • Changed; default cookie path to "home" setting. Closes #731
  • Removed pids/logs creation from express(1)

2.4.0 / 2011-06-28

  • Added chainable res.status(code)
  • Added res.json(), an explicit version of res.send(obj)
  • Added simple web-service example

2.3.12 / 2011-06-22

  • #express is now on freenode! come join!
  • Added req.get(field, param)
  • Added links to Japanese documentation, thanks @hideyukisaito!
  • Added; the express(1) generated app outputs the env
  • Added content-negotiation example
  • Dependency: connect >= 1.5.1 < 2.0.0
  • Fixed view layout bug. Closes #720
  • Fixed; ignore body on 304. Closes #701

2.3.11 / 2011-06-04

  • Added npm test
  • Removed generation of dummy test file from express(1)
  • Fixed; express(1) adds express as a dep
  • Fixed; prune on prepublish

2.3.10 / 2011-05-27

  • Added req.route, exposing the current route
  • Added package.json generation support to express(1)
  • Fixed call to app.param() function for optional params. Closes #682

2.3.9 / 2011-05-25

  • Fixed bug-ish with ../' in res.partial()` calls

2.3.8 / 2011-05-24

  • Fixed app.options()

2.3.7 / 2011-05-23

  • Added route Collection, ex: app.get('/user/:id').remove();
  • Added support for app.param(fn) to define param logic
  • Removed app.param() support for callback with return value
  • Removed module.parent check from express(1) generated app. Closes #670
  • Refactored router. Closes #639

2.3.6 / 2011-05-20

  • Changed; using devDependencies instead of git submodules
  • Fixed redis session example
  • Fixed markdown example
  • Fixed view caching, should not be enabled in development

2.3.5 / 2011-05-20

  • Added export .view as alias for .View

2.3.4 / 2011-05-08

  • Added ./examples/say
  • Fixed res.sendfile() bug preventing the transfer of files with spaces

2.3.3 / 2011-05-03

  • Added "case sensitive routes" option.
  • Changed; split methods supported per rfc [slaskis]
  • Fixed route-specific middleware when using the same callback function several times

2.3.2 / 2011-04-27

  • Fixed view hints

2.3.1 / 2011-04-26

  • Added app.match() as app.match.all()
  • Added app.lookup() as app.lookup.all()
  • Added app.remove() for app.remove.all()
  • Added app.remove.VERB()
  • Fixed template caching collision issue. Closes #644
  • Moved router over from connect and started refactor

2.3.0 / 2011-04-25

  • Added options support to res.clearCookie()
  • Added res.helpers() as alias of res.locals()
  • Added; json defaults to UTF-8 with res.send(). Closes #632. [Daniel * Dependency connect >= 1.4.0
  • Changed; auto set Content-Type in res.attachement [Aaron Heckmann]
  • Renamed "cache views" to "view cache". Closes #628
  • Fixed caching of views when using several apps. Closes #637
  • Fixed gotcha invoking app.param() callbacks once per route middleware. Closes #638
  • Fixed partial lookup precedence. Closes #631 Shaw]

2.2.2 / 2011-04-12

  • Added second callback support for res.download() connection errors
  • Fixed filename option passing to template engine

2.2.1 / 2011-04-04

  • Added layout(path) helper to change the layout within a view. Closes #610

  • Fixed partial() collection object support. Previously only anything with .length would work. When .length is present one must still be aware of holes, however now { collection: {foo: 'bar'}} is valid, exposes keyInCollection and keysInCollection.

  • Performance improved with better view caching

  • Removed request and response locals

  • Changed; errorHandler page title is now Express instead of Connect

2.2.0 / 2011-03-30

  • Added app.lookup.VERB(), ex app.lookup.put('/user/:id'). Closes #606
  • Added app.match.VERB(), ex app.match.put('/user/12'). Closes #606
  • Added app.VERB(path) as alias of app.lookup.VERB().
  • Dependency connect >= 1.2.0

2.1.1 / 2011-03-29

  • Added; expose err.view object when failing to locate a view
  • Fixed res.partial() call next(err) when no callback is given [reported by aheckmann]
  • Fixed; res.send(undefined) responds with 204 [aheckmann]

2.1.0 / 2011-03-24

  • Added <root>/_?<name> partial lookup support. Closes #447
  • Added request, response, and app local variables
  • Added settings local variable, containing the app's settings
  • Added req.flash() exception if req.session is not available
  • Added res.send(bool) support (json response)
  • Fixed stylus example for latest version
  • Fixed; wrap try/catch around res.render()

2.0.0 / 2011-03-17

  • Fixed up index view path alternative.
  • Changed; res.locals() without object returns the locals

2.0.0rc3 / 2011-03-17

  • Added res.locals(obj) to compliment res.local(key, val)
  • Added res.partial() callback support
  • Fixed recursive error reporting issue in res.render()

2.0.0rc2 / 2011-03-17

  • Changed; partial() "locals" are now optional
  • Fixed SlowBuffer support. Closes #584 [reported by tyrda01]
  • Fixed .filename view engine option [reported by drudge]
  • Fixed blog example
  • Fixed {req,res}.app reference when mounting [Ben Weaver]

2.0.0rc / 2011-03-14

  • Fixed; expose HTTPSServer constructor
  • Fixed express(1) default test charset. Closes #579 [reported by secoif]
  • Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]

2.0.0beta3 / 2011-03-09

  • Added support for res.contentType() literal The original res.contentType('.json'), res.contentType('application/json'), and res.contentType('json') will work now.
  • Added res.render() status option support back
  • Added charset option for res.render()
  • Added .charset support (via connect 1.0.4)
  • Added view resolution hints when in development and a lookup fails
  • Added layout lookup support relative to the page view. For example while rendering ./views/user/index.jade if you create ./views/user/layout.jade it will be used in favour of the root layout.
  • Fixed res.redirect(). RFC states absolute url [reported by unlink]
  • Fixed; default res.send() string charset to utf8
  • Removed Partial constructor (not currently used)

2.0.0beta2 / 2011-03-07

  • Added res.render() .locals support back to aid in migration process
  • Fixed flash example

2.0.0beta / 2011-03-03

  • Added HTTPS support
  • Added res.cookie() maxAge support
  • Added req.header() Referrer / Referer special-case, either works
  • Added mount support for res.redirect(), now respects the mount-point
  • Added union() util, taking place of merge(clone()) combo
  • Added stylus support to express(1) generated app
  • Added secret to session middleware used in examples and generated app
  • Added res.local(name, val) for progressive view locals
  • Added default param support to req.param(name, default)
  • Added app.disabled() and app.enabled()
  • Added app.register() support for omitting leading ".", either works
  • Added res.partial(), using the same interface as partial() within a view. Closes #539
  • Added app.param() to map route params to async/sync logic
  • Added; aliased app.helpers() as app.locals(). Closes #481
  • Added extname with no leading "." support to res.contentType()
  • Added cache views setting, defaulting to enabled in "production" env
  • Added index file partial resolution, eg: partial('user') may try views/user/index.jade.
  • Added req.accepts() support for extensions
  • Changed; res.download() and res.sendfile() now utilize Connect's static file server connect.static.send().
  • Changed; replaced connect.utils.mime() with npm mime module
  • Changed; allow req.query to be pre-defined (via middleware or other parent
  • Changed view partial resolution, now relative to parent view
  • Changed view engine signature. no longer engine.render(str, options, callback), now engine.compile(str, options) -> Function, the returned function accepts fn(locals).
  • Fixed req.param() bug returning Array.prototype methods. Closes #552
  • Fixed; using Stream#pipe() instead of sys.pump() in res.sendfile()
  • Fixed; using qs module instead of querystring
  • Fixed; strip unsafe chars from jsonp callbacks
  • Removed "stream threshold" setting

1.0.8 / 2011-03-01

  • Allow req.query to be pre-defined (via middleware or other parent app)
  • "connect": ">= 0.5.0 < 1.0.0". Closes #547
  • Removed the long deprecated EXPRESS_ENV support

1.0.7 / 2011-02-07

  • Fixed render() setting inheritance. Mounted apps would not inherit "view engine"

1.0.6 / 2011-02-07

  • Fixed view engine setting bug when period is in dirname

1.0.5 / 2011-02-05

  • Added secret to generated app session() call

1.0.4 / 2011-02-05

  • Added qs dependency to package.json
  • Fixed namespaced require()s for latest connect support

1.0.3 / 2011-01-13

  • Remove unsafe characters from JSONP callback names [Ryan Grove]

1.0.2 / 2011-01-10

  • Removed nested require, using connect.router

1.0.1 / 2010-12-29

  • Fixed for middleware stacked via createServer() previously the foo middleware passed to createServer(foo) would not have access to Express methods such as res.send() or props like req.query etc.

1.0.0 / 2010-11-16

  • Added; deduce partial object names from the last segment. For example by default partial('forum/post', postObject) will give you the post object, providing a meaningful default.
  • Added http status code string representation to res.redirect() body
  • Added; res.redirect() supporting text/plain and text/html via Accept.
  • Added req.is() to aid in content negotiation
  • Added partial local inheritance [suggested by masylum]. Closes #102 providing access to parent template locals.
  • Added -s, --session[s] flag to express(1) to add session related middleware
  • Added --template flag to express(1) to specify the template engine to use.
  • Added --css flag to express(1) to specify the stylesheet engine to use (or just plain css by default).
  • Added app.all() support [thanks aheckmann]
  • Added partial direct object support. You may now partial('user', user) providing the "user" local, vs previously partial('user', { object: user }).
  • Added route-separation example since many people question ways to do this with CommonJS modules. Also view the blog example for an alternative.
  • Performance; caching view path derived partial object names
  • Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454
  • Fixed jsonp support; text/javascript as per mailinglist discussion

1.0.0rc4 / 2010-10-14

  • Added NODE_ENV support, EXPRESS_ENV is deprecated and will be removed in 1.0.0
  • Added route-middleware support (very helpful, see the docs)
  • Added jsonp callback setting to enable/disable jsonp autowrapping [Dav Glass]
  • Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass]
  • Added partial() support for array-like collections. Closes #434
  • Added support for swappable querystring parsers
  • Added session usage docs. Closes #443
  • Added dynamic helper caching. Closes #439 [suggested by maritz]
  • Added authentication example
  • Added basic Range support to res.sendfile() (and res.download() etc)
  • Changed; express(1) generated app using 2 spaces instead of 4
  • Default env to "development" again [aheckmann]
  • Removed context option is no more, use "scope"
  • Fixed; exposing ./support libs to examples so they can run without installs
  • Fixed mvc example

1.0.0rc3 / 2010-09-20

  • Added confirmation for express(1) app generation. Closes #391
  • Added extending of flash formatters via app.flashFormatters
  • Added flash formatter support. Closes #411
  • Added streaming support to res.sendfile() using sys.pump() when >= "stream threshold"
  • Added stream threshold setting for res.sendfile()
  • Added res.send() HEAD support
  • Added res.clearCookie()
  • Added res.cookie()
  • Added res.render() headers option
  • Added res.redirect() response bodies
  • Added res.render() status option support. Closes #425 [thanks aheckmann]
  • Fixed res.sendfile() responding with 403 on malicious path
  • Fixed res.download() bug; when an error occurs remove Content-Disposition
  • Fixed; mounted apps settings now inherit from parent app [aheckmann]
  • Fixed; stripping Content-Length / Content-Type when 204
  • Fixed res.send() 204. Closes #419
  • Fixed multiple Set-Cookie headers via res.header(). Closes #402
  • Fixed bug messing with error handlers when listenFD() is called instead of listen(). [thanks guillermo]

1.0.0rc2 / 2010-08-17

  • Added app.register() for template engine mapping. Closes #390
  • Added res.render() callback support as second argument (no options)
  • Added callback support to res.download()
  • Added callback support for res.sendfile()
  • Added support for middleware access via express.middlewareName() vs connect.middlewareName()
  • Added "partials" setting to docs
  • Added default expresso tests to express(1) generated app. Closes #384
  • Fixed res.sendfile() error handling, defer via next()
  • Fixed res.render() callback when a layout is used [thanks guillermo]
  • Fixed; make install creating ~/.node_libraries when not present
  • Fixed issue preventing error handlers from being defined anywhere. Closes #387

1.0.0rc / 2010-07-28

  • Added mounted hook. Closes #369

  • Added connect dependency to package.json

  • Removed "reload views" setting and support code development env never caches, production always caches.

  • Removed param in route callbacks, signature is now simply (req, res, next), previously (req, res, params, next). Use req.params for path captures, req.query for GET params.

  • Fixed "home" setting

  • Fixed middleware/router precedence issue. Closes #366

  • Fixed; configure() callbacks called immediately. Closes #368

1.0.0beta2 / 2010-07-23

  • Added more examples
  • Added; exporting Server constructor
  • Added Server#helpers() for view locals
  • Added Server#dynamicHelpers() for dynamic view locals. Closes #349
  • Added support for absolute view paths
  • Added; home setting defaults to Server#route for mounted apps. Closes #363
  • Added Guillermo Rauch to the contributor list
  • Added support for "as" for non-collection partials. Closes #341
  • Fixed install.sh, ensuring ~/.node_libraries exists. Closes #362 [thanks jf]
  • Fixed res.render() exceptions, now passed to next() when no callback is given [thanks guillermo]
  • Fixed instanceof Array checks, now Array.isArray()
  • Fixed express(1) expansion of public dirs. Closes #348
  • Fixed middleware precedence. Closes #345
  • Fixed view watcher, now async [thanks aheckmann]

1.0.0beta / 2010-07-15

  • Re-write
    • much faster
    • much lighter
    • Check ExpressJS.com for migration guide and updated docs

0.14.0 / 2010-06-15

  • Utilize relative requires
  • Added Static bufferSize option [aheckmann]
  • Fixed caching of view and partial subdirectories [aheckmann]
  • Fixed mime.type() comments now that ".ext" is not supported
  • Updated haml submodule
  • Updated class submodule
  • Removed bin/express

0.13.0 / 2010-06-01

  • Added node v0.1.97 compatibility
  • Added support for deleting cookies via Request#cookie('key', null)
  • Updated haml submodule
  • Fixed not-found page, now using using charset utf-8
  • Fixed show-exceptions page, now using using charset utf-8
  • Fixed view support due to fs.readFile Buffers
  • Changed; mime.type() no longer accepts ".type" due to node extname() changes

0.12.0 / 2010-05-22

  • Added node v0.1.96 compatibility
  • Added view helpers export which act as additional local variables
  • Updated haml submodule
  • Changed ETag; removed inode, modified time only
  • Fixed LF to CRLF for setting multiple cookies
  • Fixed cookie complation; values are now urlencoded
  • Fixed cookies parsing; accepts quoted values and url escaped cookies

0.11.0 / 2010-05-06

  • Added support for layouts using different engines
    • this.render('page.html.haml', { layout: 'super-cool-layout.html.ejs' })
    • this.render('page.html.haml', { layout: 'foo' }) // assumes 'foo.html.haml'
    • this.render('page.html.haml', { layout: false }) // no layout
  • Updated ext submodule
  • Updated haml submodule
  • Fixed EJS partial support by passing along the context. Issue #307

0.10.1 / 2010-05-03

  • Fixed binary uploads.

0.10.0 / 2010-04-30

  • Added charset support via Request#charset (automatically assigned to 'UTF-8' when respond()'s encoding is set to 'utf8' or 'utf-8'.
  • Added "encoding" option to Request#render(). Closes #299
  • Added "dump exceptions" setting, which is enabled by default.
  • Added simple ejs template engine support
  • Added error reponse support for text/plain, application/json. Closes #297
  • Added callback function param to Request#error()
  • Added Request#sendHead()
  • Added Request#stream()
  • Added support for Request#respond(304, null) for empty response bodies
  • Added ETag support to Request#sendfile()
  • Added options to Request#sendfile(), passed to fs.createReadStream()
  • Added filename arg to Request#download()
  • Performance enhanced due to pre-reversing plugins so that plugins.reverse() is not called on each request
  • Performance enhanced by preventing several calls to toLowerCase() in Router#match()
  • Changed; Request#sendfile() now streams
  • Changed; Renamed Request#halt() to Request#respond(). Closes #289
  • Changed; Using sys.inspect() instead of JSON.encode() for error output
  • Changed; run() returns the http.Server instance. Closes #298
  • Changed; Defaulting Server#host to null (INADDR_ANY)
  • Changed; Logger "common" format scale of 0.4f
  • Removed Logger "request" format
  • Fixed; Catching ENOENT in view caching, preventing error when "views/partials" is not found
  • Fixed several issues with http client
  • Fixed Logger Content-Length output
  • Fixed bug preventing Opera from retaining the generated session id. Closes #292

0.9.0 / 2010-04-14

  • Added DSL level error() route support
  • Added DSL level notFound() route support
  • Added Request#error()
  • Added Request#notFound()
  • Added Request#render() callback function. Closes #258
  • Added "max upload size" setting
  • Added "magic" variables to collection partials (__index__, __length__, __isFirst__, __isLast__). Closes #254
  • Added haml.js submodule; removed haml-js
  • Added callback function support to Request#halt() as 3rd/4th arg
  • Added preprocessing of route param wildcards using param(). Closes #251
  • Added view partial support (with collections etc)
  • Fixed bug preventing falsey params (such as ?page=0). Closes #286
  • Fixed setting of multiple cookies. Closes #199
  • Changed; view naming convention is now NAME.TYPE.ENGINE (for example page.html.haml)
  • Changed; session cookie is now httpOnly
  • Changed; Request is no longer global
  • Changed; Event is no longer global
  • Changed; "sys" module is no longer global
  • Changed; moved Request#download to Static plugin where it belongs
  • Changed; Request instance created before body parsing. Closes #262
  • Changed; Pre-caching views in memory when "cache view contents" is enabled. Closes #253
  • Changed; Pre-caching view partials in memory when "cache view partials" is enabled
  • Updated support to node --version 0.1.90
  • Updated dependencies
  • Removed set("session cookie") in favour of use(Session, { cookie: { ... }})
  • Removed utils.mixin(); use Object#mergeDeep()

0.8.0 / 2010-03-19

  • Added coffeescript example app. Closes #242
  • Changed; cache api now async friendly. Closes #240
  • Removed deprecated 'express/static' support. Use 'express/plugins/static'

0.7.6 / 2010-03-19

  • Added Request#isXHR. Closes #229
  • Added make install (for the executable)
  • Added express executable for setting up simple app templates
  • Added "GET /public/*" to Static plugin, defaulting to /public
  • Added Static plugin
  • Fixed; Request#render() only calls cache.get() once
  • Fixed; Namespacing View caches with "view:"
  • Fixed; Namespacing Static caches with "static:"
  • Fixed; Both example apps now use the Static plugin
  • Fixed set("views"). Closes #239
  • Fixed missing space for combined log format
  • Deprecated Request#sendfile() and 'express/static'
  • Removed Server#running

0.7.5 / 2010-03-16

  • Added Request#flash() support without args, now returns all flashes
  • Updated ext submodule

0.7.4 / 2010-03-16

  • Fixed session reaper
  • Changed; class.js replacing js-oo Class implementation (quite a bit faster, no browser cruft)

0.7.3 / 2010-03-16

  • Added package.json
  • Fixed requiring of haml / sass due to kiwi removal

0.7.2 / 2010-03-16

  • Fixed GIT submodules (HAH!)

0.7.1 / 2010-03-16

  • Changed; Express now using submodules again until a PM is adopted
  • Changed; chat example using millisecond conversions from ext

0.7.0 / 2010-03-15

  • Added Request#pass() support (finds the next matching route, or the given path)
  • Added Logger plugin (default "common" format replaces CommonLogger)
  • Removed Profiler plugin
  • Removed CommonLogger plugin

0.6.0 / 2010-03-11

  • Added seed.yml for kiwi package management support

  • Added HTTP client query string support when method is GET. Closes #205

  • Added support for arbitrary view engines. For example "foo.engine.html" will now require('engine'), the exports from this module are cached after the first require().

  • Added async plugin support

  • Removed usage of RESTful route funcs as http client get() etc, use http.get() and friends

  • Removed custom exceptions

0.5.0 / 2010-03-10

  • Added ext dependency (library of js extensions)
  • Removed extname() / basename() utils. Use path module
  • Removed toArray() util. Use arguments.values
  • Removed escapeRegexp() util. Use RegExp.escape()
  • Removed process.mixin() dependency. Use utils.mixin()
  • Removed Collection
  • Removed ElementCollection
  • Shameless self promotion of ebook "Advanced JavaScript" (http://dev-mag.com) ;)

0.4.0 / 2010-02-11

  • Added flash() example to sample upload app
  • Added high level restful http client module (express/http)
  • Changed; RESTful route functions double as HTTP clients. Closes #69
  • Changed; throwing error when routes are added at runtime
  • Changed; defaulting render() context to the current Request. Closes #197
  • Updated haml submodule

0.3.0 / 2010-02-11

  • Updated haml / sass submodules. Closes #200
  • Added flash message support. Closes #64
  • Added accepts() now allows multiple args. fixes #117
  • Added support for plugins to halt. Closes #189
  • Added alternate layout support. Closes #119
  • Removed Route#run(). Closes #188
  • Fixed broken specs due to use(Cookie) missing

0.2.1 / 2010-02-05

  • Added "plot" format option for Profiler (for gnuplot processing)
  • Added request number to Profiler plugin
  • Fixed binary encoding for multi-part file uploads, was previously defaulting to UTF8
  • Fixed issue with routes not firing when not files are present. Closes #184
  • Fixed process.Promise -> events.Promise

0.2.0 / 2010-02-03

  • Added parseParam() support for name[] etc. (allows for file inputs with "multiple" attr) Closes #180
  • Added Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174
  • Added expiration support to cache api with reaper. Closes #133
  • Added cache Store.Memory#reap()
  • Added Cache; cache api now uses first class Cache instances
  • Added abstract session Store. Closes #172
  • Changed; cache Memory.Store#get() utilizing Collection
  • Renamed MemoryStore -> Store.Memory
  • Fixed use() of the same plugin several time will always use latest options. Closes #176

0.1.0 / 2010-02-03

  • Changed; Hooks (before / after) pass request as arg as well as evaluated in their context
  • Updated node support to 0.1.27 Closes #169
  • Updated dirname(__filename) -> __dirname
  • Updated libxmljs support to v0.2.0
  • Added session support with memory store / reaping
  • Added quick uid() helper
  • Added multi-part upload support
  • Added Sass.js support / submodule
  • Added production env caching view contents and static files
  • Added static file caching. Closes #136
  • Added cache plugin with memory stores
  • Added support to StaticFile so that it works with non-textual files.
  • Removed dirname() helper
  • Removed several globals (now their modules must be required)

0.0.2 / 2010-01-10

  • Added view benchmarks; currently haml vs ejs
  • Added Request#attachment() specs. Closes #116
  • Added use of node's parseQuery() util. Closes #123
  • Added make init for submodules
  • Updated Haml
  • Updated sample chat app to show messages on load
  • Updated libxmljs parseString -> parseHtmlString
  • Fixed make init to work with older versions of git
  • Fixed specs can now run independant specs for those who cant build deps. Closes #127
  • Fixed issues introduced by the node url module changes. Closes 126.
  • Fixed two assertions failing due to Collection#keys() returning strings
  • Fixed faulty Collection#toArray() spec due to keys() returning strings
  • Fixed make test now builds libxmljs.node before testing

0.0.1 / 2010-01-03

  • Initial release
module.exports = process.env.EXPRESS_COV
? require('./lib-cov/express')
: require('./lib/express');
/**
* Module dependencies.
*/
var connect = require('connect')
, Router = require('./router')
, methods = require('methods')
, middleware = require('./middleware')
, debug = require('debug')('express:application')
, locals = require('./utils').locals
, View = require('./view')
, utils = connect.utils
, http = require('http');
/**
* Application prototype.
*/
var app = exports = module.exports = {};
/**
* Initialize the server.
*
* - setup default configuration
* - setup default middleware
* - setup route reflection methods
*
* @api private
*/
app.init = function(){
this.cache = {};
this.settings = {};
this.engines = {};
this.defaultConfiguration();
};
/**
* Initialize application configuration.
*
* @api private
*/
app.defaultConfiguration = function(){
// default settings
this.enable('x-powered-by');
this.enable('etag');
this.set('env', process.env.NODE_ENV || 'development');
this.set('subdomain offset', 2);
debug('booting in %s mode', this.get('env'));
// implicit middleware
this.use(connect.query());
this.use(middleware.init(this));
// inherit protos
this.on('mount', function(parent){
this.request.__proto__ = parent.request;
this.response.__proto__ = parent.response;
this.engines.__proto__ = parent.engines;
this.settings.__proto__ = parent.settings;
});
// router
this._router = new Router(this);
this.routes = this._router.map;
this.__defineGetter__('router', function(){
this._usedRouter = true;
this._router.caseSensitive = this.enabled('case sensitive routing');
this._router.strict = this.enabled('strict routing');
return this._router.middleware;
});
// setup locals
this.locals = locals(this);
// default locals
this.locals.settings = this.settings;
// default configuration
this.set('view', View);
this.set('views', process.cwd() + '/views');
this.set('jsonp callback name', 'callback');
this.configure('development', function(){
this.set('json spaces', 2);
});
this.configure('production', function(){
this.enable('view cache');
});
};
/**
* Proxy `connect#use()` to apply settings to
* mounted applications.
*
* @param {String|Function|Server} route
* @param {Function|Server} fn
* @return {app} for chaining
* @api public
*/
app.use = function(route, fn){
var app;
// default route to '/'
if ('string' != typeof route) fn = route, route = '/';
// express app
if (fn.handle && fn.set) app = fn;
// restore .app property on req and res
if (app) {
app.route = route;
fn = function(req, res, next) {
var orig = req.app;
app.handle(req, res, function(err){
req.__proto__ = orig.request;
res.__proto__ = orig.response;
next(err);
});
};
}
connect.proto.use.call(this, route, fn);
// mounted an app
if (app) {
app.parent = this;
app.emit('mount', this);
}
return this;
};
/**
* Register the given template engine callback `fn`
* as `ext`.
*
* By default will `require()` the engine based on the
* file extension. For example if you try to render
* a "foo.jade" file Express will invoke the following internally:
*
* app.engine('jade', require('jade').__express);
*
* For engines that do not provide `.__express` out of the box,
* or if you wish to "map" a different extension to the template engine
* you may use this method. For example mapping the EJS template engine to
* ".html" files:
*
* app.engine('html', require('ejs').renderFile);
*
* In this case EJS provides a `.renderFile()` method with
* the same signature that Express expects: `(path, options, callback)`,
* though note that it aliases this method as `ejs.__express` internally
* so if you're using ".ejs" extensions you dont need to do anything.
*
* Some template engines do not follow this convention, the
* [Consolidate.js](https://github.com/visionmedia/consolidate.js)
* library was created to map all of node's popular template
* engines to follow this convention, thus allowing them to
* work seamlessly within Express.
*
* @param {String} ext
* @param {Function} fn
* @return {app} for chaining
* @api public
*/
app.engine = function(ext, fn){
if ('function' != typeof fn) throw new Error('callback function required');
if ('.' != ext[0]) ext = '.' + ext;
this.engines[ext] = fn;
return this;
};
/**
* Map the given param placeholder `name`(s) to the given callback(s).
*
* Parameter mapping is used to provide pre-conditions to routes
* which use normalized placeholders. For example a _:user_id_ parameter
* could automatically load a user's information from the database without
* any additional code,
*
* The callback uses the same signature as middleware, the only difference
* being that the value of the placeholder is passed, in this case the _id_
* of the user. Once the `next()` function is invoked, just like middleware
* it will continue on to execute the route, or subsequent parameter functions.
*
* app.param('user_id', function(req, res, next, id){
* User.find(id, function(err, user){
* if (err) {
* next(err);
* } else if (user) {
* req.user = user;
* next();
* } else {
* next(new Error('failed to load user'));
* }
* });
* });
*
* @param {String|Array} name
* @param {Function} fn
* @return {app} for chaining
* @api public
*/
app.param = function(name, fn){
var self = this
, fns = [].slice.call(arguments, 1);
// array
if (Array.isArray(name)) {
name.forEach(function(name){
fns.forEach(function(fn){
self.param(name, fn);
});
});
// param logic
} else if ('function' == typeof name) {
this._router.param(name);
// single
} else {
if (':' == name[0]) name = name.substr(1);
fns.forEach(function(fn){
self._router.param(name, fn);
});
}
return this;
};
/**
* Assign `setting` to `val`, or return `setting`'s value.
*
* app.set('foo', 'bar');
* app.get('foo');
* // => "bar"
*
* Mounted servers inherit their parent server's settings.
*
* @param {String} setting
* @param {String} val
* @return {Server} for chaining
* @api public
*/
app.set = function(setting, val){
if (1 == arguments.length) {
return this.settings[setting];
} else {
this.settings[setting] = val;
return this;
}
};
/**
* Return the app's absolute pathname
* based on the parent(s) that have
* mounted it.
*
* For example if the application was
* mounted as "/admin", which itself
* was mounted as "/blog" then the
* return value would be "/blog/admin".
*
* @return {String}
* @api private
*/
app.path = function(){
return this.parent
? this.parent.path() + this.route
: '';
};
/**
* Check if `setting` is enabled (truthy).
*
* app.enabled('foo')
* // => false
*
* app.enable('foo')
* app.enabled('foo')
* // => true
*
* @param {String} setting
* @return {Boolean}
* @api public
*/
app.enabled = function(setting){
return !!this.set(setting);
};
/**
* Check if `setting` is disabled.
*
* app.disabled('foo')
* // => true
*
* app.enable('foo')
* app.disabled('foo')
* // => false
*
* @param {String} setting
* @return {Boolean}
* @api public
*/
app.disabled = function(setting){
return !this.set(setting);
};
/**
* Enable `setting`.
*
* @param {String} setting
* @return {app} for chaining
* @api public
*/
app.enable = function(setting){
return this.set(setting, true);
};
/**
* Disable `setting`.
*
* @param {String} setting
* @return {app} for chaining
* @api public
*/
app.disable = function(setting){
return this.set(setting, false);
};
/**
* Configure callback for zero or more envs,
* when no `env` is specified that callback will
* be invoked for all environments. Any combination
* can be used multiple times, in any order desired.
*
* Examples:
*
* app.configure(function(){
* // executed for all envs
* });
*
* app.configure('stage', function(){
* // executed staging env
* });
*
* app.configure('stage', 'production', function(){
* // executed for stage and production
* });
*
* Note:
*
* These callbacks are invoked immediately, and
* are effectively sugar for the following:
*
* var env = process.env.NODE_ENV || 'development';
*
* switch (env) {
* case 'development':
* ...
* break;
* case 'stage':
* ...
* break;
* case 'production':
* ...
* break;
* }
*
* @param {String} env...
* @param {Function} fn
* @return {app} for chaining
* @api public
*/
app.configure = function(env, fn){
var envs = 'all'
, args = [].slice.call(arguments);
fn = args.pop();
if (args.length) envs = args;
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
return this;
};
/**
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
*/
methods.forEach(function(method){
app[method] = function(path){
if ('get' == method && 1 == arguments.length) return this.set(path);
// deprecated
if (Array.isArray(path)) {
console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
}
// if no router attached yet, attach the router
if (!this._usedRouter) this.use(this.router);
// setup route
this._router[method].apply(this._router, arguments);
return this;
};
});
/**
* Special-cased "all" method, applying the given route `path`,
* middleware, and callback to _every_ HTTP method.
*
* @param {String} path
* @param {Function} ...
* @return {app} for chaining
* @api public
*/
app.all = function(path){
var args = arguments;
methods.forEach(function(method){
app[method].apply(this, args);
}, this);
return this;
};
// del -> delete alias
app.del = app.delete;
/**
* Render the given view `name` name with `options`
* and a callback accepting an error and the
* rendered template string.
*
* Example:
*
* app.render('email', { name: 'Tobi' }, function(err, html){
* // ...
* })
*
* @param {String} name
* @param {String|Function} options or fn
* @param {Function} fn
* @api public
*/
app.render = function(name, options, fn){
var opts = {}
, cache = this.cache
, engines = this.engines
, view;
// support callback function as second arg
if ('function' == typeof options) {
fn = options, options = {};
}
// merge app.locals
utils.merge(opts, this.locals);
// merge options._locals
if (options._locals) utils.merge(opts, options._locals);
// merge options
utils.merge(opts, options);
// set .cache unless explicitly provided
opts.cache = null == opts.cache
? this.enabled('view cache')
: opts.cache;
// primed cache
if (opts.cache) view = cache[name];
// view
if (!view) {
view = new (this.get('view'))(name, {
defaultEngine: this.get('view engine'),
root: this.get('views'),
engines: engines
});
if (!view.path) {
var err = new Error('Failed to lookup view "' + name + '"');
err.view = view;
return fn(err);
}
// prime the cache
if (opts.cache) cache[name] = view;
}
// render
try {
view.render(opts, fn);
} catch (err) {
fn(err);
}
};
/**
* Listen for connections.
*
* A node `http.Server` is returned, with this
* application (which is a `Function`) as its
* callback. If you wish to create both an HTTP
* and HTTPS server you may do so with the "http"
* and "https" modules as shown here:
*
* var http = require('http')
* , https = require('https')
* , express = require('express')
* , app = express();
*
* http.createServer(app).listen(80);
* https.createServer({ ... }, app).listen(443);
*
* @return {http.Server}
* @api public
*/
app.listen = function(){
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
/**
* Module dependencies.
*/
var merge = require('merge-descriptors');
var connect = require('connect')
, proto = require('./application')
, Route = require('./router/route')
, Router = require('./router')
, req = require('./request')
, res = require('./response')
, utils = connect.utils;
/**
* Expose `createApplication()`.
*/
exports = module.exports = createApplication;
/**
* Expose mime.
*/
exports.mime = connect.mime;
/**
* Create an express application.
*
* @return {Function}
* @api public
*/
function createApplication() {
var app = connect();
utils.merge(app, proto);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
/**
* Expose connect.middleware as express.*
* for example `express.logger` etc.
*/
merge(exports, connect.middleware);
/**
* Error on createServer().
*/
exports.createServer = function(){
console.warn('Warning: express.createServer() is deprecated, express');
console.warn('applications no longer inherit from http.Server,');
console.warn('please use:');
console.warn('');
console.warn(' var express = require("express");');
console.warn(' var app = express();');
console.warn('');
return createApplication();
};
/**
* Expose the prototypes.
*/
exports.application = proto;
exports.request = req;
exports.response = res;
/**
* Expose constructors.
*/
exports.Route = Route;
exports.Router = Router;
// Error handler title
exports.errorHandler.title = 'Express';
/**
* Module dependencies.
*/
var utils = require('./utils');
/**
* Initialization middleware, exposing the
* request and response to eachother, as well
* as defaulting the X-Powered-By header field.
*
* @param {Function} app
* @return {Function}
* @api private
*/
exports.init = function(app){
return function expressInit(req, res, next){
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;
req.next = next;
req.__proto__ = app.request;
res.__proto__ = app.response;
res.locals = res.locals || utils.locals(res);
next();
}
};
/**
* Module dependencies.
*/
var http = require('http')
, utils = require('./utils')
, connect = require('connect')
, fresh = require('fresh')
, parseRange = require('range-parser')
, parse = connect.utils.parseUrl
, mime = connect.mime;
/**
* Request prototype.
*/
var req = exports = module.exports = {
__proto__: http.IncomingMessage.prototype
};
/**
* Return request header.
*
* The `Referrer` header field is special-cased,
* both `Referrer` and `Referer` are interchangeable.
*
* Examples:
*
* req.get('Content-Type');
* // => "text/plain"
*
* req.get('content-type');
* // => "text/plain"
*
* req.get('Something');
* // => undefined
*
* Aliased as `req.header()`.
*
* @param {String} name
* @return {String}
* @api public
*/
req.get =
req.header = function(name){
switch (name = name.toLowerCase()) {
case 'referer':
case 'referrer':
return this.headers.referrer
|| this.headers.referer;
default:
return this.headers[name];
}
};
/**
* Check if the given `type(s)` is acceptable, returning
* the best match when true, otherwise `undefined`, in which
* case you should respond with 406 "Not Acceptable".
*
* The `type` value may be a single mime type string
* such as "application/json", the extension name
* such as "json", a comma-delimted list such as "json, html, text/plain",
* an argument list such as `"json", "html", "text/plain"`,
* or an array `["json", "html", "text/plain"]`. When a list
* or array is given the _best_ match, if any is returned.
*
* Examples:
*
* // Accept: text/html
* req.accepts('html');
* // => "html"
*
* // Accept: text/*, application/json
* req.accepts('html');
* // => "html"
* req.accepts('text/html');
* // => "text/html"
* req.accepts('json, text');
* // => "json"
* req.accepts('application/json');
* // => "application/json"
*
* // Accept: text/*, application/json
* req.accepts('image/png');
* req.accepts('png');
* // => undefined
*
* // Accept: text/*;q=.5, application/json
* req.accepts(['html', 'json']);
* req.accepts('html', 'json');
* req.accepts('html, json');
* // => "json"
*
* @param {String|Array} type(s)
* @return {String}
* @api public
*/
req.accepts = function(type){
var args = arguments.length > 1 ? [].slice.apply(arguments) : type;
return utils.accepts(args, this.get('Accept'));
};
/**
* Check if the given `encoding` is accepted.
*
* @param {String} encoding
* @return {Boolean}
* @api public
*/
req.acceptsEncoding = function(encoding){
return !! ~this.acceptedEncodings.indexOf(encoding);
};
/**
* Check if the given `charset` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
* @param {String} charset
* @return {Boolean}
* @api public
*/
req.acceptsCharset = function(charset){
var accepted = this.acceptedCharsets;
return accepted.length
? !! ~accepted.indexOf(charset)
: true;
};
/**
* Check if the given `lang` is acceptable,
* otherwise you should respond with 406 "Not Acceptable".
*
* @param {String} lang
* @return {Boolean}
* @api public
*/
req.acceptsLanguage = function(lang){
var accepted = this.acceptedLanguages;
return accepted.length
? !! ~accepted.indexOf(lang)
: true;
};
/**
* Parse Range header field,
* capping to the given `size`.
*
* Unspecified ranges such as "0-" require
* knowledge of your resource length. In
* the case of a byte range this is of course
* the total number of bytes. If the Range
* header field is not given `null` is returned,
* `-1` when unsatisfiable, `-2` when syntactically invalid.
*
* NOTE: remember that ranges are inclusive, so
* for example "Range: users=0-3" should respond
* with 4 users when available, not 3.
*
* @param {Number} size
* @return {Array}
* @api public
*/
req.range = function(size){
var range = this.get('Range');
if (!range) return;
return parseRange(size, range);
};
/**
* Return an array of encodings.
*
* Examples:
*
* ['gzip', 'deflate']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedEncodings', function(){
var accept = this.get('Accept-Encoding');
return accept
? accept.trim().split(/ *, */)
: [];
});
/**
* Return an array of Accepted media types
* ordered from highest quality to lowest.
*
* Examples:
*
* [ { value: 'application/json',
* quality: 1,
* type: 'application',
* subtype: 'json' },
* { value: 'text/html',
* quality: 0.5,
* type: 'text',
* subtype: 'html' } ]
*
* @return {Array}
* @api public
*/
req.__defineGetter__('accepted', function(){
var accept = this.get('Accept');
return accept
? utils.parseAccept(accept)
: [];
});
/**
* Return an array of Accepted languages
* ordered from highest quality to lowest.
*
* Examples:
*
* Accept-Language: en;q=.5, en-us
* ['en-us', 'en']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedLanguages', function(){
var accept = this.get('Accept-Language');
return accept
? utils
.parseParams(accept)
.map(function(obj){
return obj.value;
})
: [];
});
/**
* Return an array of Accepted charsets
* ordered from highest quality to lowest.
*
* Examples:
*
* Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8
* ['unicode-1-1', 'iso-8859-5']
*
* @return {Array}
* @api public
*/
req.__defineGetter__('acceptedCharsets', function(){
var accept = this.get('Accept-Charset');
return accept
? utils
.parseParams(accept)
.map(function(obj){
return obj.value;
})
: [];
});
/**
* Return the value of param `name` when present or `defaultValue`.
*
* - Checks route placeholders, ex: _/user/:id_
* - Checks body params, ex: id=12, {"id":12}
* - Checks query string params, ex: ?id=12
*
* To utilize request bodies, `req.body`
* should be an object. This can be done by using
* the `connect.bodyParser()` middleware.
*
* @param {String} name
* @param {Mixed} [defaultValue]
* @return {String}
* @api public
*/
req.param = function(name, defaultValue){
var params = this.params || {};
var body = this.body || {};
var query = this.query || {};
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
if (null != body[name]) return body[name];
if (null != query[name]) return query[name];
return defaultValue;
};
/**
* Check if the incoming request contains the "Content-Type"
* header field, and it contains the give mime `type`.
*
* Examples:
*
* // With Content-Type: text/html; charset=utf-8
* req.is('html');
* req.is('text/html');
* req.is('text/*');
* // => true
*
* // When Content-Type is application/json
* req.is('json');
* req.is('application/json');
* req.is('application/*');
* // => true
*
* req.is('html');
* // => false
*
* @param {String} type
* @return {Boolean}
* @api public
*/
req.is = function(type){
var ct = this.get('Content-Type');
if (!ct) return false;
ct = ct.split(';')[0];
if (!~type.indexOf('/')) type = mime.lookup(type);
if (~type.indexOf('*')) {
type = type.split('/');
ct = ct.split('/');
if ('*' == type[0] && type[1] == ct[1]) return true;
if ('*' == type[1] && type[0] == ct[0]) return true;
return false;
}
return !! ~ct.indexOf(type);
};
/**
* Return the protocol string "http" or "https"
* when requested with TLS. When the "trust proxy"
* setting is enabled the "X-Forwarded-Proto" header
* field will be trusted. If you're running behind
* a reverse proxy that supplies https for you this
* may be enabled.
*
* @return {String}
* @api public
*/
req.__defineGetter__('protocol', function(){
var trustProxy = this.app.get('trust proxy');
if (this.connection.encrypted) return 'https';
if (!trustProxy) return 'http';
var proto = this.get('X-Forwarded-Proto') || 'http';
return proto.split(/\s*,\s*/)[0];
});
/**
* Short-hand for:
*
* req.protocol == 'https'
*
* @return {Boolean}
* @api public
*/
req.__defineGetter__('secure', function(){
return 'https' == this.protocol;
});
/**
* Return the remote address, or when
* "trust proxy" is `true` return
* the upstream addr.
*
* @return {String}
* @api public
*/
req.__defineGetter__('ip', function(){
return this.ips[0] || this.connection.remoteAddress;
});
/**
* When "trust proxy" is `true`, parse
* the "X-Forwarded-For" ip address list.
*
* For example if the value were "client, proxy1, proxy2"
* you would receive the array `["client", "proxy1", "proxy2"]`
* where "proxy2" is the furthest down-stream.
*
* @return {Array}
* @api public
*/
req.__defineGetter__('ips', function(){
var trustProxy = this.app.get('trust proxy');
var val = this.get('X-Forwarded-For');
return trustProxy && val
? val.split(/ *, */)
: [];
});
/**
* Return basic auth credentials.
*
* Examples:
*
* // http://tobi:hello@example.com
* req.auth
* // => { username: 'tobi', password: 'hello' }
*
* @return {Object} or undefined
* @api public
*/
req.__defineGetter__('auth', function(){
// missing
var auth = this.get('Authorization');
if (!auth) return;
// malformed
var parts = auth.split(' ');
if ('basic' != parts[0].toLowerCase()) return;
if (!parts[1]) return;
auth = parts[1];
// credentials
auth = new Buffer(auth, 'base64').toString().match(/^([^:]*):(.*)$/);
if (!auth) return;
return { username: auth[1], password: auth[2] };
});
/**
* Return subdomains as an array.
*
* Subdomains are the dot-separated parts of the host before the main domain of
* the app. By default, the domain of the app is assumed to be the last two
* parts of the host. This can be changed by setting "subdomain offset".
*
* For example, if the domain is "tobi.ferrets.example.com":
* If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
* If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
*
* @return {Array}
* @api public
*/
req.__defineGetter__('subdomains', function(){
var offset = this.app.get('subdomain offset');
return (this.host || '')
.split('.')
.reverse()
.slice(offset);
});
/**
* Short-hand for `url.parse(req.url).pathname`.
*
* @return {String}
* @api public
*/
req.__defineGetter__('path', function(){
return parse(this).pathname;
});
/**
* Parse the "Host" header field hostname.
*
* @return {String}
* @api public
*/
req.__defineGetter__('host', function(){
var trustProxy = this.app.get('trust proxy');
var host = trustProxy && this.get('X-Forwarded-Host');
host = host || this.get('Host');
if (!host) return;
return host.split(':')[0];
});
/**
* Check if the request is fresh, aka
* Last-Modified and/or the ETag
* still match.
*
* @return {Boolean}
* @api public
*/
req.__defineGetter__('fresh', function(){
var method = this.method;
var s = this.res.statusCode;
// GET or HEAD for weak freshness validation only
if ('GET' != method && 'HEAD' != method) return false;
// 2xx or 304 as per rfc2616 14.26
if ((s >= 200 && s < 300) || 304 == s) {
return fresh(this.headers, this.res._headers);
}
return false;
});
/**
* Check if the request is stale, aka
* "Last-Modified" and / or the "ETag" for the
* resource has changed.
*
* @return {Boolean}
* @api public
*/
req.__defineGetter__('stale', function(){
return !this.fresh;
});
/**
* Check if the request was an _XMLHttpRequest_.
*
* @return {Boolean}
* @api public
*/
req.__defineGetter__('xhr', function(){
var val = this.get('X-Requested-With') || '';
return 'xmlhttprequest' == val.toLowerCase();
});
/**
* Module dependencies.
*/
var http = require('http')
, path = require('path')
, connect = require('connect')
, utils = connect.utils
, sign = require('cookie-signature').sign
, normalizeType = require('./utils').normalizeType
, normalizeTypes = require('./utils').normalizeTypes
, etag = require('./utils').etag
, statusCodes = http.STATUS_CODES
, cookie = require('cookie')
, send = require('send')
, mime = connect.mime
, resolve = require('url').resolve
, basename = path.basename
, extname = path.extname;
/**
* Response prototype.
*/
var res = module.exports = {
__proto__: http.ServerResponse.prototype
};
/**
* Set status `code`.
*
* @param {Number} code
* @return {ServerResponse}
* @api public
*/
res.status = function(code){
this.statusCode = code;
return this;
};
/**
* Set Link header field with the given `links`.
*
* Examples:
*
* res.links({
* next: 'http://api.example.com/users?page=2',
* last: 'http://api.example.com/users?page=5'
* });
*
* @param {Object} links
* @return {ServerResponse}
* @api public
*/
res.links = function(links){
var link = this.get('Link') || '';
if (link) link += ', ';
return this.set('Link', link + Object.keys(links).map(function(rel){
return '<' + links[rel] + '>; rel="' + rel + '"';
}).join(', '));
};
/**
* Send a response.
*
* Examples:
*
* res.send(new Buffer('wahoo'));
* res.send({ some: 'json' });
* res.send('<p>some html</p>');
* res.send(404, 'Sorry, cant find that');
* res.send(404);
*
* @param {Mixed} body or status
* @param {Mixed} body
* @return {ServerResponse}
* @api public
*/
res.send = function(body){
var req = this.req;
var head = 'HEAD' == req.method;
var len;
// settings
var app = this.app;
// allow status / body
if (2 == arguments.length) {
// res.send(body, status) backwards compat
if ('number' != typeof body && 'number' == typeof arguments[1]) {
this.statusCode = arguments[1];
} else {
this.statusCode = body;
body = arguments[1];
}
}
switch (typeof body) {
// response status
case 'number':
this.get('Content-Type') || this.type('txt');
this.statusCode = body;
body = http.STATUS_CODES[body];
break;
// string defaulting to html
case 'string':
if (!this.get('Content-Type')) {
this.charset = this.charset || 'utf-8';
this.type('html');
}
break;
case 'boolean':
case 'object':
if (null == body) {
body = '';
} else if (Buffer.isBuffer(body)) {
this.get('Content-Type') || this.type('bin');
} else {
return this.json(body);
}
break;
}
// populate Content-Length
if (undefined !== body && !this.get('Content-Length')) {
this.set('Content-Length', len = Buffer.isBuffer(body)
? body.length
: Buffer.byteLength(body));
}
// ETag support
// TODO: W/ support
if (app.settings.etag && len && 'GET' == req.method) {
if (!this.get('ETag')) {
this.set('ETag', etag(body));
}
}
// freshness
if (req.fresh) this.statusCode = 304;
// strip irrelevant headers
if (204 == this.statusCode || 304 == this.statusCode) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
this.removeHeader('Transfer-Encoding');
body = '';
}
// respond
this.end(head ? null : body);
return this;
};
/**
* Send JSON response.
*
* Examples:
*
* res.json(null);
* res.json({ user: 'tj' });
* res.json(500, 'oh noes!');
* res.json(404, 'I dont have that');
*
* @param {Mixed} obj or status
* @param {Mixed} obj
* @return {ServerResponse}
* @api public
*/
res.json = function(obj){
// allow status / body
if (2 == arguments.length) {
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
} else {
this.statusCode = obj;
obj = arguments[1];
}
}
// settings
var app = this.app;
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces);
// content-type
this.charset = this.charset || 'utf-8';
this.get('Content-Type') || this.set('Content-Type', 'application/json');
return this.send(body);
};
/**
* Send JSON response with JSONP callback support.
*
* Examples:
*
* res.jsonp(null);
* res.jsonp({ user: 'tj' });
* res.jsonp(500, 'oh noes!');
* res.jsonp(404, 'I dont have that');
*
* @param {Mixed} obj or status
* @param {Mixed} obj
* @return {ServerResponse}
* @api public
*/
res.jsonp = function(obj){
// allow status / body
if (2 == arguments.length) {
// res.json(body, status) backwards compat
if ('number' == typeof arguments[1]) {
this.statusCode = arguments[1];
} else {
this.statusCode = obj;
obj = arguments[1];
}
}
// settings
var app = this.app;
var replacer = app.get('json replacer');
var spaces = app.get('json spaces');
var body = JSON.stringify(obj, replacer, spaces)
.replace(/\u2028/g, '\\u2028')
.replace(/\u2029/g, '\\u2029');
var callback = this.req.query[app.get('jsonp callback name')];
// content-type
this.charset = this.charset || 'utf-8';
this.set('Content-Type', 'application/json');
// jsonp
if (callback) {
if (Array.isArray(callback)) callback = callback[0];
this.set('Content-Type', 'text/javascript');
var cb = callback.replace(/[^\[\]\w$.]/g, '');
body = 'typeof ' + cb + ' === \'function\' && ' + cb + '(' + body + ');';
}
return this.send(body);
};
/**
* Transfer the file at the given `path`.
*
* Automatically sets the _Content-Type_ response header field.
* The callback `fn(err)` is invoked when the transfer is complete
* or when an error occurs. Be sure to check `res.sentHeader`
* if you wish to attempt responding, as the header and some data
* may have already been transferred.
*
* Options:
*
* - `maxAge` defaulting to 0
* - `root` root directory for relative filenames
*
* Examples:
*
* The following example illustrates how `res.sendfile()` may
* be used as an alternative for the `static()` middleware for
* dynamic situations. The code backing `res.sendfile()` is actually
* the same code, so HTTP cache support etc is identical.
*
* app.get('/user/:uid/photos/:file', function(req, res){
* var uid = req.params.uid
* , file = req.params.file;
*
* req.user.mayViewFilesFrom(uid, function(yes){
* if (yes) {
* res.sendfile('/uploads/' + uid + '/' + file);
* } else {
* res.send(403, 'Sorry! you cant see that.');
* }
* });
* });
*
* @param {String} path
* @param {Object|Function} options or fn
* @param {Function} fn
* @api public
*/
res.sendfile = function(path, options, fn){
var self = this
, req = self.req
, next = this.req.next
, options = options || {}
, done;
// support function as second arg
if ('function' == typeof options) {
fn = options;
options = {};
}
// socket errors
req.socket.on('error', error);
// errors
function error(err) {
if (done) return;
done = true;
// clean up
cleanup();
if (!self.headerSent) self.removeHeader('Content-Disposition');
// callback available
if (fn) return fn(err);
// list in limbo if there's no callback
if (self.headerSent) return;
// delegate
next(err);
}
// streaming
function stream(stream) {
if (done) return;
cleanup();
if (fn) stream.on('end', fn);
}
// cleanup
function cleanup() {
req.socket.removeListener('error', error);
}
// transfer
var file = send(req, path);
if (options.root) file.root(options.root);
file.maxage(options.maxAge || 0);
file.on('error', error);
file.on('directory', next);
file.on('stream', stream);
file.pipe(this);
this.on('finish', cleanup);
};
/**
* Transfer the file at the given `path` as an attachment.
*
* Optionally providing an alternate attachment `filename`,
* and optional callback `fn(err)`. The callback is invoked
* when the data transfer is complete, or when an error has
* ocurred. Be sure to check `res.headerSent` if you plan to respond.
*
* This method uses `res.sendfile()`.
*
* @param {String} path
* @param {String|Function} filename or fn
* @param {Function} fn
* @api public
*/
res.download = function(path, filename, fn){
// support function as second arg
if ('function' == typeof filename) {
fn = filename;
filename = null;
}
filename = filename || path;
this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"');
return this.sendfile(path, fn);
};
/**
* Set _Content-Type_ response header with `type` through `mime.lookup()`
* when it does not contain "/", or set the Content-Type to `type` otherwise.
*
* Examples:
*
* res.type('.html');
* res.type('html');
* res.type('json');
* res.type('application/json');
* res.type('png');
*
* @param {String} type
* @return {ServerResponse} for chaining
* @api public
*/
res.contentType =
res.type = function(type){
return this.set('Content-Type', ~type.indexOf('/')
? type
: mime.lookup(type));
};
/**
* Respond to the Acceptable formats using an `obj`
* of mime-type callbacks.
*
* This method uses `req.accepted`, an array of
* acceptable types ordered by their quality values.
* When "Accept" is not present the _first_ callback
* is invoked, otherwise the first match is used. When
* no match is performed the server responds with
* 406 "Not Acceptable".
*
* Content-Type is set for you, however if you choose
* you may alter this within the callback using `res.type()`
* or `res.set('Content-Type', ...)`.
*
* res.format({
* 'text/plain': function(){
* res.send('hey');
* },
*
* 'text/html': function(){
* res.send('<p>hey</p>');
* },
*
* 'appliation/json': function(){
* res.send({ message: 'hey' });
* }
* });
*
* In addition to canonicalized MIME types you may
* also use extnames mapped to these types:
*
* res.format({
* text: function(){
* res.send('hey');
* },
*
* html: function(){
* res.send('<p>hey</p>');
* },
*
* json: function(){
* res.send({ message: 'hey' });
* }
* });
*
* By default Express passes an `Error`
* with a `.status` of 406 to `next(err)`
* if a match is not made. If you provide
* a `.default` callback it will be invoked
* instead.
*
* @param {Object} obj
* @return {ServerResponse} for chaining
* @api public
*/
res.format = function(obj){
var req = this.req
, next = req.next;
var fn = obj.default;
if (fn) delete obj.default;
var keys = Object.keys(obj);
var key = req.accepts(keys);
this.vary("Accept");
if (key) {
var type = normalizeType(key).value;
var charset = mime.charsets.lookup(type);
if (charset) type += '; charset=' + charset;
this.set('Content-Type', type);
obj[key](req, this, next);
} else if (fn) {
fn();
} else {
var err = new Error('Not Acceptable');
err.status = 406;
err.types = normalizeTypes(keys).map(function(o){ return o.value });
next(err);
}
return this;
};
/**
* Set _Content-Disposition_ header to _attachment_ with optional `filename`.
*
* @param {String} filename
* @return {ServerResponse}
* @api public
*/
res.attachment = function(filename){
if (filename) this.type(extname(filename));
this.set('Content-Disposition', filename
? 'attachment; filename="' + basename(filename) + '"'
: 'attachment');
return this;
};
/**
* Set header `field` to `val`, or pass
* an object of header fields.
*
* Examples:
*
* res.set('Foo', ['bar', 'baz']);
* res.set('Accept', 'application/json');
* res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
*
* Aliased as `res.header()`.
*
* @param {String|Object|Array} field
* @param {String} val
* @return {ServerResponse} for chaining
* @api public
*/
res.set =
res.header = function(field, val){
if (2 == arguments.length) {
if (Array.isArray(val)) val = val.map(String);
else val = String(val);
this.setHeader(field, val);
} else {
for (var key in field) {
this.set(key, field[key]);
}
}
return this;
};
/**
* Get value for header `field`.
*
* @param {String} field
* @return {String}
* @api public
*/
res.get = function(field){
return this.getHeader(field);
};
/**
* Clear cookie `name`.
*
* @param {String} name
* @param {Object} options
* @param {ServerResponse} for chaining
* @api public
*/
res.clearCookie = function(name, options){
var opts = { expires: new Date(1), path: '/' };
return this.cookie(name, '', options
? utils.merge(opts, options)
: opts);
};
/**
* Set cookie `name` to `val`, with the given `options`.
*
* Options:
*
* - `maxAge` max-age in milliseconds, converted to `expires`
* - `signed` sign the cookie
* - `path` defaults to "/"
*
* Examples:
*
* // "Remember Me" for 15 minutes
* res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
*
* // save as above
* res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
*
* @param {String} name
* @param {String|Object} val
* @param {Options} options
* @api public
*/
res.cookie = function(name, val, options){
options = utils.merge({}, options);
var secret = this.req.secret;
var signed = options.signed;
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
if ('number' == typeof val) val = val.toString();
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
if (signed) val = 's:' + sign(val, secret);
if ('maxAge' in options) {
options.expires = new Date(Date.now() + options.maxAge);
options.maxAge /= 1000;
}
if (null == options.path) options.path = '/';
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
return this;
};
/**
* Set the location header to `url`.
*
* The given `url` can also be "back", which redirects
* to the _Referrer_ or _Referer_ headers or "/".
*
* Examples:
*
* res.location('/foo/bar').;
* res.location('http://example.com');
* res.location('../login'); // /blog/post/1 -> /blog/login
*
* Mounting:
*
* When an application is mounted and `res.location()`
* is given a path that does _not_ lead with "/" it becomes
* relative to the mount-point. For example if the application
* is mounted at "/blog", the following would become "/blog/login".
*
* res.location('login');
*
* While the leading slash would result in a location of "/login":
*
* res.location('/login');
*
* @param {String} url
* @api public
*/
res.location = function(url){
var app = this.app
, req = this.req
, path;
// "back" is an alias for the referrer
if ('back' == url) url = req.get('Referrer') || '/';
// relative
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
// relative to path
if ('.' == url[0]) {
path = req.originalUrl.split('?')[0];
path = path + ('/' == path[path.length - 1] ? '' : '/');
url = resolve(path, url);
// relative to mount-point
} else if ('/' != url[0]) {
path = app.path();
url = path + '/' + url;
}
}
// Respond
this.set('Location', url);
return this;
};
/**
* Redirect to the given `url` with optional response `status`
* defaulting to 302.
*
* The resulting `url` is determined by `res.location()`, so
* it will play nicely with mounted apps, relative paths,
* `"back"` etc.
*
* Examples:
*
* res.redirect('/foo/bar');
* res.redirect('http://example.com');
* res.redirect(301, 'http://example.com');
* res.redirect('http://example.com', 301);
* res.redirect('../login'); // /blog/post/1 -> /blog/login
*
* @param {String} url
* @param {Number} code
* @api public
*/
res.redirect = function(url){
var head = 'HEAD' == this.req.method
, status = 302
, body;
// allow status / url
if (2 == arguments.length) {
if ('number' == typeof url) {
status = url;
url = arguments[1];
} else {
status = arguments[1];
}
}
// Set location header
this.location(url);
url = this.get('Location');
// Support text/{plain,html} by default
this.format({
text: function(){
body = statusCodes[status] + '. Redirecting to ' + encodeURI(url);
},
html: function(){
var u = utils.escape(url);
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
},
default: function(){
body = '';
}
});
// Respond
this.statusCode = status;
this.set('Content-Length', Buffer.byteLength(body));
this.end(head ? null : body);
};
/**
* Add `field` to Vary. If already present in the Vary set, then
* this call is simply ignored.
*
* @param {Array|String} field
* @param {ServerResponse} for chaining
* @api public
*/
res.vary = function(field){
var self = this;
// nothing
if (!field) return this;
// array
if (Array.isArray(field)) {
field.forEach(function(field){
self.vary(field);
});
return;
}
var vary = this.get('Vary');
// append
if (vary) {
vary = vary.split(/ *, */);
if (!~vary.indexOf(field)) vary.push(field);
this.set('Vary', vary.join(', '));
return this;
}
// set
this.set('Vary', field);
return this;
};
/**
* Render `view` with the given `options` and optional callback `fn`.
* When a callback function is given a response will _not_ be made
* automatically, otherwise a response of _200_ and _text/html_ is given.
*
* Options:
*
* - `cache` boolean hinting to the engine it should cache
* - `filename` filename of the view being rendered
*
* @param {String} view
* @param {Object|Function} options or callback function
* @param {Function} fn
* @api public
*/
res.render = function(view, options, fn){
var self = this
, options = options || {}
, req = this.req
, app = req.app;
// support callback function as second arg
if ('function' == typeof options) {
fn = options, options = {};
}
// merge res.locals
options._locals = self.locals;
// default callback to respond
fn = fn || function(err, str){
if (err) return req.next(err);
self.send(str);
};
// render
app.render(view, options, fn);
};
/**
* Module dependencies.
*/
var Route = require('./route')
, utils = require('../utils')
, methods = require('methods')
, debug = require('debug')('express:router')
, parse = require('connect').utils.parseUrl;
/**
* Expose `Router` constructor.
*/
exports = module.exports = Router;
/**
* Initialize a new `Router` with the given `options`.
*
* @param {Object} options
* @api private
*/
function Router(options) {
options = options || {};
var self = this;
this.map = {};
this.params = {};
this._params = [];
this.caseSensitive = options.caseSensitive;
this.strict = options.strict;
this.middleware = function router(req, res, next){
self._dispatch(req, res, next);
};
}
/**
* Register a param callback `fn` for the given `name`.
*
* @param {String|Function} name
* @param {Function} fn
* @return {Router} for chaining
* @api public
*/
Router.prototype.param = function(name, fn){
// param logic
if ('function' == typeof name) {
this._params.push(name);
return;
}
// apply param functions
var params = this._params
, len = params.length
, ret;
for (var i = 0; i < len; ++i) {
if (ret = params[i](name, fn)) {
fn = ret;
}
}
// ensure we end up with a
// middleware function
if ('function' != typeof fn) {
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
(this.params[name] = this.params[name] || []).push(fn);
return this;
};
/**
* Route dispatcher aka the route "middleware".
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @param {Function} next
* @api private
*/
Router.prototype._dispatch = function(req, res, next){
var params = this.params
, self = this;
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
// route dispatch
(function pass(i, err){
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key;
// match next route
function nextRoute(err) {
pass(req._route_index + 1, err);
}
// match route
req.route = route = self.matchRequest(req, i);
// implied OPTIONS
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
// no route
if (!route) return next(err);
debug('matched %s %s', route.method, route.path);
// we have a route
// start at param 0
req.params = route.params;
keys = route.keys;
i = 0;
// param callbacks
function param(err) {
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if ('route' == err) {
nextRoute();
} else if (err) {
i = 0;
callbacks(err);
} else if (paramCallbacks && undefined !== paramVal) {
paramCallback();
} else if (key) {
param();
} else {
i = 0;
callbacks();
}
} catch (err) {
param(err);
}
};
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
// invoke route callbacks
function callbacks(err) {
var fn = route.callbacks[i++];
try {
if ('route' == err) {
nextRoute();
} else if (err && fn) {
if (fn.length < 4) return callbacks(err);
fn(err, req, res, callbacks);
} else if (fn) {
if (fn.length < 4) return fn(req, res, callbacks);
callbacks();
} else {
nextRoute(err);
}
} catch (err) {
callbacks(err);
}
}
})(0);
};
/**
* Respond to __OPTIONS__ method.
*
* @param {IncomingMessage} req
* @param {ServerResponse} res
* @api private
*/
Router.prototype._options = function(req, res){
var path = parse(req).pathname
, body = this._optionsFor(path).join(',');
res.set('Allow', body).send(body);
};
/**
* Return an array of HTTP verbs or "options" for `path`.
*
* @param {String} path
* @return {Array}
* @api private
*/
Router.prototype._optionsFor = function(path){
var self = this;
return methods.filter(function(method){
var routes = self.map[method];
if (!routes || 'options' == method) return;
for (var i = 0, len = routes.length; i < len; ++i) {
if (routes[i].match(path)) return true;
}
}).map(function(method){
return method.toUpperCase();
});
};
/**
* Attempt to match a route for `req`
* with optional starting index of `i`
* defaulting to 0.
*
* @param {IncomingMessage} req
* @param {Number} i
* @return {Route}
* @api private
*/
Router.prototype.matchRequest = function(req, i, head){
var method = req.method.toLowerCase()
, url = parse(req)
, path = url.pathname
, routes = this.map
, i = i || 0
, route;
// HEAD support
if (!head && 'head' == method) {
route = this.matchRequest(req, i, true);
if (route) return route;
method = 'get';
}
// routes for this method
if (routes = routes[method]) {
// matching routes
for (var len = routes.length; i < len; ++i) {
route = routes[i];
if (route.match(path)) {
req._route_index = i;
return route;
}
}
}
};
/**
* Attempt to match a route for `method`
* and `url` with optional starting
* index of `i` defaulting to 0.
*
* @param {String} method
* @param {String} url
* @param {Number} i
* @return {Route}
* @api private
*/
Router.prototype.match = function(method, url, i, head){
var req = { method: method, url: url };
return this.matchRequest(req, i, head);
};
/**
* Route `method`, `path`, and one or more callbacks.
*
* @param {String} method
* @param {String} path
* @param {Function} callback...
* @return {Router} for chaining
* @api private
*/
Router.prototype.route = function(method, path, callbacks){
var method = method.toLowerCase()
, callbacks = utils.flatten([].slice.call(arguments, 2));
// ensure path was given
if (!path) throw new Error('Router#' + method + '() requires a path');
// ensure all callbacks are functions
callbacks.forEach(function(fn){
if ('function' == typeof fn) return;
var type = {}.toString.call(fn);
var msg = '.' + method + '() requires callback functions but got a ' + type;
throw new Error(msg);
});
// create the route
debug('defined %s %s', method, path);
var route = new Route(method, path, callbacks, {
sensitive: this.caseSensitive,
strict: this.strict
});
// add it
(this.map[method] = this.map[method] || []).push(route);
return this;
};
Router.prototype.all = function(path) {
var self = this;
var args = [].slice.call(arguments);
methods.forEach(function(method){
self.route.apply(self, [method].concat(args));
});
return this;
};
methods.forEach(function(method){
Router.prototype[method] = function(path){
var args = [method].concat([].slice.call(arguments));
this.route.apply(this, args);
return this;
};
});
/**
* Module dependencies.
*/
var utils = require('../utils');
/**
* Expose `Route`.
*/
module.exports = Route;
/**
* Initialize `Route` with the given HTTP `method`, `path`,
* and an array of `callbacks` and `options`.
*
* Options:
*
* - `sensitive` enable case-sensitive routes
* - `strict` enable strict matching for trailing slashes
*
* @param {String} method
* @param {String} path
* @param {Array} callbacks
* @param {Object} options.
* @api private
*/
function Route(method, path, callbacks, options) {
options = options || {};
this.path = path;
this.method = method;
this.callbacks = callbacks;
this.regexp = utils.pathRegexp(path
, this.keys = []
, options.sensitive
, options.strict);
}
/**
* Check if this route matches `path`, if so
* populate `.params`.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
Route.prototype.match = function(path){
var keys = this.keys
, params = this.params = []
, m = this.regexp.exec(path);
if (!m) return false;
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
var val = 'string' == typeof m[i]
? utils.decode(m[i])
: m[i];
if (key) {
params[key.name] = val;
} else {
params.push(val);
}
}
return true;
};
/**
* Module dependencies.
*/
var mime = require('connect').mime
, crc32 = require('buffer-crc32');
/**
* toString ref.
*/
var toString = {}.toString;
/**
* Return ETag for `body`.
*
* @param {String|Buffer} body
* @return {String}
* @api private
*/
exports.etag = function(body){
return '"' + crc32.signed(body) + '"';
};
/**
* Make `locals()` bound to the given `obj`.
*
* This is used for `app.locals` and `res.locals`.
*
* @param {Object} obj
* @return {Function}
* @api private
*/
exports.locals = function(){
function locals(obj){
for (var key in obj) locals[key] = obj[key];
return obj;
};
return locals;
};
/**
* Check if `path` looks absolute.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
exports.isAbsolute = function(path){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
};
/**
* Flatten the given `arr`.
*
* @param {Array} arr
* @return {Array}
* @api private
*/
exports.flatten = function(arr, ret){
var ret = ret || []
, len = arr.length;
for (var i = 0; i < len; ++i) {
if (Array.isArray(arr[i])) {
exports.flatten(arr[i], ret);
} else {
ret.push(arr[i]);
}
}
return ret;
};
/**
* Normalize the given `type`, for example "html" becomes "text/html".
*
* @param {String} type
* @return {Object}
* @api private
*/
exports.normalizeType = function(type){
return ~type.indexOf('/')
? acceptParams(type)
: { value: mime.lookup(type), params: {} };
};
/**
* Normalize `types`, for example "html" becomes "text/html".
*
* @param {Array} types
* @return {Array}
* @api private
*/
exports.normalizeTypes = function(types){
var ret = [];
for (var i = 0; i < types.length; ++i) {
ret.push(exports.normalizeType(types[i]));
}
return ret;
};
/**
* Return the acceptable type in `types`, if any.
*
* @param {Array} types
* @param {String} str
* @return {String}
* @api private
*/
exports.acceptsArray = function(types, str){
// accept anything when Accept is not present
if (!str) return types[0];
// parse
var accepted = exports.parseAccept(str)
, normalized = exports.normalizeTypes(types)
, len = accepted.length;
for (var i = 0; i < len; ++i) {
for (var j = 0, jlen = types.length; j < jlen; ++j) {
if (exports.accept(normalized[j], accepted[i])) {
return types[j];
}
}
}
};
/**
* Check if `type(s)` are acceptable based on
* the given `str`.
*
* @param {String|Array} type(s)
* @param {String} str
* @return {Boolean|String}
* @api private
*/
exports.accepts = function(type, str){
if ('string' == typeof type) type = type.split(/ *, */);
return exports.acceptsArray(type, str);
};
/**
* Check if `type` array is acceptable for `other`.
*
* @param {Object} type
* @param {Object} other
* @return {Boolean}
* @api private
*/
exports.accept = function(type, other){
var t = type.value.split('/');
return (t[0] == other.type || '*' == other.type)
&& (t[1] == other.subtype || '*' == other.subtype)
&& paramsEqual(type.params, other.params);
};
/**
* Check if accept params are equal.
*
* @param {Object} a
* @param {Object} b
* @return {Boolean}
* @api private
*/
function paramsEqual(a, b){
return !Object.keys(a).some(function(k) {
return a[k] != b[k];
});
}
/**
* Parse accept `str`, returning
* an array objects containing
* `.type` and `.subtype` along
* with the values provided by
* `parseQuality()`.
*
* @param {Type} name
* @return {Type}
* @api private
*/
exports.parseAccept = function(str){
return exports
.parseParams(str)
.map(function(obj){
var parts = obj.value.split('/');
obj.type = parts[0];
obj.subtype = parts[1];
return obj;
});
};
/**
* Parse quality `str`, returning an
* array of objects with `.value`,
* `.quality` and optional `.params`
*
* @param {String} str
* @return {Array}
* @api private
*/
exports.parseParams = function(str){
return str
.split(/ *, */)
.map(acceptParams)
.filter(function(obj){
return obj.quality;
})
.sort(function(a, b){
if (a.quality === b.quality) {
return a.originalIndex - b.originalIndex;
} else {
return b.quality - a.quality;
}
});
};
/**
* Parse accept params `str` returning an
* object with `.value`, `.quality` and `.params`.
* also includes `.originalIndex` for stable sorting
*
* @param {String} str
* @return {Object}
* @api private
*/
function acceptParams(str, index) {
var parts = str.split(/ *; */);
var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index };
for (var i = 1; i < parts.length; ++i) {
var pms = parts[i].split(/ *= */);
if ('q' == pms[0]) {
ret.quality = parseFloat(pms[1]);
} else {
ret.params[pms[0]] = pms[1];
}
}
return ret;
}
/**
* Escape special characters in the given string of html.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html) {
return String(html)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
};
/**
* Normalize the given path string,
* returning a regular expression.
*
* An empty array should be passed,
* which will contain the placeholder
* key names. For example "/user/:id" will
* then contain ["id"].
*
* @param {String|RegExp|Array} path
* @param {Array} keys
* @param {Boolean} sensitive
* @param {Boolean} strict
* @return {RegExp}
* @api private
*/
exports.pathRegexp = function(path, keys, sensitive, strict) {
if (toString.call(path) == '[object RegExp]') return path;
if (Array.isArray(path)) path = '(' + path.join('|') + ')';
path = path
.concat(strict ? '' : '/?')
.replace(/\/\(/g, '(?:/')
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
keys.push({ name: key, optional: !! optional });
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '')
+ (star ? '(/*)?' : '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
/**
* Decodes a URI component. Returns
* the original string if the component
* is malformed.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.decode = function(str) {
try {
return decodeURIComponent(str);
} catch (e) {
return str;
}
}
/**
* Module dependencies.
*/
var path = require('path')
, fs = require('fs')
, utils = require('./utils')
, dirname = path.dirname
, basename = path.basename
, extname = path.extname
, exists = fs.existsSync || path.existsSync
, join = path.join;
/**
* Expose `View`.
*/
module.exports = View;
/**
* Initialize a new `View` with the given `name`.
*
* Options:
*
* - `defaultEngine` the default template engine name
* - `engines` template engine require() cache
* - `root` root path for view lookup
*
* @param {String} name
* @param {Object} options
* @api private
*/
function View(name, options) {
options = options || {};
this.name = name;
this.root = options.root;
var engines = options.engines;
this.defaultEngine = options.defaultEngine;
var ext = this.ext = extname(name);
if (!ext && !this.defaultEngine) throw new Error('No default engine was specified and no extension was provided.');
if (!ext) name += (ext = this.ext = ('.' != this.defaultEngine[0] ? '.' : '') + this.defaultEngine);
this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express);
this.path = this.lookup(name);
}
/**
* Lookup view by the given `path`
*
* @param {String} path
* @return {String}
* @api private
*/
View.prototype.lookup = function(path){
var ext = this.ext;
// <path>.<engine>
if (!utils.isAbsolute(path)) path = join(this.root, path);
if (exists(path)) return path;
// <path>/index.<engine>
path = join(dirname(path), basename(path, ext), 'index' + ext);
if (exists(path)) return path;
};
/**
* Render with the given `options` and callback `fn(err, str)`.
*
* @param {Object} options
* @param {Function} fn
* @api private
*/
View.prototype.render = function(options, fn){
this.engine(this.path, options, fn);
};
(The MIT License)
Copyright (c) 2009-2013 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
MOCHA_OPTS= --check-leaks
REPORTER = dot
check: test
test: test-unit test-acceptance
test-unit:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--globals setImmediate,clearImmediate \
$(MOCHA_OPTS)
test-acceptance:
@NODE_ENV=test ./node_modules/.bin/mocha \
--reporter $(REPORTER) \
--bail \
test/acceptance/*.js
test-cov: lib-cov
@EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
lib-cov:
@jscoverage lib lib-cov
benchmark:
@./support/bench
clean:
rm -f coverage.html
rm -fr lib-cov
.PHONY: test test-unit test-acceptance benchmark clean
language: node_js
node_js:
- 0.6
- 0.8
notifications:
email:
recipients:
- brianloveswords@gmail.com
var Buffer = require('buffer').Buffer;
var CRC_TABLE = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
0x2d02ef8d
];
function bufferizeInt(num) {
var tmp = Buffer(4);
tmp.writeInt32BE(num, 0);
return tmp;
}
function _crc32(buf, previous) {
if (!Buffer.isBuffer(buf)) {
buf = Buffer(buf);
}
if (Buffer.isBuffer(previous)) {
previous = previous.readUInt32BE(0);
}
var crc = ~~previous ^ -1;
for (var n = 0; n < buf.length; n++) {
crc = CRC_TABLE[(crc ^ buf[n]) & 0xff] ^ (crc >>> 8);
}
return (crc ^ -1);
}
function crc32() {
return bufferizeInt(_crc32.apply(null, arguments));
}
crc32.signed = function () {
return _crc32.apply(null, arguments);
};
crc32.unsigned = function () {
return _crc32.apply(null, arguments) >>> 0;
};
module.exports = crc32;
{
"author": {
"name": "Brian J. Brennan",
"email": "brianloveswords@gmail.com",
"url": "http://bjb.io"
},
"name": "buffer-crc32",
"description": "A pure javascript CRC32 algorithm that plays nice with binary data",
"version": "0.2.1",
"contributors": [
{
"name": "Vladimir Kuznetsov"
}
],
"homepage": "https://github.com/brianloveswords/buffer-crc32",
"repository": {
"type": "git",
"url": "git://github.com/brianloveswords/buffer-crc32.git"
},
"main": "index.js",
"scripts": {
"test": "./node_modules/.bin/tap tests/*.test.js"
},
"dependencies": {},
"devDependencies": {
"tap": "~0.2.5"
},
"optionalDependencies": {},
"engines": {
"node": "*"
},
"readme": "# buffer-crc32\n\n[![Build Status](https://secure.travis-ci.org/brianloveswords/buffer-crc32.png?branch=master)](http://travis-ci.org/brianloveswords/buffer-crc32)\n\ncrc32 that works with binary data and fancy character sets, outputs\nbuffer, signed or unsigned data and has tests.\n\nDerived from the sample CRC implementation in the PNG specification: http://www.w3.org/TR/PNG/#D-CRCAppendix\n\n# install\n```\nnpm install buffer-crc32\n```\n\n# example\n```js\nvar crc32 = require('buffer-crc32');\n// works with buffers\nvar buf = Buffer([0x00, 0x73, 0x75, 0x70, 0x20, 0x62, 0x72, 0x6f, 0x00])\ncrc32(buf) // -> <Buffer 94 5a ab 4a>\n\n// has convenience methods for getting signed or unsigned ints\ncrc32.signed(buf) // -> -1805997238\ncrc32.unsigned(buf) // -> 2488970058\n\n// will cast to buffer if given a string, so you can\n// directly use foreign characters safely\ncrc32('自動販売機') // -> <Buffer cb 03 1a c5>\n\n// and works in append mode too\nvar partialCrc = crc32('hey');\nvar partialCrc = crc32(' ', partialCrc);\nvar partialCrc = crc32('sup', partialCrc);\nvar partialCrc = crc32(' ', partialCrc);\nvar finalCrc = crc32('bros', partialCrc); // -> <Buffer 47 fa 55 70>\n```\n\n# tests\nThis was tested against the output of zlib's crc32 method. You can run\nthe tests with`npm test` (requires tap)\n\n# see also\nhttps://github.com/alexgorbatchev/node-crc, `crc.buffer.crc32` also\nsupports buffer inputs and return unsigned ints (thanks @tjholowaychuk).\n\n# license\nMIT/X11\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/brianloveswords/buffer-crc32/issues"
},
"_id": "buffer-crc32@0.2.1",
"dist": {
"shasum": "1959697b2a620453c7f875e99e2554231f7ebc05"
},
"_from": "buffer-crc32@0.2.1",
"_resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz"
}

buffer-crc32

Build Status

crc32 that works with binary data and fancy character sets, outputs buffer, signed or unsigned data and has tests.

Derived from the sample CRC implementation in the PNG specification: http://www.w3.org/TR/PNG/#D-CRCAppendix

install

npm install buffer-crc32

example

var crc32 = require('buffer-crc32');
// works with buffers
var buf = Buffer([0x00, 0x73, 0x75, 0x70, 0x20, 0x62, 0x72, 0x6f, 0x00])
crc32(buf) // -> <Buffer 94 5a ab 4a>

// has convenience methods for getting signed or unsigned ints
crc32.signed(buf) // -> -1805997238
crc32.unsigned(buf) // -> 2488970058

// will cast to buffer if given a string, so you can
// directly use foreign characters safely
crc32('自動販売機') // -> <Buffer cb 03 1a c5>

// and works in append mode too
var partialCrc = crc32('hey');
var partialCrc = crc32(' ', partialCrc);
var partialCrc = crc32('sup', partialCrc);
var partialCrc = crc32(' ', partialCrc);
var finalCrc = crc32('bros', partialCrc); // -> <Buffer 47 fa 55 70>

tests

This was tested against the output of zlib's crc32 method. You can run the tests withnpm test (requires tap)

see also

https://github.com/alexgorbatchev/node-crc, crc.buffer.crc32 also supports buffer inputs and return unsigned ints (thanks @tjholowaychuk).

license

MIT/X11

var crc32 = require('..');
var test = require('tap').test;
test('simple crc32 is no problem', function (t) {
var input = Buffer('hey sup bros');
var expected = Buffer([0x47, 0xfa, 0x55, 0x70]);
t.same(crc32(input), expected);
t.end();
});
test('another simple one', function (t) {
var input = Buffer('IEND');
var expected = Buffer([0xae, 0x42, 0x60, 0x82]);
t.same(crc32(input), expected);
t.end();
});
test('slightly more complex', function (t) {
var input = Buffer([0x00, 0x00, 0x00]);
var expected = Buffer([0xff, 0x41, 0xd9, 0x12]);
t.same(crc32(input), expected);
t.end();
});
test('complex crc32 gets calculated like a champ', function (t) {
var input = Buffer('शीर्षक');
var expected = Buffer([0x17, 0xb8, 0xaf, 0xf1]);
t.same(crc32(input), expected);
t.end();
});
test('casts to buffer if necessary', function (t) {
var input = 'शीर्षक';
var expected = Buffer([0x17, 0xb8, 0xaf, 0xf1]);
t.same(crc32(input), expected);
t.end();
});
test('can do signed', function (t) {
var input = 'ham sandwich';
var expected = -1891873021;
t.same(crc32.signed(input), expected);
t.end();
});
test('can do unsigned', function (t) {
var input = 'bear sandwich';
var expected = 3711466352;
t.same(crc32.unsigned(input), expected);
t.end();
});
test('simple crc32 in append mode', function (t) {
var input = [Buffer('hey'), Buffer(' '), Buffer('sup'), Buffer(' '), Buffer('bros')];
var expected = Buffer([0x47, 0xfa, 0x55, 0x70]);
for (var crc = 0, i = 0; i < input.length; i++) {
crc = crc32(input[i], crc);
}
t.same(crc, expected);
t.end();
});
test('can do signed in append mode', function (t) {
var input1 = 'ham';
var input2 = ' ';
var input3 = 'sandwich';
var expected = -1891873021;
var crc = crc32.signed(input1);
crc = crc32.signed(input2, crc);
crc = crc32.signed(input3, crc);
t.same(crc, expected);
t.end();
});
test('can do unsigned in append mode', function (t) {
var input1 = 'bear san';
var input2 = 'dwich';
var expected = 3711466352;
var crc = crc32.unsigned(input1);
crc = crc32.unsigned(input2, crc);
t.same(crc, expected);
t.end();
});

1.3.2 / 2013-07-18

  • add support for sub-commands to co-exist with the original command

1.3.1 / 2013-07-18

  • add quick .runningCommand hack so you can opt-out of other logic when running a sub command

1.3.0 / 2013-07-09

  • add EACCES error handling
  • fix sub-command --help

1.2.0 / 2013-06-13

  • allow "-" hyphen as an option argument
  • support for RegExp coercion

1.1.1 / 2012-11-20

  • add more sub-command padding
  • fix .usage() when args are present. Closes #106

1.1.0 / 2012-11-16

  • add git-style executable subcommand support. Closes #94

1.0.5 / 2012-10-09

  • fix --name clobbering. Closes #92
  • fix examples/help. Closes #89

1.0.4 / 2012-09-03

  • add outputHelp() method.

1.0.3 / 2012-08-30

  • remove invalid .version() defaulting

1.0.2 / 2012-08-24

  • add --foo=bar support [arv]
  • fix password on node 0.8.8. Make backward compatible with 0.6 [focusaurus]

1.0.1 / 2012-08-03

  • fix issue #56
  • fix tty.setRawMode(mode) was moved to tty.ReadStream#setRawMode() (i.e. process.stdin.setRawMode())

1.0.0 / 2012-07-05

  • add support for optional option descriptions
  • add defaulting of .version() to package.json's version

0.6.1 / 2012-06-01

  • Added: append (yes or no) on confirmation
  • Added: allow node.js v0.7.x

0.6.0 / 2012-04-10

  • Added .prompt(obj, callback) support. Closes #49
  • Added default support to .choose(). Closes #41
  • Fixed the choice example

0.5.1 / 2011-12-20

  • Fixed password() for recent nodes. Closes #36

0.5.0 / 2011-12-04

  • Added sub-command option support [itay]

0.4.3 / 2011-12-04

  • Fixed custom help ordering. Closes #32

0.4.2 / 2011-11-24

  • Added travis support
  • Fixed: line-buffered input automatically trimmed. Closes #31

0.4.1 / 2011-11-18

  • Removed listening for "close" on --help

0.4.0 / 2011-11-15

  • Added support for --. Closes #24

0.3.3 / 2011-11-14

  • Fixed: wait for close event when writing help info [Jerry Hamlet]

0.3.2 / 2011-11-01

  • Fixed long flag definitions with values [felixge]

0.3.1 / 2011-10-31

  • Changed --version short flag to -V from -v
  • Changed .version() so it's configurable [felixge]

0.3.0 / 2011-10-31

  • Added support for long flags only. Closes #18

0.2.1 / 2011-10-24

  • "node": ">= 0.4.x < 0.7.0". Closes #20

0.2.0 / 2011-09-26

  • Allow for defaults that are not just boolean. Default peassignment only occurs for --no-*, optional, and required arguments. [Jim Isaacs]

0.1.0 / 2011-08-24

  • Added support for custom --help output

0.0.5 / 2011-08-18

  • Changed: when the user enters nothing prompt for password again
  • Fixed issue with passwords beginning with numbers [NuckChorris]

0.0.4 / 2011-08-15

  • Fixed Commander#args

0.0.3 / 2011-08-15

  • Added default option value support

0.0.2 / 2011-08-15

  • Added mask support to Command#password(str[, mask], fn)
  • Added Command#password(str, fn)

0.0.1 / 2010-01-03

  • Initial release
/*!
* commander
* Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, spawn = require('child_process').spawn
, keypress = require('keypress')
, fs = require('fs')
, exists = fs.existsSync
, path = require('path')
, tty = require('tty')
, dirname = path.dirname
, basename = path.basename;
/**
* Expose the root command.
*/
exports = module.exports = new Command;
/**
* Expose `Command`.
*/
exports.Command = Command;
/**
* Expose `Option`.
*/
exports.Option = Option;
/**
* Initialize a new `Option` with the given `flags` and `description`.
*
* @param {String} flags
* @param {String} description
* @api public
*/
function Option(flags, description) {
this.flags = flags;
this.required = ~flags.indexOf('<');
this.optional = ~flags.indexOf('[');
this.bool = !~flags.indexOf('-no-');
flags = flags.split(/[ ,|]+/);
if (flags.length > 1 && !/^[[<]/.test(flags[1])) this.short = flags.shift();
this.long = flags.shift();
this.description = description || '';
}
/**
* Return option name.
*
* @return {String}
* @api private
*/
Option.prototype.name = function(){
return this.long
.replace('--', '')
.replace('no-', '');
};
/**
* Check if `arg` matches the short or long flag.
*
* @param {String} arg
* @return {Boolean}
* @api private
*/
Option.prototype.is = function(arg){
return arg == this.short
|| arg == this.long;
};
/**
* Initialize a new `Command`.
*
* @param {String} name
* @api public
*/
function Command(name) {
this.commands = [];
this.options = [];
this._execs = [];
this._args = [];
this._name = name;
}
/**
* Inherit from `EventEmitter.prototype`.
*/
Command.prototype.__proto__ = EventEmitter.prototype;
/**
* Add command `name`.
*
* The `.action()` callback is invoked when the
* command `name` is specified via __ARGV__,
* and the remaining arguments are applied to the
* function for access.
*
* When the `name` is "*" an un-matched command
* will be passed as the first arg, followed by
* the rest of __ARGV__ remaining.
*
* Examples:
*
* program
* .version('0.0.1')
* .option('-C, --chdir <path>', 'change the working directory')
* .option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
* .option('-T, --no-tests', 'ignore test hook')
*
* program
* .command('setup')
* .description('run remote setup commands')
* .action(function(){
* console.log('setup');
* });
*
* program
* .command('exec <cmd>')
* .description('run the given remote command')
* .action(function(cmd){
* console.log('exec "%s"', cmd);
* });
*
* program
* .command('*')
* .description('deploy the given env')
* .action(function(env){
* console.log('deploying "%s"', env);
* });
*
* program.parse(process.argv);
*
* @param {String} name
* @param {String} [desc]
* @return {Command} the new command
* @api public
*/
Command.prototype.command = function(name, desc){
var args = name.split(/ +/);
var cmd = new Command(args.shift());
if (desc) cmd.description(desc);
if (desc) this.executables = true;
if (desc) this._execs[cmd._name] = true;
this.commands.push(cmd);
cmd.parseExpectedArgs(args);
cmd.parent = this;
if (desc) return this;
return cmd;
};
/**
* Add an implicit `help [cmd]` subcommand
* which invokes `--help` for the given command.
*
* @api private
*/
Command.prototype.addImplicitHelpCommand = function() {
this.command('help [cmd]', 'display help for [cmd]');
};
/**
* Parse expected `args`.
*
* For example `["[type]"]` becomes `[{ required: false, name: 'type' }]`.
*
* @param {Array} args
* @return {Command} for chaining
* @api public
*/
Command.prototype.parseExpectedArgs = function(args){
if (!args.length) return;
var self = this;
args.forEach(function(arg){
switch (arg[0]) {
case '<':
self._args.push({ required: true, name: arg.slice(1, -1) });
break;
case '[':
self._args.push({ required: false, name: arg.slice(1, -1) });
break;
}
});
return this;
};
/**
* Register callback `fn` for the command.
*
* Examples:
*
* program
* .command('help')
* .description('display verbose help')
* .action(function(){
* // output help here
* });
*
* @param {Function} fn
* @return {Command} for chaining
* @api public
*/
Command.prototype.action = function(fn){
var self = this;
this.parent.on(this._name, function(args, unknown){
// Parse any so-far unknown options
unknown = unknown || [];
var parsed = self.parseOptions(unknown);
// Output help if necessary
outputHelpIfNecessary(self, parsed.unknown);
// If there are still any unknown options, then we simply
// die, unless someone asked for help, in which case we give it
// to them, and then we die.
if (parsed.unknown.length > 0) {
self.unknownOption(parsed.unknown[0]);
}
// Leftover arguments need to be pushed back. Fixes issue #56
if (parsed.args.length) args = parsed.args.concat(args);
self._args.forEach(function(arg, i){
if (arg.required && null == args[i]) {
self.missingArgument(arg.name);
}
});
// Always append ourselves to the end of the arguments,
// to make sure we match the number of arguments the user
// expects
if (self._args.length) {
args[self._args.length] = self;
} else {
args.push(self);
}
fn.apply(this, args);
});
return this;
};
/**
* Define option with `flags`, `description` and optional
* coercion `fn`.
*
* The `flags` string should contain both the short and long flags,
* separated by comma, a pipe or space. The following are all valid
* all will output this way when `--help` is used.
*
* "-p, --pepper"
* "-p|--pepper"
* "-p --pepper"
*
* Examples:
*
* // simple boolean defaulting to false
* program.option('-p, --pepper', 'add pepper');
*
* --pepper
* program.pepper
* // => Boolean
*
* // simple boolean defaulting to false
* program.option('-C, --no-cheese', 'remove cheese');
*
* program.cheese
* // => true
*
* --no-cheese
* program.cheese
* // => true
*
* // required argument
* program.option('-C, --chdir <path>', 'change the working directory');
*
* --chdir /tmp
* program.chdir
* // => "/tmp"
*
* // optional argument
* program.option('-c, --cheese [type]', 'add cheese [marble]');
*
* @param {String} flags
* @param {String} description
* @param {Function|Mixed} fn or default
* @param {Mixed} defaultValue
* @return {Command} for chaining
* @api public
*/
Command.prototype.option = function(flags, description, fn, defaultValue){
var self = this
, option = new Option(flags, description)
, oname = option.name()
, name = camelcase(oname);
// default as 3rd arg
if ('function' != typeof fn) defaultValue = fn, fn = null;
// preassign default value only for --no-*, [optional], or <required>
if (false == option.bool || option.optional || option.required) {
// when --no-* we make sure default is true
if (false == option.bool) defaultValue = true;
// preassign only if we have a default
if (undefined !== defaultValue) self[name] = defaultValue;
}
// register the option
this.options.push(option);
// when it's passed assign the value
// and conditionally invoke the callback
this.on(oname, function(val){
// coercion
if (null != val && fn) val = fn(val);
// unassigned or bool
if ('boolean' == typeof self[name] || 'undefined' == typeof self[name]) {
// if no value, bool true, and we have a default, then use it!
if (null == val) {
self[name] = option.bool
? defaultValue || true
: false;
} else {
self[name] = val;
}
} else if (null !== val) {
// reassign
self[name] = val;
}
});
return this;
};
/**
* Parse `argv`, settings options and invoking commands when defined.
*
* @param {Array} argv
* @return {Command} for chaining
* @api public
*/
Command.prototype.parse = function(argv){
// implicit help
if (this.executables) this.addImplicitHelpCommand();
// store raw args
this.rawArgs = argv;
// guess name
this._name = this._name || basename(argv[1]);
// process argv
var parsed = this.parseOptions(this.normalize(argv.slice(2)));
var args = this.args = parsed.args;
var result = this.parseArgs(this.args, parsed.unknown);
// executable sub-commands
var name = result.args[0];
if (this._execs[name]) return this.executeSubCommand(argv, args, parsed.unknown);
return result;
};
/**
* Execute a sub-command executable.
*
* @param {Array} argv
* @param {Array} args
* @param {Array} unknown
* @api private
*/
Command.prototype.executeSubCommand = function(argv, args, unknown) {
args = args.concat(unknown);
if (!args.length) this.help();
if ('help' == args[0] && 1 == args.length) this.help();
// <cmd> --help
if ('help' == args[0]) {
args[0] = args[1];
args[1] = '--help';
}
// executable
var dir = dirname(argv[1]);
var bin = basename(argv[1]) + '-' + args[0];
// check for ./<bin> first
var local = path.join(dir, bin);
// run it
args = args.slice(1);
var proc = spawn(local, args, { stdio: 'inherit', customFds: [0, 1, 2] });
proc.on('error', function(err){
if (err.code == "ENOENT") {
console.error('\n %s(1) does not exist, try --help\n', bin);
} else if (err.code == "EACCES") {
console.error('\n %s(1) not executable. try chmod or run with root\n', bin);
}
});
this.runningCommand = proc;
};
/**
* Normalize `args`, splitting joined short flags. For example
* the arg "-abc" is equivalent to "-a -b -c".
* This also normalizes equal sign and splits "--abc=def" into "--abc def".
*
* @param {Array} args
* @return {Array}
* @api private
*/
Command.prototype.normalize = function(args){
var ret = []
, arg
, index;
for (var i = 0, len = args.length; i < len; ++i) {
arg = args[i];
if (arg.length > 1 && '-' == arg[0] && '-' != arg[1]) {
arg.slice(1).split('').forEach(function(c){
ret.push('-' + c);
});
} else if (/^--/.test(arg) && ~(index = arg.indexOf('='))) {
ret.push(arg.slice(0, index), arg.slice(index + 1));
} else {
ret.push(arg);
}
}
return ret;
};
/**
* Parse command `args`.
*
* When listener(s) are available those
* callbacks are invoked, otherwise the "*"
* event is emitted and those actions are invoked.
*
* @param {Array} args
* @return {Command} for chaining
* @api private
*/
Command.prototype.parseArgs = function(args, unknown){
var cmds = this.commands
, len = cmds.length
, name;
if (args.length) {
name = args[0];
if (this.listeners(name).length) {
this.emit(args.shift(), args, unknown);
} else {
this.emit('*', args);
}
} else {
outputHelpIfNecessary(this, unknown);
// If there were no args and we have unknown options,
// then they are extraneous and we need to error.
if (unknown.length > 0) {
this.unknownOption(unknown[0]);
}
}
return this;
};
/**
* Return an option matching `arg` if any.
*
* @param {String} arg
* @return {Option}
* @api private
*/
Command.prototype.optionFor = function(arg){
for (var i = 0, len = this.options.length; i < len; ++i) {
if (this.options[i].is(arg)) {
return this.options[i];
}
}
};
/**
* Parse options from `argv` returning `argv`
* void of these options.
*
* @param {Array} argv
* @return {Array}
* @api public
*/
Command.prototype.parseOptions = function(argv){
var args = []
, len = argv.length
, literal
, option
, arg;
var unknownOptions = [];
// parse options
for (var i = 0; i < len; ++i) {
arg = argv[i];
// literal args after --
if ('--' == arg) {
literal = true;
continue;
}
if (literal) {
args.push(arg);
continue;
}
// find matching Option
option = this.optionFor(arg);
// option is defined
if (option) {
// requires arg
if (option.required) {
arg = argv[++i];
if (null == arg) return this.optionMissingArgument(option);
if ('-' == arg[0] && '-' != arg) return this.optionMissingArgument(option, arg);
this.emit(option.name(), arg);
// optional arg
} else if (option.optional) {
arg = argv[i+1];
if (null == arg || ('-' == arg[0] && '-' != arg)) {
arg = null;
} else {
++i;
}
this.emit(option.name(), arg);
// bool
} else {
this.emit(option.name());
}
continue;
}
// looks like an option
if (arg.length > 1 && '-' == arg[0]) {
unknownOptions.push(arg);
// If the next argument looks like it might be
// an argument for this option, we pass it on.
// If it isn't, then it'll simply be ignored
if (argv[i+1] && '-' != argv[i+1][0]) {
unknownOptions.push(argv[++i]);
}
continue;
}
// arg
args.push(arg);
}
return { args: args, unknown: unknownOptions };
};
/**
* Argument `name` is missing.
*
* @param {String} name
* @api private
*/
Command.prototype.missingArgument = function(name){
console.error();
console.error(" error: missing required argument `%s'", name);
console.error();
process.exit(1);
};
/**
* `Option` is missing an argument, but received `flag` or nothing.
*
* @param {String} option
* @param {String} flag
* @api private
*/
Command.prototype.optionMissingArgument = function(option, flag){
console.error();
if (flag) {
console.error(" error: option `%s' argument missing, got `%s'", option.flags, flag);
} else {
console.error(" error: option `%s' argument missing", option.flags);
}
console.error();
process.exit(1);
};
/**
* Unknown option `flag`.
*
* @param {String} flag
* @api private
*/
Command.prototype.unknownOption = function(flag){
console.error();
console.error(" error: unknown option `%s'", flag);
console.error();
process.exit(1);
};
/**
* Set the program version to `str`.
*
* This method auto-registers the "-V, --version" flag
* which will print the version number when passed.
*
* @param {String} str
* @param {String} flags
* @return {Command} for chaining
* @api public
*/
Command.prototype.version = function(str, flags){
if (0 == arguments.length) return this._version;
this._version = str;
flags = flags || '-V, --version';
this.option(flags, 'output the version number');
this.on('version', function(){
console.log(str);
process.exit(0);
});
return this;
};
/**
* Set the description `str`.
*
* @param {String} str
* @return {String|Command}
* @api public
*/
Command.prototype.description = function(str){
if (0 == arguments.length) return this._description;
this._description = str;
return this;
};
/**
* Set / get the command usage `str`.
*
* @param {String} str
* @return {String|Command}
* @api public
*/
Command.prototype.usage = function(str){
var args = this._args.map(function(arg){
return arg.required
? '<' + arg.name + '>'
: '[' + arg.name + ']';
});
var usage = '[options'
+ (this.commands.length ? '] [command' : '')
+ ']'
+ (this._args.length ? ' ' + args : '');
if (0 == arguments.length) return this._usage || usage;
this._usage = str;
return this;
};
/**
* Return the largest option length.
*
* @return {Number}
* @api private
*/
Command.prototype.largestOptionLength = function(){
return this.options.reduce(function(max, option){
return Math.max(max, option.flags.length);
}, 0);
};
/**
* Return help for options.
*
* @return {String}
* @api private
*/
Command.prototype.optionHelp = function(){
var width = this.largestOptionLength();
// Prepend the help information
return [pad('-h, --help', width) + ' ' + 'output usage information']
.concat(this.options.map(function(option){
return pad(option.flags, width)
+ ' ' + option.description;
}))
.join('\n');
};
/**
* Return command help documentation.
*
* @return {String}
* @api private
*/
Command.prototype.commandHelp = function(){
if (!this.commands.length) return '';
return [
''
, ' Commands:'
, ''
, this.commands.map(function(cmd){
var args = cmd._args.map(function(arg){
return arg.required
? '<' + arg.name + '>'
: '[' + arg.name + ']';
}).join(' ');
return pad(cmd._name
+ (cmd.options.length
? ' [options]'
: '') + ' ' + args, 22)
+ (cmd.description()
? ' ' + cmd.description()
: '');
}).join('\n').replace(/^/gm, ' ')
, ''
].join('\n');
};
/**
* Return program help documentation.
*
* @return {String}
* @api private
*/
Command.prototype.helpInformation = function(){
return [
''
, ' Usage: ' + this._name + ' ' + this.usage()
, '' + this.commandHelp()
, ' Options:'
, ''
, '' + this.optionHelp().replace(/^/gm, ' ')
, ''
, ''
].join('\n');
};
/**
* Prompt for a `Number`.
*
* @param {String} str
* @param {Function} fn
* @api private
*/
Command.prototype.promptForNumber = function(str, fn){
var self = this;
this.promptSingleLine(str, function parseNumber(val){
val = Number(val);
if (isNaN(val)) return self.promptSingleLine(str + '(must be a number) ', parseNumber);
fn(val);
});
};
/**
* Prompt for a `Date`.
*
* @param {String} str
* @param {Function} fn
* @api private
*/
Command.prototype.promptForDate = function(str, fn){
var self = this;
this.promptSingleLine(str, function parseDate(val){
val = new Date(val);
if (isNaN(val.getTime())) return self.promptSingleLine(str + '(must be a date) ', parseDate);
fn(val);
});
};
/**
* Prompt for a `Regular Expression`.
*
* @param {String} str
* @param {Object} pattern regular expression object to test
* @param {Function} fn
* @api private
*/
Command.prototype.promptForRegexp = function(str, pattern, fn){
var self = this;
this.promptSingleLine(str, function parseRegexp(val){
if(!pattern.test(val)) return self.promptSingleLine(str + '(regular expression mismatch) ', parseRegexp);
fn(val);
});
};
/**
* Single-line prompt.
*
* @param {String} str
* @param {Function} fn
* @api private
*/
Command.prototype.promptSingleLine = function(str, fn){
// determine if the 2nd argument is a regular expression
if (arguments[1].global !== undefined && arguments[1].multiline !== undefined) {
return this.promptForRegexp(str, arguments[1], arguments[2]);
} else if ('function' == typeof arguments[2]) {
return this['promptFor' + (fn.name || fn)](str, arguments[2]);
}
process.stdout.write(str);
process.stdin.setEncoding('utf8');
process.stdin.once('data', function(val){
fn(val.trim());
}).resume();
};
/**
* Multi-line prompt.
*
* @param {String} str
* @param {Function} fn
* @api private
*/
Command.prototype.promptMultiLine = function(str, fn){
var buf = [];
console.log(str);
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(val){
if ('\n' == val || '\r\n' == val) {
process.stdin.removeAllListeners('data');
fn(buf.join('\n'));
} else {
buf.push(val.trimRight());
}
}).resume();
};
/**
* Prompt `str` and callback `fn(val)`
*
* Commander supports single-line and multi-line prompts.
* To issue a single-line prompt simply add white-space
* to the end of `str`, something like "name: ", whereas
* for a multi-line prompt omit this "description:".
*
*
* Examples:
*
* program.prompt('Username: ', function(name){
* console.log('hi %s', name);
* });
*
* program.prompt('Description:', function(desc){
* console.log('description was "%s"', desc.trim());
* });
*
* @param {String|Object} str
* @param {Function} fn
* @api public
*/
Command.prototype.prompt = function(str, fn){
var self = this;
if ('string' == typeof str) {
if (/ $/.test(str)) return this.promptSingleLine.apply(this, arguments);
this.promptMultiLine(str, fn);
} else {
var keys = Object.keys(str)
, obj = {};
function next() {
var key = keys.shift()
, label = str[key];
if (!key) return fn(obj);
self.prompt(label, function(val){
obj[key] = val;
next();
});
}
next();
}
};
/**
* Prompt for password with `str`, `mask` char and callback `fn(val)`.
*
* The mask string defaults to '', aka no output is
* written while typing, you may want to use "*" etc.
*
* Examples:
*
* program.password('Password: ', function(pass){
* console.log('got "%s"', pass);
* process.stdin.destroy();
* });
*
* program.password('Password: ', '*', function(pass){
* console.log('got "%s"', pass);
* process.stdin.destroy();
* });
*
* @param {String} str
* @param {String} mask
* @param {Function} fn
* @api public
*/
Command.prototype.password = function(str, mask, fn){
var self = this
, buf = '';
// default mask
if ('function' == typeof mask) {
fn = mask;
mask = '';
}
keypress(process.stdin);
function setRawMode(mode) {
if (process.stdin.setRawMode) {
process.stdin.setRawMode(mode);
} else {
tty.setRawMode(mode);
}
};
setRawMode(true);
process.stdout.write(str);
// keypress
process.stdin.on('keypress', function(c, key){
if (key && 'enter' == key.name) {
console.log();
process.stdin.pause();
process.stdin.removeAllListeners('keypress');
setRawMode(false);
if (!buf.trim().length) return self.password(str, mask, fn);
fn(buf);
return;
}
if (key && key.ctrl && 'c' == key.name) {
console.log('%s', buf);
process.exit();
}
process.stdout.write(mask);
buf += c;
}).resume();
};
/**
* Confirmation prompt with `str` and callback `fn(bool)`
*
* Examples:
*
* program.confirm('continue? ', function(ok){
* console.log(' got %j', ok);
* process.stdin.destroy();
* });
*
* @param {String} str
* @param {Function} fn
* @api public
*/
Command.prototype.confirm = function(str, fn, verbose){
var self = this;
this.prompt(str, function(ok){
if (!ok.trim()) {
if (!verbose) str += '(yes or no) ';
return self.confirm(str, fn, true);
}
fn(parseBool(ok));
});
};
/**
* Choice prompt with `list` of items and callback `fn(index, item)`
*
* Examples:
*
* var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];
*
* console.log('Choose the coolest pet:');
* program.choose(list, function(i){
* console.log('you chose %d "%s"', i, list[i]);
* process.stdin.destroy();
* });
*
* @param {Array} list
* @param {Number|Function} index or fn
* @param {Function} fn
* @api public
*/
Command.prototype.choose = function(list, index, fn){
var self = this
, hasDefault = 'number' == typeof index;
if (!hasDefault) {
fn = index;
index = null;
}
list.forEach(function(item, i){
if (hasDefault && i == index) {
console.log('* %d) %s', i + 1, item);
} else {
console.log(' %d) %s', i + 1, item);
}
});
function again() {
self.prompt(' : ', function(val){
val = parseInt(val, 10) - 1;
if (hasDefault && isNaN(val)) val = index;
if (null == list[val]) {
again();
} else {
fn(val, list[val]);
}
});
}
again();
};
/**
* Output help information for this command
*
* @api public
*/
Command.prototype.outputHelp = function(){
process.stdout.write(this.helpInformation());
this.emit('--help');
};
/**
* Output help information and exit.
*
* @api public
*/
Command.prototype.help = function(){
this.outputHelp();
process.exit();
};
/**
* Camel-case the given `flag`
*
* @param {String} flag
* @return {String}
* @api private
*/
function camelcase(flag) {
return flag.split('-').reduce(function(str, word){
return str + word[0].toUpperCase() + word.slice(1);
});
}
/**
* Parse a boolean `str`.
*
* @param {String} str
* @return {Boolean}
* @api private
*/
function parseBool(str) {
return /^y|yes|ok|true$/i.test(str);
}
/**
* Pad `str` to `width`.
*
* @param {String} str
* @param {Number} width
* @return {String}
* @api private
*/
function pad(str, width) {
var len = Math.max(0, width - str.length);
return str + Array(len + 1).join(' ');
}
/**
* Output help information if necessary
*
* @param {Command} command to output help for
* @param {Array} array of options to search for -h or --help
* @api private
*/
function outputHelpIfNecessary(cmd, options) {
options = options || [];
for (var i = 0; i < options.length; i++) {
if (options[i] == '--help' || options[i] == '-h') {
cmd.outputHelp();
process.exit(0);
}
}
}
/**
* This module offers the internal "keypress" functionality from node-core's
* `readline` module, for your own programs and modules to use.
*
* Usage:
*
* require('keypress')(process.stdin);
*
* process.stdin.on('keypress', function (ch, key) {
* console.log(ch, key);
* if (key.ctrl && key.name == 'c') {
* process.stdin.pause();
* }
* });
* proces.stdin.resume();
*/
var exports = module.exports = keypress;
exports.enableMouse = function (stream) {
stream.write('\x1b' +'[?1000h')
}
exports.disableMouse = function (stream) {
stream.write('\x1b' +'[?1000l')
}
/**
* accepts a readable Stream instance and makes it emit "keypress" events
*/
function keypress(stream) {
if (isEmittingKeypress(stream)) return;
stream._emitKeypress = true;
function onData(b) {
if (stream.listeners('keypress').length > 0) {
emitKey(stream, b);
} else {
// Nobody's watching anyway
stream.removeListener('data', onData);
stream.on('newListener', onNewListener);
}
}
function onNewListener(event) {
if (event == 'keypress') {
stream.on('data', onData);
stream.removeListener('newListener', onNewListener);
}
}
if (stream.listeners('keypress').length > 0) {
stream.on('data', onData);
} else {
stream.on('newListener', onNewListener);
}
}
/**
* Returns `true` if the stream is already emitting "keypress" events.
* `false` otherwise.
*/
function isEmittingKeypress(stream) {
var rtn = stream._emitKeypress;
if (!rtn) {
// hack: check for the v0.6.x "data" event
stream.listeners('data').forEach(function (l) {
if (l.name == 'onData' && /emitKey/.test(l.toString())) {
rtn = true;
stream._emitKeypress = true;
}
});
}
if (!rtn) {
// hack: check for the v0.6.x "newListener" event
stream.listeners('newListener').forEach(function (l) {
if (l.name == 'onNewListener' && /keypress/.test(l.toString())) {
rtn = true;
stream._emitKeypress = true;
}
});
}
return rtn;
}
/*
Some patterns seen in terminal key escape codes, derived from combos seen
at http://www.midnight-commander.org/browser/lib/tty/key.c
ESC letter
ESC [ letter
ESC [ modifier letter
ESC [ 1 ; modifier letter
ESC [ num char
ESC [ num ; modifier char
ESC O letter
ESC O modifier letter
ESC O 1 ; modifier letter
ESC N letter
ESC [ [ num ; modifier char
ESC [ [ 1 ; modifier letter
ESC ESC [ num char
ESC ESC O letter
- char is usually ~ but $ and ^ also happen with rxvt
- modifier is 1 +
(shift * 1) +
(left_alt * 2) +
(ctrl * 4) +
(right_alt * 8)
- two leading ESCs apparently mean the same as one leading ESC
*/
// Regexes used for ansi escape code splitting
var metaKeyCodeRe = /^(?:\x1b)([a-zA-Z0-9])$/;
var functionKeyCodeRe =
/^(?:\x1b+)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/;
function emitKey(stream, s) {
var ch,
key = {
name: undefined,
ctrl: false,
meta: false,
shift: false
},
parts;
if (Buffer.isBuffer(s)) {
if (s[0] > 127 && s[1] === undefined) {
s[0] -= 128;
s = '\x1b' + s.toString(stream.encoding || 'utf-8');
} else {
s = s.toString(stream.encoding || 'utf-8');
}
}
key.sequence = s;
if (s === '\r' || s === '\n') {
// enter
key.name = 'enter';
} else if (s === '\t') {
// tab
key.name = 'tab';
} else if (s === '\b' || s === '\x7f' ||
s === '\x1b\x7f' || s === '\x1b\b') {
// backspace or ctrl+h
key.name = 'backspace';
key.meta = (s.charAt(0) === '\x1b');
} else if (s === '\x1b' || s === '\x1b\x1b') {
// escape key
key.name = 'escape';
key.meta = (s.length === 2);
} else if (s === ' ' || s === '\x1b ') {
key.name = 'space';
key.meta = (s.length === 2);
} else if (s <= '\x1a') {
// ctrl+letter
key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1);
key.ctrl = true;
} else if (s.length === 1 && s >= 'a' && s <= 'z') {
// lowercase letter
key.name = s;
} else if (s.length === 1 && s >= 'A' && s <= 'Z') {
// shift+letter
key.name = s.toLowerCase();
key.shift = true;
} else if (parts = metaKeyCodeRe.exec(s)) {
// meta+character key
key.name = parts[1].toLowerCase();
key.meta = true;
key.shift = /^[A-Z]$/.test(parts[1]);
} else if (parts = functionKeyCodeRe.exec(s)) {
// ansi escape sequence
// reassemble the key code leaving out leading \x1b's,
// the modifier key bitflag and any meaningless "1;" sequence
var code = (parts[1] || '') + (parts[2] || '') +
(parts[4] || '') + (parts[6] || ''),
modifier = (parts[3] || parts[5] || 1) - 1;
// Parse the key modifier
key.ctrl = !!(modifier & 4);
key.meta = !!(modifier & 10);
key.shift = !!(modifier & 1);
key.code = code;
// Parse the key itself
switch (code) {
/* xterm/gnome ESC O letter */
case 'OP': key.name = 'f1'; break;
case 'OQ': key.name = 'f2'; break;
case 'OR': key.name = 'f3'; break;
case 'OS': key.name = 'f4'; break;
/* xterm/rxvt ESC [ number ~ */
case '[11~': key.name = 'f1'; break;
case '[12~': key.name = 'f2'; break;
case '[13~': key.name = 'f3'; break;
case '[14~': key.name = 'f4'; break;
/* from Cygwin and used in libuv */
case '[[A': key.name = 'f1'; break;
case '[[B': key.name = 'f2'; break;
case '[[C': key.name = 'f3'; break;
case '[[D': key.name = 'f4'; break;
case '[[E': key.name = 'f5'; break;
/* common */
case '[15~': key.name = 'f5'; break;
case '[17~': key.name = 'f6'; break;
case '[18~': key.name = 'f7'; break;
case '[19~': key.name = 'f8'; break;
case '[20~': key.name = 'f9'; break;
case '[21~': key.name = 'f10'; break;
case '[23~': key.name = 'f11'; break;
case '[24~': key.name = 'f12'; break;
/* xterm ESC [ letter */
case '[A': key.name = 'up'; break;
case '[B': key.name = 'down'; break;
case '[C': key.name = 'right'; break;
case '[D': key.name = 'left'; break;
case '[E': key.name = 'clear'; break;
case '[F': key.name = 'end'; break;
case '[H': key.name = 'home'; break;
/* xterm/gnome ESC O letter */
case 'OA': key.name = 'up'; break;
case 'OB': key.name = 'down'; break;
case 'OC': key.name = 'right'; break;
case 'OD': key.name = 'left'; break;
case 'OE': key.name = 'clear'; break;
case 'OF': key.name = 'end'; break;
case 'OH': key.name = 'home'; break;
/* xterm/rxvt ESC [ number ~ */
case '[1~': key.name = 'home'; break;
case '[2~': key.name = 'insert'; break;
case '[3~': key.name = 'delete'; break;
case '[4~': key.name = 'end'; break;
case '[5~': key.name = 'pageup'; break;
case '[6~': key.name = 'pagedown'; break;
/* putty */
case '[[5~': key.name = 'pageup'; break;
case '[[6~': key.name = 'pagedown'; break;
/* rxvt */
case '[7~': key.name = 'home'; break;
case '[8~': key.name = 'end'; break;
/* rxvt keys with modifiers */
case '[a': key.name = 'up'; key.shift = true; break;
case '[b': key.name = 'down'; key.shift = true; break;
case '[c': key.name = 'right'; key.shift = true; break;
case '[d': key.name = 'left'; key.shift = true; break;
case '[e': key.name = 'clear'; key.shift = true; break;
case '[2$': key.name = 'insert'; key.shift = true; break;
case '[3$': key.name = 'delete'; key.shift = true; break;
case '[5$': key.name = 'pageup'; key.shift = true; break;
case '[6$': key.name = 'pagedown'; key.shift = true; break;
case '[7$': key.name = 'home'; key.shift = true; break;
case '[8$': key.name = 'end'; key.shift = true; break;
case 'Oa': key.name = 'up'; key.ctrl = true; break;
case 'Ob': key.name = 'down'; key.ctrl = true; break;
case 'Oc': key.name = 'right'; key.ctrl = true; break;
case 'Od': key.name = 'left'; key.ctrl = true; break;
case 'Oe': key.name = 'clear'; key.ctrl = true; break;
case '[2^': key.name = 'insert'; key.ctrl = true; break;
case '[3^': key.name = 'delete'; key.ctrl = true; break;
case '[5^': key.name = 'pageup'; key.ctrl = true; break;
case '[6^': key.name = 'pagedown'; key.ctrl = true; break;
case '[7^': key.name = 'home'; key.ctrl = true; break;
case '[8^': key.name = 'end'; key.ctrl = true; break;
/* misc. */
case '[Z': key.name = 'tab'; key.shift = true; break;
default: key.name = 'undefined'; break;
}
} else if (s.length > 1 && s[0] !== '\x1b') {
// Got a longer-than-one string of characters.
// Probably a paste, since it wasn't a control sequence.
Array.prototype.forEach.call(s, function(c) {
emitKey(stream, c);
});
return;
}
if (key.code == '[M') {
key.name = 'mouse';
var s = key.sequence;
var b = s.charCodeAt(3);
key.x = s.charCodeAt(4) - 040;
key.y = s.charCodeAt(5) - 040;
key.scroll = 0;
key.ctrl = !!(1<<4 & b);
key.meta = !!(1<<3 & b);
key.shift = !!(1<<2 & b);
key.release = (3 & b) === 3;
if (1<<6 & b) { //scroll
key.scroll = 1 & b ? 1 : -1;
}
if (!key.release && !key.scroll) {
key.button = b & 3;
}
}
// Don't emit a key if no name was found
if (key.name === undefined) {
key = undefined;
}
if (s.length === 1) {
ch = s;
}
if (key && key.name == 'mouse') {
stream.emit('mousepress', key)
} else if (key || ch) {
stream.emit('keypress', ch, key);
}
}
{
"name": "keypress",
"version": "0.1.0",
"description": "Make any Node ReadableStream emit \"keypress\" events",
"author": {
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net",
"url": "http://tootallnate.net"
},
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git://github.com/TooTallNate/keypress.git"
},
"keywords": [
"keypress",
"readline",
"core"
],
"license": "MIT",
"readme": "keypress\n========\n### Make any Node ReadableStream emit \"keypress\" events\n\n\nPrevious to Node `v0.8.x`, there was an undocumented `\"keypress\"` event that\n`process.stdin` would emit when it was a TTY. Some people discovered this hidden\ngem, and started using it in their own code.\n\nNow in Node `v0.8.x`, this `\"keypress\"` event does not get emitted by default,\nbut rather only when it is being used in conjuction with the `readline` (or by\nextension, the `repl`) module.\n\nThis module is the exact logic from the node `v0.8.x` releases ripped out into its\nown module.\n\n__Bonus:__ Now with mouse support!\n\nInstallation\n------------\n\nInstall with `npm`:\n\n``` bash\n$ npm install keypress\n```\n\nOr add it to the `\"dependencies\"` section of your _package.json_ file.\n\n\nExample\n-------\n\n#### Listening for \"keypress\" events\n\n``` js\nvar keypress = require('keypress');\n\n// make `process.stdin` begin emitting \"keypress\" events\nkeypress(process.stdin);\n\n// listen for the \"keypress\" event\nprocess.stdin.on('keypress', function (ch, key) {\n console.log('got \"keypress\"', key);\n if (key && key.ctrl && key.name == 'c') {\n process.stdin.pause();\n }\n});\n\nprocess.stdin.setRawMode(true);\nprocess.stdin.resume();\n```\n\n#### Listening for \"mousepress\" events\n\n``` js\nvar keypress = require('keypress');\n\n// make `process.stdin` begin emitting \"mousepress\" (and \"keypress\") events\nkeypress(process.stdin);\n\n// you must enable the mouse events before they will begin firing\nkeypress.enableMouse(process.stdout);\n\nprocess.stdin.on('mousepress', function (info) {\n console.log('got \"mousepress\" event at %d x %d', info.x, info.y);\n});\n\nprocess.on('exit', function () {\n // disable mouse on exit, so that the state\n // is back to normal for the terminal\n keypress.disableMouse(process.stdout);\n});\n```\n\n\nLicense\n-------\n\n(The MIT License)\n\nCopyright (c) 2012 Nathan Rajlich &lt;nathan@tootallnate.net&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/TooTallNate/keypress/issues"
},
"homepage": "https://github.com/TooTallNate/keypress",
"_id": "keypress@0.1.0",
"dist": {
"shasum": "a132efaa2333b26b2c4839471a484c56b0f1c9fd"
},
"_from": "keypress@0.1.x",
"_resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz"
}

keypress

Make any Node ReadableStream emit "keypress" events

Previous to Node v0.8.x, there was an undocumented "keypress" event that process.stdin would emit when it was a TTY. Some people discovered this hidden gem, and started using it in their own code.

Now in Node v0.8.x, this "keypress" event does not get emitted by default, but rather only when it is being used in conjuction with the readline (or by extension, the repl) module.

This module is the exact logic from the node v0.8.x releases ripped out into its own module.

Bonus: Now with mouse support!

Installation

Install with npm:

$ npm install keypress

Or add it to the "dependencies" section of your package.json file.

Example

Listening for "keypress" events

var keypress = require('keypress');

// make `process.stdin` begin emitting "keypress" events
keypress(process.stdin);

// listen for the "keypress" event
process.stdin.on('keypress', function (ch, key) {
  console.log('got "keypress"', key);
  if (key && key.ctrl && key.name == 'c') {
    process.stdin.pause();
  }
});

process.stdin.setRawMode(true);
process.stdin.resume();

Listening for "mousepress" events

var keypress = require('keypress');

// make `process.stdin` begin emitting "mousepress" (and "keypress") events
keypress(process.stdin);

// you must enable the mouse events before they will begin firing
keypress.enableMouse(process.stdout);

process.stdin.on('mousepress', function (info) {
  console.log('got "mousepress" event at %d x %d', info.x, info.y);
});

process.on('exit', function () {
  // disable mouse on exit, so that the state
  // is back to normal for the terminal
  keypress.disableMouse(process.stdout);
});

License

(The MIT License)

Copyright (c) 2012 Nathan Rajlich <nathan@tootallnate.net>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

var keypress = require('./')
keypress(process.stdin)
if (process.stdin.setRawMode)
process.stdin.setRawMode(true)
else
require('tty').setRawMode(true)
process.stdin.on('keypress', function (c, key) {
console.log(0, c, key)
if (key && key.ctrl && key.name == 'c') {
process.stdin.pause()
}
})
process.stdin.on('mousepress', function (mouse) {
console.log(mouse)
})
keypress.enableMouse(process.stdout)
process.on('exit', function () {
//disable mouse on exit, so that the state is back to normal
//for the terminal.
keypress.disableMouse(process.stdout)
})
process.stdin.resume()
{
"name": "commander",
"version": "1.3.2",
"description": "the complete solution for node.js command-line programs",
"keywords": [
"command",
"option",
"parser",
"prompt",
"stdin"
],
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
"repository": {
"type": "git",
"url": "https://github.com/visionmedia/commander.js.git"
},
"dependencies": {
"keypress": "0.1.x"
},
"devDependencies": {
"should": ">= 0.0.1"
},
"scripts": {
"test": "make test"
},
"main": "index",
"engines": {
"node": ">= 0.6.x"
},
"readme": "# Commander.js\n\n The complete solution for [node.js](http://nodejs.org) command-line interfaces, inspired by Ruby's [commander](https://github.com/visionmedia/commander).\n\n [![Build Status](https://secure.travis-ci.org/visionmedia/commander.js.png)](http://travis-ci.org/visionmedia/commander.js)\n\n## Installation\n\n $ npm install commander\n\n## Option parsing\n\n Options with commander are defined with the `.option()` method, also serving as documentation for the options. The example below parses args and options from `process.argv`, leaving remaining args as the `program.args` array which were not consumed by options.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('commander');\n\nprogram\n .version('0.0.1')\n .option('-p, --peppers', 'Add peppers')\n .option('-P, --pineapple', 'Add pineapple')\n .option('-b, --bbq', 'Add bbq sauce')\n .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')\n .parse(process.argv);\n\nconsole.log('you ordered a pizza with:');\nif (program.peppers) console.log(' - peppers');\nif (program.pineapple) console.log(' - pineapple');\nif (program.bbq) console.log(' - bbq');\nconsole.log(' - %s cheese', program.cheese);\n```\n\n Short flags may be passed as a single arg, for example `-abc` is equivalent to `-a -b -c`. Multi-word options such as \"--template-engine\" are camel-cased, becoming `program.templateEngine` etc.\n\n## Automated --help\n\n The help information is auto-generated based on the information commander already knows about your program, so the following `--help` info is for free:\n\n``` \n $ ./examples/pizza --help\n\n Usage: pizza [options]\n\n Options:\n\n -V, --version output the version number\n -p, --peppers Add peppers\n -P, --pineapple Add pineapple\n -b, --bbq Add bbq sauce\n -c, --cheese <type> Add the specified type of cheese [marble]\n -h, --help output usage information\n\n```\n\n## Coercion\n\n```js\nfunction range(val) {\n return val.split('..').map(Number);\n}\n\nfunction list(val) {\n return val.split(',');\n}\n\nprogram\n .version('0.0.1')\n .usage('[options] <file ...>')\n .option('-i, --integer <n>', 'An integer argument', parseInt)\n .option('-f, --float <n>', 'A float argument', parseFloat)\n .option('-r, --range <a>..<b>', 'A range', range)\n .option('-l, --list <items>', 'A list', list)\n .option('-o, --optional [value]', 'An optional value')\n .parse(process.argv);\n\nconsole.log(' int: %j', program.integer);\nconsole.log(' float: %j', program.float);\nconsole.log(' optional: %j', program.optional);\nprogram.range = program.range || [];\nconsole.log(' range: %j..%j', program.range[0], program.range[1]);\nconsole.log(' list: %j', program.list);\nconsole.log(' args: %j', program.args);\n```\n\n## Custom help\n\n You can display arbitrary `-h, --help` information\n by listening for \"--help\". Commander will automatically\n exit once you are done so that the remainder of your program\n does not execute causing undesired behaviours, for example\n in the following executable \"stuff\" will not output when\n `--help` is used.\n\n```js\n#!/usr/bin/env node\n\n/**\n * Module dependencies.\n */\n\nvar program = require('../');\n\nfunction list(val) {\n return val.split(',').map(Number);\n}\n\nprogram\n .version('0.0.1')\n .option('-f, --foo', 'enable some foo')\n .option('-b, --bar', 'enable some bar')\n .option('-B, --baz', 'enable some baz');\n\n// must be before .parse() since\n// node's emit() is immediate\n\nprogram.on('--help', function(){\n console.log(' Examples:');\n console.log('');\n console.log(' $ custom-help --help');\n console.log(' $ custom-help -h');\n console.log('');\n});\n\nprogram.parse(process.argv);\n\nconsole.log('stuff');\n```\n\nyielding the following help output:\n\n```\n\nUsage: custom-help [options]\n\nOptions:\n\n -h, --help output usage information\n -V, --version output the version number\n -f, --foo enable some foo\n -b, --bar enable some bar\n -B, --baz enable some baz\n\nExamples:\n\n $ custom-help --help\n $ custom-help -h\n\n```\n\n## .prompt(msg, fn)\n\n Single-line prompt:\n\n```js\nprogram.prompt('name: ', function(name){\n console.log('hi %s', name);\n});\n```\n\n Multi-line prompt:\n\n```js\nprogram.prompt('description:', function(name){\n console.log('hi %s', name);\n});\n```\n\n Coercion:\n\n```js\nprogram.prompt('Age: ', Number, function(age){\n console.log('age: %j', age);\n});\n```\n\n```js\nprogram.prompt('Birthdate: ', Date, function(date){\n console.log('date: %s', date);\n});\n```\n\n```js\nprogram.prompt('Email: ', /^.+@.+\\..+$/, function(email){\n console.log('email: %j', email);\n});\n```\n\n## .password(msg[, mask], fn)\n\nPrompt for password without echoing:\n\n```js\nprogram.password('Password: ', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\nPrompt for password with mask char \"*\":\n\n```js\nprogram.password('Password: ', '*', function(pass){\n console.log('got \"%s\"', pass);\n process.stdin.destroy();\n});\n```\n\n## .confirm(msg, fn)\n\n Confirm with the given `msg`:\n\n```js\nprogram.confirm('continue? ', function(ok){\n console.log(' got %j', ok);\n});\n```\n\n## .choose(list, fn)\n\n Let the user choose from a `list`:\n\n```js\nvar list = ['tobi', 'loki', 'jane', 'manny', 'luna'];\n\nconsole.log('Choose the coolest pet:');\nprogram.choose(list, function(i){\n console.log('you chose %d \"%s\"', i, list[i]);\n});\n```\n\n## .outputHelp()\n\n Output help information without exiting.\n\n## .help()\n\n Output help information and exit immediately.\n\n## Links\n\n - [API documentation](http://visionmedia.github.com/commander.js/)\n - [ascii tables](https://github.com/LearnBoost/cli-table)\n - [progress bars](https://github.com/visionmedia/node-progress)\n - [more progress bars](https://github.com/substack/node-multimeter)\n - [examples](https://github.com/visionmedia/commander.js/tree/master/examples)\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2011 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "Readme.md",
"bugs": {
"url": "https://github.com/visionmedia/commander.js/issues"
},
"homepage": "https://github.com/visionmedia/commander.js",
"_id": "commander@1.3.2",
"dist": {
"shasum": "5bd29133e1cd4760b4727451bb196d63a859376d"
},
"_from": "commander@1.3.2",
"_resolved": "https://registry.npmjs.org/commander/-/commander-1.3.2.tgz"
}

Commander.js

The complete solution for node.js command-line interfaces, inspired by Ruby's commander.

Build Status

Installation

$ npm install commander

Option parsing

Options with commander are defined with the .option() method, also serving as documentation for the options. The example below parses args and options from process.argv, leaving remaining args as the program.args array which were not consumed by options.

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var program = require('commander');

program
  .version('0.0.1')
  .option('-p, --peppers', 'Add peppers')
  .option('-P, --pineapple', 'Add pineapple')
  .option('-b, --bbq', 'Add bbq sauce')
  .option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
  .parse(process.argv);

console.log('you ordered a pizza with:');
if (program.peppers) console.log('  - peppers');
if (program.pineapple) console.log('  - pineapple');
if (program.bbq) console.log('  - bbq');
console.log('  - %s cheese', program.cheese);

Short flags may be passed as a single arg, for example -abc is equivalent to -a -b -c. Multi-word options such as "--template-engine" are camel-cased, becoming program.templateEngine etc.

Automated --help

The help information is auto-generated based on the information commander already knows about your program, so the following --help info is for free:

 $ ./examples/pizza --help

   Usage: pizza [options]

   Options:

     -V, --version        output the version number
     -p, --peppers        Add peppers
     -P, --pineapple      Add pineapple
     -b, --bbq            Add bbq sauce
     -c, --cheese <type>  Add the specified type of cheese [marble]
     -h, --help           output usage information

Coercion

function range(val) {
  return val.split('..').map(Number);
}

function list(val) {
  return val.split(',');
}

program
  .version('0.0.1')
  .usage('[options] <file ...>')
  .option('-i, --integer <n>', 'An integer argument', parseInt)
  .option('-f, --float <n>', 'A float argument', parseFloat)
  .option('-r, --range <a>..<b>', 'A range', range)
  .option('-l, --list <items>', 'A list', list)
  .option('-o, --optional [value]', 'An optional value')
  .parse(process.argv);

console.log(' int: %j', program.integer);
console.log(' float: %j', program.float);
console.log(' optional: %j', program.optional);
program.range = program.range || [];
console.log(' range: %j..%j', program.range[0], program.range[1]);
console.log(' list: %j', program.list);
console.log(' args: %j', program.args);

Custom help

You can display arbitrary -h, --help information by listening for "--help". Commander will automatically exit once you are done so that the remainder of your program does not execute causing undesired behaviours, for example in the following executable "stuff" will not output when --help is used.

#!/usr/bin/env node

/**
 * Module dependencies.
 */

var program = require('../');

function list(val) {
  return val.split(',').map(Number);
}

program
  .version('0.0.1')
  .option('-f, --foo', 'enable some foo')
  .option('-b, --bar', 'enable some bar')
  .option('-B, --baz', 'enable some baz');

// must be before .parse() since
// node's emit() is immediate

program.on('--help', function(){
  console.log('  Examples:');
  console.log('');
  console.log('    $ custom-help --help');
  console.log('    $ custom-help -h');
  console.log('');
});

program.parse(process.argv);

console.log('stuff');

yielding the following help output:


Usage: custom-help [options]

Options:

  -h, --help     output usage information
  -V, --version  output the version number
  -f, --foo      enable some foo
  -b, --bar      enable some bar
  -B, --baz      enable some baz

Examples:

  $ custom-help --help
  $ custom-help -h

.prompt(msg, fn)

Single-line prompt:

program.prompt('name: ', function(name){
  console.log('hi %s', name);
});

Multi-line prompt:

program.prompt('description:', function(name){
  console.log('hi %s', name);
});

Coercion:

program.prompt('Age: ', Number, function(age){
  console.log('age: %j', age);
});
program.prompt('Birthdate: ', Date, function(date){
  console.log('date: %s', date);
});
program.prompt('Email: ', /^.+@.+\..+$/, function(email){
  console.log('email: %j', email);
});

.password(msg[, mask], fn)

Prompt for password without echoing:

program.password('Password: ', function(pass){
  console.log('got "%s"', pass);
  process.stdin.destroy();
});

Prompt for password with mask char "*":

program.password('Password: ', '*', function(pass){
  console.log('got "%s"', pass);
  process.stdin.destroy();
});

.confirm(msg, fn)

Confirm with the given msg:

program.confirm('continue? ', function(ok){
  console.log(' got %j', ok);
});

.choose(list, fn)

Let the user choose from a list:

var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];

console.log('Choose the coolest pet:');
program.choose(list, function(i){
  console.log('you chose %d "%s"', i, list[i]);
});

.outputHelp()

Output help information without exiting.

.help()

Output help information and exit immediately.

Links

License

(The MIT License)

Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*.markdown
*.md
.git*
Makefile
benchmarks/
docs/
examples/
install.sh
support/
test/
.DS_Store
coverage.html
module.exports = process.env.CONNECT_COV
? require('./lib-cov/connect')
: require('./lib/connect');
/*!
* Connect - Cache
* Copyright(c) 2011 Sencha Inc.
* MIT Licensed
*/
/**
* Expose `Cache`.
*/
module.exports = Cache;
/**
* LRU cache store.
*
* @param {Number} limit
* @api private
*/
function Cache(limit) {
this.store = {};
this.keys = [];
this.limit = limit;
}
/**
* Touch `key`, promoting the object.
*
* @param {String} key
* @param {Number} i
* @api private
*/
Cache.prototype.touch = function(key, i){
this.keys.splice(i,1);
this.keys.push(key);
};
/**
* Remove `key`.
*
* @param {String} key
* @api private
*/
Cache.prototype.remove = function(key){
delete this.store[key];
};
/**
* Get the object stored for `key`.
*
* @param {String} key
* @return {Array}
* @api private
*/
Cache.prototype.get = function(key){
return this.store[key];
};
/**
* Add a cache `key`.
*
* @param {String} key
* @return {Array}
* @api private
*/
Cache.prototype.add = function(key){
// initialize store
var len = this.keys.push(key);
// limit reached, invalidate LRU
if (len > this.limit) this.remove(this.keys.shift());
var arr = this.store[key] = [];
arr.createdAt = new Date;
return arr;
};
/*!
* Connect
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, proto = require('./proto')
, utils = require('./utils')
, path = require('path')
, basename = path.basename
, fs = require('fs');
// node patches
require('./patch');
// expose createServer() as the module
exports = module.exports = createServer;
/**
* Framework version.
*/
exports.version = '2.7.11';
/**
* Expose mime module.
*/
exports.mime = require('./middleware/static').mime;
/**
* Expose the prototype.
*/
exports.proto = proto;
/**
* Auto-load middleware getters.
*/
exports.middleware = {};
/**
* Expose utilities.
*/
exports.utils = utils;
/**
* Create a new connect server.
*
* @return {Function}
* @api public
*/
function createServer() {
function app(req, res, next){ app.handle(req, res, next); }
utils.merge(app, proto);
utils.merge(app, EventEmitter.prototype);
app.route = '/';
app.stack = [];
for (var i = 0; i < arguments.length; ++i) {
app.use(arguments[i]);
}
return app;
};
/**
* Support old `.createServer()` method.
*/
createServer.createServer = createServer;
/**
* Auto-load bundled middleware with getters.
*/
fs.readdirSync(__dirname + '/middleware').forEach(function(filename){
if (!/\.js$/.test(filename)) return;
var name = basename(filename, '.js');
function load(){ return require('./middleware/' + name); }
exports.middleware.__defineGetter__(name, load);
exports.__defineGetter__(name, load);
});
/**
* Connect is a middleware framework for node,
* shipping with over 18 bundled middleware and a rich selection of
* 3rd-party middleware.
*
* var app = connect()
* .use(connect.logger('dev'))
* .use(connect.static('public'))
* .use(function(req, res){
* res.end('hello world\n');
* })
*
* http.createServer(app).listen(3000);
*
* Installation:
*
* $ npm install connect
*
* Middleware:
*
* - [logger](logger.html) request logger with custom format support
* - [csrf](csrf.html) Cross-site request forgery protection
* - [compress](compress.html) Gzip compression middleware
* - [basicAuth](basicAuth.html) basic http authentication
* - [bodyParser](bodyParser.html) extensible request body parser
* - [json](json.html) application/json parser
* - [urlencoded](urlencoded.html) application/x-www-form-urlencoded parser
* - [multipart](multipart.html) multipart/form-data parser
* - [timeout](timeout.html) request timeouts
* - [cookieParser](cookieParser.html) cookie parser
* - [session](session.html) session management support with bundled MemoryStore
* - [cookieSession](cookieSession.html) cookie-based session support
* - [methodOverride](methodOverride.html) faux HTTP method support
* - [responseTime](responseTime.html) calculates response-time and exposes via X-Response-Time
* - [staticCache](staticCache.html) memory cache layer for the static() middleware
* - [static](static.html) streaming static file server supporting `Range` and more
* - [directory](directory.html) directory listing middleware
* - [vhost](vhost.html) virtual host sub-domain mapping middleware
* - [favicon](favicon.html) efficient favicon server (with default icon)
* - [limit](limit.html) limit the bytesize of request bodies
* - [query](query.html) automatic querystring parser, populating `req.query`
* - [errorHandler](errorHandler.html) flexible error handler
*
* Links:
*
* - list of [3rd-party](https://github.com/senchalabs/connect/wiki) middleware
* - GitHub [repository](http://github.com/senchalabs/connect)
*
*/
/*!
* Connect - basicAuth
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils')
, unauthorized = utils.unauthorized;
/**
* Basic Auth:
*
* Status: Deprecated. No bug reports or pull requests are welcomed
* for this middleware. However, this middleware will not be removed.
* Instead, you should use [basic-auth](https://github.com/visionmedia/node-basic-auth).
*
* Enfore basic authentication by providing a `callback(user, pass)`,
* which must return `true` in order to gain access. Alternatively an async
* method is provided as well, invoking `callback(user, pass, callback)`. Populates
* `req.user`. The final alternative is simply passing username / password
* strings.
*
* Simple username and password
*
* connect(connect.basicAuth('username', 'password'));
*
* Callback verification
*
* connect()
* .use(connect.basicAuth(function(user, pass){
* return 'tj' == user && 'wahoo' == pass;
* }))
*
* Async callback verification, accepting `fn(err, user)`.
*
* connect()
* .use(connect.basicAuth(function(user, pass, fn){
* User.authenticate({ user: user, pass: pass }, fn);
* }))
*
* @param {Function|String} callback or username
* @param {String} realm
* @api public
*/
module.exports = function basicAuth(callback, realm) {
var username, password;
// user / pass strings
if ('string' == typeof callback) {
username = callback;
password = realm;
if ('string' != typeof password) throw new Error('password argument required');
realm = arguments[2];
callback = function(user, pass){
return user == username && pass == password;
}
}
realm = realm || 'Authorization Required';
return function(req, res, next) {
var authorization = req.headers.authorization;
if (req.user) return next();
if (!authorization) return unauthorized(res, realm);
var parts = authorization.split(' ');
if (parts.length !== 2) return next(utils.error(400));
var scheme = parts[0]
, credentials = new Buffer(parts[1], 'base64').toString()
, index = credentials.indexOf(':');
if ('Basic' != scheme || index < 0) return next(utils.error(400));
var user = credentials.slice(0, index)
, pass = credentials.slice(index + 1);
// async
if (callback.length >= 3) {
var pause = utils.pause(req);
callback(user, pass, function(err, user){
if (err || !user) return unauthorized(res, realm);
req.user = req.remoteUser = user;
next();
pause.resume();
});
// sync
} else {
if (callback(user, pass)) {
req.user = req.remoteUser = user;
next();
} else {
unauthorized(res, realm);
}
}
}
};
/*!
* Connect - bodyParser
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var multipart = require('./multipart')
, urlencoded = require('./urlencoded')
, json = require('./json');
/**
* Body parser:
*
* Status: the multipart body parser will be removed in Connect 3.
*
* Parse request bodies, supports _application/json_,
* _application/x-www-form-urlencoded_, and _multipart/form-data_.
*
* This is equivalent to:
*
* app.use(connect.json());
* app.use(connect.urlencoded());
* app.use(connect.multipart());
*
* Examples:
*
* connect()
* .use(connect.bodyParser())
* .use(function(req, res) {
* res.end('viewing user ' + req.body.user.name);
* });
*
* $ curl -d 'user[name]=tj' http://local/
* $ curl -d '{"user":{"name":"tj"}}' -H "Content-Type: application/json" http://local/
*
* View [json](json.html), [urlencoded](urlencoded.html), and [multipart](multipart.html) for more info.
*
* If you wish to create your own body parser, you may be interested in:
*
* - [raw-body](https://github.com/stream-utils/raw-body)
* - [body](https://github.com/raynos/body)
*
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function bodyParser(options){
var _urlencoded = urlencoded(options)
, _multipart = multipart(options)
, _json = json(options);
return function bodyParser(req, res, next) {
_json(req, res, function(err){
if (err) return next(err);
_urlencoded(req, res, function(err){
if (err) return next(err);
_multipart(req, res, next);
});
});
}
};
/*!
* Connect - compress
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var zlib = require('zlib');
var utils = require('../utils');
var Negotiator = require('negotiator');
/**
* Supported content-encoding methods.
*/
exports.methods = {
gzip: zlib.createGzip
, deflate: zlib.createDeflate
};
/**
* Default filter function.
*/
exports.filter = function(req, res){
return /json|text|javascript|dart|image\/svg\+xml|application\/x-font-ttf|application\/vnd\.ms-opentype|application\/vnd\.ms-fontobject/.test(res.getHeader('Content-Type'));
};
/**
* Compress:
*
* Compress response data with gzip/deflate.
*
* Filter:
*
* A `filter` callback function may be passed to
* replace the default logic of:
*
* exports.filter = function(req, res){
* return /json|text|javascript/.test(res.getHeader('Content-Type'));
* };
*
* Threshold:
*
* Only compress the response if the byte size is at or above a threshold.
* Always compress while streaming.
*
* - `threshold` - string representation of size or bytes as an integer.
*
* Options:
*
* All remaining options are passed to the gzip/deflate
* creation functions. Consult node's docs for additional details.
*
* - `chunkSize` (default: 16*1024)
* - `windowBits`
* - `level`: 0-9 where 0 is no compression, and 9 is slow but best compression
* - `memLevel`: 1-9 low is slower but uses less memory, high is fast but uses more
* - `strategy`: compression strategy
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function compress(options) {
options = options || {};
var filter = options.filter || exports.filter;
var threshold;
if (false === options.threshold || 0 === options.threshold) {
threshold = 0
} else if ('string' === typeof options.threshold) {
threshold = utils.parseBytes(options.threshold)
} else {
threshold = options.threshold || 1024
}
return function compress(req, res, next){
var accept = req.headers['accept-encoding']
, write = res.write
, end = res.end
, compress = true
, stream;
// see #724
req.on('close', function(){
res.write = res.end = function(){};
});
// flush is noop by default
res.flush = noop;
// proxy
res.write = function(chunk, encoding){
if (!this.headerSent) {
// if content-length is set and is lower
// than the threshold, don't compress
var length = res.getHeader('content-length');
if (!isNaN(length) && length < threshold) compress = false;
this._implicitHeader();
}
return stream
? stream.write(new Buffer(chunk, encoding))
: write.call(res, chunk, encoding);
};
res.end = function(chunk, encoding){
if (chunk) {
if (!this.headerSent && getSize(chunk) < threshold) compress = false;
this.write(chunk, encoding);
} else if (!this.headerSent) {
// response size === 0
compress = false;
}
return stream
? stream.end()
: end.call(res);
};
res.on('header', function(){
// default request filter
if (!filter(req, res)) return;
// vary
var vary = res.getHeader('Vary');
if (!vary) {
res.setHeader('Vary', 'Accept-Encoding');
} else if (!~vary.indexOf('Accept-Encoding')) {
res.setHeader('Vary', vary + ', Accept-Encoding');
}
if (!compress) return;
var encoding = res.getHeader('Content-Encoding') || 'identity';
// already encoded
if ('identity' != encoding) return;
// SHOULD use identity
if (!accept) return;
// head
if ('HEAD' == req.method) return;
// compression method
var method = new Negotiator(req).preferredEncoding(['gzip', 'deflate', 'identity']);
// negotiation failed
if (method === 'identity') return;
// compression stream
stream = exports.methods[method](options);
// overwrite the flush method
res.flush = function(){
stream.flush();
}
// header fields
res.setHeader('Content-Encoding', method);
res.removeHeader('Content-Length');
// compression
stream.on('data', function(chunk){
write.call(res, chunk);
});
stream.on('end', function(){
end.call(res);
});
stream.on('drain', function() {
res.emit('drain');
});
});
next();
};
};
function getSize(chunk) {
return Buffer.isBuffer(chunk)
? chunk.length
: Buffer.byteLength(chunk);
}
function noop(){}
/*!
* Connect - cookieParser
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('./../utils')
, cookie = require('cookie');
/**
* Cookie parser:
*
* Status: Deprecated. This middleware will be removed in
* Connect 3.0 and will be replaced by a `cookies` middleware,
* which will use [cookies](http://github.com/jed/cookies)
* and [keygrip](https://github.com/jed/keygrip).
*
* Parse _Cookie_ header and populate `req.cookies`
* with an object keyed by the cookie names. Optionally
* you may enabled signed cookie support by passing
* a `secret` string, which assigns `req.secret` so
* it may be used by other middleware.
*
* Examples:
*
* connect()
* .use(connect.cookieParser('optional secret string'))
* .use(function(req, res, next){
* res.end(JSON.stringify(req.cookies));
* })
*
* @param {String} secret
* @return {Function}
* @api public
*/
module.exports = function cookieParser(secret, opt){
return function cookieParser(req, res, next) {
if (req.cookies) return next();
var cookies = req.headers.cookie;
req.secret = secret;
req.cookies = {};
req.signedCookies = {};
if (cookies) {
try {
req.cookies = cookie.parse(cookies, opt);
if (secret) {
req.signedCookies = utils.parseSignedCookies(req.cookies, secret);
req.signedCookies = utils.parseJSONCookies(req.signedCookies);
}
req.cookies = utils.parseJSONCookies(req.cookies);
} catch (err) {
err.status = 400;
return next(err);
}
}
next();
};
};
/*!
* Connect - cookieSession
* Copyright(c) 2011 Sencha Inc.
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('./../utils')
, Cookie = require('./session/cookie')
, debug = require('debug')('connect:cookieSession')
, signature = require('cookie-signature')
, crc32 = require('buffer-crc32')
, url = require('url');
/**
* Cookie Session:
*
* Cookie session middleware.
*
* var app = connect();
* app.use(connect.cookieParser());
* app.use(connect.cookieSession({ secret: 'tobo!', cookie: { maxAge: 60 * 60 * 1000 }}));
*
* Options:
*
* - `key` cookie name defaulting to `connect.sess`
* - `secret` prevents cookie tampering
* - `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }`
* - `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto")
*
* Clearing sessions:
*
* To clear the session simply set its value to `null`,
* `cookieSession()` will then respond with a 1970 Set-Cookie.
*
* req.session = null;
*
* If you are interested in more sophisticated solutions,
* you may be interested in:
*
* - [client-sessions](https://github.com/mozilla/node-client-sessions)
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function cookieSession(options){
// TODO: utilize Session/Cookie to unify API
options = options || {};
var key = options.key || 'connect.sess'
, trustProxy = options.proxy;
return function cookieSession(req, res, next) {
// req.secret is for backwards compatibility
var secret = options.secret || req.secret;
if (!secret) throw new Error('`secret` option required for cookie sessions');
// default session
req.session = {};
var cookie = req.session.cookie = new Cookie(options.cookie);
// pathname mismatch
var originalPath = url.parse(req.originalUrl).pathname;
if (0 != originalPath.indexOf(cookie.path)) return next();
// cookieParser secret
if (!options.secret && req.secret) {
req.session = req.signedCookies[key] || {};
req.session.cookie = cookie;
} else {
// TODO: refactor
var rawCookie = req.cookies[key];
if (rawCookie) {
var unsigned = utils.parseSignedCookie(rawCookie, secret);
if (unsigned) {
var originalHash = crc32.signed(unsigned);
req.session = utils.parseJSONCookie(unsigned) || {};
req.session.cookie = cookie;
}
}
}
res.on('header', function(){
// removed
if (!req.session) {
debug('clear session');
cookie.expires = new Date(0);
res.setHeader('Set-Cookie', cookie.serialize(key, ''));
return;
}
delete req.session.cookie;
// check security
var proto = (req.headers['x-forwarded-proto'] || '').toLowerCase()
, tls = req.connection.encrypted || (trustProxy && 'https' == proto.split(/\s*,\s*/)[0]);
// only send secure cookies via https
if (cookie.secure && !tls) return debug('not secured');
// serialize
debug('serializing %j', req.session);
var val = 'j:' + JSON.stringify(req.session);
// compare hashes, no need to set-cookie if unchanged
if (originalHash == crc32.signed(val)) return debug('unmodified session');
// set-cookie
val = 's:' + signature.sign(val, secret);
val = cookie.serialize(key, val);
debug('set-cookie %j', cookie);
res.setHeader('Set-Cookie', val);
});
next();
};
};
/*!
* Connect - csrf
* Copyright(c) 2011 Sencha Inc.
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils');
var uid = require('uid2');
var crypto = require('crypto');
/**
* Anti CSRF:
*
* CSRF protection middleware.
*
* This middleware adds a `req.csrfToken()` function to make a token
* which should be added to requests which mutate
* state, within a hidden form field, query-string etc. This
* token is validated against the visitor's session.
*
* The default `value` function checks `req.body` generated
* by the `bodyParser()` middleware, `req.query` generated
* by `query()`, and the "X-CSRF-Token" header field.
*
* This middleware requires session support, thus should be added
* somewhere _below_ `session()` and `cookieParser()`.
*
* Options:
*
* - `value` a function accepting the request, returning the token
*
* @param {Object} options
* @api public
*/
module.exports = function csrf(options) {
options = options || {};
var value = options.value || defaultValue;
return function(req, res, next){
// already have one
var secret = req.session._csrfSecret;
if (secret) return createToken(secret);
// generate secret
uid(24, function(err, secret){
if (err) return next(err);
req.session._csrfSecret = secret;
createToken(secret);
});
// generate the token
function createToken(secret) {
var token;
// lazy-load token
req.csrfToken = function csrfToken() {
return token || (token = saltedToken(secret));
};
// compatibility with old middleware
Object.defineProperty(req.session, '_csrf', {
configurable: true,
get: function() {
console.warn('req.session._csrf is deprecated, use req.csrfToken() instead');
return req.csrfToken();
}
});
// ignore these methods
if ('GET' == req.method || 'HEAD' == req.method || 'OPTIONS' == req.method) return next();
// determine user-submitted value
var val = value(req);
// check
if (!checkToken(val, secret)) return next(utils.error(403));
next();
}
}
};
/**
* Default value function, checking the `req.body`
* and `req.query` for the CSRF token.
*
* @param {IncomingMessage} req
* @return {String}
* @api private
*/
function defaultValue(req) {
return (req.body && req.body._csrf)
|| (req.query && req.query._csrf)
|| (req.headers['x-csrf-token'])
|| (req.headers['x-xsrf-token']);
}
/**
* Return salted token.
*
* @param {String} secret
* @return {String}
* @api private
*/
function saltedToken(secret) {
return createToken(generateSalt(10), secret);
}
/**
* Creates a CSRF token from a given salt and secret.
*
* @param {String} salt (should be 10 characters)
* @param {String} secret
* @return {String}
* @api private
*/
function createToken(salt, secret) {
return salt + crypto
.createHash('sha1')
.update(salt + secret)
.digest('base64');
}
/**
* Checks if a given CSRF token matches the given secret.
*
* @param {String} token
* @param {String} secret
* @return {Boolean}
* @api private
*/
function checkToken(token, secret) {
if ('string' != typeof token) return false;
return token === createToken(token.slice(0, 10), secret);
}
/**
* Generates a random salt, using a fast non-blocking PRNG (Math.random()).
*
* @param {Number} length
* @return {String}
* @api private
*/
function generateSalt(length) {
var i, r = [];
for (i = 0; i < length; ++i) {
r.push(SALTCHARS[Math.floor(Math.random() * SALTCHARS.length)]);
}
return r.join('');
}
var SALTCHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
/*!
* Connect - directory
* Copyright(c) 2011 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
// TODO: arrow key navigation
// TODO: make icons extensible
/**
* Module dependencies.
*/
var fs = require('fs')
, parse = require('url').parse
, utils = require('../utils')
, path = require('path')
, normalize = path.normalize
, sep = path.sep
, extname = path.extname
, join = path.join;
var Batch = require('batch');
var Negotiator = require('negotiator');
/*!
* Icon cache.
*/
var cache = {};
/**
* Media types and the map for content negotiation.
*/
var mediaTypes = [
'text/html',
'text/plain',
'application/json'
];
var mediaType = {
'text/html': 'html',
'text/plain': 'plain',
'application/json': 'json'
};
/**
* Directory:
*
* Serve directory listings with the given `root` path.
*
* Options:
*
* - `hidden` display hidden (dot) files. Defaults to false.
* - `icons` display icons. Defaults to false.
* - `filter` Apply this filter function to files. Defaults to false.
*
* @param {String} root
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function directory(root, options){
options = options || {};
// root required
if (!root) throw new Error('directory() root path required');
var hidden = options.hidden
, icons = options.icons
, view = options.view || 'tiles'
, filter = options.filter
, root = normalize(root + sep);
return function directory(req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) return next();
var url = parse(req.url)
, dir = decodeURIComponent(url.pathname)
, path = normalize(join(root, dir))
, originalUrl = parse(req.originalUrl)
, originalDir = decodeURIComponent(originalUrl.pathname)
, showUp = path != root;
// null byte(s), bad request
if (~path.indexOf('\0')) return next(utils.error(400));
// malicious path, forbidden
if (0 != path.indexOf(root)) return next(utils.error(403));
// check if we have a directory
fs.stat(path, function(err, stat){
if (err) return 'ENOENT' == err.code
? next()
: next(err);
if (!stat.isDirectory()) return next();
// fetch files
fs.readdir(path, function(err, files){
if (err) return next(err);
if (!hidden) files = removeHidden(files);
if (filter) files = files.filter(filter);
files.sort();
// content-negotiation
var type = new Negotiator(req).preferredMediaType(mediaTypes);
// not acceptable
if (!type) return next(utils.error(406));
exports[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view);
});
});
};
};
/**
* Respond with text/html.
*/
exports.html = function(req, res, files, next, dir, showUp, icons, path, view){
fs.readFile(__dirname + '/../public/directory.html', 'utf8', function(err, str){
if (err) return next(err);
fs.readFile(__dirname + '/../public/style.css', 'utf8', function(err, style){
if (err) return next(err);
stat(path, files, function(err, stats){
if (err) return next(err);
files = files.map(function(file, i){ return { name: file, stat: stats[i] }; });
files.sort(fileSort);
if (showUp) files.unshift({ name: '..' });
str = str
.replace('{style}', style.concat(iconStyle(files, icons)))
.replace('{files}', html(files, dir, icons, view))
.replace('{directory}', dir)
.replace('{linked-path}', htmlPath(dir));
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', str.length);
res.end(str);
});
});
});
};
/**
* Respond with application/json.
*/
exports.json = function(req, res, files){
files = JSON.stringify(files);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Length', files.length);
res.end(files);
};
/**
* Respond with text/plain.
*/
exports.plain = function(req, res, files){
files = files.join('\n') + '\n';
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Length', files.length);
res.end(files);
};
/**
* Sort function for with directories first.
*/
function fileSort(a, b) {
return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) ||
String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase());
}
/**
* Map html `dir`, returning a linked path.
*/
function htmlPath(dir) {
var curr = [];
return dir.split('/').map(function(part){
curr.push(encodeURIComponent(part));
return part ? '<a href="' + curr.join('/') + '">' + part + '</a>' : '';
}).join(' / ');
}
/**
* Load icon images, return css string.
*/
function iconStyle (files, useIcons) {
if (!useIcons) return '';
var data = {};
var views = { tiles: [], details: [], mobile: [] };
for (var i=0; i < files.length; i++) {
var file = files[i];
if (file.name == '..') continue;
var isDir = '..' == file.name || (file.stat && file.stat.isDirectory());
var icon = isDir ? icons.folder : icons[extname(file.name)] || icons.default;
var ext = extname(file.name);
ext = isDir ? '.directory' : (icons[ext] ? ext : '.default');
if (data[icon]) continue;
data[icon] = ext + ' .name{background-image: url(data:image/png;base64,' + load(icon)+');}';
views.tiles.push('.view-tiles ' + data[icon]);
views.details.push('.view-details ' + data[icon]);
views.mobile.push('#files ' + data[icon]);
}
var style = views.tiles.join('\n')
+ '\n'+views.details.join('\n')
+ '\n@media (max-width: 768px) {\n\t'
+ views.mobile.join('\n\t')
+ '\n}';
return style;
}
/**
* Map html `files`, returning an html unordered list.
*/
function html(files, dir, useIcons, view) {
return '<ul id="files" class="view-'+view+'">'
+ (view == 'details' ? (
'<li class="header">'
+ '<span class="name">Name</span>'
+ '<span class="size">Size</span>'
+ '<span class="date">Modified</span>'
+ '</li>') : '')
+ files.map(function(file){
var isDir
, classes = []
, path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
if (useIcons) {
var ext = extname(file.name);
isDir = '..' == file.name || (file.stat && file.stat.isDirectory());
ext = isDir ? '.directory' : (icons[ext] ? ext : '.default');
classes.push('icon');
classes.push(ext.replace('.',''));
}
path.push(encodeURIComponent(file.name));
var date = file.name == '..' ? ''
: file.stat.mtime.toDateString()+' '+file.stat.mtime.toLocaleTimeString();
var size = file.name == '..' ? '' : file.stat.size;
return '<li><a href="'
+ utils.normalizeSlashes(normalize(path.join('/')))
+ '" class="'
+ classes.join(' ') + '"'
+ ' title="' + file.name + '">'
+ '<span class="name">'+file.name+'</span>'
+ '<span class="size">'+size+'</span>'
+ '<span class="date">'+date+'</span>'
+ '</a></li>';
}).join('\n') + '</ul>';
}
/**
* Load and cache the given `icon`.
*
* @param {String} icon
* @return {String}
* @api private
*/
function load(icon) {
if (cache[icon]) return cache[icon];
return cache[icon] = fs.readFileSync(__dirname + '/../public/icons/' + icon, 'base64');
}
/**
* Filter "hidden" `files`, aka files
* beginning with a `.`.
*
* @param {Array} files
* @return {Array}
* @api private
*/
function removeHidden(files) {
return files.filter(function(file){
return '.' != file[0];
});
}
/**
* Stat all files and return array of stat
* in same order.
*/
function stat(dir, files, cb) {
var batch = new Batch();
batch.concurrency(10);
files.forEach(function(file, i){
batch.push(function(done){
fs.stat(join(dir, file), done);
});
});
batch.end(cb);
}
/**
* Icon map.
*/
var icons = {
'.js': 'page_white_code_red.png'
, '.c': 'page_white_c.png'
, '.h': 'page_white_h.png'
, '.cc': 'page_white_cplusplus.png'
, '.php': 'page_white_php.png'
, '.rb': 'page_white_ruby.png'
, '.cpp': 'page_white_cplusplus.png'
, '.swf': 'page_white_flash.png'
, '.pdf': 'page_white_acrobat.png'
, 'folder': 'folder.png'
, 'default': 'page_white.png'
};
/*!
* Connect - errorHandler
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils')
, fs = require('fs');
// environment
var env = process.env.NODE_ENV || 'development';
/**
* Error handler:
*
* Development error handler, providing stack traces
* and error message responses for requests accepting text, html,
* or json.
*
* Text:
*
* By default, and when _text/plain_ is accepted a simple stack trace
* or error message will be returned.
*
* JSON:
*
* When _application/json_ is accepted, connect will respond with
* an object in the form of `{ "error": error }`.
*
* HTML:
*
* When accepted connect will output a nice html stack trace.
*
* @return {Function}
* @api public
*/
exports = module.exports = function errorHandler(){
return function errorHandler(err, req, res, next){
if (err.status) res.statusCode = err.status;
if (res.statusCode < 400) res.statusCode = 500;
if ('test' != env) console.error(err.stack);
var accept = req.headers.accept || '';
// html
if (~accept.indexOf('html')) {
fs.readFile(__dirname + '/../public/style.css', 'utf8', function(e, style){
fs.readFile(__dirname + '/../public/error.html', 'utf8', function(e, html){
var stack = (err.stack || '')
.split('\n').slice(1)
.map(function(v){ return '<li>' + v + '</li>'; }).join('');
html = html
.replace('{style}', style)
.replace('{stack}', stack)
.replace('{title}', exports.title)
.replace('{statusCode}', res.statusCode)
.replace(/\{error\}/g, utils.escape(err.toString().replace(/\n/g, '<br/>')));
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(html);
});
});
// json
} else if (~accept.indexOf('json')) {
var error = { message: err.message, stack: err.stack };
for (var prop in err) error[prop] = err[prop];
var json = JSON.stringify({ error: error });
res.setHeader('Content-Type', 'application/json');
res.end(json);
// plain text
} else {
res.setHeader('Content-Type', 'text/plain');
res.end(err.stack);
}
};
};
/**
* Template title, framework authors may override this value.
*/
exports.title = 'Connect';
/*!
* Connect - favicon
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var fs = require('fs')
, utils = require('../utils');
/**
* Favicon:
*
* By default serves the connect favicon, or the favicon
* located by the given `path`.
*
* Options:
*
* - `maxAge` cache-control max-age directive, defaulting to 1 day
*
* Examples:
*
* Serve default favicon:
*
* connect()
* .use(connect.favicon())
*
* Serve favicon before logging for brevity:
*
* connect()
* .use(connect.favicon())
* .use(connect.logger('dev'))
*
* Serve custom favicon:
*
* connect()
* .use(connect.favicon('public/favicon.ico'))
*
* @param {String} path
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function favicon(path, options){
var options = options || {}
, path = path || __dirname + '/../public/favicon.ico'
, maxAge = options.maxAge || 86400000
, icon; // favicon cache
return function favicon(req, res, next){
if ('/favicon.ico' == req.url) {
if (icon) {
res.writeHead(200, icon.headers);
res.end(icon.body);
} else {
fs.readFile(path, function(err, buf){
if (err) return next(err);
icon = {
headers: {
'Content-Type': 'image/x-icon'
, 'Content-Length': buf.length
, 'ETag': '"' + utils.md5(buf) + '"'
, 'Cache-Control': 'public, max-age=' + (maxAge / 1000)
},
body: buf
};
res.writeHead(200, icon.headers);
res.end(icon.body);
});
}
} else {
next();
}
};
};
/*!
* Connect - json
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils');
var getBody = require('raw-body');
/**
* JSON:
*
* Parse JSON request bodies, providing the
* parsed object as `req.body`.
*
* Options:
*
* - `strict` when `false` anything `JSON.parse()` accepts will be parsed
* - `reviver` used as the second "reviver" argument for JSON.parse
* - `limit` byte limit [1mb]
*
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function(options){
options = options || {};
var strict = options.strict !== false;
var verify = typeof options.verify === 'function' && options.verify;
return function json(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
if (!utils.hasBody(req)) return next();
// check Content-Type
if (!exports.regexp.test(utils.mime(req))) return next();
// flag as parsed
req._body = true;
// parse
getBody(req, {
limit: options.limit || '1mb',
length: req.headers['content-length'],
encoding: 'utf8'
}, function (err, buf) {
if (err) return next(err);
if (verify) {
try {
verify(req, res, buf)
} catch (err) {
if (!err.status) err.status = 403;
return next(err);
}
}
var first = buf.trim()[0];
if (0 == buf.length) {
return next(utils.error(400, 'invalid json, empty body'));
}
if (strict && '{' != first && '[' != first) return next(utils.error(400, 'invalid json'));
try {
req.body = JSON.parse(buf, options.reviver);
} catch (err){
err.body = buf;
err.status = 400;
return next(err);
}
next();
})
};
};
exports.regexp = /^application\/([\w!#\$%&\*`\-\.\^~]*\+)?json$/i;
/*!
* Connect - limit
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils'),
brokenPause = utils.brokenPause;
/**
* Limit:
*
* Status: Deprecated. This middleware will be removed in Connect 3.0.
* If you still wish to use some type of limit middleware,
* you may be interested in:
*
* - [raw-body](https://github.com/stream-utils/raw-body)
*
* Limit request bodies to the given size in `bytes`.
*
* A string representation of the bytesize may also be passed,
* for example "5mb", "200kb", "1gb", etc.
*
* connect()
* .use(connect.limit('5.5mb'))
* .use(handleImageUpload)
*
* @param {Number|String} bytes
* @return {Function}
* @api public
*/
module.exports = function limit(bytes){
if ('string' == typeof bytes) bytes = utils.parseBytes(bytes);
if ('number' != typeof bytes) throw new Error('limit() bytes required');
if (process.env.NODE_ENV !== 'test') {
console.warn('connect.limit() will be removed in connect 3.0');
}
return function limit(req, res, next){
var received = 0
, len = req.headers['content-length']
? parseInt(req.headers['content-length'], 10)
: null;
// self-awareness
if (req._limit) return next();
req._limit = true;
// limit by content-length
if (len && len > bytes) return next(utils.error(413));
// limit
if (brokenPause) {
listen();
} else {
req.on('newListener', function handler(event) {
if (event !== 'data') return;
req.removeListener('newListener', handler);
// Start listening at the end of the current loop
// otherwise the request will be consumed too early.
// Sideaffect is `limit` will miss the first chunk,
// but that's not a big deal.
// Unfortunately, the tests don't have large enough
// request bodies to test this.
process.nextTick(listen);
});
};
next();
function listen() {
req.on('data', function(chunk) {
received += Buffer.isBuffer(chunk)
? chunk.length :
Buffer.byteLength(chunk);
if (received > bytes) req.destroy();
});
};
};
};
/*!
* Connect - logger
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var bytes = require('bytes');
/*!
* Log buffer.
*/
var buf = [];
/*!
* Default log buffer duration.
*/
var defaultBufferDuration = 1000;
/**
* Logger:
*
* Log requests with the given `options` or a `format` string.
*
* Options:
*
* - `format` Format string, see below for tokens
* - `stream` Output stream, defaults to _stdout_
* - `buffer` Buffer duration, defaults to 1000ms when _true_
* - `immediate` Write log line on request instead of response (for response times)
*
* Tokens:
*
* - `:req[header]` ex: `:req[Accept]`
* - `:res[header]` ex: `:res[Content-Length]`
* - `:http-version`
* - `:response-time`
* - `:remote-addr`
* - `:date`
* - `:method`
* - `:url`
* - `:referrer`
* - `:user-agent`
* - `:status`
*
* Formats:
*
* Pre-defined formats that ship with connect:
*
* - `default` ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'
* - `short` ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms'
* - `tiny` ':method :url :status :res[content-length] - :response-time ms'
* - `dev` concise output colored by response status for development use
*
* Examples:
*
* connect.logger() // default
* connect.logger('short')
* connect.logger('tiny')
* connect.logger({ immediate: true, format: 'dev' })
* connect.logger(':method :url - :referrer')
* connect.logger(':req[content-type] -> :res[content-type]')
* connect.logger(function(tokens, req, res){ return 'some format string' })
*
* Defining Tokens:
*
* To define a token, simply invoke `connect.logger.token()` with the
* name and a callback function. The value returned is then available
* as ":type" in this case.
*
* connect.logger.token('type', function(req, res){ return req.headers['content-type']; })
*
* Defining Formats:
*
* All default formats are defined this way, however it's public API as well:
*
* connect.logger.format('name', 'string or function')
*
* @param {String|Function|Object} format or options
* @return {Function}
* @api public
*/
exports = module.exports = function logger(options) {
if ('object' == typeof options) {
options = options || {};
} else if (options) {
options = { format: options };
} else {
options = {};
}
// output on request instead of response
var immediate = options.immediate;
// format name
var fmt = exports[options.format] || options.format || exports.default;
// compile format
if ('function' != typeof fmt) fmt = compile(fmt);
// options
var stream = options.stream || process.stdout
, buffer = options.buffer;
// buffering support
if (buffer) {
var realStream = stream
, interval = 'number' == typeof buffer
? buffer
: defaultBufferDuration;
// flush interval
setInterval(function(){
if (buf.length) {
realStream.write(buf.join(''));
buf.length = 0;
}
}, interval);
// swap the stream
stream = {
write: function(str){
buf.push(str);
}
};
}
return function logger(req, res, next) {
var sock = req.socket;
req._startTime = new Date;
req._remoteAddress = sock.socket ? sock.socket.remoteAddress : sock.remoteAddress;
function logRequest(){
res.removeListener('finish', logRequest);
res.removeListener('close', logRequest);
var line = fmt(exports, req, res);
if (null == line) return;
stream.write(line + '\n');
};
// immediate
if (immediate) {
logRequest();
// proxy end to output logging
} else {
res.on('finish', logRequest);
res.on('close', logRequest);
}
next();
};
};
/**
* Compile `fmt` into a function.
*
* @param {String} fmt
* @return {Function}
* @api private
*/
function compile(fmt) {
fmt = fmt.replace(/"/g, '\\"');
var js = ' return "' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function(_, name, arg){
return '"\n + (tokens["' + name + '"](req, res, "' + arg + '") || "-") + "';
}) + '";'
return new Function('tokens, req, res', js);
};
/**
* Define a token function with the given `name`,
* and callback `fn(req, res)`.
*
* @param {String} name
* @param {Function} fn
* @return {Object} exports for chaining
* @api public
*/
exports.token = function(name, fn) {
exports[name] = fn;
return this;
};
/**
* Define a `fmt` with the given `name`.
*
* @param {String} name
* @param {String|Function} fmt
* @return {Object} exports for chaining
* @api public
*/
exports.format = function(name, str){
exports[name] = str;
return this;
};
/**
* Default format.
*/
exports.format('default', ':remote-addr - - [:date] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"');
/**
* Short format.
*/
exports.format('short', ':remote-addr - :method :url HTTP/:http-version :status :res[content-length] - :response-time ms');
/**
* Tiny format.
*/
exports.format('tiny', ':method :url :status :res[content-length] - :response-time ms');
/**
* dev (colored)
*/
exports.format('dev', function(tokens, req, res){
var status = res.statusCode
, len = parseInt(res.getHeader('Content-Length'), 10)
, color = 32;
if (status >= 500) color = 31
else if (status >= 400) color = 33
else if (status >= 300) color = 36;
len = isNaN(len)
? ''
: len = ' - ' + bytes(len);
return '\x1b[90m' + req.method
+ ' ' + req.originalUrl + ' '
+ '\x1b[' + color + 'm' + res.statusCode
+ ' \x1b[90m'
+ (new Date - req._startTime)
+ 'ms' + len
+ '\x1b[0m';
});
/**
* request url
*/
exports.token('url', function(req){
return req.originalUrl || req.url;
});
/**
* request method
*/
exports.token('method', function(req){
return req.method;
});
/**
* response time in milliseconds
*/
exports.token('response-time', function(req){
return String(Date.now() - req._startTime);
});
/**
* UTC date
*/
exports.token('date', function(){
return new Date().toUTCString();
});
/**
* response status code
*/
exports.token('status', function(req, res){
return res.headerSent ? res.statusCode : null;
});
/**
* normalized referrer
*/
exports.token('referrer', function(req){
return req.headers['referer'] || req.headers['referrer'];
});
/**
* remote address
*/
exports.token('remote-addr', function(req){
if (req.ip) return req.ip;
if (req._remoteAddress) return req._remoteAddress;
var sock = req.socket;
if (sock.socket) return sock.socket.remoteAddress;
return sock.remoteAddress;
});
/**
* HTTP version
*/
exports.token('http-version', function(req){
return req.httpVersionMajor + '.' + req.httpVersionMinor;
});
/**
* UA string
*/
exports.token('user-agent', function(req){
return req.headers['user-agent'];
});
/**
* request header
*/
exports.token('req', function(req, res, field){
return req.headers[field.toLowerCase()];
});
/**
* response header
*/
exports.token('res', function(req, res, field){
return (res._headers || {})[field.toLowerCase()];
});
/*!
* Connect - methodOverride
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var methods = require('methods');
/**
* Method Override:
*
* Provides faux HTTP method support.
*
* Pass an optional `key` to use when checking for
* a method override, otherwise defaults to _\_method_.
* The original method is available via `req.originalMethod`.
*
* @param {String} key
* @return {Function}
* @api public
*/
module.exports = function methodOverride(key){
key = key || "_method";
return function methodOverride(req, res, next) {
var method;
req.originalMethod = req.originalMethod || req.method;
// req.body
if (req.body && typeof req.body === 'object' && key in req.body) {
method = req.body[key].toLowerCase();
delete req.body[key];
}
// check X-HTTP-Method-Override
if (req.headers['x-http-method-override']) {
method = req.headers['x-http-method-override'].toLowerCase();
}
// replace
if (supports(method)) req.method = method.toUpperCase();
next();
};
};
/**
* Check if node supports `method`.
*/
function supports(method) {
return ~methods.indexOf(method);
}
/*!
* Connect - multipart
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var multiparty = require('multiparty')
, _limit = require('./limit')
, utils = require('../utils')
, qs = require('qs');
/**
* Multipart:
*
* Status: Deprecated. The multipart parser will be removed in Connect 3.0.
* Please use one of the following parsers/middleware directly:
*
* - [formidable](https://github.com/felixge/node-formidable)
* - [connect-multiparty](https://github.com/superjoe30/connect-multiparty) or [multiparty]
* - [connect-busboy](https://github.com/mscdex/connect-busboy) or [busboy](https://github.com/mscdex/busboy)
*
* Parse multipart/form-data request bodies,
* providing the parsed object as `req.body`
* and `req.files`.
*
* Configuration:
*
* The options passed are merged with [multiparty](https://github.com/superjoe30/node-multiparty)'s
* `Form` object, allowing you to configure the upload directory,
* size limits, etc. For example if you wish to change the upload dir do the following.
*
* app.use(connect.multipart({ uploadDir: path }));
*
* Options:
*
* - `limit` byte limit defaulting to [100mb]
* - `defer` defers processing and exposes the multiparty form object as `req.form`.
* `next()` is called without waiting for the form's "end" event.
* This option is useful if you need to bind to the "progress" or "part" events, for example.
*
* Temporary Files:
*
* By default temporary files are used, stored in `os.tmpDir()`. These
* are not automatically garbage collected, you are in charge of moving them
* or deleting them. When `defer` is not used and these files are created you
* may refernce them via the `req.files` object.
*
* req.files.images.forEach(function(file){
* console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path);
* });
*
* It is highly recommended to monitor and clean up tempfiles in any production
* environment, you may use tools like [reap](https://github.com/visionmedia/reap)
* to do so.
*
* Streaming:
*
* When `defer` is used files are _not_ streamed to tmpfiles, you may
* access them via the "part" events and stream them accordingly:
*
* req.form.on('part', function(part){
* // transfer to s3 etc
* console.log('upload %s %s', part.name, part.filename);
* var out = fs.createWriteStream('/tmp/' + part.filename);
* part.pipe(out);
* });
*
* req.form.on('close', function(){
* res.end('uploaded!');
* });
*
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function(options){
options = options || {};
if (process.env.NODE_ENV !== 'test') {
console.warn('connect.multipart() will be removed in connect 3.0');
console.warn('visit https://github.com/senchalabs/connect/wiki/Connect-3.0 for alternatives');
}
var limit = _limit(options.limit || '100mb');
return function multipart(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
req.files = req.files || {};
if (!utils.hasBody(req)) return next();
// ignore GET
if ('GET' == req.method || 'HEAD' == req.method) return next();
// check Content-Type
if ('multipart/form-data' != utils.mime(req)) return next();
// flag as parsed
req._body = true;
// parse
limit(req, res, function(err){
if (err) return next(err);
var form = new multiparty.Form(options)
, data = {}
, files = {}
, done;
Object.keys(options).forEach(function(key){
form[key] = options[key];
});
function ondata(name, val, data){
if (Array.isArray(data[name])) {
data[name].push(val);
} else if (data[name]) {
data[name] = [data[name], val];
} else {
data[name] = val;
}
}
form.on('field', function(name, val){
ondata(name, val, data);
});
if (!options.defer) {
form.on('file', function(name, val){
val.name = val.originalFilename;
val.type = val.headers['content-type'] || null;
ondata(name, val, files);
});
}
form.on('error', function(err){
if (!options.defer) {
err.status = 400;
next(err);
}
done = true;
});
form.on('close', function(){
if (done) return;
try {
req.body = qs.parse(data);
req.files = qs.parse(files);
} catch (err) {
form.emit('error', err);
return;
}
if (!options.defer) next();
});
form.parse(req);
if (options.defer) {
req.form = form;
next();
}
});
}
};
/*!
* Connect - query
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2011 Sencha Inc.
* MIT Licensed
*/
/**
* Module dependencies.
*/
var qs = require('qs')
, parse = require('../utils').parseUrl;
/**
* Query:
*
* Automatically parse the query-string when available,
* populating the `req.query` object using
* [qs](https://github.com/visionmedia/node-querystring).
*
* Examples:
*
* connect()
* .use(connect.query())
* .use(function(req, res){
* res.end(JSON.stringify(req.query));
* });
*
* The `options` passed are provided to qs.parse function.
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function query(options){
return function query(req, res, next){
if (!req.query) {
req.query = ~req.url.indexOf('?')
? qs.parse(parse(req).query, options)
: {};
}
next();
};
};
/*!
* Connect - responseTime
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Reponse time:
*
* Adds the `X-Response-Time` header displaying the response
* duration in milliseconds.
*
* @return {Function}
* @api public
*/
module.exports = function responseTime(){
return function(req, res, next){
var start = new Date;
if (res._responseTime) return next();
res._responseTime = true;
res.on('header', function(){
var duration = new Date - start;
res.setHeader('X-Response-Time', duration + 'ms');
});
next();
};
};
/*!
* Connect - session
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var Session = require('./session/session')
, debug = require('debug')('connect:session')
, MemoryStore = require('./session/memory')
, signature = require('cookie-signature')
, Cookie = require('./session/cookie')
, Store = require('./session/store')
, utils = require('./../utils')
, uid = require('uid2')
, crc32 = require('buffer-crc32')
, parse = require('url').parse;
// environment
var env = process.env.NODE_ENV;
/**
* Expose the middleware.
*/
exports = module.exports = session;
/**
* Expose constructors.
*/
exports.Store = Store;
exports.Cookie = Cookie;
exports.Session = Session;
exports.MemoryStore = MemoryStore;
/**
* Warning message for `MemoryStore` usage in production.
*/
var warning = 'Warning: connection.session() MemoryStore is not\n'
+ 'designed for a production environment, as it will leak\n'
+ 'memory, and will not scale past a single process.';
/**
* Session:
*
* Setup session store with the given `options`.
*
* Session data is _not_ saved in the cookie itself, however
* cookies are used, so we must use the [cookieParser()](cookieParser.html)
* middleware _before_ `session()`.
*
* Examples:
*
* connect()
* .use(connect.cookieParser())
* .use(connect.session({ secret: 'keyboard cat', key: 'sid', cookie: { secure: true }}))
*
* Options:
*
* - `key` cookie name defaulting to `connect.sid`
* - `store` session store instance
* - `secret` session cookie is signed with this secret to prevent tampering
* - `cookie` session cookie settings, defaulting to `{ path: '/', httpOnly: true, maxAge: null }`
* - `proxy` trust the reverse proxy when setting secure cookies (via "x-forwarded-proto")
*
* Cookie option:
*
* By default `cookie.maxAge` is `null`, meaning no "expires" parameter is set
* so the cookie becomes a browser-session cookie. When the user closes the
* browser the cookie (and session) will be removed.
*
* ## req.session
*
* To store or access session data, simply use the request property `req.session`,
* which is (generally) serialized as JSON by the store, so nested objects
* are typically fine. For example below is a user-specific view counter:
*
* connect()
* .use(connect.favicon())
* .use(connect.cookieParser())
* .use(connect.session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))
* .use(function(req, res, next){
* var sess = req.session;
* if (sess.views) {
* res.setHeader('Content-Type', 'text/html');
* res.write('<p>views: ' + sess.views + '</p>');
* res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>');
* res.end();
* sess.views++;
* } else {
* sess.views = 1;
* res.end('welcome to the session demo. refresh!');
* }
* }
* )).listen(3000);
*
* ## Session#regenerate()
*
* To regenerate the session simply invoke the method, once complete
* a new SID and `Session` instance will be initialized at `req.session`.
*
* req.session.regenerate(function(err){
* // will have a new session here
* });
*
* ## Session#destroy()
*
* Destroys the session, removing `req.session`, will be re-generated next request.
*
* req.session.destroy(function(err){
* // cannot access session here
* });
*
* ## Session#reload()
*
* Reloads the session data.
*
* req.session.reload(function(err){
* // session updated
* });
*
* ## Session#save()
*
* Save the session.
*
* req.session.save(function(err){
* // session saved
* });
*
* ## Session#touch()
*
* Updates the `.maxAge` property. Typically this is
* not necessary to call, as the session middleware does this for you.
*
* ## Session#cookie
*
* Each session has a unique cookie object accompany it. This allows
* you to alter the session cookie per visitor. For example we can
* set `req.session.cookie.expires` to `false` to enable the cookie
* to remain for only the duration of the user-agent.
*
* ## Session#maxAge
*
* Alternatively `req.session.cookie.maxAge` will return the time
* remaining in milliseconds, which we may also re-assign a new value
* to adjust the `.expires` property appropriately. The following
* are essentially equivalent
*
* var hour = 3600000;
* req.session.cookie.expires = new Date(Date.now() + hour);
* req.session.cookie.maxAge = hour;
*
* For example when `maxAge` is set to `60000` (one minute), and 30 seconds
* has elapsed it will return `30000` until the current request has completed,
* at which time `req.session.touch()` is called to reset `req.session.maxAge`
* to its original value.
*
* req.session.cookie.maxAge;
* // => 30000
*
* Session Store Implementation:
*
* Every session store _must_ implement the following methods
*
* - `.get(sid, callback)`
* - `.set(sid, session, callback)`
* - `.destroy(sid, callback)`
*
* Recommended methods include, but are not limited to:
*
* - `.length(callback)`
* - `.clear(callback)`
*
* For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo.
*
* @param {Object} options
* @return {Function}
* @api public
*/
function session(options){
var options = options || {}
, key = options.key || 'connect.sid'
, store = options.store || new MemoryStore
, cookie = options.cookie || {}
, trustProxy = options.proxy
, storeReady = true
, rollingSessions = options.rolling || false;
// notify user that this store is not
// meant for a production environment
if ('production' == env && store instanceof MemoryStore) {
console.warn(warning);
}
// generates the new session
store.generate = function(req){
req.sessionID = uid(24);
req.session = new Session(req);
req.session.cookie = new Cookie(cookie);
};
store.on('disconnect', function(){ storeReady = false; });
store.on('connect', function(){ storeReady = true; });
return function session(req, res, next) {
// self-awareness
if (req.session) return next();
// Handle connection as if there is no session if
// the store has temporarily disconnected etc
if (!storeReady) return debug('store is disconnected'), next();
// pathname mismatch
var originalPath = parse(req.originalUrl).pathname;
if (0 != originalPath.indexOf(cookie.path || '/')) return next();
// backwards compatibility for signed cookies
// req.secret is passed from the cookie parser middleware
var secret = options.secret || req.secret;
// ensure secret is available or bail
if (!secret) throw new Error('`secret` option required for sessions');
var originalHash
, originalId;
// expose store
req.sessionStore = store;
// grab the session cookie value and check the signature
var rawCookie = req.cookies[key];
// get signedCookies for backwards compat with signed cookies
var unsignedCookie = req.signedCookies[key];
if (!unsignedCookie && rawCookie) {
unsignedCookie = utils.parseSignedCookie(rawCookie, secret);
}
// set-cookie
res.on('header', function(){
if (!req.session) return;
var cookie = req.session.cookie
, proto = (req.headers['x-forwarded-proto'] || '').split(',')[0].toLowerCase().trim()
, tls = req.connection.encrypted || (trustProxy && 'https' == proto)
, isNew = unsignedCookie != req.sessionID;
// only send secure cookies via https
if (cookie.secure && !tls) return debug('not secured');
// in case of rolling session, always reset the cookie
if (!rollingSessions) {
// browser-session length cookie
if (null == cookie.expires) {
if (!isNew) return debug('already set browser-session cookie');
// compare hashes and ids
} else if (originalHash == hash(req.session) && originalId == req.session.id) {
return debug('unmodified session');
}
}
var val = 's:' + signature.sign(req.sessionID, secret);
val = cookie.serialize(key, val);
debug('set-cookie %s', val);
res.setHeader('Set-Cookie', val);
});
// proxy end() to commit the session
var end = res.end;
res.end = function(data, encoding){
res.end = end;
if (!req.session) return res.end(data, encoding);
debug('saving');
req.session.resetMaxAge();
req.session.save(function(err){
if (err) console.error(err.stack);
debug('saved');
res.end(data, encoding);
});
};
// generate the session
function generate() {
store.generate(req);
}
// get the sessionID from the cookie
req.sessionID = unsignedCookie;
// generate a session if the browser doesn't send a sessionID
if (!req.sessionID) {
debug('no SID sent, generating session');
generate();
next();
return;
}
// generate the session object
var pause = utils.pause(req);
debug('fetching %s', req.sessionID);
store.get(req.sessionID, function(err, sess){
// proxy to resume() events
var _next = next;
next = function(err){
_next(err);
pause.resume();
};
// error handling
if (err) {
debug('error %j', err);
if ('ENOENT' == err.code) {
generate();
next();
} else {
next(err);
}
// no session
} else if (!sess) {
debug('no session found');
generate();
next();
// populate req.session
} else {
debug('session found');
store.createSession(req, sess);
originalId = req.sessionID;
originalHash = hash(sess);
next();
}
});
};
};
/**
* Hash the given `sess` object omitting changes
* to `.cookie`.
*
* @param {Object} sess
* @return {String}
* @api private
*/
function hash(sess) {
return crc32.signed(JSON.stringify(sess, function(key, val){
if ('cookie' != key) return val;
}));
}
/*!
* Connect - session - MemoryStore
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var Store = require('./store');
/**
* Initialize a new `MemoryStore`.
*
* @api public
*/
var MemoryStore = module.exports = function MemoryStore() {
this.sessions = {};
};
/**
* Inherit from `Store.prototype`.
*/
MemoryStore.prototype.__proto__ = Store.prototype;
/**
* Attempt to fetch session by the given `sid`.
*
* @param {String} sid
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.get = function(sid, fn){
var self = this;
process.nextTick(function(){
var expires
, sess = self.sessions[sid];
if (sess) {
sess = JSON.parse(sess);
expires = 'string' == typeof sess.cookie.expires
? new Date(sess.cookie.expires)
: sess.cookie.expires;
if (!expires || new Date < expires) {
fn(null, sess);
} else {
self.destroy(sid, fn);
}
} else {
fn();
}
});
};
/**
* Commit the given `sess` object associated with the given `sid`.
*
* @param {String} sid
* @param {Session} sess
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.set = function(sid, sess, fn){
var self = this;
process.nextTick(function(){
self.sessions[sid] = JSON.stringify(sess);
fn && fn();
});
};
/**
* Destroy the session associated with the given `sid`.
*
* @param {String} sid
* @api public
*/
MemoryStore.prototype.destroy = function(sid, fn){
var self = this;
process.nextTick(function(){
delete self.sessions[sid];
fn && fn();
});
};
/**
* Invoke the given callback `fn` with all active sessions.
*
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.all = function(fn){
var arr = []
, keys = Object.keys(this.sessions);
for (var i = 0, len = keys.length; i < len; ++i) {
arr.push(this.sessions[keys[i]]);
}
fn(null, arr);
};
/**
* Clear all sessions.
*
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.clear = function(fn){
this.sessions = {};
fn && fn();
};
/**
* Fetch number of sessions.
*
* @param {Function} fn
* @api public
*/
MemoryStore.prototype.length = function(fn){
fn(null, Object.keys(this.sessions).length);
};
/*!
* Connect - session - Session
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../../utils');
/**
* Create a new `Session` with the given request and `data`.
*
* @param {IncomingRequest} req
* @param {Object} data
* @api private
*/
var Session = module.exports = function Session(req, data) {
Object.defineProperty(this, 'req', { value: req });
Object.defineProperty(this, 'id', { value: req.sessionID });
if ('object' == typeof data) utils.merge(this, data);
};
/**
* Update reset `.cookie.maxAge` to prevent
* the cookie from expiring when the
* session is still active.
*
* @return {Session} for chaining
* @api public
*/
Session.prototype.touch = function(){
return this.resetMaxAge();
};
/**
* Reset `.maxAge` to `.originalMaxAge`.
*
* @return {Session} for chaining
* @api public
*/
Session.prototype.resetMaxAge = function(){
this.cookie.maxAge = this.cookie.originalMaxAge;
return this;
};
/**
* Save the session data with optional callback `fn(err)`.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
Session.prototype.save = function(fn){
this.req.sessionStore.set(this.id, this, fn || function(){});
return this;
};
/**
* Re-loads the session data _without_ altering
* the maxAge properties. Invokes the callback `fn(err)`,
* after which time if no exception has occurred the
* `req.session` property will be a new `Session` object,
* although representing the same session.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
Session.prototype.reload = function(fn){
var req = this.req
, store = this.req.sessionStore;
store.get(this.id, function(err, sess){
if (err) return fn(err);
if (!sess) return fn(new Error('failed to load session'));
store.createSession(req, sess);
fn();
});
return this;
};
/**
* Destroy `this` session.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
Session.prototype.destroy = function(fn){
delete this.req.session;
this.req.sessionStore.destroy(this.id, fn);
return this;
};
/**
* Regenerate this request's session.
*
* @param {Function} fn
* @return {Session} for chaining
* @api public
*/
Session.prototype.regenerate = function(fn){
this.req.sessionStore.regenerate(this.req, fn);
return this;
};
/*!
* Connect - session - Store
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, Session = require('./session')
, Cookie = require('./cookie');
/**
* Initialize abstract `Store`.
*
* @api private
*/
var Store = module.exports = function Store(options){};
/**
* Inherit from `EventEmitter.prototype`.
*/
Store.prototype.__proto__ = EventEmitter.prototype;
/**
* Re-generate the given requests's session.
*
* @param {IncomingRequest} req
* @return {Function} fn
* @api public
*/
Store.prototype.regenerate = function(req, fn){
var self = this;
this.destroy(req.sessionID, function(err){
self.generate(req);
fn(err);
});
};
/**
* Load a `Session` instance via the given `sid`
* and invoke the callback `fn(err, sess)`.
*
* @param {String} sid
* @param {Function} fn
* @api public
*/
Store.prototype.load = function(sid, fn){
var self = this;
this.get(sid, function(err, sess){
if (err) return fn(err);
if (!sess) return fn();
var req = { sessionID: sid, sessionStore: self };
sess = self.createSession(req, sess);
fn(null, sess);
});
};
/**
* Create session from JSON `sess` data.
*
* @param {IncomingRequest} req
* @param {Object} sess
* @return {Session}
* @api private
*/
Store.prototype.createSession = function(req, sess){
var expires = sess.cookie.expires
, orig = sess.cookie.originalMaxAge;
sess.cookie = new Cookie(sess.cookie);
if ('string' == typeof expires) sess.cookie.expires = new Date(expires);
sess.cookie.originalMaxAge = orig;
req.session = new Session(req, sess);
return req.session;
};
/*!
* Connect - static
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var send = require('send')
, utils = require('../utils')
, parse = utils.parseUrl
, url = require('url');
/**
* Static:
*
* Static file server with the given `root` path.
*
* Examples:
*
* var oneDay = 86400000;
*
* connect()
* .use(connect.static(__dirname + '/public'))
*
* connect()
* .use(connect.static(__dirname + '/public', { maxAge: oneDay }))
*
* Options:
*
* - `maxAge` Browser cache maxAge in milliseconds. defaults to 0
* - `hidden` Allow transfer of hidden files. defaults to false
* - `redirect` Redirect to trailing "/" when the pathname is a dir. defaults to true
* - `index` Default file name, defaults to 'index.html'
*
* @param {String} root
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function(root, options){
options = options || {};
// root required
if (!root) throw new Error('static() root path required');
// default redirect
var redirect = false !== options.redirect;
return function staticMiddleware(req, res, next) {
if ('GET' != req.method && 'HEAD' != req.method) return next();
var originalUrl = url.parse(req.originalUrl);
var path = parse(req).pathname;
var pause = utils.pause(req);
if (path == '/' && originalUrl.pathname[originalUrl.pathname.length - 1] != '/') {
return directory();
}
function resume() {
next();
pause.resume();
}
function directory() {
if (!redirect) return resume();
var target;
originalUrl.pathname += '/';
target = url.format(originalUrl);
res.statusCode = 303;
res.setHeader('Location', target);
res.end('Redirecting to ' + utils.escape(target));
}
function error(err) {
if (404 == err.status) return resume();
next(err);
}
send(req, path)
.maxage(options.maxAge || 0)
.root(root)
.index(options.index || 'index.html')
.hidden(options.hidden)
.on('error', error)
.on('directory', directory)
.pipe(res);
};
};
/**
* Expose mime module.
*
* If you wish to extend the mime table use this
* reference to the "mime" module in the npm registry.
*/
exports.mime = send.mime;
/*!
* Connect - staticCache
* Copyright(c) 2011 Sencha Inc.
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils')
, Cache = require('../cache')
, fresh = require('fresh');
/**
* Static cache:
*
* Status: Deprecated. This middleware will be removed in
* Connect 3.0. You may be interested in:
*
* - [st](https://github.com/isaacs/st)
*
* Enables a memory cache layer on top of
* the `static()` middleware, serving popular
* static files.
*
* By default a maximum of 128 objects are
* held in cache, with a max of 256k each,
* totalling ~32mb.
*
* A Least-Recently-Used (LRU) cache algo
* is implemented through the `Cache` object,
* simply rotating cache objects as they are
* hit. This means that increasingly popular
* objects maintain their positions while
* others get shoved out of the stack and
* garbage collected.
*
* Benchmarks:
*
* static(): 2700 rps
* node-static: 5300 rps
* static() + staticCache(): 7500 rps
*
* Options:
*
* - `maxObjects` max cache objects [128]
* - `maxLength` max cache object length 256kb
*
* @param {Object} options
* @return {Function}
* @api public
*/
module.exports = function staticCache(options){
var options = options || {}
, cache = new Cache(options.maxObjects || 128)
, maxlen = options.maxLength || 1024 * 256;
if (process.env.NODE_ENV !== 'test') {
console.warn('connect.staticCache() is deprecated and will be removed in 3.0');
console.warn('use varnish or similar reverse proxy caches.');
}
return function staticCache(req, res, next){
var key = cacheKey(req)
, ranges = req.headers.range
, hasCookies = req.headers.cookie
, hit = cache.get(key);
// cache static
// TODO: change from staticCache() -> cache()
// and make this work for any request
req.on('static', function(stream){
var headers = res._headers
, cc = utils.parseCacheControl(headers['cache-control'] || '')
, contentLength = headers['content-length']
, hit;
// dont cache set-cookie responses
if (headers['set-cookie']) return hasCookies = true;
// dont cache when cookies are present
if (hasCookies) return;
// ignore larger files
if (!contentLength || contentLength > maxlen) return;
// don't cache partial files
if (headers['content-range']) return;
// dont cache items we shouldn't be
// TODO: real support for must-revalidate / no-cache
if ( cc['no-cache']
|| cc['no-store']
|| cc['private']
|| cc['must-revalidate']) return;
// if already in cache then validate
if (hit = cache.get(key)){
if (headers.etag == hit[0].etag) {
hit[0].date = new Date;
return;
} else {
cache.remove(key);
}
}
// validation notifiactions don't contain a steam
if (null == stream) return;
// add the cache object
var arr = [];
// store the chunks
stream.on('data', function(chunk){
arr.push(chunk);
});
// flag it as complete
stream.on('end', function(){
var cacheEntry = cache.add(key);
delete headers['x-cache']; // Clean up (TODO: others)
cacheEntry.push(200);
cacheEntry.push(headers);
cacheEntry.push.apply(cacheEntry, arr);
});
});
if (req.method == 'GET' || req.method == 'HEAD') {
if (ranges) {
next();
} else if (!hasCookies && hit && !mustRevalidate(req, hit)) {
res.setHeader('X-Cache', 'HIT');
respondFromCache(req, res, hit);
} else {
res.setHeader('X-Cache', 'MISS');
next();
}
} else {
next();
}
}
};
/**
* Respond with the provided cached value.
* TODO: Assume 200 code, that's iffy.
*
* @param {Object} req
* @param {Object} res
* @param {Object} cacheEntry
* @return {String}
* @api private
*/
function respondFromCache(req, res, cacheEntry) {
var status = cacheEntry[0]
, headers = utils.merge({}, cacheEntry[1])
, content = cacheEntry.slice(2);
headers.age = (new Date - new Date(headers.date)) / 1000 || 0;
switch (req.method) {
case 'HEAD':
res.writeHead(status, headers);
res.end();
break;
case 'GET':
if (utils.conditionalGET(req) && fresh(req.headers, headers)) {
headers['content-length'] = 0;
res.writeHead(304, headers);
res.end();
} else {
res.writeHead(status, headers);
function write() {
while (content.length) {
if (false === res.write(content.shift())) {
res.once('drain', write);
return;
}
}
res.end();
}
write();
}
break;
default:
// This should never happen.
res.writeHead(500, '');
res.end();
}
}
/**
* Determine whether or not a cached value must be revalidated.
*
* @param {Object} req
* @param {Object} cacheEntry
* @return {String}
* @api private
*/
function mustRevalidate(req, cacheEntry) {
var cacheHeaders = cacheEntry[1]
, reqCC = utils.parseCacheControl(req.headers['cache-control'] || '')
, cacheCC = utils.parseCacheControl(cacheHeaders['cache-control'] || '')
, cacheAge = (new Date - new Date(cacheHeaders.date)) / 1000 || 0;
if ( cacheCC['no-cache']
|| cacheCC['must-revalidate']
|| cacheCC['proxy-revalidate']) return true;
if (reqCC['no-cache']) return true;
if (null != reqCC['max-age']) return reqCC['max-age'] < cacheAge;
if (null != cacheCC['max-age']) return cacheCC['max-age'] < cacheAge;
return false;
}
/**
* The key to use in the cache. For now, this is the URL path and query.
*
* 'http://example.com?key=value' -> '/?key=value'
*
* @param {Object} req
* @return {String}
* @api private
*/
function cacheKey(req) {
return utils.parseUrl(req).path;
}
/*!
* Connect - timeout
* Ported from https://github.com/LearnBoost/connect-timeout
* MIT Licensed
*/
/**
* Module dependencies.
*/
var debug = require('debug')('connect:timeout');
/**
* Timeout:
*
* Times out the request in `ms`, defaulting to `5000`. The
* method `req.clearTimeout()` is added to revert this behaviour
* programmatically within your application's middleware, routes, etc.
*
* The timeout error is passed to `next()` so that you may customize
* the response behaviour. This error has the `.timeout` property as
* well as `.status == 503`.
*
* @param {Number} ms
* @return {Function}
* @api public
*/
module.exports = function timeout(ms) {
ms = ms || 5000;
return function(req, res, next) {
var id = setTimeout(function(){
req.emit('timeout', ms);
}, ms);
req.on('timeout', function(){
if (res.headerSent) return debug('response started, cannot timeout');
var err = new Error('Response timeout');
err.timeout = ms;
err.status = 503;
next(err);
});
req.clearTimeout = function(){
clearTimeout(id);
};
res.on('header', function(){
clearTimeout(id);
});
next();
};
};
/*!
* Connect - urlencoded
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils');
var getBody = require('raw-body');
var qs = require('qs');
/**
* Urlencoded:
*
* Parse x-ww-form-urlencoded request bodies,
* providing the parsed object as `req.body` using
* [qs](https://github.com/visionmedia/node-querystring).
*
* Options:
*
* - `limit` byte limit [1mb]
*
* @param {Object} options
* @return {Function}
* @api public
*/
exports = module.exports = function(options){
options = options || {};
var verify = typeof options.verify === 'function' && options.verify;
return function urlencoded(req, res, next) {
if (req._body) return next();
req.body = req.body || {};
if (!utils.hasBody(req)) return next();
// check Content-Type
if ('application/x-www-form-urlencoded' != utils.mime(req)) return next();
// flag as parsed
req._body = true;
// parse
getBody(req, {
limit: options.limit || '1mb',
length: req.headers['content-length'],
encoding: 'utf8'
}, function (err, buf) {
if (err) return next(err);
if (verify) {
try {
verify(req, res, buf)
} catch (err) {
if (!err.status) err.status = 403;
return next(err);
}
}
try {
req.body = buf.length
? qs.parse(buf, options)
: {};
} catch (err){
err.body = buf;
return next(err);
}
next();
})
}
};
/*!
* Connect - vhost
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Vhost:
*
* Setup vhost for the given `hostname` and `server`.
*
* connect()
* .use(connect.vhost('foo.com', fooApp))
* .use(connect.vhost('bar.com', barApp))
* .use(connect.vhost('*.com', mainApp))
*
* The `server` may be a Connect server or
* a regular Node `http.Server`.
*
* @param {String} hostname
* @param {Server} server
* @return {Function}
* @api public
*/
module.exports = function vhost(hostname, server){
if (!hostname) throw new Error('vhost hostname required');
if (!server) throw new Error('vhost server required');
var regexp = new RegExp('^' + hostname.replace(/[^*\w]/g, '\\$&').replace(/[*]/g, '(?:.*?)') + '$', 'i');
if (server.onvhost) server.onvhost(hostname);
return function vhost(req, res, next){
if (!req.headers.host) return next();
var host = req.headers.host.split(':')[0];
if (!regexp.test(host)) return next();
if ('function' == typeof server) return server(req, res, next);
server.emit('request', req, res);
};
};
/*!
* Connect
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var http = require('http')
, res = http.ServerResponse.prototype
, setHeader = res.setHeader
, _renderHeaders = res._renderHeaders
, writeHead = res.writeHead;
// apply only once
if (!res._hasConnectPatch) {
/**
* Provide a public "header sent" flag
* until node does.
*
* @return {Boolean}
* @api public
*/
res.__defineGetter__('headerSent', function(){
return this._header;
});
/**
* Set header `field` to `val`, special-casing
* the `Set-Cookie` field for multiple support.
*
* @param {String} field
* @param {String} val
* @api public
*/
res.setHeader = function(field, val){
var key = field.toLowerCase()
, prev;
// special-case Set-Cookie
if (this._headers && 'set-cookie' == key) {
if (prev = this.getHeader(field)) {
if (Array.isArray(prev)) {
val = prev.concat(val);
} else if (Array.isArray(val)) {
val = val.concat(prev);
} else {
val = [prev, val];
}
}
// charset
} else if ('content-type' == key && this.charset) {
val += '; charset=' + this.charset;
}
return setHeader.call(this, field, val);
};
/**
* Proxy to emit "header" event.
*/
res._renderHeaders = function(){
if (!this._emittedHeader) this.emit('header');
this._emittedHeader = true;
return _renderHeaders.call(this);
};
res.writeHead = function(statusCode, reasonPhrase, headers){
if (typeof reasonPhrase === 'object') headers = reasonPhrase;
if (typeof headers === 'object') {
Object.keys(headers).forEach(function(key){
this.setHeader(key, headers[key]);
}, this);
}
if (!this._emittedHeader) this.emit('header');
this._emittedHeader = true;
return writeHead.call(this, statusCode, reasonPhrase);
};
res._hasConnectPatch = true;
}
/*!
* Connect - HTTPServer
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var http = require('http')
, utils = require('./utils')
, debug = require('debug')('connect:dispatcher');
// prototype
var app = module.exports = {};
// environment
var env = process.env.NODE_ENV || 'development';
/**
* Utilize the given middleware `handle` to the given `route`,
* defaulting to _/_. This "route" is the mount-point for the
* middleware, when given a value other than _/_ the middleware
* is only effective when that segment is present in the request's
* pathname.
*
* For example if we were to mount a function at _/admin_, it would
* be invoked on _/admin_, and _/admin/settings_, however it would
* not be invoked for _/_, or _/posts_.
*
* Examples:
*
* var app = connect();
* app.use(connect.favicon());
* app.use(connect.logger());
* app.use(connect.static(__dirname + '/public'));
*
* If we wanted to prefix static files with _/public_, we could
* "mount" the `static()` middleware:
*
* app.use('/public', connect.static(__dirname + '/public'));
*
* This api is chainable, so the following is valid:
*
* connect()
* .use(connect.favicon())
* .use(connect.logger())
* .use(connect.static(__dirname + '/public'))
* .listen(3000);
*
* @param {String|Function|Server} route, callback or server
* @param {Function|Server} callback or server
* @return {Server} for chaining
* @api public
*/
app.use = function(route, fn){
// default route to '/'
if ('string' != typeof route) {
fn = route;
route = '/';
}
// wrap sub-apps
if ('function' == typeof fn.handle) {
var server = fn;
fn.route = route;
fn = function(req, res, next){
server.handle(req, res, next);
};
}
// wrap vanilla http.Servers
if (fn instanceof http.Server) {
fn = fn.listeners('request')[0];
}
// strip trailing slash
if ('/' == route[route.length - 1]) {
route = route.slice(0, -1);
}
// add the middleware
debug('use %s %s', route || '/', fn.name || 'anonymous');
this.stack.push({ route: route, handle: fn });
return this;
};
/**
* Handle server requests, punting them down
* the middleware stack.
*
* @api private
*/
app.handle = function(req, res, out) {
var stack = this.stack
, search = 1 + req.url.indexOf('?')
, pathlength = search ? search - 1 : req.url.length
, fqdn = 1 + req.url.substr(0, pathlength).indexOf('://')
, protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : ''
, removed = ''
, slashAdded = false
, index = 0;
function next(err) {
var layer, path, c;
if (slashAdded) {
req.url = req.url.substr(1);
slashAdded = false;
}
req.url = protohost + removed + req.url.substr(protohost.length);
req.originalUrl = req.originalUrl || req.url;
removed = '';
// next callback
layer = stack[index++];
// all done
if (!layer || res.headerSent) {
// delegate to parent
if (out) return out(err);
// unhandled error
if (err) {
// default to 500
if (res.statusCode < 400) res.statusCode = 500;
debug('default %s', res.statusCode);
// respect err.status
if (err.status) res.statusCode = err.status;
// production gets a basic error message
var msg = 'production' == env
? http.STATUS_CODES[res.statusCode]
: err.stack || err.toString();
msg = utils.escape(msg);
// log to stderr in a non-test env
if ('test' != env) console.error(err.stack || err.toString());
if (res.headerSent) return req.socket.destroy();
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', Buffer.byteLength(msg));
if ('HEAD' == req.method) return res.end();
res.end(msg);
} else {
debug('default 404');
res.statusCode = 404;
res.setHeader('Content-Type', 'text/html');
if ('HEAD' == req.method) return res.end();
res.end('Cannot ' + utils.escape(req.method) + ' ' + utils.escape(req.originalUrl) + '\n');
}
return;
}
try {
path = utils.parseUrl(req).pathname;
if (undefined == path) path = '/';
// skip this layer if the route doesn't match.
if (0 != path.toLowerCase().indexOf(layer.route.toLowerCase())) return next(err);
c = path[layer.route.length];
if (c && '/' != c && '.' != c) return next(err);
// Call the layer handler
// Trim off the part of the url that matches the route
removed = layer.route;
req.url = protohost + req.url.substr(protohost.length + removed.length);
// Ensure leading slash
if (!fqdn && '/' != req.url[0]) {
req.url = '/' + req.url;
slashAdded = true;
}
debug('%s %s : %s', layer.handle.name || 'anonymous', layer.route, req.originalUrl);
var arity = layer.handle.length;
if (err) {
if (arity === 4) {
layer.handle(err, req, res, next);
} else {
next(err);
}
} else if (arity < 4) {
layer.handle(req, res, next);
} else {
next();
}
} catch (e) {
next(e);
}
}
next();
};
/**
* Listen for connections.
*
* This method takes the same arguments
* as node's `http.Server#listen()`.
*
* HTTP and HTTPS:
*
* If you run your application both as HTTP
* and HTTPS you may wrap them individually,
* since your Connect "server" is really just
* a JavaScript `Function`.
*
* var connect = require('connect')
* , http = require('http')
* , https = require('https');
*
* var app = connect();
*
* http.createServer(app).listen(80);
* https.createServer(options, app).listen(443);
*
* @return {http.Server}
* @api public
*/
app.listen = function(){
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>listing directory {directory}</title>
<style>{style}</style>
<script>
function $(id){
var el = 'string' == typeof id
? document.getElementById(id)
: id;
el.on = function(event, fn){
if ('content loaded' == event) {
event = window.attachEvent ? "load" : "DOMContentLoaded";
}
el.addEventListener
? el.addEventListener(event, fn, false)
: el.attachEvent("on" + event, fn);
};
el.all = function(selector){
return $(el.querySelectorAll(selector));
};
el.each = function(fn){
for (var i = 0, len = el.length; i < len; ++i) {
fn($(el[i]), i);
}
};
el.getClasses = function(){
return this.getAttribute('class').split(/\s+/);
};
el.addClass = function(name){
var classes = this.getAttribute('class');
el.setAttribute('class', classes
? classes + ' ' + name
: name);
};
el.removeClass = function(name){
var classes = this.getClasses().filter(function(curr){
return curr != name;
});
this.setAttribute('class', classes.join(' '));
};
return el;
}
function search() {
var str = $('search').value
, links = $('files').all('a');
links.each(function(link){
var text = link.textContent;
if ('..' == text) return;
if (str.length && ~text.indexOf(str)) {
link.addClass('highlight');
} else {
link.removeClass('highlight');
}
});
}
$(window).on('content loaded', function(){
$('search').on('keyup', search);
});
</script>
</head>
<body class="directory">
<input id="search" type="text" placeholder="Search" autocomplete="off" />
<div id="wrapper">
<h1>{linked-path}</h1>
{files}
</div>
</body>
</html>
<html>
<head>
<meta charset='utf-8'>
<title>{error}</title>
<style>{style}</style>
</head>
<body>
<div id="wrapper">
<h1>{title}</h1>
<h2><em>{statusCode}</em> {error}</h2>
<ul id="stacktrace">{stack}</ul>
</div>
</body>
</html>
h( ��}@�_�U�C˩��~�6�G�����c�c�<�e�p›l�u4�ơu 
 
     
 
��������������������
�PNG

IHDR(-SgAMA�� �akPLTE�����\ڮ4ڤ5ګ6���ژ5ڟ5�����D��hگ6���ڳ6ڦ7�ޔ���ڪ6������گ5ګ5ڡ6�Ԕ��0��O��������r��v��t�����v���ښ6�����y��z��ٴ4����ܓ��;��]��&�������Ҁ؆,�Έ�ޑ��^���������Ӆ�������Ձ��I��i��.��������l��l��k������������s��l��9��j����۶<�����W�����|��������_�������������r��G����y�����ݧ��e��v�����۞��T����r�ږ��c�؎�׆���ڔ5َ3���؇-�����k�����`���؈-�s��tRNS@��f�IDAT�c` h������!�4���ݝKJJT�A�"����Ŗ���y2<@!a-ko' 59Y^f������Gpai�Bq^9+P��\�*�4��T����( �b��#���P6w �OIN
�і�d
仅%�ff��x��J�
"Ӳrr3�=m�
@�(����
C"��'�iǭIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e< IDAT����s���>�{��SG��3�;1Ј���.��bc#61�l��I��AKD"$�X$ �TD�y����UI��ޥ�w�O�6�9�D�B����כ��Y�}���o*��>���'o�{t�:& ���ﱋ����oG�����g��p�fS=�3Og~�jUiִe�[ǫ����;�k�ҏ�x��O������T��$��4ls1 ������w���}�؝��>��``�ʨ2��*c*��u˽��=v���̝���*� �D)��ke���������{���O�y,0CB0��(�4�;�.ݓ�3�*J �4� PC�%# 0C/$4
I��2O!E��6k3tGB��T����%`��YC��am�)6Ӱl0C/щ���*�ER4���\�g���� ˲ZW�7��{谆��$$|u����̰u/�ٮ�+���Jt�(��<W/k/0�������t׻�o�� ���� awS��f�֥�� U��ŷ/�RUODNA8��o��"�D8V5�jIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<uIDAT�M��U��=ߙ;�c�YA!Y؟H*FE�n�Z�)�����U��-K���T�r��Ybj?��D�V�N)��{���<��������f&�*a=� �A6��~[<�_�^:�g� 2Ӷ׏�������ʤ�dB:{i���������:�g�T(]�>��ʅt%� }6�q�2�=����V}��.�vDžw}z�᷶/���� �b�P����fvf`džY��:���L�I@A J��D(JΜ���#w�
�o�^7�=  ��BFh}RB�dq�\��ֆX=)jC�L5��QB ��Z�BP!�A�D6&:@ч,�!���d��LP
Z�]�A�B��P����$)H ��~b�Ԝ��/�G���&�F�d�HВ��:}�so��UkZ������q��7+�qj�^�a(�d��4��'�?�/��g�:x�#�m����;+�ǽ�gvf�}7-�>h�gߞ1+<��Y��#�;���*��Z���^|�g�EJZr�ҿN��G��u�7<c����FW���� /�Ngf�4��˪}��9��6ߵ�܉�Q��0��i�΃;ǣ|<"�M�@�Hi�����P�*}k�����'!1f����IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�K��e���7�{'�P�eD�IF��a&F Q�D�jE���t1�27� !#($o�ٽ ��0�2-%Ǚ��Ή�t�{�O�=T�LB�@Yu�;{:?��<�w˪q��L+7���� �ӓF� ����:��_.����G.۷e�Eh�4q�`�L��M� �J?��K&��^7ͭsZ���)Y�Y��{�~���"b(3���(�ASh��@5cxЪ�3̾�7���H
JP"�%B�P�p�� f M����m�k���d�6��j?)�m���i�vj����hK-d�h�H)%��Ma�RC��T�ZHTDB"���E�C�T� �P;2�d&(5�M������� ���̜���n��{
Ԛ2�$3H �SǏڻs���{��M�]�a����B��O�R$�ɯ?��7͛��������1�=y��` �K5�s�� ��A��_���vݬEvM_��uk<��j�G�ߺnv ]���3cx��+�����ߏ�j�V?M���8��
���'�#�X;��+O�.��՚�}FN����w��m�⯟=�l�[V���w��yk�ȶ�&����6W<s����,������Ο;���=v���׷�ɽ�s����"��O�7"nN9 l4s��^��;�w�[�侱Z����I�? ��NIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT=�Kh�e��������%��!5������ Jt!�dcA�������.7�t�BJp�Ľ A7�Q�X���`�X i�ƙI����s����w���wN��fU8
��Kp#������d�~j��L�]��t=����7�N�ʐC���;���[�\������K#ߟ�iEz4Ƚy C�Tܠp#%g�S����ND~��������_+="���!J��BEc| g��8��+ǁ�J��l�T@EPTA����6w�w�oDx��;Yx1�c(-4iΩ�F��\��`v�F B��� �m �6O��C�&e�C^�<f��@E A� g�6i��,(}y��Jy�� �>�����UW&��k�Q��##�'�d�QP)��?�Y�m�,*y�E�w���}�PZ�wx�H��Zā�r|��Z31^���B��B JaP����.��vF���b��Fwg�V�_� E叵6O�W�T�*J�P$G/}2���R}���u+��Ͼ�Fa0�p�jI������OL ��98�%���tM�����;��n��rx���lބ���h.t��q��sϼ���1K�7���/�S��]��98���������IDzR�,�y�@~\)0q���\5!F�TX��tv��jF�~�]uw���m��r�r&�`Y��E�qw�����d�� "�����������1��
���r;�Q\�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<)IDAT8�u�=hTA������l�nv��B0I%VA�Q��JA0�(Z�������`!��N'Q �e�i��$�Ƹ�w�������L3�c�332v����|t���& �* ���j�y�Tz6?5^�C��G��^��m�� �'gaza��ō7��7n�=�Pm`�HO��veK��#����y=fO�cd���H������N'0
b�2V�X!�1ln�� ' ���\��D ��
j��3�.�>����qk�KE��Nݯ���_4K�'�S�5�� XcZ*JJ#��~�0[�@j��Ӳ�^�~���1<zc�S��v"�@��D�YUV�c��vj�3�݌ �e�ř���NU�0b
V@I׬Kg��&����s]7 jH�o�-'�Z6�%��j�_5%r'jm×��͝l貈��(��f��|���Q#�H���FT AT��D�]��x��)�� ��,���1�@�U��L�k7���Kq�k=�]�CL�O�֥6���H0�:m�������!Қ^����jۀ�w޿�F��V�ڴX%hR.��?��)�zIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<vIDAT�K��e����o��l��4x��FA����EA����hF�R��h.�u�PW��h�`�j"� ATDJQ �6���xΜ�{{��L��~����gJ؆)d�͸�3W.�B\�����{F��v�����l���X�t�LH��-{��+Ο^?��/k�<�gJw j���?�A��>��8--���&;�T߶řlw^~p������=,+2�t�D�]� "(�������6�69]�f߅I@A J��D(J��wɆ��^�߹V7�=  ��BFh}RB�d���4jc��<�yR�2�
�$j)%��@6�+ (-��I�(�B�!��lLt�(�YRC&P��ɤ!����P�$�$���7�Bk)3IR�@��G�Y>��S���1�Xe�
}�O��$hɍ�#�N}ྦྷ�3��K?}b��B����8�L ��A##=}�-���`���x���U6o���_��
�q���u]ђ>郖���[6���༮��
�ak��rԋo��Z��%�Wλ��1�G_4\:�:�]����*,��/���_�w4��E�+�����;}����V� �����I���L��}�o���G��)g$���[?��/������`��/}���� -%nQP`IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�Oh�u���y�w��9�ge��&"2�&A�:u�Vt�.AA]<L�:����:dE��<Ě�jH3��ܜ��M�������Ia��_�^���"م�H !�U9O.����֍W� ���p��g>9�롢H낆 ��Ҋ?/���}�_'֏ jC E#m�*�Ưi)��LYU��ھ�}��ظx��q`�u��#Rk��RJ���j4$!� d�D!iOn��9�C/��i��m&N{�� A�� DJ�D��)u\�~���'ו:��nһaK A� I"%���wyĖU���־zRWO��؄�wR�D(S�HI�F��^<mK|����Z�}(5oh�����^?����g�
d�D��"%�+_Z����՚V4Wtn�o��y�{�2Ud( WD��}�k=�?���3:��4�B��}fFF���6lWg���CDL��Խ𓕼�ՋGd-���6sn��ޣR��ͤ�Pg� 2�?�𞗭�gĥ/��f��wc�{GճY��(T�
����#��K�����Ww<���ɱI?����+��*pbS�J;���V� �m���?�{����;,=yܣ}�� � �*��J�%T휫��SK�^���&%��HK�D�9)˔�:WP�J��Y�ո뵡Κ�(��]���IJr���*OA )�{O�r�����=�@ @+�<ZN���X��WyIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�K��e���?�� �yi 1+��Z�IH7 "(!*
�Em���m#\D� iэ�p�A��V&�!RV�V�86�s�����L����Ʌ#��K؄� ���_.N侘�|���ms���{������tC)1�4�LH�.�{�ۋ�>5{���/�r�Y�Aib�`�=vVS"��eնiz��u�[�{�s��^?y׳�-���g D���l�b�� �BDP���A�6���������[P ((A�P"�%Bi�_��^<o���Ԗ+4�#�Ȥ�BF�]RB�a0��'�\m�:�k��WZ(�I�E�PД0Є^��#V]x�Psق��HTDP"�%Bd�C�?�|�MF�ݣ�+*2��%��Df"����=���hdl���{�gO�*]
Ԛ2�$3��?9���Q�ԧ������7��MP��tIV�JW�jUO�o���qx�;�N�3͞9��kAj�j����R ��?���I�KV�~%���7�l}��_���?zж��cld��+����v�p�z����6=�N�;c�� �������=�U�����5�:�>ݙ:�����Z4|^�D㻏N��3',_���f���Gf��}'��r����s(3��Ǐ�����l'�(����s��w|�i������|0"6�\�ښ�����)k���ZeMYk�5K�mdw�+RO�����IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�M�UU��{���� ���S#
�r�� �-�M��RT�ڄ�K
�E�aBD!�Ѣ�H!j�M �dI�� e�c�yw�9}_�9[�������,G?ȁ��)�v�|8\�|�؞��!��w&�}���%1�Y�B g�.^��w⊿�M�����w{�OA ����b��19�䤮�N���™V�Q:�.�x��W��v���!�9碯��H("E ��d�p����36�(��>��Ԏ�gS�5�B��e㏻|��o~�+�U�Фںe����KZU�ݝ�齓���WU%N����[;�?�~��S'��S۲���nj�vG2j|�ӎ���4m�m-��5Dl���� y��аEw�S�A��^�nw��\[ּoxhD_����EB���M��'{_L�d`����ݢ���� kw�Z�2�+
�2J���n.����]���M�q璻M�&��㐲�*�2e5�&��7nz�Z��j��r����G<k�%��n�Չ<�kL�A�"�U��Dl#�8}�/gЙn|r�#O�=gd�l�7/��֕2R&�*�z��w���͉ξ���/^����/�rb���I!D#�7�"U �7�@ u�hF��ڲv7���^�꩔�2@Y�T7��z���P_1G� 圣L� � g�(�*�M��C !�k��BX��A��,tSJ��h�Y+X�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT�Kh�U���;��g&�d�&Mh�K��&h�Z��� ��Huݍ�.�U�� ^6"�(���؂��!B���" �bjB�m�$3��d�&M:�̜s>�GT�W?��VK&y��� �p!�m��o��~r��`@T�ß_����C�����*�R��d� �������u�uzp;0V�'"�cfkD@@x 8�Ԛ��{�y�шˡܪ�o��wFw����_w./�,�7���_�t��?=���S� ϋ�)fʊ����E~�����j�ή�O:����:5I��������`.;þ���X<J�Mz�ʞ6��=I�O��}���NO[-��W�s��$��� �?�G�l�Y!a 7ׄˋ� 7�$c�Dƙ��g�ת��������(߹K&�ή�ݴ�����"bkH&,��%��Q ��<���v�=�/#CTV�t�%(�����7��s�� [!6B֒� P��=��2��n-�X��2��a
�y^<�r����"��
����s.�';;��j��R�1��7�QX�@D!2�T �)����b~�z�F>�������;\�fY])�V.T�
����(�x����g�8��`O����­ ���U����|v� �*`hwLs; �
���k�'�M}9��CP�
^ ((�
�0��8� �w���\����OE���5� BIp>��Y��t����@#���t�D�BK,�b+[ޅ��*Ϝ���k�"򤢭((�((�”������4���LIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�Mh�e����~��|���ϗ������HF�!E�^�
"�<��:tAt)�."#���{��@�"�R+�T��mn�9����=�>���`���E�^��t!��HD�.���XN7o�wt��Y�^���_��am�iqP " \�5��qCoO�u�L�O��߆r�謫���rJ$��D�n���3�-����_˵�(�����K�|��v��҂��:��#S'�L�H�,�X�������=0��^4�+�$�ȉ��������\%W�O�k��u}ɛO.UuT/C A]�$���9�+:#�g,̖�Rfm�g�T�6�A�NY����(D�M]s���
��A ���H�@�ы&G.X��Q]�:��Z���ȡ�6-�J�
�0q��ի�4}��V�eŊ�ç ܫ���՞={zj(%DA=}����588���ۺu�LMMͫ�Ov�:��)4A�BS8��eR��r��Y�v�o�+����˖-�����޽{���%‡��JBI옘PU���~�V�G��)�Z���I�.]r��y5�ۍ�a���[Y ��/�|����!k֬���o�K�f�Jccc����Ҫ�}��v)1�H�\n�B��+��]khh���133�… z{{��U�an���Yݱ������Ɨ:p���o�r�� ���6=�9����4�,���7�6)"l�uhW{.v��� �����FFF��z}��r"s���/���0Wjk;vUIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�K�Ve���ΙGm�$�d7�fe�"��EIѢUPm�M�v�"ڹ+�6��.�v����]�2»6�8�f����ޞ'2Ӷw�>>1�J 1d�����+y �M�}xώ@d��>8z��77�-%'� �䍡�'��tf������Ȟs�Bi�ѶY�ۿ�A��>��K3���n�����:9?��SϾ����?|~�@D,��f�)F
m�� �"P���㩕�l��X�
$%(J��D(M��ߌ{=�4�|�v�H�2��� !d��'%� �Y����S����c�-��I��"�D��J6�QJ �6R!
(����D(J��0҄��8�ώn6��@�ڑIE"3�J�Dh溡U������J_�jM�I���v+��Π�\����\����}���A }�O�R]���k���ګ҅�֭���`�O���{���h�v�fz�灐j���:}�/����а�6w�����1���?Gf[�^߳rbĆ�EM\����;&���^����3�.\��٣��y:g����Z�ZsЋc�{5ҭn��c� ��l7�f�ZO�����G���d|r��S??��p���`�Y�κ���,���/`�x ^�{�(�&�O�r��v<-D���oz)"�H9@�H���sS����_]}r���m��
aS���/IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�?��s����~~����TI�!!�b�F� ,H8���b�1a5�l� �N�QB� b`�^8M�A��~����y*��޿������\�)(��0���k����������J�������i�ΆIH ���q�������~�����{�СM�кOg��Ԫ(2X2�s��,�}�O>�};���x��3o}~�W�tܠ��$��S�j�bjLEMa8�����y�޿��ww>�����J�Ҫ�*m*��u�=g6=W�|�v�jz :@B�(%U�Z��4�\��1����m��f��j"�j��A�ƚ6��hT��*J � V��ZJZ $@�1�0PHZ�(}
)B�e� ���� @)zc�Ԋe� ���0�2FH����d�#�����W?�9��</����;���C�9�A��x�y���轍y3t�-�;��w^�t'I����WM����eС*>��W����i!�Gc��{s���ގl6�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�_��s������w��Ι���2-�!���I(vaŅ\p��"nȕ;q��j��f�����$�!�q���眝�����<���zeϣ ��=\��G�@Y�j�����ϼ�w��@d�{_�f���l���X�4�LH'����Y�������K�޾uZ(M��͒��5%� +]V�Q�8�l�j�[ֶ�� ��?��靗|��}s"b,3�~S�
m�� �"P���z�
W\9oA;��=(���D(J��4���V� ]�4<��RM�yZ�Lچ2B�چ~��J�:R���5�D[F�B&�6��QB +�z����6�v�n!V���J��D� �&�J;��K��f�#���Z?(PGdR��L�R(���U�&��d��]z Vzj��]Ϯy��ZSf�� �޹�M�z�n���V����z˵����-t�.�J��&��J�3*�IQ���O2;㿣sM u�j�W���a��!z�Ģ�:�U�Y�b9�q�_9�D �Q��X1�s�eEM������ݸQ>�>N�Y�oN=0��|i���\��ZsЉo�:5�c�>�~�'�k� ?~����������K=�ܷs��p�;���,{����,���F4S�y��bFoݓ^��~��~�Ѝ��������mqCʅ�my����}���#��sth����n����o��?N#=�ƙ?IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�Kh�U���9ߙ[&��IzPR�5 �Ġ.�Z+�f��ƅ�Fp!"� Bw��ݹ�Ѕ�p!�`m�l��%hSm &�:��|3��}' �'����Z�r�;&�*�p�Ȉf � }붷޺xf��$���.~63y��nH�!��ڝ�ٟ[�,v����tf�|���!�}��;d�d�(�"q����bk}����^�zW84���-��Ɗ���d(%,D��8r��l���jr��R��\�4l��&�L�J}/}�ݨ����^N�5����Xe7˛9c��R`�##\���<�����赬T�v��|K�N��/�~�r��Ḳ���6�R� >���G�J����8�a�
�.[������<O(�2e��0��'XJG���AsȚP�\{��_Q*r
h�}Wn"/ � (�'ՑG�y,�`����������̡��]�p$�d�-��w�.���F�������ݡ������C|�<������ȼ�Zr�(�$�x��|vf�ھp�j�~n�V)��@w�����dB7=�߽� E>�&��0�7�|Y?K�6�F����+Qt�p}Χ�[|U{���� ��@�_W#2Qt"`�Π��k��!���9!&p�;�9Bp�����\�}�ѫK� �$���a���Nі�$�N�? =�;"��@����]�0�?c�|�5$�#IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�K��e��������Z��Y���L ����( Z�)�.�$��"ZDTAD���(�Tav��ݴ2�15o3�x�9�?���ĝ���>�d�%l�Bd�UW��S�q��ڱ��m}��O���G��/%�%� ҉����v�����~�w�g/o�hKkG�fٯ�5%� +ì�.] ݾ�b�^����X�u�ԛ/޼v�X�2{rr���2�i�^� �BDP��%#��<n��ѫ����u�=�5s��mPA
�JP�p��W_�(�V��1�z��?y��ɳ���I�BF�ä��a$��N���+Ͽm���>��~���� v��$�(RJD ��dS��]y�6�>���?<Y�͕��=s�_�%B�zMh�'��y�����{ϯ��A���}���R;2�Hd&���I3?���'Ml�0�C����w�`:���5e&IJ0��~3�{�n���3~�����f�SƮ�d�}� +�$+����C�ػO{�U6�t��k�����Z���5w�5E����K5�?�-�36��˖_��y����"u��Ε+������|�v��p�����O�ɞSG�8w��7:fb�+V����㝬i0�h��Z�Z�?��A5R
P��ҡ��,��жQ�a������#��7�gf�T���d2� j���aW�h#���ވ�1�"I"%�`���}[��R�Y^��13IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<QIDAT�1��e����}���AW��`^� E�h 6�\C�� N��m-
E) ��RX�AD�V�Y�������������_~�����.��(�^z�Q���]�w{፳�����:�ó��޺�ҩ �`��g^9�o��{����2S�m��t��>KGO�̵�ο�,.����W���1޶��;d"]�����w�W�����Eu��Ve���i-Cp�-���^}��1[������|nm{�h���g��?�6�7�[�1 �ila������}�>���:���2�΍�鏍�ui���v��C;?��Ҥ[�6�OD��:�ƹ�M������/&}�8�\��v�N^|b�:�b��q.k����-�RX��$�bq�\�7M��y|� Cg��sQ���A��T}�%E$;z�+��� �&�M�&j#�E��d�LE)E&Աjc����DKCGW:��.0a�@f �U�M�&k�HТb��(\ޞNO�ڷ*k�����N���T�� x��/?ݍ�p�����,�2�JQ
P�ww�������r�n�ܢ AQJd��YZ��?�5I83��IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT����s���>��מ��*�v"�6iD ��@��"�DLb�5�D���h�2HDDIY$⥪ ����<��uUϼs��c�^� �@!E�������������g�*������ǯ�9=F��@\�q��W�����?|���ϟ݃�Tm�����FE�5mY�����n����/�ʑ��מz�;>;��ހ�:�d�N�f0�`*�
�Ա����r���v�c�€P00�QeTUF�1����鞣9Q^}�N�fzf��y��T�5�2Ol3�t5�{ѽ����y,0CB0��(�4�[F��I j��F�A����֒����$` t���"DY����# !@�R*̃�� �0�ڬ!M�6A��iX�0C/щ�o�"��W*Ҁ�
�|�`�� ˲ZW�w��{�4�*��% �*�����qpk3,{�Kw�׍o����
-*5b�G/k/0�������t�_o���d �}g ;��Jo��w]�W��*�?�������!�����y���!��!�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�mh�e���u?�9���|��b*�VI+R$he!������A�!A�e��SB?D�-!��}�r$�sJ�Z��f��6u;��<�}]�~��ǽoJu۝�X��)Au|����;3���Sy�r�ѝuN  �����c��L^�U�YR>�^�.���>i�|�ĉ��)DSB0�Y���x��3�S����<����{��Zp"��̒|��9���D@(�Jy�+�gms��K�#�@��N'��%¿�U�,̘�N�&�%�x3� ���Ѹ��1��\���0 i��a5Y��~� ����/�0`�6���︯~ �/mGM�Q��T*n�/\�i��fo�" ��`Y����
�&�%KM�WCd�J��g��0 03��\b��N��-���gN�{�)nMW G������X𪆙��a�Xw3P���ポl[���+9��'��9ȹO�W|T���AS�_��q�:���v����\�O�
����`��/��
� �#��!M ����|�4Yx�i-۷���ȟ�����!Db�U��;� ��{��K�6�-�X�:�E�"�W���+_�!���T-����������{�������! �,��B��W׵�݋7��dپִhfm����w�ܙ���+"h!��������E���}��ȓ��``��&19�k�y&��w�%ńev��qIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�M�Ve����7��8���b�Dfai*QT�@�h妍�P;����]B�@hQTP��6Rd%XAT(T����0���s���x����M(a6�/�AV]��-_��c��������gN�ʞ���|� ��#�[�����/?�_8s|qmibG�m���)%de�Uץ�������O�l]��z���^�té������̦�m�� �"Pm��Yܻٶ�'׵s3�@I
J��D(J.][s���} ��÷h&���L��8)�m�e���4��Z��kR���6�DE�����'.X?�갳a��+k>+��4;u����ʚ/~�{���������=�o��v�:��_�� >�rI�q�ԎL*2�V#�nt����+���"��֜�LI)�IF��ء�jM�F�n Vn��a�G5�^�n(#e����?y��?��Wu\%2�dz�j�nl<f�܄n-��88{! Ɲ�L ����5eV]72v�nPkWk��K�����D�-���!
IF5�����EWWnjG���^�����3Y�T��S�1�Z���/�8�U(�V�tp�[�)���6"�=}���#��� �K�o��ڶ��goxr�>�-��[�6ә��F��:���;�=2?9o���Ց�U�X�U�/��������F�/�ˮIEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT(�u�[� E���&}������O(��@_֖��9a.і�?v�9\ ���bB�WҴJ�/\Y���+DI��O�)n,T(pbe���F��8��D�`����ñ_9υ�N�8�s]蔊�d!���4����i���/&~��<EOK��E�K�.�:'� 1%�u;Š/2�[�y�OIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��KQ��eV�J���q�ҕ`����Q�X>b%��BB �*t!�ЕB�iU| Q�d&�d��+3�D��� i���ν��s9U���0��O�ƨq�
�e�s��]>�G9�9|>��BI����uE��� �� À���T
�$q�(������ �&]05�w��Lӄ�iH�ӈ�b\��d��:����X`�6����߸�TUE</)���X��@�;mk���n��D"�3����}@�3G�H(�,˕��+��!::���? �C=!QE�8���/0�/a�� ���fyHB�ؒ�P/�H�����v�A�mn��������)+/� �8���Ŀ�B������"nƆ��@Z[)�j~Uh��^���N��3Q��O�R6��]����n��^Sݑ����I1��Oc�
c������4e�sKx�C�A� fw5>��V�<c�h�[�$9���ȶIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<*IDAT8ˍ�͋RQƧ�� ��6������H*)ZS���E����$���ZD�Е�ĕ� .�Р 1�
�E���z����<��Ls�q���pϽ缿���9��)rٸf��C\����M��.cl8 0I|^���(d0C���@UUK�$���V� H�Z��� ���@ 4M�a�޼�����t�]��u����$~� 2�`#��v��U��/@�e4�=% O�������B�2�;��R�ͦ�I�R� �1���V��~Ƴ2���#��h�Z���hKW��.�x�kw��B��L&#�O�����d�����6ݐ��P�<ba*�B�\��;��>�D��{�; ����XL�" �
��u��S� ��2d�ƻ�07>�,�D<G6��i�B���F��Y����U�s0��u��O�c���R �d��B��8V���(�}�X� �u �Wm�Y$�H��n�1)� ���t|?��>�76'�@�XG"�r9-�y����8��]"��������L�?�y[��ӿ�ح�YQ�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8˕��K�P�[����8���V�"���Apj�������[�JS,�889U�`���(Z�R�����ǜKSӷ��pr�/�w�k`PkR�Y�e��U�ѣ���+�J�^�7 ���>���,4�E(��R.�C&�A<�X,I�� ,#�P.�Q�V�J
d�Y$ (�J�K���th�D �|�d�k�f&�6�:Fk�9�6J*��Ȳ��)=��0�t:���q� A#�Z�&r!��.Bo8
ذw�����ٛ��4_�q�`�ST»��ʼn-�<VL����p[��D��,Od��>�_�Z?^D(z }=~H@����� �a\a�0��m4�e5eE���0�������<�#�̕�"�&`��AGya{���4)l��}�O�/�����!&RIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�KK�`���
���I]�t�^�����R�Y��U�8L�V7*�ځʠ0^t�JEQT*R��IS�&����~�S/���\�'��+PB����aT�A9㣓s <�lV���B/��C�n�[PE�d2�T*�H$�idY�I� ��#��H@d2X����i��u��pA:����������{���ksƷ,÷��V {�:P�0 �b�g-��]z��򍰦b��aE���(XK�o�UUy%�H�Q�,�� _���l�ܺ��ٿ� �HB��<�����I�+��a⻻� k�tlK�� �A�/ ��(*j:^�|D.���% ��&�!/T6w�;s�� �9�*H@P5����w1��?C�qȄ��00}���+�<HP����[�5�.�Ω0Z�+��^baU~vS$��� ;�ZQ��)/
�Ż� � ���A� (E7�fϚ|��e$y�r���_vN��k)7FIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<"IDAT8ˍ�=�ZA�߹*��Ic��4)$A��,
k����f!��X����w���&���&**qר�\�?W��3���G����9��0�9�`�y(�)L���Qܑn'� ��j�Z�6����n�cA�<�J}���(�;���l����!��1����JH����t�1�LĂ�r����"��)�F���I�b��b�l6+!,�H|�7�FE"�"$�I��qP��d2�0�͗
Qo"�|>TUE�P���/���O�J%�b18�N8D�Q P5G��nG�XD ���B(��_-��W���|@�ل�jE�\��b��t<(����~|�}�ܝB�Xq�Vx�!��! �R����<e�ɡ��f÷��.0f��9�.��D�.�d,�]�]@r�4v��邛��S�=P��4 �z�O48�J� ܢZ�J�!'rrg���}鬪��;2QT�h4�Ղ��s^�WV# ��K@��NS�j�3����^f�������<H���5�����sƎ� *��'�麾�1�]P���sV��Y9�]<�����@�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<,IDAT8ˍ�_k�P�g?���d����G�J:��bE/d�u��@� ���.ZA7� �ԭ�е�f�]�6iҦi��=?�9ڬ�� <���9�y�w ��^)���p�
rC� �q4����^���D"sÒa�$ ��4:���6$I�z��h4H� ��$f6�V>�O+x�iB(���v�idY�˲�6��b�%�;v��#�����B6}T$�HeEZ��f�y�H�'4<�ֈn���I�@�&p�x}�Y��7�QEᕈ�x*��TEviPC�Ճf��#��:"��]�y?TU=,�+�|�BbGE����br���o5� ӱ�� ��0Q Xxߨ~����ٚ�W�Pu&����O3�<��r #̭�EG��"|g.6\l��-�x�2������ ��V��/�H���@�5����t�F�����G�s5^����[.��>�O/����@86�-*�:�/�E��@0  ~�+��g"�n-dqc�3�s�̝��GqF�n�C�9A?��\���l�<a�n�O�`�I�1����`�����IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�=O�P�)C��0�[��!�k�_@� �@
��
&~@[%C
*UA���V�J�;dB� ,@l�_��ر�o}n�qHP����=�<>���.]����D\��c�n?/ �8������F��N�q$�ɷAIP0Hɦi²,�0�iDQdA�J�|�������mT�U�&��2t]�$ILP�T@I��MIP�[5��AJ(�J�e��%6O�(��~�0�[d�ā���a��(��*�y�U�t�O�л�����M�̾��_
^�I��
�P%4UUo�VNcc_E{v_B|�NuX�ת��A��"��*k�fB"_0�-K?�6��љ��c.oal���7�.`Z�������:g�|)bb��ܬ�*��ā�G�
����å��2��D����a.��a<�g�ɏ^f$V �u�Z�A�m� {���T�o�Y�����7�A��A��{:;r���{ЁK_p-pdQ�>���2��A�$���t�C��ʘ��?�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�OkA�k//9ւG/"�!GE �Ԇ4M[Ү�D���!�@?�?�=�'�%�����n6�I�9��7&�.<,̻�ggޙ�0G��dER�%�:�� 6�G�f!�h6�����@� ����}x��n��$�i��jiI�C� �"�q�ބ�1X���~��v�=�dC�p
(�9�m�SKR=I�� �������AuA�@��z)�㨙t:�I��Nj����S�����X�� �h@ ����fB�p]���y����K�o���$�*&�K��*h�/�����C=!���5_�=Fo�8���ܶo'�^��޼�W+a
%!����v�泈{p�M�W?�3�3��e��J��G�\:=8+�˝���
� U��$58�j�{�$QPxJ@��|~�z_�U_SEw��8�\�2%Hҿ��9���n�3�ւ?��lPH�ۄ�B�ތ`�$���)t��~���{F�f:IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�_O�P��o�?��}�"Je&^�xO �h���� ?W���qdR�0�B����v�v�s��ԕn�M~iӞ�wz�� `�E �+�C�r!��eY��8���=G<��7 �P�ՠ�*J���
$�@������V�%ό���4 �rY
��&x�d2�K�n�(K�^�cI��n-#�׊j�UV`�x~ ������nO�+'.�+ �G�Z�Lf���?}Y�(y�,{0?�h�3��}�㹋b��^�_
�~�n�7�/���!0��� V�&���4�|.:�&�d���ɔ�[�#��B������?��X%N5[���å@��gTP���|{���±���F�L۶%��e�a���(�Y1*��x�F��U7� vqs4��hʳ�)�YG)��Z�����E�}�/��ܣ� ~p�0���� �Xr��܆���?V^֫��T�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<fIDAT8˅�MK�Q����̼3N�Nb��a`T�B�6�5�M����_��jS��֢����H�D~V̌��;�眧�69���9p��澹�������g�""a r8��r�$�9'��8�edd�� hiM��W�U��j��T*R*�d~~^�s2;;+��� ����gP�<(�ӛf�拉Š5@�ZM��k�n���)
���B`ll�����5��ޑ�:�_��Z�
$h��BCC(�MfXk��\>*t�v3�3G�͓�/����3�9v���� >!�2��eB8����\�(�$�Yaz�e��ӵ @&JQ��`��Z��c�eE�l������\&�-� 8�1h�QJ�6�����7f��7wn��  �u����[����^?1֓��5BPJ`�iVP+��~|&�V:�͙��k1sK]�>�#�J��Xm\_Me���Ks�f�\������O�?Ż:��|����5�K�*D�Mr�p��{��*KQk������Ή��x�z��ɗ����x�i�W�z��/�M0YYZZTٜb��N�:��� ���Τ#�6 /��ֵ�H�^�0�d�A#��~9�N��X��F��~{TB�|v9�IEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT(�u�Mn�0��E��9GBꂫt�P k�BH�$�4]8�&����瑟��J&ѨF%���@d�s3W$�q&гݨyFz�\<jU�RU��wN|qDk��G2��H�Ǽg���' �7�=�Bp��{��K��q�=���1�,=��Фon���Y�( U��<��B��*�D�2���u�M�x#��>���M��M��IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��kQū��]w%R�.�Z�V� (HWJەJ���QqF�n�H�SBm��PA7� �EII�������$��w��d�Fځü���o�wn��0��v�A��q\�
��j6�[�V ���!$$ ��Z���bٶ �4��*�Ȳ QcH��t��}�a��$�uaY4M�z��H6�� !����C���2s*2s
f����jq8�]�wX�59q}=5QP ��� �Rq���Z^��}GV ���T*�6�(峋�]z?��.h}������۩����Q�Vۀ�Y7Y��CƓ�� ��ʴ�3�\��7xM�R �x!c���op�Bp��c�rW�Ivp�TJ3@�ɼ��s�X^sx���C���"K1����ѫ�+8s��|)�0�S�7q�^˫6�Y����u��6^;���<�pV,c��:��Qy ��h�P ��>��X�lv�:�lWW{��s�E[1��0+�G{ fg]>� @/A�0Αh�{�=��ҧ���IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<NIDAT8ˍ��kAƫ���@"�G_�,>�X,� 5��R���x M�چ4�i4!��R�Tkђ
�ň��msiv�l���S?g6&��3��f�9s��Q���HE4��%���)�Z�Vh6�ۭV �D�a�XLJ�0@͒$�\.wU,!2�� I��`� �wQ=P�VQ��噪T*�P(��8P�T@/�Z�m ����n���yL�9�r�e5���Qw�W�Q �,�[�99n��n=���79ؗ����1������琀����20�¸8�_#�m���6�c����,�j��|zo
ZgcO��MGqv2�3L��ȳ�r8xb�2������M�#�M�ϲ08���&0���j� ��p�������.[0"���C�La!���L��0V>���m��;��� �,J�OG#���:�dg��fq��н��98zcC3��Õ���5&A(�a����%�f��,f0����\�,'� �e<�j/LE�QW
W�m�m,�Hɼ� N�#�
U\&f�5�F�!�@�#�_/b�H
-e/�������~A�/\�;1��%�{�~
�A;wD[��'�V��~.m�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8ˍ��kSQ��k� .�W�q���:��΍݋��\!X%Ym7A�.B"Đ�@��@�����9y!S�yϱyM�b|��ws�{�{7l�.�l����rݚ`k:�����r��E�yh4�w�u�6��cH�$3 ����h4XR�ՠ�je  ��j��M4E$A8F0d���4��" &� h�N�Gb2��|��l6�z���j�K�U*��q�\.������.��}�-_����?����o3��
>������,i6��V�g�ш�p����>���G�.��ç�O�m�5~}�B���yt:�3��f���9'L�\�ە��i��n�h4�e����I�R��Ár���Z.�C"��q*�b(Db�/�N���~
�/�J铠�n�l6�|6�q��@�R�ŽL&�
�ڥHB�$�z��=*>'�5�RyW���b1�h�Z, �Bp:����Z! NO��B�KX�V�g�d���� h >���=��Yp*�%�h�2��#��k�M�\�:�����{�F`k��IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��j"A�MbV �2��2/�bP$j��lu!���q'F���kN��.�\����������� �qƺd]�G�SUg\�V��v���v;�L�NN��b\�x�X�r�T�N�4���� �����R��n�q�������T���$e2�u���!��s�L&���`����
����[��z�ш��5� )h4�H$�`6��`0�Iz�ŵ
o�jPi�&��(p'�N�����|�'��mj�ZT(� 
Є� ��
��k�a�l6e�R��0���l4!p��t���ipg�j�*�R�$����*�f���f<�Fc:f�s���yx�^� ���:߱Q��%�X�r�,��!�@(>�Oq��f tׇ|p���;*�J���i ��v����q�D"�s�N�h:x/x��O����_��O�W�7�On� �)�sy��9 �xΦ���_K��<����IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8˕�;KBa�գ���nBCM�MBh����("�Zڂ���38��PC.(8TK �4�u∷�=^����#��~������y�
���Q��a��$CCUM`�$)Y*�*�r�`��r��Z%��H�l6 Q�R)$ ��aY
���|�����!�ϣP(�O"�� �L"�Ȃ\.:��v˒6�H@�t:�h4��Rm&�&k���ީo��X,&W"BwA+�������/h�P;4 $ �Ţ<� ���['^�f���2~^��PH!�9Ǜ݄��)��^�׻l�V|f�N_���:� �iC������Ͼ�����4����fm�QP�6���qd/���*�2�[|V�G� sl�"���2}ݜ �9 ��GdO�� 5�6m�k���Iz]�˥a��� T��� _��#��>U��)ےIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8ˍ��n�@EK��J�CQ�����o<%A  U%R0�u�T�!�DI��\�mdlǹ߯�f΀M�E��~�g�:���X!��6���t�����k��=��.��9�[����Z�,�����1 \u:�Z-T�U1M>�υ8�[�|�T*!�N#�L"��B�u�z=��mض���T������r9O&�y�J��f�o$S�р$I���<E�V��� 3o�y4���*��.X;\� �\.sH�^� �� `f��)�£RD�d2�˲���<L��.@��
�z�[�B<:.*:�^�9�'@`j�@. �x_1ʻ�RO�3%����qnu�8=CJ3�>�t:婜�.��m������$N��8��0,��3�|�!Db��(� �F��a�7�8�v�c�q5���jױ��_
%��D�?�(z�{/=�xq\�B.�Ç�!d-�@ 6#^�8rΏ�`�������6��� ,�4�8������݃kt�~AZ������ۻX%� ��#���?y"�CupX�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��KSa�WEDBԅЍ�}a? ��P3B� ��]t� +$aA ��I���X^�ns�\[sK�p;;g�������ɦ�p���s���6qt �w�`��԰U]�|^)�J�r�L+�:N�sb��&��\.���u��,�L�d2iI�YBӗ������)��v��>)��iR(��D�4E!�J��߰�EYyZ���,�ǽ�M�!�a��*�?fH�>��>�ƧG�9�Q7���&�h�޳|�}+� ����n�� ���$�j������cM����}w����+���U;��}��92XͶ$�����jx�w?Y�=“g�~���Z��\�-��32L>vE�d��i��/��� �e��{;���^D� ��t��T�߈�]v��5Jh�.�A���z�{��������<N
� ���̍%�S,��(��9���N\ϱ8s�l2ji#����A *2�8 '�ߧ˅��E�&�Q�!%{�r�;d�?!n�,���pIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8˕��OA��W�5F"�������w ���xT#Ab��!jژ&������a�"I5Qh���]������v�?gF�H�'y2��ϼ��N@��c��� ')GX� �#B,��6�M� ��p�i�D�Yq�^�mۻT*���R��%�� �쑰��l���<�όZ�˲��*8�vH4�}V,`VP�V�iھH;wұG@c�n`k�[D�u�w"����V��\\���( m �a^�c�U����� g^�H�%���@�:��������� k� �A�y
�wȧ��7H{��h����v0 R��4BØ���0r�����#��i8����- �ʿG��5O�t����m�&Lzq��]��W���q�'(��a�v0�*�%ۙ9�)x����vv�F�㳓�G�Gr�\9}���x ��g�b��>��G߀WN�����_���}���$G�bBp�Iij�&{-�aI?�@>L�'��n� �����"��$>����Z� �?�zL�^IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<)IDAT8ˍ�OHTQ���;�D�:�L���&���DD��)-7A����Q�Z�����b�3�c�8o޻��6��T�|���;��)@)�H>�W���P� ��E�5�ȿ�����v��4�Q�R�$�r�Y^^���E1���‚����!jp�Wֲ9'�u8'8+X�p�a����q��$a�#�ˍ �ep�W6R��)
�d��\���/�-�$�4E�����%����Ȥ���q��"�9�1�R)�֎&����-�J��C��4�c������y�����Z���Z��ʋ����~>|��4�¼�М��f�5W� ��Š��}��h���b�0��|����3����Y=�jK����<~�� �����~TT���$՚F�021����LW'$���d�0�(��}���m��+g�Hli�ZÞ�>�Ͼ��珄���Q� "(�p�5���� ��}i��a�����p��!�i|g���V�S�M�Ƙ��bsrZ�^�P8kmp��֮V�V�]�:{�L`��/��G*�_>IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<6IDAT8ˍ��k�a�W�@�%t\#
�&j�Pɉ�lk�h�k���E�$�XZl��:�2X��#�0Z���/��v]=��zg�h/\/�s����x�$��d3I��]L�_ @\�V��Fc��l���9�^�VH+@J�b��R�$(��!�� �sH,��� ��pz�ZG5�e�~�`px C�K�T*(
�f�H$P.�AE��o3nq��Ϻf�@s�?A�Έ��sx4����y$��F�L���L[alb
�#ZL���Қ`w=��[�h�0J*��,//�$�CS:�枾��<���i��Y1���4n����B�P�tz ��g�Ύ@�7A�����@�PA�>q���t���!4?eB �2�.�q��KL�p���,�F�l�<��N���z�Z�CH`��U�}�m���Cx
�l���o?��&�����Hԍ0Xyu��8䪣_��� n����ö�;��: ��/@�RQ�5�������
�!�+�<��Dzr�B0�J���M����R�vZ�s�=h��
���Jt�3�[���d��G���_�ٝ]<W�=IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��KQƵE
]7���4�m���Z��… Q� A|�E)�d�?F(Rh�tӢ��.D�j2�;�;��{�f���6s��;�|����$�.����z&����!˲j���h'q�t:���Ȭ�:c�EA�ZE�P��|>�L&�A��N��4a�6�o�a��m�P��P,%�0 P�l6{i��>8��vn* �r
���T*�If�
p��aμ_[�13 ����\.�Nr��=�am�'��H��������B�P�J�=��=�9�z��/�����bB�k�&Am��O��� �#a�͔7��82���6���Qe�� s��X@]Ȁ�����|���ė������s���4���}"�U�DO���ށ�����~y-�BW�� {iJl��x�&�U@��?����o��q��Q���6�>��ǘ�]Mn��M�G�sS����v�����/�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8ˍ��kSaƛ�8���bA�c�JE'�������:tp�!��KEp�i��C�6�iC�6چ��ބ4��77��9�Mz��� �����sx�����bM�����,���v�}`��a���Y�:<�;'� �s�لi���u�j5��e)�J�z�H���<h�Z�t:H7x�����J �4M,˂4��|!�1�u�Lo��I�X��r�:ߍ��2��~m=���on���>|'�T*5I�X<�l�7(���[X� �E�'y4x-ɣZ�h%>�r�_�s�
w���G]C���@d�0h�L�KW��1<H�1��E pW�t�]��@DÀx�5�������Bs�<&����U �iNe��zf�|��5�����h)�����@$�!w%� �t�U6 ���{��X�%V�3��:D#_p$�{���7���������m5��N�P�B�}���`YGRx����� �<�u�� 28��.dJ���4�[C�^`\ �����?�7³ �WIEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<$IDAT(�m�=KB���[���#�. 54��PQC5h8��B��EI��1�aEZ=�s�n�#�ų^��p iDA} h!$��Uv�.��X�t�� �Y��Ĥ�'��<��!�O�K�:u�)S¥F�hD�����S㋷�+�u������I�wJ<yAwm�!�����,%N�rO���? �-���+���E.�a�eR��뻐�� 4����>�x@��Ӡ��D����9"N�49���p��d�<
���)���M�+�$E�Y�5��Y;�_!o�Ɛ������\�H��IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8˕�MKQ��M�!Z�� ��M� ��[�.�PladdF������qѢ@Â
�m��s����5?��0s��s���ak�a`�0�����%0��Xl4��f��# �%��H�j�
��۔J%
��iY�J�
�:$��$� �Z �$�O�R��X,"���A@�D"Y�#�� (P.���f{Zj�D�!`m�7�;�������J��dA7j UB���� �j�fB� �z�.υ$ĿT ��C��]� (�@��N�p>���]P8c6��� #��֍+ޥ/�>?>�/�'��n�qg�ջ�7lQ+LG�����Ղ�AsX���zߋk�f���껰Ȇij�}/��bg���<���7G!�Z�#ɠ�lrs`%Co����4�t��Q��Л1�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8˕��KQ�(R�J}�B��.FSkJ7-�"�RQ�U����P�FQH�P��4�UĂTJq���B�JB^�s�֯� ���h��1s�o��0��|���ܠ�TXPD"o<?J$�(�9�z��,��T !�I|><�v���l6 �S-�!#��WJ ������P(z��h�s�B��~8��s-%�����8�@��w���%��j���F��I�{�����H�j�-���8�Pb���\kD��=�"����#��X���r���ѿ�K:�-?�J�����Ǘ����s+�z���ڍ>�v��iQ�|����,;-�����=�|���z;֚1��s[�x��
w߱DZ��������eo��q�o�q�5f���n�C����}��R�X
<�-A�.�ӷQ5��W ���@�Y�2�dU��,��O��i�I1v M�(5y���@(
�l���Y�P���R�49�T���}�vs��IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�KK�Pǵ�����|�R7�B7nD�0�B�R�: � tD����G)��� �υ�}hT&1ɤyg���{n�%�H ��=�w�9p���id41���!�ȋ�J��U�V�k��`�$i(*�
���y|�ض �4��*�(��L&#$ar= (�X,�\.�7�.,˂�i\�l6�W
ZRRj����- ��/$��8t]���I(x�b$5�i1��Ë���x�a+�B�W"��m}�m����= s;&fs&L�,$T ��0�x���_��Y�� �/iZ+@Z�E%�̈́D����F_���������y�W*>�������S{'.�,��V�<H@P5BК�L��H>�H/�H/�<y�����+����d�������L��`�J����Ք���yt��蘖1�� A�<�;kh�:y�hn{��������W��E����U���;���]{õ�H�ـ|:�O2�u��F ��c�Ch�Y��S�M�nIEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT(�}��� `�9j�t�g[��G�ܲD��N��0-���C?�#DX�������l�2�B��Y|氍D�g�:J�+�8P؍D���RhP��7=N�3 �I D��x��%���gG��0���2�����c��� ��(�� �f���W.hlǠԝ}ewqCC=QYe�,N!V��N��P�L=TIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��kA�S��Pς�HɭЊՃz�*X�A��=���!�E!EI�)��zi��Q��R�Jj ]kQR�d����5��&>�t�&5���.��}fް/ "���P��%��<�";���8��Z�Z��$�#�L>h��n2�� ˲��4 �J����B��T*���A�m���O�a`��T*��9h�t:�Cv��d�u�ryW��;�h��z��AUU��Ȳ�Ь0D�~f�zy���������x�2��q�쾑��_�s(e��'`k�P���x|�~q�����Q���p�8��$X6��4��Z�联��07�?�a篃��u��b�?�����
��KХ!��S�7a����aw�\W�l�9��N�4�og��cXI��˽~����޽�͗��!��a�y�j <׏�}Ƚ��k0��V#O� G��g c�L��kp�����Xy��ݳ^��=t$\ݹ[���SG��'b�'��n�5U���Mu���+�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e</IDAT8ˍ��kQ�����;���tW��h���+A[���>�t�& 5��*�&BP�EiA��X-Z[��-i4ii��Lf&�yf�?zijZ������}�s.�@�|[���������U m�ΕJ��r��� ���%Ղ&6 �i:��
EQ ��<�`0�H*�u��;ڳ�Ӑ�����8�/�>��>B �X�Z$
��PA��� _\O�D'�d�� ��P�� ��(�O �BMKl&T��ɳ�q�6�c� �9?�i/��f(�>�I���d�qܒ@߿13��v��}h��0~v!�jr�^����Q��il�,/ �Q�+�q�hs~�w�M����0?�8q�� a���w��q��D����r3�L��> =�#�[P�^�ߜ@j��G�!N�"o(l�G��wu��������r��T� 7�4r�����|6�Z)
�Ҳ!�g����b�y��ǐx��h R��"��W��ڐ|q�T=���� WS#�_��1����eo]��K���u�)K�������#�+�Edҋ��ɿ:��*A=��a�+�կ� _Z��MHyBIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�IK�@���C��7����/�= ŋ�Im������\� E*q�[A\j�U��6�4S�:_4�U1 d�g�2..1�����TR���!��g���i�� �n�{�\b )��:c��r9d2��qK�(
<�7 �
��0P(�'�i��,��%���E�^�%�!�� (��*���JgR�M j|N�w�mWI�R�NdY�[P�W ��#�N;�f�P ��5&��1��_�/�L�}~��`7
�]�c�V��i���3��$��Q�A4)°+�������E8�!��� ��_j�8�������C��=L=p����U��U��^���X}�L�wr }�u=�R��Y�8�yƂ8��]c�w��!�6i��Mwֶ�w�A�}L���L6�����xG)�>_o��$qp�-����7Z����7�����ZIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<TIDAT8˅��KTQ�?����8�O3��J�@�Z�[7�]HX B�~�m�F
�A�@��EB���E��.*��(�y��j��8E���{��|��� ���O (z��{"��z������7��u����8H�=�­fx ��q���G^(6���pΑ�)�B���-VVV���/� ��2~r���F���8�)t��^BJ��!a233�sssK�k*�1G�:HbMR7��uR���,J)��-�L&C.�cvvv�֘����Q9�x!ω�<Qy�Wo6�q�4RJ�Rd2*�
QQ*�Z��P�~s������U�YO9���Oq��]P]Xk�s�0 ��l�,�gF�&E��K�f�^�M��K���ɏ�C)Ռ!D35�ӵk��+ظ��]��Ⱦ v>>i�V�RA����҃�U�5���u�")��ym�������}���/����.�::�;�f��dit���8���[���uw�Z[)�R� T����3��@f"s��ÏOoɏ^�my�hY���0�X�E\w)]:�z �]���m�|��R� ΋�6N$I�Z)��>�t��Z6���.0ܨ�~��X���X��ҡ3IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��KSQ�G�g$T�C�aYQ�hEHY�� ��eIŌ(� !)J��i��~1
��� _p]��~1u�v���{w�v�݋~;��^��.|~:�����r���Pr)y�`+eS�.M��i������8�=��d�J�lgK��y1W��u��Ħ,����!# BEx�^&�y���I�a2s;��O�*�����~� PU��@�$����B!���ߒoDz�r�\�>�~!�� ���^F�Z��D���#D7��2�gc�V~YB��� �B���|ܢ�e/.Y����f����@����;'&:��m���<���»��+�*�#��ƽ��x N�ȓbȽ-PF:����y_�p��CS�DIf�Ȳ�(�-��ㅙ��w�i!�Q���cBd��&��"|�ª�B�I
4�q�:� ��b�(�0Ua�-���y�
�OU�-� � �>RJ�4�ȟ�a�oKd��5 J;y2zo�{�� ��h��� ��W���������D�a.�u�6��FfP�������`e����a7��W؝�.��@&w��<��:���O����j��nt��u�N��~��a(��=�+h
������ �D@�?�5&H[�}4��~�eJ/�w��i� ]��@_��_�
�8O �pIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<BIDAT8ˍ��OA��/�8&b���&('��ś1���$���41��PK�($�D�i�P��)�VmR�
i������~��l��62�s��=3�ym6�m�A�A�8`�����t]���V�TB5�}8�NG5 ]�4,�2E�E<�#�J����r�L���nop��o���n
�MӐ�硨E�$I�t���
z�þ��٫11�Y�A��pB�N�Xde���\.�L&c��� �`���>����{��(Q�c~S����0'[�B�nD���y� �i��F}����]���uL�Kx��C�S��Q �"肞}�ԗ xҾ���Pg5�,X�j�,�p�")��W�$��@�&K��� �:�������0���D�$ Oay����W.M;���'�� bD�p8��ES~�|���8���$���a��0/�*m���av_�C��_��Yx
��ӈ���=�)�H��cx��n=�\�[��L"Ů�P(��P�n?@k�.u>CK�(Z�����lf�Z�_��
[4S9 G�_�Ѝ턍��JA#�lc����o, ~���Nč3IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��kQ��.�q�E�����Ǫu�Ɲ�+7Ս�.,؅�E[(XhUS�@P�/�D�H(}a�EJ����ؚF�8ɴ�$����{.�$���1����sf� �F��?�cT��Ք�db�|��P(�lv��VJB����d���h��h4
EQ�D�e8.����&Z���+Ёt:�l6˯D"�@,C8�T*z��.i��Ê�n
(l@
��qD"����Oъ�:����x�Wk3� � �S�F)��"t ��q٨�؁��:�tO]�)��b�*���02GsP��ī޳�;����OM�E`�!�;����E\�p�˝�(8TA�Z�����о�fMkD8� ���˞)x�>���*v���o�_����֗n,�@(�q�5f]����=�(�;��^Ĩg�&}�>���y(���r�������5<�Ρ7��@׃%��{�����}�S�x��ߧ��I��l��X�@����~�!�/\�b��fIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8�u�;hTQ���{�q�F�%�ш� ��]*k_��r]�!��IJ�+��!�A ��X)6�.I@Hv�����̙����#Ks��3��qf�����7ƣ��h�pB��n��rz�Gk�t۟������'���xl-7���1�u������4��W̌��`f,^�g������7o�c�K��i�~ *47�`m������85{!1H�s�
�94`"L̜E�`��hw�������vZ=��ι`��40UT�=����V�{k�����!��h<[YY�;�vs���c����Dq��AD�ޓ�)�Je5�cj�Z}"T<&  �is�EQ� I���*0�cA1QT � EQ?ONN��t!���`��YUE%`jX#w�"2��}�D1 ��� �-���d�3����!�A�=�z�gPX��υ����-N��τ��Bɷ���o�zx�a{{��;�K'�B������]�89p��d0s�a,�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�KKQ������� [զ}����mJ"��&����zl�4��E�i�E���$nfԔy���tϭw��s��O8�\�?���:F��jR��h5�M��P(d%���Ś�����,�(�P(�D�Ö�,�C&T�U0 ����� I�E*�T*�?�8�G��������l�� ��q�����R����e4�d��)�5!I� n�,@��<��&ѫ'��<�� v�3�����D��W���8���/P.�m;>�%X߸�� �w����������(6A�6&f2Ek*l�Ƴ� �
�0A� �|{���-�X����V0J��pl<p��|^� p��OF�g|~�VVOm�>$���|������=H���D7A��=�@��J&�ֱ��b������(��:���w~⭞)���IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8ˍ��OSa��s���ZbMJPA�q�h$���?p1�ͅ�فф�EcT����ĄE �+h�����^n���8Pj��$���7�s�sDU���������PUz&��4mc��V�S�������U2�6Ƹv���N��V���FC�ժZku{{[����&2��ëR��x��NF�b�8���x�M����q��2Y��gee�����k^,���u����q���� ˟�^�A��VB�P(ʏ�]>m<c��$3l��q��w�^@��f���k-�J�H�����5Fre<]|�A���ՓYKE��9��b�ʮ*�{IF#Mqڡm��$) ��B� �AD�~`p𕰗z6�.���MB�q�+c��� �#K �������<OZf��N�$�"Ȍ�7�aH T�￞b�a-���sd�1������""x�D���l�����)�c8� �P� �$]}��m�yn
q1�q�,���c��M����/�p��Q���R�;���f�\88��9'L���_`FgS���IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ���SA�?����b.H��l��,m�^��B ZZha�P�H
�5�-�^c��؄$B�;�{;�c�葠va��|gvƨ*Ƙ-�,�o�U5��� W�,;缈�*ϲL���[�"`U�`�9�ө�i���d���H������z=�t: ���n)��^Q�Q��+O� O��h4(���nw��j�a����~<����p�$`'V){�)������ݾ������h�H���xL��l`� �g��y���!=������;�8&I*�
�ju�%��L� �VC3���}:,�rGEc0�̛^BQ��ό�v "PA f��b�Y
��+/�� y��P84s��6�
YGQ��vp��#� �P ��m6�,8AU��%��l<�_9��>Dz�M��;7�,��!�a��03i�&"�a= q_���R x�a�`����_�|~��vM��,|��NIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8˕�KKa��E�"!hQAIe���1- ��J �\�
j�BA� Rh�-\H�hA��(��::��ۜ���30���9g822�P h�0,0@ YU��8.U*�*�r����v��%�@C�\.�ek��i$�ID"Q���x$t�I@
��Ţx'��,R��Ѩ(���0 #JZ� 2� b�XKKՙ�B��L}K���q��P(�Y�L��*�y$���j�fB�����\HB�-�*vױ}o�9� ӭ���BT�%h������� (L����<��ѧǨvd����x:�ٿ {Њ�7'\����;T�S����;�s���`�ۄ����#�\�0�?���vaV�2+:.�ŷ�w � - � Z��eR���:�ؕߓֱ���+(�\��ҴphIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8ˍ�OOA����B��H�V�&&$$� ����H���m�r3 �"��'.� I����?��������v ���f~�̼�N���0����*#�b��V��B� � 6Y���%qA?7�Mضa��ժ�T*
�H�_���u]x�'F�eY�u�ZM��DQ��.��j�ɝ�(��} ��K�K/�ZX�;����S�w�O\IԄ�d�Q�����g�o_A?m�Vh�<�\��y0�:|�+�PU���D��kk@� vI`cf&�ߒ��j:��ss('S��݇�iA����9|�����a �:8}d{,�# ����_0M�#h���d�d���.��ghf*���E��9 ct�n�"rN .'׽B~F'!pXX���qw>k%����Q�"��4��2���&ۙ�}�����\Mvl�w�q�I����)��Yz�;�B �I���|�"a���7&�� <�6������vj��IEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT(�u�M
�0FG=�R�P��3��:E�#�q����C("�ڟ��9��i
���y�0��x4�!
T�B�A�}LkZ��������݉6�G�
7(L{P ��� �أo�X#�b��x���G� @�)���yxp0�—��X,�X� �<4i0H�ȧ<.m{��b�4O:�a"נ�JF@��zt�� 7uN�x���x��?,?X��.YwIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ��KQ��X�?Tpi��b$Kq�.J�E�MQ
���.�J7�d�Mq)n�}7""hq� � �E�|5���{of�������Dt�0�y�7瞙;`�$�R �����| �97}߿
��$�#��|�!: If۶�8N�n��N��z�� �Z �l6���a����N�, �i��h(�뺠��r��w��� ������l���:���v��#���QZ��JR�Vo��n�.,�;O���{��7��4��ώA( ��n�u�rN�Z/�w��8�v������X�:!� ��8�O�vy��|����<\c �/�q<�S����<~��_BTR� �{����Xa�",���֏gϙ�
��X�e���������8�yklMn�_�?�_n��u ���~_=���oۃ���!Ӳ ���,�Zj{5�8A�Ρh�ǯ#��f*�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e< IDAT8ˍ��j�A�����G Q�D�+Q K K;�B�/@/@$waag#�5,�DPD�p�p��~gw��8'�D�80L1�/3�$��p��;��/�*�B.��~朽���2�����i�$�\�9��ښ���G����^��n��R�5;;;�4C�6�!�H�Z+��J��bP"� ��In޺� 6cv����&i p��2��w>�W��,�1�-�`�Nc&�6����Y�o4�*��D#�1�z�� N���#����h�!D@��B `�@'F6�~���'��z*B �D�F��2�5�bd�@%��|�<[�SY��u�(1�hp� ��4�M�6�.V3���u]��T�� \�
n�1���|� I�� �P3�5}�t�e}��(U\;�r̶�12��Qk�a�&���
}�\�������Ž^h]��c�Cr��k�ު�6��e�Ŧ�Těne�����PB*z�*,�H����;s��=�v���7�m�|rx΁� �~P%S�!ӂIEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�1nSA��Ǟ�4A����(�I�Sp.KPp"��Q�A�7%�����1Ӣ�;���+����Z######�����DWk=�C��?����|��X��j�9昇���S�ekMs9���NNV��G@���kD)��4�hn�������.Q�@�nJ)1]:�;1@�� �T��:�t���i��:���I�$fM��-�+g�]�����^LvIEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT(υ�M
�0�'Z��q�J�[A=��q/�� ܺtc+Z[�t|I[l�?�<x_��S����ӇW\J�,������涘&�6�J�Yc����is�c�E/< r,,���1�Y�X�� J��������E�a�Y���Ҷ
�#C�����I;w{\��_�� X��rM�9_�o ��غ�ru�e�K��]��u��Y�� �IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<.IDAT8ˍ�O�RQƧV����
Z��r��EX�d��� rjmJ��F")hD�����E!��r
Ǭ!��� BS���'�����{<�|�Ź�w���;cƨH�$� ���]D��:`��j��n����0J$�P(4���\�����q�\.��~T*T�Uv
�����D-�D�Tƾ�6�8�iE&�A�^�,�(� �l6�h4�D~C�Q�w��n�91�t:�
j�J�҆��'z@Y��܇#(~r��)+��߳�Ea��h'�\n4 �\��`��,<|�����P?$I��8�l4E��lf�<�k:�����g�f��
�5��� ���`�n��jpl��Q�{� �H$`4�J�@���� Ο�>�pF�\� 5n�`˸���X�(C�»˻��z�oÛ��V�}�)&��vl'Ɖ@�X�z�{���{�`aJ�8ސ��w���xqˌ�l�4���3�-��yK��7w�0;�#v� ����Cx͏ڍ����O9I Rh�oˤ/&w�l{u���:���o�Ѥbu�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8˅�OoQšF�.���ąw5�{�;w~�a��o ��1!�EB�Z W@i��պP�� Q �a@�0�Ϳz|wZȔ�:�ټ���{�\I|ׄV�V���BK3��b�ȶ�#�q�H��h4�B��U2O&����V���h@�e�����f���O��9�i�`��P(@�4��u(��h�I&�ǐy�骥R ��MlU��l6���ϴ�f2�, �J��w����-s�#�����7�$i1@d�`0��Ϟ#�!�ˑ�,Z���֚�-���@U k�r�a�e�S�Dod����������e0�=XzZ,�f_G|���Z��q��-�3,h���!�<dn�d�ه�T��D~Qx�¼��0f����=7`z!n�P���k�\.��d2���t�6���E S�T.�!Q . ��?j�������>�S��٫Y���O$W��Zlv-���`�s�^���5-�*�f⼙8�� p�-Bҩ����� �b��N��L�^�y������/]0�4����q�� �`IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<PIDAT8ˍ�oHqǗ��8 -���7FDh�B�ȔJ�0��ތeь\��zFQ�"�‘V
��^� �Lbc���s���M���vw����]�Om�����~����Щ(�I�Q��j*�"Ac:��$I�f2�C9���yT,)��p*�˲y��$H�D,�$A����%��U��<A��
�0�(
�x\p�!>��$'�ޖ� = �kneT�M,�n���ɒO�z�
㗏��D��\d� ��V���\o�|�gܣx��$���S��6Y�T?�@�� �s7��v"n�F�Z�c��h� a$��D� ��x�g��%���zEL�@�R��²D�6,�3����a�{�O���邠g̹z�� ��xxK�`/a2�0�A�UAx�G��f�5�@��.��y�}�����4N� �A� c�V0m��
�+m��/�{��ʊ��<��Ď�!Wǝ>ؚ�0T ���� �f`J)�����.�]*Pか��?? ��^�~�V � ��Z!�Z03� ^`!��F� 7H��LgY��k�A�y��~e�X�`=a��Ѷ�H`V%�X���������yIEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT(υ�A�0E��֕n\x���qob♼� �R�w(���g�I�u2�U��5�S����6�VX���ʲJ6Yhl�Oe<�Ƥj�56�S&����5���n�2 ��,��be� %,r�[;�z�ֳ��
X`���<�Ɛ�dz�B����7|�!A��/�n2=V����Y��� �r�o� v��ZdIEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT8ˍ��k�Q���}ɢ��7.Ĥ�Sc����R+SY��`+"���6�_���F����,���]�f�ǽ3Y7Q����[̜{��cTc�i`p���7UT��ȅ,˺eY���,�tii�50XU�C`�,˸����~�^���NG[�����l����H��{����B7��zuN�B� A�C����Z��(VVV�,..�����y�2ȡl�������n�� ����!q��o?��"���7L$��q�ڗ]b��iJ��ED!P����\I�И��� '��,�h�T���iJ�$����Vb��{��+�k]�B�h�|�^�p�6(�9��x�I�d_ {�,s����.���F����i$*�Z�s#����
��`s�����J�r��$S���1��ڃ� ™����O���n,�q� �[1������T��s�?>
�,���ܨ��������x)����} y6>ۘ�AT�5�#� 3�f�K!�U�p4����J�19Js�q��@��8�#:���/n�y�����IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<pIDAT8ˍ��ORaǭ�#���+/���ڢ��2�+u��6S�Q��H�e��UV"5�L����Ks�a� ž=�)�����>;��}��{+PB!�B���%���x���������V>��� �P��*��/(��kkk�d2�d�x+++�$C��p�B�'�W3N:� �Ӌɻ��z,,��H$�DXA6���V��)��Z���D+ �ި`�O`<��{+:��quL���I�����[鐨��n���W �)^}[�)���G�c��At[�1�مh4��$
 ���`���F�aг�Kv/jl�q�{��>����O�I�Rl=b�ض��d��F؀�$O?��9 q�
*�� v��~�L#�:�fE|�w}@���og!q���ی�:>�Q��!1r�+�l��-���<��ǘPy͎C*;Ι�p�z��+T@������� ��M��?��a�_p� C/z��NWfƀ�� 6�wAE��ܠ� �c��|�y�5�F�Nԡ��2�Hw��'(�A�Fle��7>ųI\��H�"����^�LiEn�5ۙ�`t���;��'�%�G
��;-?�|�f��'(���X�t�K<�PU̗IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT8ˍ�OkQ��~��R���t��@���"�Z� m�U�HC VS��L���8��F�"c2(������R���6�Y�w~���ޝ0Gb�<�"��t���3{�^}8^�F#�[���xc��Kdn�Zh����rE� �C�4QUN�Ӏ��w@��.j�|>��2��,B���*t:�!.��2 �������èT*p�ݐ$ �@�Xl�%��4���B��`0EQ��x`�ۑH$`�Xx%�'���#�N��S��'�IX�Vޞ��I��N[�CȲ�h4ʳ�*l6�5�M4���'�v�/�O!�c�{���+�d2�F�dʇB$M�,�m��ߟ�uD~�؊kXy�LF��D��0��3o�/] �]ǧ���_Unk0p�y�*�tw�<���~k��FU�T��,��m���@����_"g7J�%�������\_^��q9c�!�6 S�4B�s�Y@mZ����m��5r�q�F�
�OźX��IEND�B`�
�PNG

IHDR��7�gAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT(�]�MJ����+���"/$Bׂ�Q�BT)����"Vt�� D�*��ǦI��%�� �����$1���17� ����t�A�Ȏ&鰲�O�r���/���7�"o�l�ܐ_�jP�C�M�U����5<j�E!� ��+����5�����5Nc�钯<x�J�0!m���AY�O��]]�Vr�P�$ �5'Ι�Ȣ^6Є��9��slRJ��d@�ݽ,��
glq�F���(�I�uݻ����~Ü>(�M�_��Ǚ��r�
�L�[�n���IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<�IDAT�M�Ue���ι��u��q���I�W�h�"(²l6�Ԧ(��E�h��]d�lآE�hS�Z��Q34%-gl�c�{�9������_�O�^��n�!��HDє���k�]���މ# C�����o�~ �4T���G��z�?+gO�[�Ñ�Ԑ���[W�g��rJ$��F�4�Ψ�����n��,�k�l_~�ͯg�}�� CJi""�n�u2u��T�����ɮ�=s6m�����Ϡ���������E�3}����ܱ�������3Ӷ̮s�D�שmzxֱ��z/�f����L9~i��0<�k��š�~`� ג�ߟ_��TR�Ȋ_n���W���Ҳ��Z���W���6gM���Vƺ��nGȤ �hm�U�vᎏ��d���-��z����?:c�)Km9Du�ʸ`)e�ez5IS��Ң�{7�)�� AH�B[�C�h���:�ʃ��d8*n���I�"�sR�d���M���b�Jt���f\������ J���@ "ѩ�f؀:�i�����z�&��Nvq��̾Y%�*S��(F��v~0]_�^[��n���;@�0Zm@�fPJSJ�u.M[�a���;ޭ�F��0կA�N��ҩҭ�)C )�GO�{����|�5�@ �j)�T�����' ��J�IEND�B`�
�PNG

IHDR��agAMA��7��tEXtSoftwareAdobe ImageReadyq�e<IDAT�Kh\U���9��;w�$�4�M[�Զ�Ɣ4�BK*TM�Tp�B�e-
�V\�ąB�Y!��A�]�$jbR��Zͳ���ә�&3��{�~�^����tS���B@�Q ��|�(��v铱��^�|b���}�h��<D��n��t���Z%7w�u��`�hO�
����&�V
��D�
�q�ŧ9�0�
u�N�>���(��4���W��PM,��4����z�����M�������ꇕ�p�׮����O^���J��~��'9W�8�}Z3�t7;�)�P�F��Ռ��l�0���ݫg.y�n����|���n���sTl��r�b1�x���ɵHw�g?�?���
��-������y�n�o�s�� ��Ha�>��ч��J&b�����]�% K���93�ԣ�tP,/�M�y���"`�����g��Gi�#cZR/me��� G�D��A�8H��ݝ�)®:��Һ��-,��f�.��޺Fe-)�(��>$Vй����*��8��
*��&Œ%�<��L��p���������W���y��7/��?Rh�Y�Ո�AW��>�V�;� ٞ����q�H�O\s��/���^シ����y�DC�M��� EV�y��g��D@�mZ� q�b����w����v�A���(�Y����9��s�:T��D+�S��M�5.n[�hh(�K��h�I;D �+�����I��+����k6�!��A�@�ι;F��Qx�- �1IEND�B`�
* {
margin: 0;
padding: 0;
outline: 0;
}
body {
padding: 80px 100px;
font: 13px "Helvetica Neue", "Lucida Grande", "Arial";
background: #ECE9E9 -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ECE9E9));
background: #ECE9E9 -moz-linear-gradient(top, #fff, #ECE9E9);
background-repeat: no-repeat;
color: #555;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3 {
font-size: 22px;
color: #343434;
}
h1 em, h2 em {
padding: 0 5px;
font-weight: normal;
}
h1 {
font-size: 60px;
}
h2 {
margin-top: 10px;
}
h3 {
margin: 5px 0 10px 0;
padding-bottom: 5px;
border-bottom: 1px solid #eee;
font-size: 18px;
}
ul li {
list-style: none;
}
ul li:hover {
cursor: pointer;
color: #2e2e2e;
}
ul li .path {
padding-left: 5px;
font-weight: bold;
}
ul li .line {
padding-right: 5px;
font-style: italic;
}
ul li:first-child .path {
padding-left: 0;
}
p {
line-height: 1.5;
}
a {
color: #555;
text-decoration: none;
}
a:hover {
color: #303030;
}
#stacktrace {
margin-top: 15px;
}
.directory h1 {
margin-bottom: 15px;
font-size: 18px;
}
ul#files {
width: 100%;
height: 100%;
overflow: hidden;
}
ul#files li {
float: left;
width: 30%;
line-height: 25px;
margin: 1px;
}
ul#files li a {
display: block;
height: 25px;
border: 1px solid transparent;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
overflow: hidden;
white-space: nowrap;
}
ul#files li a:focus,
ul#files li a:hover {
background: rgba(255,255,255,0.65);
border: 1px solid #ececec;
}
ul#files li a.highlight {
-webkit-transition: background .4s ease-in-out;
background: #ffff4f;
border-color: #E9DC51;
}
#search {
display: block;
position: fixed;
top: 20px;
right: 20px;
width: 90px;
-webkit-transition: width ease 0.2s, opacity ease 0.4s;
-moz-transition: width ease 0.2s, opacity ease 0.4s;
-webkit-border-radius: 32px;
-moz-border-radius: 32px;
-webkit-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03);
-moz-box-shadow: inset 0px 0px 3px rgba(0, 0, 0, 0.25), inset 0px 1px 3px rgba(0, 0, 0, 0.7), 0px 1px 0px rgba(255, 255, 255, 0.03);
-webkit-font-smoothing: antialiased;
text-align: left;
font: 13px "Helvetica Neue", Arial, sans-serif;
padding: 4px 10px;
border: none;
background: transparent;
margin-bottom: 0;
outline: none;
opacity: 0.7;
color: #888;
}
#search:focus {
width: 120px;
opacity: 1.0;
}
/*views*/
#files span {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
text-indent: 10px;
}
#files .name {
background-repeat: no-repeat;
}
#files .icon .name {
text-indent: 28px;
}
/*tiles*/
.view-tiles .name {
width: 100%;
background-position: 8px 5px;
}
.view-tiles .size,
.view-tiles .date {
display: none;
}
/*details*/
ul#files.view-details li {
float: none;
display: block;
width: 90%;
}
ul#files.view-details li.header {
height: 25px;
background: #000;
color: #fff;
font-weight: bold;
}
.view-details .header {
border-radius: 5px;
}
.view-details .name {
width: 60%;
background-position: 8px 5px;
}
.view-details .size {
width: 10%;
}
.view-details .date {
width: 30%;
}
.view-details .size,
.view-details .date {
text-align: right;
direction: rtl;
}
/*mobile*/
@media (max-width: 768px) {
body {
font-size: 13px;
line-height: 16px;
padding: 0;
}
#search {
position: static;
width: 100%;
font-size: 2em;
line-height: 1.8em;
text-indent: 10px;
border: 0;
border-radius: 0;
padding: 10px 0;
margin: 0;
}
#search:focus {
width: 100%;
border: 0;
opacity: 1;
}
.directory h1 {
font-size: 2em;
line-height: 1.5em;
color: #fff;
background: #000;
padding: 15px 10px;
margin: 0;
}
ul#files {
border-top: 1px solid #cacaca;
}
ul#files li {
float: none;
width: auto !important;
display: block;
border-bottom: 1px solid #cacaca;
font-size: 2em;
line-height: 1.2em;
text-indent: 0;
margin: 0;
}
ul#files li:nth-child(odd) {
background: #e0e0e0;
}
ul#files li a {
height: auto;
border: 0;
border-radius: 0;
padding: 15px 10px;
}
ul#files li a:focus,
ul#files li a:hover {
border: 0;
}
#files .header,
#files .size,
#files .date {
display: none !important;
}
#files .name {
float: none;
display: inline-block;
width: 100%;
text-indent: 0;
background-position: 0 0;
}
#files .icon .name {
text-indent: 41px;
}
}
/*!
* Connect - utils
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* MIT Licensed
*/
/**
* Module dependencies.
*/
var http = require('http')
, crypto = require('crypto')
, parse = require('url').parse
, sep = require('path').sep
, signature = require('cookie-signature')
, nodeVersion = process.versions.node.split('.');
// pause is broken in node < 0.10
exports.brokenPause = parseInt(nodeVersion[0], 10) === 0
&& parseInt(nodeVersion[1], 10) < 10;
/**
* Return `true` if the request has a body, otherwise return `false`.
*
* @param {IncomingMessage} req
* @return {Boolean}
* @api private
*/
exports.hasBody = function(req) {
var encoding = 'transfer-encoding' in req.headers;
var length = 'content-length' in req.headers && req.headers['content-length'] !== '0';
return encoding || length;
};
/**
* Extract the mime type from the given request's
* _Content-Type_ header.
*
* @param {IncomingMessage} req
* @return {String}
* @api private
*/
exports.mime = function(req) {
var str = req.headers['content-type'] || '';
return str.split(';')[0];
};
/**
* Generate an `Error` from the given status `code`
* and optional `msg`.
*
* @param {Number} code
* @param {String} msg
* @return {Error}
* @api private
*/
exports.error = function(code, msg){
var err = new Error(msg || http.STATUS_CODES[code]);
err.status = code;
return err;
};
/**
* Return md5 hash of the given string and optional encoding,
* defaulting to hex.
*
* utils.md5('wahoo');
* // => "e493298061761236c96b02ea6aa8a2ad"
*
* @param {String} str
* @param {String} encoding
* @return {String}
* @api private
*/
exports.md5 = function(str, encoding){
return crypto
.createHash('md5')
.update(str, 'utf8')
.digest(encoding || 'hex');
};
/**
* Merge object b with object a.
*
* var a = { foo: 'bar' }
* , b = { bar: 'baz' };
*
* utils.merge(a, b);
* // => { foo: 'bar', bar: 'baz' }
*
* @param {Object} a
* @param {Object} b
* @return {Object}
* @api private
*/
exports.merge = function(a, b){
if (a && b) {
for (var key in b) {
a[key] = b[key];
}
}
return a;
};
/**
* Escape the given string of `html`.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html){
return String(html)
.replace(/&(?!\w+;)/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
/**
* Sign the given `val` with `secret`.
*
* @param {String} val
* @param {String} secret
* @return {String}
* @api private
*/
exports.sign = function(val, secret){
console.warn('do not use utils.sign(), use https://github.com/visionmedia/node-cookie-signature')
return val + '.' + crypto
.createHmac('sha256', secret)
.update(val)
.digest('base64')
.replace(/=+$/, '');
};
/**
* Unsign and decode the given `val` with `secret`,
* returning `false` if the signature is invalid.
*
* @param {String} val
* @param {String} secret
* @return {String|Boolean}
* @api private
*/
exports.unsign = function(val, secret){
console.warn('do not use utils.unsign(), use https://github.com/visionmedia/node-cookie-signature')
var str = val.slice(0, val.lastIndexOf('.'));
return exports.sign(str, secret) == val
? str
: false;
};
/**
* Parse signed cookies, returning an object
* containing the decoded key/value pairs,
* while removing the signed key from `obj`.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
exports.parseSignedCookies = function(obj, secret){
var ret = {};
Object.keys(obj).forEach(function(key){
var val = obj[key];
if (0 == val.indexOf('s:')) {
val = signature.unsign(val.slice(2), secret);
if (val) {
ret[key] = val;
delete obj[key];
}
}
});
return ret;
};
/**
* Parse a signed cookie string, return the decoded value
*
* @param {String} str signed cookie string
* @param {String} secret
* @return {String} decoded value
* @api private
*/
exports.parseSignedCookie = function(str, secret){
return 0 == str.indexOf('s:')
? signature.unsign(str.slice(2), secret)
: str;
};
/**
* Parse JSON cookies.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
exports.parseJSONCookies = function(obj){
Object.keys(obj).forEach(function(key){
var val = obj[key];
var res = exports.parseJSONCookie(val);
if (res) obj[key] = res;
});
return obj;
};
/**
* Parse JSON cookie string
*
* @param {String} str
* @return {Object} Parsed object or null if not json cookie
* @api private
*/
exports.parseJSONCookie = function(str) {
if (0 == str.indexOf('j:')) {
try {
return JSON.parse(str.slice(2));
} catch (err) {
// no op
}
}
};
/**
* Pause `data` and `end` events on the given `obj`.
* Middleware performing async tasks _should_ utilize
* this utility (or similar), to re-emit data once
* the async operation has completed, otherwise these
* events may be lost. Pause is only required for
* node versions less than 10, and is replaced with
* noop's otherwise.
*
* var pause = utils.pause(req);
* fs.readFile(path, function(){
* next();
* pause.resume();
* });
*
* @param {Object} obj
* @return {Object}
* @api private
*/
exports.pause = exports.brokenPause
? require('pause')
: function () {
return {
end: noop,
resume: noop
}
}
/**
* Strip `Content-*` headers from `res`.
*
* @param {ServerResponse} res
* @api private
*/
exports.removeContentHeaders = function(res){
if (!res._headers) return;
Object.keys(res._headers).forEach(function(field){
if (0 == field.indexOf('content')) {
res.removeHeader(field);
}
});
};
/**
* Check if `req` is a conditional GET request.
*
* @param {IncomingMessage} req
* @return {Boolean}
* @api private
*/
exports.conditionalGET = function(req) {
return req.headers['if-modified-since']
|| req.headers['if-none-match'];
};
/**
* Respond with 401 "Unauthorized".
*
* @param {ServerResponse} res
* @param {String} realm
* @api private
*/
exports.unauthorized = function(res, realm) {
res.statusCode = 401;
res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
res.end('Unauthorized');
};
/**
* Respond with 304 "Not Modified".
*
* @param {ServerResponse} res
* @param {Object} headers
* @api private
*/
exports.notModified = function(res) {
exports.removeContentHeaders(res);
res.statusCode = 304;
res.end();
};
/**
* Return an ETag in the form of `"<size>-<mtime>"`
* from the given `stat`.
*
* @param {Object} stat
* @return {String}
* @api private
*/
exports.etag = function(stat) {
return '"' + stat.size + '-' + Number(stat.mtime) + '"';
};
/**
* Parse the given Cache-Control `str`.
*
* @param {String} str
* @return {Object}
* @api private
*/
exports.parseCacheControl = function(str){
var directives = str.split(',')
, obj = {};
for(var i = 0, len = directives.length; i < len; i++) {
var parts = directives[i].split('=')
, key = parts.shift().trim()
, val = parseInt(parts.shift(), 10);
obj[key] = isNaN(val) ? true : val;
}
return obj;
};
/**
* Parse the `req` url with memoization.
*
* @param {ServerRequest} req
* @return {Object}
* @api private
*/
exports.parseUrl = function(req){
var parsed = req._parsedUrl;
if (parsed && parsed.href == req.url) {
return parsed;
} else {
parsed = parse(req.url);
if (parsed.auth && !parsed.protocol && ~parsed.href.indexOf('//')) {
// This parses pathnames, and a strange pathname like //r@e should work
parsed = parse(req.url.replace(/@/g, '%40'));
}
return req._parsedUrl = parsed;
}
};
/**
* Parse byte `size` string.
*
* @param {String} size
* @return {Number}
* @api private
*/
exports.parseBytes = require('bytes');
/**
* Normalizes the path separator from system separator
* to URL separator, aka `/`.
*
* @param {String} path
* @return {String}
* @api private
*/
exports.normalizeSlashes = function normalizeSlashes(path) {
return path.split(sep).join('/');
};
function noop() {}
(The MIT License)
Copyright (c) 2010 Sencha Inc.
Copyright (c) 2011 LearnBoost
Copyright (c) 2011 TJ Holowaychuk
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
{
"name": "batch",
"repo": "visionmedia/batch",
"description": "Async task batching",
"version": "0.5.0",
"keywords": ["batch", "async", "utility", "concurrency", "concurrent"],
"dependencies": {
"component/emitter": "*"
},
"development": {},
"scripts": [
"index.js"
]
}

0.5.0 / 2013-07-29

  • add .throws(true) to opt-in to responding with an array of error objects
  • make new optional

0.4.0 / 2013-06-05

  • add catching of immediate callback errors

0.3.2 / 2013-03-15

  • remove Emitter call in constructor

0.3.1 / 2013-03-13

  • add Emitter() mixin for client. Closes #8

0.3.0 / 2013-03-13

  • add component.json
  • add result example
  • add .concurrency support
  • add concurrency example
  • add parallel example

0.2.1 / 2012-11-08

  • add .start, .end, and .duration properties
  • change dependencies to devDependencies

0.2.0 / 2012-10-04

  • add progress events. Closes #5 (BREAKING CHANGE)

0.1.1 / 2012-07-03

  • change "complete" event to "progress"

0.1.0 / 2012-07-03

  • add Emitter inheritance and emit "complete" [burcu]

0.0.3 / 2012-06-02

  • Callback results should be in the order of the queued functions.

0.0.2 / 2012-02-12

  • any node

0.0.1 / 2010-01-03

  • Initial release
/**
* Module dependencies.
*/
try {
var EventEmitter = require('events').EventEmitter;
} catch (err) {
var Emitter = require('emitter');
}
/**
* Noop.
*/
function noop(){}
/**
* Expose `Batch`.
*/
module.exports = Batch;
/**
* Create a new Batch.
*/
function Batch() {
if (!(this instanceof Batch)) return new Batch;
this.fns = [];
this.concurrency(Infinity);
this.throws(true);
for (var i = 0, len = arguments.length; i < len; ++i) {
this.push(arguments[i]);
}
}
/**
* Inherit from `EventEmitter.prototype`.
*/
if (EventEmitter) {
Batch.prototype.__proto__ = EventEmitter.prototype;
} else {
Emitter(Batch.prototype);
}
/**
* Set concurrency to `n`.
*
* @param {Number} n
* @return {Batch}
* @api public
*/
Batch.prototype.concurrency = function(n){
this.n = n;
return this;
};
/**
* Queue a function.
*
* @param {Function} fn
* @return {Batch}
* @api public
*/
Batch.prototype.push = function(fn){
this.fns.push(fn);
return this;
};
/**
* Set wether Batch will or will not throw up.
*
* @param {Boolean} throws
* @return {Batch}
* @api public
*/
Batch.prototype.throws = function(throws) {
this.e = !!throws;
return this;
};
/**
* Execute all queued functions in parallel,
* executing `cb(err, results)`.
*
* @param {Function} cb
* @return {Batch}
* @api public
*/
Batch.prototype.end = function(cb){
var self = this
, total = this.fns.length
, pending = total
, results = []
, errors = []
, cb = cb || noop
, fns = this.fns
, max = this.n
, throws = this.e
, index = 0
, done;
// empty
if (!fns.length) return cb(null, results);
// process
function next() {
var i = index++;
var fn = fns[i];
if (!fn) return;
var start = new Date;
try {
fn(callback);
} catch (err) {
callback(err);
}
function callback(err, res){
if (done) return;
if (err && throws) return done = true, cb(err);
var complete = total - pending + 1;
var end = new Date;
results[i] = res;
errors[i] = err;
self.emit('progress', {
index: i,
value: res,
error: err,
pending: pending,
total: total,
complete: complete,
percent: complete / total * 100 | 0,
start: start,
end: end,
duration: end - start
});
if (--pending) next()
else if(!throws) cb(errors, results);
else cb(null, results);
}
}
// concurrency
for (var i = 0; i < fns.length; i++) {
if (i == max) break;
next();
}
return this;
};
test:
@./node_modules/.bin/mocha \
--require should
.PHONY: test
{
"name": "batch",
"version": "0.5.0",
"description": "Simple async batch",
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca"
},
"devDependencies": {
"mocha": "*",
"should": "*"
},
"main": "index",
"readme": "\n# batch\n\n Simple async batch with concurrency control and progress reporting.\n\n## Installation\n\n```\n$ npm install batch\n```\n\n## API\n\n```js\nvar Batch = require('batch')\n , batch = new Batch;\n\nbatch.concurrency(4);\n\nids.forEach(function(id){\n batch.push(function(done){\n User.get(id, done);\n });\n});\n\nbatch.on('progress', function(e){\n\n});\n\nbatch.end(function(err, users){\n\n});\n```\n\n### Progress events\n\n Contain the \"job\" index, response value, duration information, and completion data.\n\n```js\n{ index: 1,\n value: 'bar',\n pending: 2,\n total: 3,\n complete: 2,\n percent: 66,\n start: Thu Oct 04 2012 12:25:53 GMT-0700 (PDT),\n end: Thu Oct 04 2012 12:25:53 GMT-0700 (PDT),\n duration: 0 }\n```\n\n## License\n\n(The MIT License)\n\nCopyright (c) 2013 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "Readme.md",
"_id": "batch@0.5.0",
"dist": {
"shasum": "f49faa98b92a7a3b51ef14802af4f47fa19e3d58"
},
"_from": "batch@0.5.0",
"_resolved": "https://registry.npmjs.org/batch/-/batch-0.5.0.tgz"
}

batch

Simple async batch with concurrency control and progress reporting.

Installation

$ npm install batch

API

var Batch = require('batch')
  , batch = new Batch;

batch.concurrency(4);

ids.forEach(function(id){
  batch.push(function(done){
    User.get(id, done);
  });
});

batch.on('progress', function(e){

});

batch.end(function(err, users){

});

Progress events

Contain the "job" index, response value, duration information, and completion data.

{ index: 1,
  value: 'bar',
  pending: 2,
  total: 3,
  complete: 2,
  percent: 66,
  start: Thu Oct 04 2012 12:25:53 GMT-0700 (PDT),
  end: Thu Oct 04 2012 12:25:53 GMT-0700 (PDT),
  duration: 0 }

License

(The MIT License)

Copyright (c) 2013 TJ Holowaychuk <tj@vision-media.ca>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

{
"name": "bytes",
"description": "byte size string parser / serializer",
"keywords": ["bytes", "utility"],
"version": "0.2.1",
"scripts": ["index.js"]
}

0.2.1 / 2013-04-01

  • add .component

0.2.0 / 2012-10-28

  • bytes(200).should.eql('200b')

0.1.0 / 2012-07-04

  • add bytes to string conversion [yields]
/**
* Parse byte `size` string.
*
* @param {String} size
* @return {Number}
* @api public
*/
module.exports = function(size) {
if ('number' == typeof size) return convert(size);
var parts = size.match(/^(\d+(?:\.\d+)?) *(kb|mb|gb)$/)
, n = parseFloat(parts[1])
, type = parts[2];
var map = {
kb: 1 << 10
, mb: 1 << 20
, gb: 1 << 30
};
return map[type] * n;
};
/**
* convert bytes into string.
*
* @param {Number} b - bytes to convert
* @return {String}
* @api public
*/
function convert (b) {
var gb = 1 << 30, mb = 1 << 20, kb = 1 << 10;
if (b >= gb) return (Math.round(b / gb * 100) / 100) + 'gb';
if (b >= mb) return (Math.round(b / mb * 100) / 100) + 'mb';
if (b >= kb) return (Math.round(b / kb * 100) / 100) + 'kb';
return b + 'b';
}
test:
@./node_modules/.bin/mocha \
--reporter spec \
--require should
.PHONY: test
{
"name": "bytes",
"author": {
"name": "TJ Holowaychuk",
"email": "tj@vision-media.ca",
"url": "http://tjholowaychuk.com"
},
"description": "byte size string parser / serializer",
"version": "0.2.1",
"main": "index.js",
"dependencies": {},
"devDependencies": {
"mocha": "*",
"should": "*"
},
"component": {
"scripts": {
"bytes/index.js": "index.js"
}
},
"readme": "# node-bytes\n\n Byte string parser / formatter.\n\n## Example:\n\n```js\nbytes('1kb')\n// => 1024\n\nbytes('2mb')\n// => 2097152\n\nbytes('1gb')\n// => 1073741824\n\nbytes(1073741824)\n// => 1gb\n```\n\n## Installation\n\n```\n$ npm install bytes\n$ component install visionmedia/bytes.js\n```\n\n## License \n\n(The MIT License)\n\nCopyright (c) 2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n'Software'), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
"readmeFilename": "Readme.md",
"_id": "bytes@0.2.1",
"dist": {
"shasum": "2c4303e8eb3159c2087497838a027fbb2c571c82"
},
"_from": "bytes@0.2.1",
"_resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.1.tgz"
}

node-bytes

Byte string parser / formatter.

Example:

bytes('1kb')
// => 1024

bytes('2mb')
// => 2097152

bytes('1gb')
// => 1073741824

bytes(1073741824)
// => 1gb

Installation

$ npm install bytes
$ component install visionmedia/bytes.js

License

(The MIT License)

Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

{
// Settings
"passfail" : false, // Stop on first error.
"maxerr" : 100, // Maximum errors before stopping.
// Predefined globals whom JSHint will ignore.
"browser" : false, // Standard browser globals e.g. `window`, `document`.
"node" : true,
"rhino" : false,
"couch" : false,
"wsh" : false, // Windows Scripting Host.
"jquery" : false,
"prototypejs" : false,
"mootools" : false,
"dojo" : false,
"predef" : [
"describe", "it", "before", "after"
],
// Development.
"debug" : true, // Allow debugger statements e.g. browser breakpoints.
"devel" : true, // Allow development statements e.g. `console.log();`.
// EcmaScript 5.
"es5" : true, // Allow EcmaScript 5 syntax.
"strict" : false, // Require `use strict` pragma in every file.
"globalstrict" : true, // Allow global "use strict" (also enables 'strict').
// The Good Parts.
"asi" : true, // Tolerate Automatic Semicolon Insertion (no semicolons).
"laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
"laxcomma" : true,
"bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.).
"boss" : true, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
"curly" : false, // Require {} for every new block or scope.
"eqeqeq" : true, // Require triple equals i.e. `===`.
"eqnull" : true, // Tolerate use of `== null`.
"evil" : false, // Tolerate use of `eval`.
"expr" : false, // Tolerate `ExpressionStatement` as Programs.
"forin" : false, // Prohibt `for in` loops without `hasOwnProperty`.
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
"latedef" : false, // Prohibit variable use before definition.
"loopfunc" : false, // Allow functions to be defined within loops.
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
"regexp" : false, // Prohibit `.` and `[^...]` in regular expressions.
"regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
"scripturl" : false, // Tolerate script-targeted URLs.
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
"undef" : true, // Require all non-global variables be declared before they are used.
// Persone styling prefrences.
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
"noempty" : true, // Prohibit use of empty blocks.
"nonew" : true, // Prohibit use of constructors for side-effects.
"nomen" : false, // Prohibit use of initial or trailing underbars in names.
"onevar" : false, // Allow only one `var` statement per function.
"plusplus" : false, // Prohibit use of `++` & `--`.
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
"trailing" : true, // Prohibit trailing whitespaces.
"white" : false // Check against strict whitespace and indentation rules.
}
language: node_js
node_js:
- "0.8"
- "0.10"
before_script:
- ulimit -n 500

2.2.0

  • additional callback API to support multiple files with same field name
  • fix assertion crash when max field count is exceeded
  • fix assertion crash when client aborts an invalid request
  • (>=v0.10 only) unpipe the request when an error occurs to save resources.
  • update readable-stream to ~1.1.9
  • fix assertion crash when EMFILE occurrs
  • (no more assertions - only 'error' events)

2.1.9

  • relax content-type detection regex. (thanks amitaibu)

2.1.8

  • replace deprecated Buffer.write(). (thanks hueniverse)

2.1.7

  • add repository field to package.json

2.1.6

  • expose hash as an option to Form. (thanks wookiehangover)

2.1.5

  • fix possible 'close' event before all temp files are done

2.1.4

  • fix crash for invalid requests

2.1.3

  • add file.size

2.1.2

  • proper backpressure support
  • update s3 example

2.1.1

  • fix uploads larger than 2KB
  • fix both s3 and upload example
  • add part.byteCount and part.byteOffset

2.1.0 (recalled)

  • Complete rewrite. See README for changes and new API.

v1.0.13

  • Only update hash if update method exists (Sven Lito)
  • According to travis v0.10 needs to go quoted (Sven Lito)
  • Bumping build node versions (Sven Lito)
  • Additional fix for empty requests (Eugene Girshov)
  • Change the default to 1000, to match the new Node behaviour. (OrangeDog)
  • Add ability to control maxKeys in the querystring parser. (OrangeDog)
  • Adjust test case to work with node 0.9.x (Eugene Girshov)
  • Update package.json (Sven Lito)
  • Path adjustment according to eb4468b (Markus Ast)

v1.0.12

  • Emit error on aborted connections (Eugene Girshov)
  • Add support for empty requests (Eugene Girshov)
  • Fix name/filename handling in Content-Disposition (jesperp)
  • Tolerate malformed closing boundary in multipart (Eugene Girshov)
  • Ignore preamble in multipart messages (Eugene Girshov)
  • Add support for application/json (Mike Frey, Carlos Rodriguez)
  • Add support for Base64 encoding (Elmer Bulthuis)
  • Add File#toJSON (TJ Holowaychuk)
  • Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)
  • Documentation improvements (Sven Lito, Andre Azevedo)
  • Add support for application/octet-stream (Ion Lupascu, Chris Scribner)
  • Use os.tmpDir() to get tmp directory (Andrew Kelley)
  • Improve package.json (Andrew Kelley, Sven Lito)
  • Fix benchmark script (Andrew Kelley)
  • Fix scope issue in incoming_forms (Sven Lito)
  • Fix file handle leak on error (OrangeDog)

v1.0.11

  • Calculate checksums for incoming files (sreuter)
  • Add definition parameters to "IncomingForm" as an argument (Math-)

v1.0.10

  • Make parts to be proper Streams (Matt Robenolt)

v1.0.9

  • Emit progress when content length header parsed (Tim Koschützki)
  • Fix Readme syntax due to GitHub changes (goob)
  • Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara)

v1.0.8

  • Strip potentially unsafe characters when using keepExtensions: true.
  • Switch to utest / urun for testing
  • Add travis build

v1.0.7

  • Remove file from package that was causing problems when installing on windows. (#102)
  • Fix typos in Readme (Jason Davies).

v1.0.6

  • Do not default to the default to the field name for file uploads where filename="".

v1.0.5

  • Support filename="" in multipart parts
  • Explain unexpected end() errors in parser better

Note: Starting with this version, formidable emits 'file' events for empty file input fields. Previously those were incorrectly emitted as regular file input fields with value = "".

v1.0.4

  • Detect a good default tmp directory regardless of platform. (#88)

v1.0.3

  • Fix problems with utf8 characters (#84) / semicolons in filenames (#58)
  • Small performance improvements
  • New test suite and fixture system

v1.0.2

  • Exclude node_modules folder from git
  • Implement new 'aborted' event
  • Fix files in example folder to work with recent node versions
  • Make gently a devDependency

See Commits

v1.0.1

  • Fix package.json to refer to proper main directory. (#68, Dean Landolt)

See Commits

v1.0.0

  • Add support for multipart boundaries that are quoted strings. (Jeff Craig)

This marks the beginning of development on version 2.0 which will include several architectural improvements.

See Commits

v0.9.11

  • Emit 'progress' event when receiving data, regardless of parsing it. (Tim Koschützki)
  • Use W3C FileAPI Draft properties for File class

Important: The old property names of the File class will be removed in a future release.

See Commits

Older releases

These releases were done before starting to maintain the above Changelog:

var http = require('http')
, util = require('util')
, multiparty = require('../')
, azure = require('azure')
, PORT = process.env.PORT || 27372
var server = http.createServer(function(req, res) {
if (req.url === '/') {
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="text" name="title"><br>'+
'<input type="file" name="upload"><br>'+
'<input type="submit" value="Upload">'+
'</form>'
);
} else if (req.url === '/upload') {
var blobService = azure.createBlobService();
var form = new multiparty.Form();
form.on('part', function(part) {
if (!part.filename) return;
var size = part.byteCount - part.byteOffset;
var name = part.filename;
var container = 'blobContainerName';
blobService.createBlockBlobFromStream(container, name, part, size, function(error) {
if (error) {
// error handling
}
});
});
form.parse(req);
res.send('File uploaded successfully');
}
});
server.listen(PORT, function() {
console.info('listening on http://0.0.0.0:'+PORT+'/');
});
var http = require('http')
, util = require('util')
, multiparty = require('../')
, knox = require('knox')
, Batch = require('batch')
, PORT = process.env.PORT || 27372
var s3Client = knox.createClient({
secure: false,
key: process.env.S3_KEY,
secret: process.env.S3_SECRET,
bucket: process.env.S3_BUCKET,
});
var server = http.createServer(function(req, res) {
if (req.url === '/') {
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="text" name="path"><br>'+
'<input type="file" name="upload"><br>'+
'<input type="submit" value="Upload">'+
'</form>'
);
} else if (req.url === '/upload') {
var headers = {
'x-amz-acl': 'public-read',
};
var form = new multiparty.Form();
var batch = new Batch();
batch.push(function(cb) {
form.on('field', function(name, value) {
if (name === 'path') {
var destPath = value;
if (destPath[0] !== '/') destPath = '/' + destPath;
cb(null, destPath);
}
});
});
batch.push(function(cb) {
form.on('part', function(part) {
if (! part.filename) return;
cb(null, part);
});
});
batch.end(function(err, results) {
if (err) throw err;
form.removeListener('close', onEnd);
var destPath = results[0]
, part = results[1];
headers['Content-Length'] = part.byteCount;
s3Client.putStream(part, destPath, headers, function(err, s3Response) {
if (err) throw err;
res.statusCode = s3Response.statusCode;
s3Response.pipe(res);
console.log("https://s3.amazonaws.com/" + process.env.S3_BUCKET + destPath);
});
});
form.on('close', onEnd);
form.parse(req);
} else {
res.writeHead(404, {'content-type': 'text/plain'});
res.end('404');
}
function onEnd() {
throw new Error("no uploaded file");
}
});
server.listen(PORT, function() {
console.info('listening on http://0.0.0.0:'+PORT+'/');
});
var http = require('http')
, util = require('util')
, multiparty = require('../')
, PORT = process.env.PORT || 27372
var server = http.createServer(function(req, res) {
if (req.url === '/') {
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="text" name="title"><br>'+
'<input type="file" name="upload" multiple="multiple"><br>'+
'<input type="submit" value="Upload">'+
'</form>'
);
} else if (req.url === '/upload') {
var form = new multiparty.Form();
form.parse(req, function(err, fields, files) {
if (err) {
res.writeHead(400, {'content-type': 'text/plain'});
res.end("invalid request: " + err.message);
return;
}
res.writeHead(200, {'content-type': 'text/plain'});
res.write('received fields:\n\n '+util.inspect(fields));
res.write('\n\n');
res.end('received files:\n\n '+util.inspect(files));
});
} else {
res.writeHead(404, {'content-type': 'text/plain'});
res.end('404');
}
});
server.listen(PORT, function() {
console.info('listening on http://0.0.0.0:'+PORT+'/');
});
exports.Form = Form;
var stream = require('readable-stream')
, util = require('util')
, fs = require('fs')
, crypto = require('crypto')
, path = require('path')
, os = require('os')
, StringDecoder = require('string_decoder').StringDecoder
, StreamCounter = require('stream-counter')
var START = 0
, START_BOUNDARY = 1
, HEADER_FIELD_START = 2
, HEADER_FIELD = 3
, HEADER_VALUE_START = 4
, HEADER_VALUE = 5
, HEADER_VALUE_ALMOST_DONE = 6
, HEADERS_ALMOST_DONE = 7
, PART_DATA_START = 8
, PART_DATA = 9
, PART_END = 10
, END = 11
, LF = 10
, CR = 13
, SPACE = 32
, HYPHEN = 45
, COLON = 58
, A = 97
, Z = 122
var CONTENT_TYPE_RE = /^multipart\/(form-data|related);\s*boundary=(?:"([^"]+)"|([^;]+))$/i;
var FILE_EXT_RE = /(\.[_\-a-zA-Z0-9]{0,16}).*/;
var LAST_BOUNDARY_SUFFIX_LEN = 4; // --\r\n
util.inherits(Form, stream.Writable);
function Form(options) {
var self = this;
stream.Writable.call(self);
options = options || {};
self.error = null;
self.finished = false;
self.autoFields = !!options.autoFields;
self.autoFiles = !!options.autoFields;
self.maxFields = options.maxFields || 1000;
self.maxFieldsSize = options.maxFieldsSize || 2 * 1024 * 1024;
self.uploadDir = options.uploadDir || os.tmpDir();
self.encoding = options.encoding || 'utf8';
self.hash = options.hash || false;
self.bytesReceived = 0;
self.bytesExpected = null;
self.openedFiles = [];
self.totalFieldSize = 0;
self.totalFieldCount = 0;
self.flushing = 0;
self.backpressure = false;
self.writeCbs = [];
if (options.boundary) setUpParser(self, options.boundary);
self.on('newListener', function(eventName) {
if (eventName === 'file') {
self.autoFiles = true;
} else if (eventName === 'field') {
self.autoFields = true;
}
});
}
Form.prototype.parse = function(req, cb) {
var self = this;
// if the user supplies a callback, this implies autoFields and autoFiles
if (cb) {
self.autoFields = true;
self.autoFiles = true;
}
self.handleError = handleError;
self.bytesExpected = getBytesExpected(req.headers);
req.on('error', handleError);
req.on('aborted', onReqAborted);
var contentType = req.headers['content-type'];
if (!contentType) {
handleError(new Error('missing content-type header'));
return;
}
var m = contentType.match(CONTENT_TYPE_RE);
if (!m) {
handleError(new Error('unrecognized content-type: ' + contentType));
return;
}
var boundary = m[2] || m[3];
setUpParser(self, boundary);
req.pipe(self);
if (cb) {
var fieldsTable = {};
var filesTable = {};
var fieldsList = [];
var filesList = [];
self.on('error', function(err) {
cb(err);
});
self.on('field', function(name, value) {
fieldsTable[name] = value;
fieldsList.push({name: name, value: value});
});
self.on('file', function(name, file) {
filesTable[name] = file;
filesList.push(file);
});
self.on('close', function() {
cb(null, fieldsTable, filesTable, fieldsList, filesList);
});
}
function onReqAborted() {
self.emit('aborted');
handleError(new Error("Request aborted"));
}
function handleError(err) {
var first = !self.error;
if (first) {
self.error = err;
req.removeListener('aborted', onReqAborted);
// welp. 0.8 doesn't support unpipe, too bad so sad.
// let's drop support for 0.8 soon.
if (req.unpipe) {
req.unpipe(self);
}
}
self.openedFiles.forEach(function(file) {
file.ws.destroy();
fs.unlink(file.path, function(err) {
// this is already an error condition, ignore 2nd error
});
});
self.openedFiles = [];
if (first) {
self.emit('error', err);
}
}
};
Form.prototype._write = function(buffer, encoding, cb) {
var self = this
, i = 0
, len = buffer.length
, prevIndex = self.index
, index = self.index
, state = self.state
, lookbehind = self.lookbehind
, boundary = self.boundary
, boundaryChars = self.boundaryChars
, boundaryLength = self.boundary.length
, boundaryEnd = boundaryLength - 1
, bufferLength = buffer.length
, c
, cl
for (i = 0; i < len; i++) {
c = buffer[i];
switch (state) {
case START:
index = 0;
state = START_BOUNDARY;
/* falls through */
case START_BOUNDARY:
if (index === boundaryLength - 2) {
if (c !== CR) return self.handleError(new Error("Expected CR Received " + c));
index++;
break;
} else if (index === boundaryLength - 1) {
if (c !== LF) return self.handleError(new Error("Expected LF Received " + c));
index = 0;
self.onParsePartBegin();
state = HEADER_FIELD_START;
break;
}
if (c !== boundary[index+2]) index = -2;
if (c === boundary[index+2]) index++;
break;
case HEADER_FIELD_START:
state = HEADER_FIELD;
self.headerFieldMark = i;
index = 0;
/* falls through */
case HEADER_FIELD:
if (c === CR) {
self.headerFieldMark = null;
state = HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) break;
if (c === COLON) {
if (index === 1) {
// empty header field
self.handleError(new Error("Empty header field"));
return;
}
self.onParseHeaderField(buffer.slice(self.headerFieldMark, i));
self.headerFieldMark = null;
state = HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
self.handleError(new Error("Expected alphabetic character, received " + c));
return;
}
break;
case HEADER_VALUE_START:
if (c === SPACE) break;
self.headerValueMark = i;
state = HEADER_VALUE;
/* falls through */
case HEADER_VALUE:
if (c === CR) {
self.onParseHeaderValue(buffer.slice(self.headerValueMark, i));
self.headerValueMark = null;
self.onParseHeaderEnd();
state = HEADER_VALUE_ALMOST_DONE;
}
break;
case HEADER_VALUE_ALMOST_DONE:
if (c !== LF) return self.handleError(new Error("Expected LF Received " + c));
state = HEADER_FIELD_START;
break;
case HEADERS_ALMOST_DONE:
if (c !== LF) return self.handleError(new Error("Expected LF Received " + c));
var err = self.onParseHeadersEnd(i + 1);
if (err) return self.handleError(err);
state = PART_DATA_START;
break;
case PART_DATA_START:
state = PART_DATA;
self.partDataMark = i;
/* falls through */
case PART_DATA:
prevIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(buffer[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = buffer[i];
}
if (index < boundaryLength) {
if (boundary[index] === c) {
if (index === 0) {
self.onParsePartData(buffer.slice(self.partDataMark, i));
self.partDataMark = null;
}
index++;
} else {
index = 0;
}
} else if (index === boundaryLength) {
index++;
if (c === CR) {
// CR = part boundary
self.partBoundaryFlag = true;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
self.lastBoundaryFlag = true;
} else {
index = 0;
}
} else if (index - 1 === boundaryLength) {
if (self.partBoundaryFlag) {
index = 0;
if (c === LF) {
self.partBoundaryFlag = false;
self.onParsePartEnd();
self.onParsePartBegin();
state = HEADER_FIELD_START;
break;
}
} else if (self.lastBoundaryFlag) {
if (c === HYPHEN) {
self.onParsePartEnd();
self.end();
state = END;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index-1] = c;
} else if (prevIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
self.onParsePartData(lookbehind.slice(0, prevIndex));
prevIndex = 0;
self.partDataMark = i;
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case END:
break;
default:
self.handleError(new Error("Parser has invalid state."));
return;
}
}
if (self.headerFieldMark != null) {
self.onParseHeaderField(buffer.slice(self.headerFieldMark));
self.headerFieldMark = 0;
}
if (self.headerValueMark != null) {
self.onParseHeaderValue(buffer.slice(self.headerValueMark));
self.headerValueMark = 0;
}
if (self.partDataMark != null) {
self.onParsePartData(buffer.slice(self.partDataMark));
self.partDataMark = 0;
}
self.index = index;
self.state = state;
self.bytesReceived += buffer.length;
self.emit('progress', self.bytesReceived, self.bytesExpected);
if (self.backpressure) {
self.writeCbs.push(cb);
} else {
cb();
}
};
Form.prototype.onParsePartBegin = function() {
clearPartVars(this);
}
Form.prototype.onParseHeaderField = function(b) {
this.headerField += this.headerFieldDecoder.write(b);
}
Form.prototype.onParseHeaderValue = function(b) {
this.headerValue += this.headerValueDecoder.write(b);
}
Form.prototype.onParseHeaderEnd = function() {
this.headerField = this.headerField.toLowerCase();
this.partHeaders[this.headerField] = this.headerValue;
var m;
if (this.headerField === 'content-disposition') {
if (m = this.headerValue.match(/\bname="([^"]+)"/i)) {
this.partName = m[1];
}
this.partFilename = parseFilename(this.headerValue);
} else if (this.headerField === 'content-transfer-encoding') {
this.partTransferEncoding = this.headerValue.toLowerCase();
}
this.headerFieldDecoder = new StringDecoder(this.encoding);
this.headerField = '';
this.headerValueDecoder = new StringDecoder(this.encoding);
this.headerValue = '';
}
Form.prototype.onParsePartData = function(b) {
if (this.partTransferEncoding === 'base64') {
this.backpressure = ! this.destStream.write(b.toString('ascii'), 'base64');
} else {
this.backpressure = ! this.destStream.write(b);
}
}
Form.prototype.onParsePartEnd = function() {
if (this.destStream) {
flushWriteCbs(this);
var s = this.destStream;
process.nextTick(function() {
s.end();
});
}
clearPartVars(this);
}
Form.prototype.onParseHeadersEnd = function(offset) {
var self = this;
switch(self.partTransferEncoding){
case 'binary':
case '7bit':
case '8bit':
self.partTransferEncoding = 'binary';
break;
case 'base64': break;
default:
return new Error("unknown transfer-encoding: " + self.partTransferEncoding);
}
self.totalFieldCount += 1;
if (self.totalFieldCount >= self.maxFields) {
return new Error("maxFields " + self.maxFields + " exceeded.");
}
self.destStream = new stream.PassThrough();
self.destStream.on('drain', function() {
flushWriteCbs(self);
});
self.destStream.headers = self.partHeaders;
self.destStream.name = self.partName;
self.destStream.filename = self.partFilename;
self.destStream.byteOffset = self.bytesReceived + offset;
var partContentLength = self.destStream.headers['content-length'];
self.destStream.byteCount = partContentLength ?
parseInt(partContentLength, 10) :
(self.bytesExpected - self.destStream.byteOffset -
self.boundary.length - LAST_BOUNDARY_SUFFIX_LEN);
self.emit('part', self.destStream);
if (self.destStream.filename == null && self.autoFields) {
handleField(self, self.destStream);
} else if (self.destStream.filename != null && self.autoFiles) {
handleFile(self, self.destStream);
}
}
function flushWriteCbs(self) {
self.writeCbs.forEach(function(cb) {
process.nextTick(cb);
});
self.writeCbs = [];
self.backpressure = false;
}
function getBytesExpected(headers) {
var contentLength = headers['content-length'];
if (contentLength) {
return parseInt(contentLength, 10);
} else if (headers['transfer-encoding'] == null) {
return 0;
} else {
return null;
}
}
function beginFlush(self) {
self.flushing += 1;
}
function endFlush(self) {
self.flushing -= 1;
maybeClose(self);
}
function maybeClose(self) {
if (!self.flushing && self.finished && !self.error) {
self.emit('close');
}
}
function handleFile(self, fileStream) {
beginFlush(self);
var file = {
fieldName: fileStream.name,
originalFilename: fileStream.filename,
path: uploadPath(self.uploadDir, fileStream.filename),
headers: fileStream.headers,
};
file.ws = fs.createWriteStream(file.path);
self.openedFiles.push(file);
fileStream.pipe(file.ws);
var counter = new StreamCounter();
fileStream.pipe(counter);
var hashWorkaroundStream
, hash = null;
if (self.hash) {
// workaround stream because https://github.com/joyent/node/issues/5216
hashWorkaroundStream = stream.Writable();
hash = crypto.createHash(self.hash);
hashWorkaroundStream._write = function(buffer, encoding, callback) {
hash.update(buffer);
callback();
};
fileStream.pipe(hashWorkaroundStream);
}
file.ws.on('error', function(err) {
if (!self.error) self.handleError(err);
});
file.ws.on('close', function() {
if (hash) file.hash = hash.digest('hex');
file.size = counter.bytes;
self.emit('file', fileStream.name, file);
endFlush(self);
});
}
function handleField(self, fieldStream) {
var value = '';
var decoder = new StringDecoder(self.encoding);
beginFlush(self);
fieldStream.on('readable', function() {
var buffer = fieldStream.read();
if (!buffer) return;
self.totalFieldSize += buffer.length;
if (self.totalFieldSize > self.maxFieldsSize) {
self.handleError(new Error("maxFieldsSize " + self.maxFieldsSize + " exceeded"));
return;
}
value += decoder.write(buffer);
});
fieldStream.on('end', function() {
self.emit('field', fieldStream.name, value);
endFlush(self);
});
}
function clearPartVars(self) {
self.partHeaders = {};
self.partName = null;
self.partFilename = null;
self.partTransferEncoding = 'binary';
self.destStream = null;
self.headerFieldDecoder = new StringDecoder(self.encoding);
self.headerField = "";
self.headerValueDecoder = new StringDecoder(self.encoding);
self.headerValue = "";
}
function setUpParser(self, boundary) {
self.boundary = new Buffer(boundary.length + 4);
self.boundary.write('\r\n--', 0, boundary.length + 4, 'ascii');
self.boundary.write(boundary, 4, boundary.length, 'ascii');
self.lookbehind = new Buffer(self.boundary.length + 8);
self.state = START;
self.boundaryChars = {};
for (var i = 0; i < self.boundary.length; i++) {
self.boundaryChars[self.boundary[i]] = true;
}
self.index = null;
self.partBoundaryFlag = false;
self.lastBoundaryFlag = false;
self.on('finish', function() {
if ((self.state === HEADER_FIELD_START && self.index === 0) ||
(self.state === PART_DATA && self.index === self.boundary.length))
{
self.onParsePartEnd();
} else if (self.state !== END) {
self.handleError(new Error('stream ended unexpectedly'));
}
self.finished = true;
maybeClose(self);
});
}
function uploadPath(baseDir, filename) {
var ext = path.extname(filename).replace(FILE_EXT_RE, '$1');
var name = process.pid + '-' +
(Math.random() * 0x100000000 + 1).toString(36) + ext;
return path.join(baseDir, name);
}
function parseFilename(headerValue) {
var m = headerValue.match(/\bfilename="(.*?)"($|; )/i);
if (!m) return;
var filename = m[1].substr(m[1].lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#([\d]{4});/g, function(m, code) {
return String.fromCharCode(code);
});
return filename;
}
function lower(c) {
return c | 0x20;
}
Copyright (C) 2011-2013 Felix Geisendörfer, Andrew Kelley
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
var Transform = require('../transform');
var inherits = require('util').inherits;
// subclass
function MyStream () {
Transform.call(this, {
lowWaterMark: 0,
encoding: 'utf8'
});
}
inherits(MyStream, Transform);
MyStream.prototype._transform = function (chunk, outputFn, callback) {
outputFn(new Buffer(String(chunk).toUpperCase()));
callback();
};
// use it!
var s = new MyStream();
process.stdin.resume();
process.stdin.pipe(s).pipe(process.stdout);
if (process.stdin.setRawMode)
process.stdin.setRawMode(true);
process.stdin.on('data', function (c) {
c = c.toString();
if (c === '\u0003' || c === '\u0004') {
process.stdin.pause();
s.end();
}
if (c === '\r')
process.stdout.write('\n');
});
var fs = require('fs');
var FSReadable = require('../fs.js');
var rst = new FSReadable(__filename);
rst.on('end', function() {
process.stdin.pause();
});
process.stdin.setRawMode(true);
process.stdin.on('data', function() {
var c = rst.read(3);
if (!c) return;
process.stdout.write(c);
});
process.stdin.resume();
var fs = require('fs');
var fst = fs.createReadStream(__filename);
var Readable = require('../readable.js');
var rst = new Readable();
rst.wrap(fst);
rst.on('end', function() {
process.stdin.pause();
});
process.stdin.setRawMode(true);
process.stdin.on('data', function() {
var c = rst.read(3);
if (!c) return setTimeout(process.exit, 500)
process.stdout.write(c);
});
process.stdin.resume();
diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js
index c5a741c..a2e0d8e 100644
--- a/lib/_stream_duplex.js
+++ b/lib/_stream_duplex.js
@@ -26,8 +26,8 @@
module.exports = Duplex;
var util = require('util');
-var Readable = require('_stream_readable');
-var Writable = require('_stream_writable');
+var Readable = require('./_stream_readable');
+var Writable = require('./_stream_writable');
util.inherits(Duplex, Readable);
diff --git a/lib/_stream_passthrough.js b/lib/_stream_passthrough.js
index a5e9864..330c247 100644
--- a/lib/_stream_passthrough.js
+++ b/lib/_stream_passthrough.js
@@ -25,7 +25,7 @@
module.exports = PassThrough;
-var Transform = require('_stream_transform');
+var Transform = require('./_stream_transform');
var util = require('util');
util.inherits(PassThrough, Transform);
diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js
index 0c3fe3e..90a8298 100644
--- a/lib/_stream_readable.js
+++ b/lib/_stream_readable.js
@@ -23,10 +23,34 @@ module.exports = Readable;
Readable.ReadableState = ReadableState;
var EE = require('events').EventEmitter;
+if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
+ return emitter.listeners(type).length;
+};
+
+if (!global.setImmediate) global.setImmediate = function setImmediate(fn) {
+ return setTimeout(fn, 0);
+};
+if (!global.clearImmediate) global.clearImmediate = function clearImmediate(i) {
+ return clearTimeout(i);
+};
+
var Stream = require('stream');
var util = require('util');
+if (!util.isUndefined) {
+ var utilIs = require('core-util-is');
+ for (var f in utilIs) {
+ util[f] = utilIs[f];
+ }
+}
var StringDecoder;
-var debug = util.debuglog('stream');
+var debug;
+if (util.debuglog)
+ debug = util.debuglog('stream');
+else try {
+ debug = require('debuglog')('stream');
+} catch (er) {
+ debug = function() {};
+}
util.inherits(Readable, Stream);
@@ -380,7 +404,7 @@ function chunkInvalid(state, chunk) {
function onEofChunk(stream, state) {
- if (state.decoder && !state.ended) {
+ if (state.decoder && !state.ended && state.decoder.end) {
var chunk = state.decoder.end();
if (chunk && chunk.length) {
state.buffer.push(chunk);
diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js
index b1f9fcc..b0caf57 100644
--- a/lib/_stream_transform.js
+++ b/lib/_stream_transform.js
@@ -64,8 +64,14 @@
module.exports = Transform;
-var Duplex = require('_stream_duplex');
+var Duplex = require('./_stream_duplex');
var util = require('util');
+if (!util.isUndefined) {
+ var utilIs = require('core-util-is');
+ for (var f in utilIs) {
+ util[f] = utilIs[f];
+ }
+}
util.inherits(Transform, Duplex);
diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js
index ba2e920..f49288b 100644
--- a/lib/_stream_writable.js
+++ b/lib/_stream_writable.js
@@ -27,6 +27,12 @@ module.exports = Writable;
Writable.WritableState = WritableState;
var util = require('util');
+if (!util.isUndefined) {
+ var utilIs = require('core-util-is');
+ for (var f in utilIs) {
+ util[f] = utilIs[f];
+ }
+}
var Stream = require('stream');
util.inherits(Writable, Stream);
@@ -119,7 +125,7 @@ function WritableState(options, stream) {
function Writable(options) {
// Writable ctor is applied to Duplexes, though they're not
// instanceof Writable, they're instanceof Readable.
- if (!(this instanceof Writable) && !(this instanceof Stream.Duplex))
+ if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex')))
return new Writable(options);
this._writableState = new WritableState(options, this);
diff --git a/test/simple/test-stream-big-push.js b/test/simple/test-stream-big-push.js
index e3787e4..8cd2127 100644
--- a/test/simple/test-stream-big-push.js
+++ b/test/simple/test-stream-big-push.js
@@ -21,7 +21,7 @@
var common = require('../common');
var assert = require('assert');
-var stream = require('stream');
+var stream = require('../../');
var str = 'asdfasdfasdfasdfasdf';
var r = new stream.Readable({
diff --git a/test/simple/test-stream-end-paused.js b/test/simple/test-stream-end-paused.js
index bb73777..d40efc7 100644
--- a/test/simple/test-stream-end-paused.js
+++ b/test/simple/test-stream-end-paused.js
@@ -25,7 +25,7 @@ var gotEnd = false;
// Make sure we don't miss the end event for paused 0-length streams
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
var stream = new Readable();
var calledRead = false;
stream._read = function() {
diff --git a/test/simple/test-stream-pipe-after-end.js b/test/simple/test-stream-pipe-after-end.js
index b46ee90..0be8366 100644
--- a/test/simple/test-stream-pipe-after-end.js
+++ b/test/simple/test-stream-pipe-after-end.js
@@ -22,8 +22,8 @@
var common = require('../common');
var assert = require('assert');
-var Readable = require('_stream_readable');
-var Writable = require('_stream_writable');
+var Readable = require('../../lib/_stream_readable');
+var Writable = require('../../lib/_stream_writable');
var util = require('util');
util.inherits(TestReadable, Readable);
diff --git a/test/simple/test-stream-pipe-cleanup.js b/test/simple/test-stream-pipe-cleanup.js
deleted file mode 100644
index f689358..0000000
--- a/test/simple/test-stream-pipe-cleanup.js
+++ /dev/null
@@ -1,122 +0,0 @@
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-// This test asserts that Stream.prototype.pipe does not leave listeners
-// hanging on the source or dest.
-
-var common = require('../common');
-var stream = require('stream');
-var assert = require('assert');
-var util = require('util');
-
-function Writable() {
- this.writable = true;
- this.endCalls = 0;
- stream.Stream.call(this);
-}
-util.inherits(Writable, stream.Stream);
-Writable.prototype.end = function() {
- this.endCalls++;
-};
-
-Writable.prototype.destroy = function() {
- this.endCalls++;
-};
-
-function Readable() {
- this.readable = true;
- stream.Stream.call(this);
-}
-util.inherits(Readable, stream.Stream);
-
-function Duplex() {
- this.readable = true;
- Writable.call(this);
-}
-util.inherits(Duplex, Writable);
-
-var i = 0;
-var limit = 100;
-
-var w = new Writable();
-
-var r;
-
-for (i = 0; i < limit; i++) {
- r = new Readable();
- r.pipe(w);
- r.emit('end');
-}
-assert.equal(0, r.listeners('end').length);
-assert.equal(limit, w.endCalls);
-
-w.endCalls = 0;
-
-for (i = 0; i < limit; i++) {
- r = new Readable();
- r.pipe(w);
- r.emit('close');
-}
-assert.equal(0, r.listeners('close').length);
-assert.equal(limit, w.endCalls);
-
-w.endCalls = 0;
-
-r = new Readable();
-
-for (i = 0; i < limit; i++) {
- w = new Writable();
- r.pipe(w);
- w.emit('close');
-}
-assert.equal(0, w.listeners('close').length);
-
-r = new Readable();
-w = new Writable();
-var d = new Duplex();
-r.pipe(d); // pipeline A
-d.pipe(w); // pipeline B
-assert.equal(r.listeners('end').length, 2); // A.onend, A.cleanup
-assert.equal(r.listeners('close').length, 2); // A.onclose, A.cleanup
-assert.equal(d.listeners('end').length, 2); // B.onend, B.cleanup
-assert.equal(d.listeners('close').length, 3); // A.cleanup, B.onclose, B.cleanup
-assert.equal(w.listeners('end').length, 0);
-assert.equal(w.listeners('close').length, 1); // B.cleanup
-
-r.emit('end');
-assert.equal(d.endCalls, 1);
-assert.equal(w.endCalls, 0);
-assert.equal(r.listeners('end').length, 0);
-assert.equal(r.listeners('close').length, 0);
-assert.equal(d.listeners('end').length, 2); // B.onend, B.cleanup
-assert.equal(d.listeners('close').length, 2); // B.onclose, B.cleanup
-assert.equal(w.listeners('end').length, 0);
-assert.equal(w.listeners('close').length, 1); // B.cleanup
-
-d.emit('end');
-assert.equal(d.endCalls, 1);
-assert.equal(w.endCalls, 1);
-assert.equal(r.listeners('end').length, 0);
-assert.equal(r.listeners('close').length, 0);
-assert.equal(d.listeners('end').length, 0);
-assert.equal(d.listeners('close').length, 0);
-assert.equal(w.listeners('end').length, 0);
-assert.equal(w.listeners('close').length, 0);
diff --git a/test/simple/test-stream-pipe-error-handling.js b/test/simple/test-stream-pipe-error-handling.js
index c5d724b..c7d6b7d 100644
--- a/test/simple/test-stream-pipe-error-handling.js
+++ b/test/simple/test-stream-pipe-error-handling.js
@@ -21,7 +21,7 @@
var common = require('../common');
var assert = require('assert');
-var Stream = require('stream').Stream;
+var Stream = require('../../').Stream;
(function testErrorListenerCatches() {
var source = new Stream();
diff --git a/test/simple/test-stream-pipe-event.js b/test/simple/test-stream-pipe-event.js
index cb9d5fe..56f8d61 100644
--- a/test/simple/test-stream-pipe-event.js
+++ b/test/simple/test-stream-pipe-event.js
@@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
-var stream = require('stream');
+var stream = require('../../');
var assert = require('assert');
var util = require('util');
diff --git a/test/simple/test-stream-push-order.js b/test/simple/test-stream-push-order.js
index f2e6ec2..a5c9bf9 100644
--- a/test/simple/test-stream-push-order.js
+++ b/test/simple/test-stream-push-order.js
@@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
var assert = require('assert');
var s = new Readable({
diff --git a/test/simple/test-stream-push-strings.js b/test/simple/test-stream-push-strings.js
index 06f43dc..1701a9a 100644
--- a/test/simple/test-stream-push-strings.js
+++ b/test/simple/test-stream-push-strings.js
@@ -22,7 +22,7 @@
var common = require('../common');
var assert = require('assert');
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
var util = require('util');
util.inherits(MyStream, Readable);
diff --git a/test/simple/test-stream-readable-event.js b/test/simple/test-stream-readable-event.js
index ba6a577..a8e6f7b 100644
--- a/test/simple/test-stream-readable-event.js
+++ b/test/simple/test-stream-readable-event.js
@@ -22,7 +22,7 @@
var common = require('../common');
var assert = require('assert');
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
(function first() {
// First test, not reading when the readable is added.
diff --git a/test/simple/test-stream-readable-flow-recursion.js b/test/simple/test-stream-readable-flow-recursion.js
index 2891ad6..11689ba 100644
--- a/test/simple/test-stream-readable-flow-recursion.js
+++ b/test/simple/test-stream-readable-flow-recursion.js
@@ -27,7 +27,7 @@ var assert = require('assert');
// more data continuously, but without triggering a nextTick
// warning or RangeError.
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
// throw an error if we trigger a nextTick warning.
process.throwDeprecation = true;
diff --git a/test/simple/test-stream-unshift-empty-chunk.js b/test/simple/test-stream-unshift-empty-chunk.js
index 0c96476..7827538 100644
--- a/test/simple/test-stream-unshift-empty-chunk.js
+++ b/test/simple/test-stream-unshift-empty-chunk.js
@@ -24,7 +24,7 @@ var assert = require('assert');
// This test verifies that stream.unshift(Buffer(0)) or
// stream.unshift('') does not set state.reading=false.
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
var r = new Readable();
var nChunks = 10;
diff --git a/test/simple/test-stream-unshift-read-race.js b/test/simple/test-stream-unshift-read-race.js
index 83fd9fa..17c18aa 100644
--- a/test/simple/test-stream-unshift-read-race.js
+++ b/test/simple/test-stream-unshift-read-race.js
@@ -29,7 +29,7 @@ var assert = require('assert');
// 3. push() after the EOF signaling null is an error.
// 4. _read() is not called after pushing the EOF null chunk.
-var stream = require('stream');
+var stream = require('../../');
var hwm = 10;
var r = stream.Readable({ highWaterMark: hwm });
var chunks = 10;
@@ -51,7 +51,14 @@ r._read = function(n) {
function push(fast) {
assert(!pushedNull, 'push() after null push');
- var c = pos >= data.length ? null : data.slice(pos, pos + n);
+ var c;
+ if (pos >= data.length)
+ c = null;
+ else {
+ if (n + pos > data.length)
+ n = data.length - pos;
+ c = data.slice(pos, pos + n);
+ }
pushedNull = c === null;
if (fast) {
pos += n;
diff --git a/test/simple/test-stream-writev.js b/test/simple/test-stream-writev.js
index 5b49e6e..b5321f3 100644
--- a/test/simple/test-stream-writev.js
+++ b/test/simple/test-stream-writev.js
@@ -22,7 +22,7 @@
var common = require('../common');
var assert = require('assert');
-var stream = require('stream');
+var stream = require('../../');
var queue = [];
for (var decode = 0; decode < 2; decode++) {
diff --git a/test/simple/test-stream2-basic.js b/test/simple/test-stream2-basic.js
index 3814bf0..248c1be 100644
--- a/test/simple/test-stream2-basic.js
+++ b/test/simple/test-stream2-basic.js
@@ -21,7 +21,7 @@
var common = require('../common.js');
-var R = require('_stream_readable');
+var R = require('../../lib/_stream_readable');
var assert = require('assert');
var util = require('util');
diff --git a/test/simple/test-stream2-compatibility.js b/test/simple/test-stream2-compatibility.js
index 6cdd4e9..f0fa84b 100644
--- a/test/simple/test-stream2-compatibility.js
+++ b/test/simple/test-stream2-compatibility.js
@@ -21,7 +21,7 @@
var common = require('../common.js');
-var R = require('_stream_readable');
+var R = require('../../lib/_stream_readable');
var assert = require('assert');
var util = require('util');
diff --git a/test/simple/test-stream2-finish-pipe.js b/test/simple/test-stream2-finish-pipe.js
index 39b274f..006a19b 100644
--- a/test/simple/test-stream2-finish-pipe.js
+++ b/test/simple/test-stream2-finish-pipe.js
@@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
-var stream = require('stream');
+var stream = require('../../');
var Buffer = require('buffer').Buffer;
var r = new stream.Readable();
diff --git a/test/simple/test-stream2-fs.js b/test/simple/test-stream2-fs.js
deleted file mode 100644
index e162406..0000000
--- a/test/simple/test-stream2-fs.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
-var common = require('../common.js');
-var R = require('_stream_readable');
-var assert = require('assert');
-
-var fs = require('fs');
-var FSReadable = fs.ReadStream;
-
-var path = require('path');
-var file = path.resolve(common.fixturesDir, 'x1024.txt');
-
-var size = fs.statSync(file).size;
-
-var expectLengths = [1024];
-
-var util = require('util');
-var Stream = require('stream');
-
-util.inherits(TestWriter, Stream);
-
-function TestWriter() {
- Stream.apply(this);
- this.buffer = [];
- this.length = 0;
-}
-
-TestWriter.prototype.write = function(c) {
- this.buffer.push(c.toString());
- this.length += c.length;
- return true;
-};
-
-TestWriter.prototype.end = function(c) {
- if (c) this.buffer.push(c.toString());
- this.emit('results', this.buffer);
-}
-
-var r = new FSReadable(file);
-var w = new TestWriter();
-
-w.on('results', function(res) {
- console.error(res, w.length);
- assert.equal(w.length, size);
- var l = 0;
- assert.deepEqual(res.map(function (c) {
- return c.length;
- }), expectLengths);
- console.log('ok');
-});
-
-r.pipe(w);
diff --git a/test/simple/test-stream2-httpclient-response-end.js b/test/simple/test-stream2-httpclient-response-end.js
deleted file mode 100644
index 15cffc2..0000000
--- a/test/simple/test-stream2-httpclient-response-end.js
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-var common = require('../common.js');
-var assert = require('assert');
-var http = require('http');
-var msg = 'Hello';
-var readable_event = false;
-var end_event = false;
-var server = http.createServer(function(req, res) {
- res.writeHead(200, {'Content-Type': 'text/plain'});
- res.end(msg);
-}).listen(common.PORT, function() {
- http.get({port: common.PORT}, function(res) {
- var data = '';
- res.on('readable', function() {
- console.log('readable event');
- readable_event = true;
- data += res.read();
- });
- res.on('end', function() {
- console.log('end event');
- end_event = true;
- assert.strictEqual(msg, data);
- server.close();
- });
- });
-});
-
-process.on('exit', function() {
- assert(readable_event);
- assert(end_event);
-});
-
diff --git a/test/simple/test-stream2-large-read-stall.js b/test/simple/test-stream2-large-read-stall.js
index 2fbfbca..667985b 100644
--- a/test/simple/test-stream2-large-read-stall.js
+++ b/test/simple/test-stream2-large-read-stall.js
@@ -30,7 +30,7 @@ var PUSHSIZE = 20;
var PUSHCOUNT = 1000;
var HWM = 50;
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
var r = new Readable({
highWaterMark: HWM
});
@@ -39,23 +39,23 @@ var rs = r._readableState;
r._read = push;
r.on('readable', function() {
- console.error('>> readable');
+ //console.error('>> readable');
do {
- console.error(' > read(%d)', READSIZE);
+ //console.error(' > read(%d)', READSIZE);
var ret = r.read(READSIZE);
- console.error(' < %j (%d remain)', ret && ret.length, rs.length);
+ //console.error(' < %j (%d remain)', ret && ret.length, rs.length);
} while (ret && ret.length === READSIZE);
- console.error('<< after read()',
- ret && ret.length,
- rs.needReadable,
- rs.length);
+ //console.error('<< after read()',
+ // ret && ret.length,
+ // rs.needReadable,
+ // rs.length);
});
var endEmitted = false;
r.on('end', function() {
endEmitted = true;
- console.error('end');
+ //console.error('end');
});
var pushes = 0;
@@ -64,11 +64,11 @@ function push() {
return;
if (pushes++ === PUSHCOUNT) {
- console.error(' push(EOF)');
+ //console.error(' push(EOF)');
return r.push(null);
}
- console.error(' push #%d', pushes);
+ //console.error(' push #%d', pushes);
if (r.push(new Buffer(PUSHSIZE)))
setTimeout(push);
}
diff --git a/test/simple/test-stream2-objects.js b/test/simple/test-stream2-objects.js
index 3e6931d..ff47d89 100644
--- a/test/simple/test-stream2-objects.js
+++ b/test/simple/test-stream2-objects.js
@@ -21,8 +21,8 @@
var common = require('../common.js');
-var Readable = require('_stream_readable');
-var Writable = require('_stream_writable');
+var Readable = require('../../lib/_stream_readable');
+var Writable = require('../../lib/_stream_writable');
var assert = require('assert');
// tiny node-tap lookalike.
diff --git a/test/simple/test-stream2-pipe-error-handling.js b/test/simple/test-stream2-pipe-error-handling.js
index cf7531c..e3f3e4e 100644
--- a/test/simple/test-stream2-pipe-error-handling.js
+++ b/test/simple/test-stream2-pipe-error-handling.js
@@ -21,7 +21,7 @@
var common = require('../common');
var assert = require('assert');
-var stream = require('stream');
+var stream = require('../../');
(function testErrorListenerCatches() {
var count = 1000;
diff --git a/test/simple/test-stream2-pipe-error-once-listener.js b/test/simple/test-stream2-pipe-error-once-listener.js
index 5e8e3cb..53b2616 100755
--- a/test/simple/test-stream2-pipe-error-once-listener.js
+++ b/test/simple/test-stream2-pipe-error-once-listener.js
@@ -24,7 +24,7 @@ var common = require('../common.js');
var assert = require('assert');
var util = require('util');
-var stream = require('stream');
+var stream = require('../../');
var Read = function() {
diff --git a/test/simple/test-stream2-push.js b/test/simple/test-stream2-push.js
index b63edc3..eb2b0e9 100644
--- a/test/simple/test-stream2-push.js
+++ b/test/simple/test-stream2-push.js
@@ -20,7 +20,7 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
-var stream = require('stream');
+var stream = require('../../');
var Readable = stream.Readable;
var Writable = stream.Writable;
var assert = require('assert');
diff --git a/test/simple/test-stream2-read-sync-stack.js b/test/simple/test-stream2-read-sync-stack.js
index e8a7305..9740a47 100644
--- a/test/simple/test-stream2-read-sync-stack.js
+++ b/test/simple/test-stream2-read-sync-stack.js
@@ -21,7 +21,7 @@
var common = require('../common');
var assert = require('assert');
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
var r = new Readable();
var N = 256 * 1024;
diff --git a/test/simple/test-stream2-readable-empty-buffer-no-eof.js b/test/simple/test-stream2-readable-empty-buffer-no-eof.js
index cd30178..4b1659d 100644
--- a/test/simple/test-stream2-readable-empty-buffer-no-eof.js
+++ b/test/simple/test-stream2-readable-empty-buffer-no-eof.js
@@ -22,10 +22,9 @@
var common = require('../common');
var assert = require('assert');
-var Readable = require('stream').Readable;
+var Readable = require('../../').Readable;
test1();
-test2();
function test1() {
var r = new Readable();
@@ -88,31 +87,3 @@ function test1() {
console.log('ok');
});
}
-
-function test2() {
- var r = new Readable({ encoding: 'base64' });
- var reads = 5;
- r._read = function(n) {
- if (!reads--)
- return r.push(null); // EOF
- else
- return r.push(new Buffer('x'));
- };
-
- var results = [];
- function flow() {
- var chunk;
- while (null !== (chunk = r.read()))
- results.push(chunk + '');
- }
- r.on('readable', flow);
- r.on('end', function() {
- results.push('EOF');
- });
- flow();
-
- process.on('exit', function() {
- assert.deepEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]);
- console.log('ok');
- });
-}
diff --git a/test/simple/test-stream2-readable-from-list.js b/test/simple/test-stream2-readable-from-list.js
index 7c96ffe..04a96f5 100644
--- a/test/simple/test-stream2-readable-from-list.js
+++ b/test/simple/test-stream2-readable-from-list.js
@@ -21,7 +21,7 @@
var assert = require('assert');
var common = require('../common.js');
-var fromList = require('_stream_readable')._fromList;
+var fromList = require('../../lib/_stream_readable')._fromList;
// tiny node-tap lookalike.
var tests = [];
diff --git a/test/simple/test-stream2-readable-legacy-drain.js b/test/simple/test-stream2-readable-legacy-drain.js
index 675da8e..51fd3d5 100644
--- a/test/simple/test-stream2-readable-legacy-drain.js
+++ b/test/simple/test-stream2-readable-legacy-drain.js
@@ -22,7 +22,7 @@
var common = require('../common');
var assert = require('assert');
-var Stream = require('stream');
+var Stream = require('../../');
var Readable = Stream.Readable;
var r = new Readable();
diff --git a/test/simple/test-stream2-readable-non-empty-end.js b/test/simple/test-stream2-readable-non-empty-end.js
index 7314ae7..c971898 100644
--- a/test/simple/test-stream2-readable-non-empty-end.js
+++ b/test/simple/test-stream2-readable-non-empty-end.js
@@ -21,7 +21,7 @@
var assert = require('assert');
var common = require('../common.js');
-var Readable = require('_stream_readable');
+var Readable = require('../../lib/_stream_readable');
var len = 0;
var chunks = new Array(10);
diff --git a/test/simple/test-stream2-readable-wrap-empty.js b/test/simple/test-stream2-readable-wrap-empty.js
index 2e5cf25..fd8a3dc 100644
--- a/test/simple/test-stream2-readable-wrap-empty.js
+++ b/test/simple/test-stream2-readable-wrap-empty.js
@@ -22,7 +22,7 @@
var common = require('../common');
var assert = require('assert');
-var Readable = require('_stream_readable');
+var Readable = require('../../lib/_stream_readable');
var EE = require('events').EventEmitter;
var oldStream = new EE();
diff --git a/test/simple/test-stream2-readable-wrap.js b/test/simple/test-stream2-readable-wrap.js
index 90eea01..6b177f7 100644
--- a/test/simple/test-stream2-readable-wrap.js
+++ b/test/simple/test-stream2-readable-wrap.js
@@ -22,8 +22,8 @@
var common = require('../common');
var assert = require('assert');
-var Readable = require('_stream_readable');
-var Writable = require('_stream_writable');
+var Readable = require('../../lib/_stream_readable');
+var Writable = require('../../lib/_stream_writable');
var EE = require('events').EventEmitter;
var testRuns = 0, completedRuns = 0;
diff --git a/test/simple/test-stream2-set-encoding.js b/test/simple/test-stream2-set-encoding.js
index 5d2c32a..685531b 100644
--- a/test/simple/test-stream2-set-encoding.js
+++ b/test/simple/test-stream2-set-encoding.js
@@ -22,7 +22,7 @@
var common = require('../common.js');
var assert = require('assert');
-var R = require('_stream_readable');
+var R = require('../../lib/_stream_readable');
var util = require('util');
// tiny node-tap lookalike.
diff --git a/test/simple/test-stream2-transform.js b/test/simple/test-stream2-transform.js
index 9c9ddd8..a0cacc6 100644
--- a/test/simple/test-stream2-transform.js
+++ b/test/simple/test-stream2-transform.js
@@ -21,8 +21,8 @@
var assert = require('assert');
var common = require('../common.js');
-var PassThrough = require('_stream_passthrough');
-var Transform = require('_stream_transform');
+var PassThrough = require('../../').PassThrough;
+var Transform = require('../../').Transform;
// tiny node-tap lookalike.
var tests = [];
diff --git a/test/simple/test-stream2-unpipe-drain.js b/test/simple/test-stream2-unpipe-drain.js
index d66dc3c..365b327 100644
--- a/test/simple/test-stream2-unpipe-drain.js
+++ b/test/simple/test-stream2-unpipe-drain.js
@@ -22,7 +22,7 @@
var common = require('../common.js');
var assert = require('assert');
-var stream = require('stream');
+var stream = require('../../');
var crypto = require('crypto');
var util = require('util');
diff --git a/test/simple/test-stream2-unpipe-leak.js b/test/simple/test-stream2-unpipe-leak.js
index 99f8746..17c92ae 100644
--- a/test/simple/test-stream2-unpipe-leak.js
+++ b/test/simple/test-stream2-unpipe-leak.js
@@ -22,7 +22,7 @@
var common = require('../common.js');
var assert = require('assert');
-var stream = require('stream');
+var stream = require('../../');
var chunk = new Buffer('hallo');
diff --git a/test/simple/test-stream2-writable.js b/test/simple/test-stream2-writable.js
index 704100c..209c3a6 100644
--- a/test/simple/test-stream2-writable.js
+++ b/test/simple/test-stream2-writable.js
@@ -20,8 +20,8 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
-var W = require('_stream_writable');
-var D = require('_stream_duplex');
+var W = require('../../').Writable;
+var D = require('../../').Duplex;
var assert = require('assert');
var util = require('util');
diff --git a/test/simple/test-stream3-pause-then-read.js b/test/simple/test-stream3-pause-then-read.js
index b91bde3..2f72c15 100644
--- a/test/simple/test-stream3-pause-then-read.js
+++ b/test/simple/test-stream3-pause-then-read.js
@@ -22,7 +22,7 @@
var common = require('../common');
var assert = require('assert');
-var stream = require('stream');
+var stream = require('../../');
var Readable = stream.Readable;
var Writable = stream.Writable;
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// Maintainers, keep in mind that octal literals are not allowed
// in strict mode. Use the decimal value and add a comment with
// the octal value. Example:
//
// var mode = 438; /* mode=0666 */
var util = require('util');
var pathModule = require('path');
var binding = process.binding('fs');
var constants = process.binding('constants');
var fs = exports;
var Stream = require('stream').Stream;
var EventEmitter = require('events').EventEmitter;
var Readable = require('./lib/_stream_readable.js');
var Writable = require('./lib/_stream_writable.js');
var kMinPoolSpace = 128;
var kPoolSize = 40 * 1024;
var O_APPEND = constants.O_APPEND || 0;
var O_CREAT = constants.O_CREAT || 0;
var O_DIRECTORY = constants.O_DIRECTORY || 0;
var O_EXCL = constants.O_EXCL || 0;
var O_NOCTTY = constants.O_NOCTTY || 0;
var O_NOFOLLOW = constants.O_NOFOLLOW || 0;
var O_RDONLY = constants.O_RDONLY || 0;
var O_RDWR = constants.O_RDWR || 0;
var O_SYMLINK = constants.O_SYMLINK || 0;
var O_SYNC = constants.O_SYNC || 0;
var O_TRUNC = constants.O_TRUNC || 0;
var O_WRONLY = constants.O_WRONLY || 0;
var isWindows = process.platform === 'win32';
var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG);
function rethrow() {
// Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and
// is fairly slow to generate.
if (DEBUG) {
var backtrace = new Error;
return function(err) {
if (err) {
backtrace.message = err.message;
err = backtrace;
throw err;
}
};
}
return function(err) {
if (err) {
throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs
}
};
}
function maybeCallback(cb) {
return typeof cb === 'function' ? cb : rethrow();
}
// Ensure that callbacks run in the global context. Only use this function
// for callbacks that are passed to the binding layer, callbacks that are
// invoked from JS already run in the proper scope.
function makeCallback(cb) {
if (typeof cb !== 'function') {
return rethrow();
}
return function() {
return cb.apply(null, arguments);
};
}
function assertEncoding(encoding) {
if (encoding && !Buffer.isEncoding(encoding)) {
throw new Error('Unknown encoding: ' + encoding);
}
}
function nullCheck(path, callback) {
if (('' + path).indexOf('\u0000') !== -1) {
var er = new Error('Path must be a string without null bytes.');
if (!callback)
throw er;
process.nextTick(function() {
callback(er);
});
return false;
}
return true;
}
fs.Stats = binding.Stats;
fs.Stats.prototype._checkModeProperty = function(property) {
return ((this.mode & constants.S_IFMT) === property);
};
fs.Stats.prototype.isDirectory = function() {
return this._checkModeProperty(constants.S_IFDIR);
};
fs.Stats.prototype.isFile = function() {
return this._checkModeProperty(constants.S_IFREG);
};
fs.Stats.prototype.isBlockDevice = function() {
return this._checkModeProperty(constants.S_IFBLK);
};
fs.Stats.prototype.isCharacterDevice = function() {
return this._checkModeProperty(constants.S_IFCHR);
};
fs.Stats.prototype.isSymbolicLink = function() {
return this._checkModeProperty(constants.S_IFLNK);
};
fs.Stats.prototype.isFIFO = function() {
return this._checkModeProperty(constants.S_IFIFO);
};
fs.Stats.prototype.isSocket = function() {
return this._checkModeProperty(constants.S_IFSOCK);
};
fs.exists = function(path, callback) {
if (!nullCheck(path, cb)) return;
binding.stat(pathModule._makeLong(path), cb);
function cb(err, stats) {
if (callback) callback(err ? false : true);
}
};
fs.existsSync = function(path) {
try {
nullCheck(path);
binding.stat(pathModule._makeLong(path));
return true;
} catch (e) {
return false;
}
};
fs.readFile = function(path, encoding_) {
var encoding = typeof(encoding_) === 'string' ? encoding_ : null;
var callback = maybeCallback(arguments[arguments.length - 1]);
assertEncoding(encoding);
// first, stat the file, so we know the size.
var size;
var buffer; // single buffer with file data
var buffers; // list for when size is unknown
var pos = 0;
var fd;
fs.open(path, constants.O_RDONLY, 438 /*=0666*/, function(er, fd_) {
if (er) return callback(er);
fd = fd_;
fs.fstat(fd, function(er, st) {
if (er) return callback(er);
size = st.size;
if (size === 0) {
// the kernel lies about many files.
// Go ahead and try to read some bytes.
buffers = [];
return read();
}
buffer = new Buffer(size);
read();
});
});
function read() {
if (size === 0) {
buffer = new Buffer(8192);
fs.read(fd, buffer, 0, 8192, -1, afterRead);
} else {
fs.read(fd, buffer, pos, size - pos, -1, afterRead);
}
}
function afterRead(er, bytesRead) {
if (er) {
return fs.close(fd, function(er2) {
return callback(er);
});
}
if (bytesRead === 0) {
return close();
}
pos += bytesRead;
if (size !== 0) {
if (pos === size) close();
else read();
} else {
// unknown size, just read until we don't get bytes.
buffers.push(buffer.slice(0, bytesRead));
read();
}
}
function close() {
fs.close(fd, function(er) {
if (size === 0) {
// collected the data into the buffers list.
buffer = Buffer.concat(buffers, pos);
} else if (pos < size) {
buffer = buffer.slice(0, pos);
}
if (encoding) buffer = buffer.toString(encoding);
return callback(er, buffer);
});
}
};
fs.readFileSync = function(path, encoding) {
assertEncoding(encoding);
var fd = fs.openSync(path, constants.O_RDONLY, 438 /*=0666*/);
var size;
var threw = true;
try {
size = fs.fstatSync(fd).size;
threw = false;
} finally {
if (threw) fs.closeSync(fd);
}
var pos = 0;
var buffer; // single buffer with file data
var buffers; // list for when size is unknown
if (size === 0) {
buffers = [];
} else {
buffer = new Buffer(size);
}
var done = false;
while (!done) {
var threw = true;
try {
if (size !== 0) {
var bytesRead = fs.readSync(fd, buffer, pos, size - pos);
} else {
// the kernel lies about many files.
// Go ahead and try to read some bytes.
buffer = new Buffer(8192);
var bytesRead = fs.readSync(fd, buffer, 0, 8192);
if (bytesRead) {
buffers.push(buffer.slice(0, bytesRead));
}
}
threw = false;
} finally {
if (threw) fs.closeSync(fd);
}
pos += bytesRead;
done = (bytesRead === 0) || (size !== 0 && pos >= size);
}
fs.closeSync(fd);
if (size === 0) {
// data was collected into the buffers list.
buffer = Buffer.concat(buffers, pos);
} else if (pos < size) {
buffer = buffer.slice(0, pos);
}
if (encoding) buffer = buffer.toString(encoding);
return buffer;
};
// Used by binding.open and friends
function stringToFlags(flag) {
// Only mess with strings
if (typeof flag !== 'string') {
return flag;
}
// O_EXCL is mandated by POSIX, Windows supports it too.
// Let's add a check anyway, just in case.
if (!O_EXCL && ~flag.indexOf('x')) {
throw errnoException('ENOSYS', 'fs.open(O_EXCL)');
}
switch (flag) {
case 'r' : return O_RDONLY;
case 'rs' : return O_RDONLY | O_SYNC;
case 'r+' : return O_RDWR;
case 'rs+' : return O_RDWR | O_SYNC;
case 'w' : return O_TRUNC | O_CREAT | O_WRONLY;
case 'wx' : // fall through
case 'xw' : return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL;
case 'w+' : return O_TRUNC | O_CREAT | O_RDWR;
case 'wx+': // fall through
case 'xw+': return O_TRUNC | O_CREAT | O_RDWR | O_EXCL;
case 'a' : return O_APPEND | O_CREAT | O_WRONLY;
case 'ax' : // fall through
case 'xa' : return O_APPEND | O_CREAT | O_WRONLY | O_EXCL;
case 'a+' : return O_APPEND | O_CREAT | O_RDWR;
case 'ax+': // fall through
case 'xa+': return O_APPEND | O_CREAT | O_RDWR | O_EXCL;
}
throw new Error('Unknown file open flag: ' + flag);
}
// exported but hidden, only used by test/simple/test-fs-open-flags.js
Object.defineProperty(exports, '_stringToFlags', {
enumerable: false,
value: stringToFlags
});
// Yes, the follow could be easily DRYed up but I provide the explicit
// list to make the arguments clear.
fs.close = function(fd, callback) {
binding.close(fd, makeCallback(callback));
};
fs.closeSync = function(fd) {
return binding.close(fd);
};
function modeNum(m, def) {
switch (typeof m) {
case 'number': return m;
case 'string': return parseInt(m, 8);
default:
if (def) {
return modeNum(def);
} else {
return undefined;
}
}
}
fs.open = function(path, flags, mode, callback) {
callback = makeCallback(arguments[arguments.length - 1]);
mode = modeNum(mode, 438 /*=0666*/);
if (!nullCheck(path, callback)) return;
binding.open(pathModule._makeLong(path),
stringToFlags(flags),
mode,
callback);
};
fs.openSync = function(path, flags, mode) {
mode = modeNum(mode, 438 /*=0666*/);
nullCheck(path);
return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
};
fs.read = function(fd, buffer, offset, length, position, callback) {
if (!Buffer.isBuffer(buffer)) {
// legacy string interface (fd, length, position, encoding, callback)
var cb = arguments[4],
encoding = arguments[3];
assertEncoding(encoding);
position = arguments[2];
length = arguments[1];
buffer = new Buffer(length);
offset = 0;
callback = function(err, bytesRead) {
if (!cb) return;
var str = (bytesRead > 0) ? buffer.toString(encoding, 0, bytesRead) : '';
(cb)(err, str, bytesRead);
};
}
function wrapper(err, bytesRead) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback && callback(err, bytesRead || 0, buffer);
}
binding.read(fd, buffer, offset, length, position, wrapper);
};
fs.readSync = function(fd, buffer, offset, length, position) {
var legacy = false;
if (!Buffer.isBuffer(buffer)) {
// legacy string interface (fd, length, position, encoding, callback)
legacy = true;
var encoding = arguments[3];
assertEncoding(encoding);
position = arguments[2];
length = arguments[1];
buffer = new Buffer(length);
offset = 0;
}
var r = binding.read(fd, buffer, offset, length, position);
if (!legacy) {
return r;
}
var str = (r > 0) ? buffer.toString(encoding, 0, r) : '';
return [str, r];
};
fs.write = function(fd, buffer, offset, length, position, callback) {
if (!Buffer.isBuffer(buffer)) {
// legacy string interface (fd, data, position, encoding, callback)
callback = arguments[4];
position = arguments[2];
assertEncoding(arguments[3]);
buffer = new Buffer('' + arguments[1], arguments[3]);
offset = 0;
length = buffer.length;
}
if (!length) {
if (typeof callback == 'function') {
process.nextTick(function() {
callback(undefined, 0);
});
}
return;
}
callback = maybeCallback(callback);
function wrapper(err, written) {
// Retain a reference to buffer so that it can't be GC'ed too soon.
callback(err, written || 0, buffer);
}
binding.write(fd, buffer, offset, length, position, wrapper);
};
fs.writeSync = function(fd, buffer, offset, length, position) {
if (!Buffer.isBuffer(buffer)) {
// legacy string interface (fd, data, position, encoding)
position = arguments[2];
assertEncoding(arguments[3]);
buffer = new Buffer('' + arguments[1], arguments[3]);
offset = 0;
length = buffer.length;
}
if (!length) return 0;
return binding.write(fd, buffer, offset, length, position);
};
fs.rename = function(oldPath, newPath, callback) {
callback = makeCallback(callback);
if (!nullCheck(oldPath, callback)) return;
if (!nullCheck(newPath, callback)) return;
binding.rename(pathModule._makeLong(oldPath),
pathModule._makeLong(newPath),
callback);
};
fs.renameSync = function(oldPath, newPath) {
nullCheck(oldPath);
nullCheck(newPath);
return binding.rename(pathModule._makeLong(oldPath),
pathModule._makeLong(newPath));
};
fs.truncate = function(path, len, callback) {
if (typeof path === 'number') {
// legacy
return fs.ftruncate(path, len, callback);
}
if (typeof len === 'function') {
callback = len;
len = 0;
} else if (typeof len === 'undefined') {
len = 0;
}
callback = maybeCallback(callback);
fs.open(path, 'w', function(er, fd) {
if (er) return callback(er);
binding.ftruncate(fd, len, function(er) {
fs.close(fd, function(er2) {
callback(er || er2);
});
});
});
};
fs.truncateSync = function(path, len) {
if (typeof path === 'number') {
// legacy
return fs.ftruncateSync(path, len);
}
if (typeof len === 'undefined') {
len = 0;
}
// allow error to be thrown, but still close fd.
var fd = fs.openSync(path, 'w');
try {
var ret = fs.ftruncateSync(fd, len);
} finally {
fs.closeSync(fd);
}
return ret;
};
fs.ftruncate = function(fd, len, callback) {
if (typeof len === 'function') {
callback = len;
len = 0;
} else if (typeof len === 'undefined') {
len = 0;
}
binding.ftruncate(fd, len, makeCallback(callback));
};
fs.ftruncateSync = function(fd, len) {
if (typeof len === 'undefined') {
len = 0;
}
return binding.ftruncate(fd, len);
};
fs.rmdir = function(path, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.rmdir(pathModule._makeLong(path), callback);
};
fs.rmdirSync = function(path) {
nullCheck(path);
return binding.rmdir(pathModule._makeLong(path));
};
fs.fdatasync = function(fd, callback) {
binding.fdatasync(fd, makeCallback(callback));
};
fs.fdatasyncSync = function(fd) {
return binding.fdatasync(fd);
};
fs.fsync = function(fd, callback) {
binding.fsync(fd, makeCallback(callback));
};
fs.fsyncSync = function(fd) {
return binding.fsync(fd);
};
fs.mkdir = function(path, mode, callback) {
if (typeof mode === 'function') callback = mode;
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.mkdir(pathModule._makeLong(path),
modeNum(mode, 511 /*=0777*/),
callback);
};
fs.mkdirSync = function(path, mode) {
nullCheck(path);
return binding.mkdir(pathModule._makeLong(path),
modeNum(mode, 511 /*=0777*/));
};
fs.sendfile = function(outFd, inFd, inOffset, length, callback) {
binding.sendfile(outFd, inFd, inOffset, length, makeCallback(callback));
};
fs.sendfileSync = function(outFd, inFd, inOffset, length) {
return binding.sendfile(outFd, inFd, inOffset, length);
};
fs.readdir = function(path, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.readdir(pathModule._makeLong(path), callback);
};
fs.readdirSync = function(path) {
nullCheck(path);
return binding.readdir(pathModule._makeLong(path));
};
fs.fstat = function(fd, callback) {
binding.fstat(fd, makeCallback(callback));
};
fs.lstat = function(path, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.lstat(pathModule._makeLong(path), callback);
};
fs.stat = function(path, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.stat(pathModule._makeLong(path), callback);
};
fs.fstatSync = function(fd) {
return binding.fstat(fd);
};
fs.lstatSync = function(path) {
nullCheck(path);
return binding.lstat(pathModule._makeLong(path));
};
fs.statSync = function(path) {
nullCheck(path);
return binding.stat(pathModule._makeLong(path));
};
fs.readlink = function(path, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.readlink(pathModule._makeLong(path), callback);
};
fs.readlinkSync = function(path) {
nullCheck(path);
return binding.readlink(pathModule._makeLong(path));
};
function preprocessSymlinkDestination(path, type) {
if (!isWindows) {
// No preprocessing is needed on Unix.
return path;
} else if (type === 'junction') {
// Junctions paths need to be absolute and \\?\-prefixed.
return pathModule._makeLong(path);
} else {
// Windows symlinks don't tolerate forward slashes.
return ('' + path).replace(/\//g, '\\');
}
}
fs.symlink = function(destination, path, type_, callback) {
var type = (typeof type_ === 'string' ? type_ : null);
var callback = makeCallback(arguments[arguments.length - 1]);
if (!nullCheck(destination, callback)) return;
if (!nullCheck(path, callback)) return;
binding.symlink(preprocessSymlinkDestination(destination, type),
pathModule._makeLong(path),
type,
callback);
};
fs.symlinkSync = function(destination, path, type) {
type = (typeof type === 'string' ? type : null);
nullCheck(destination);
nullCheck(path);
return binding.symlink(preprocessSymlinkDestination(destination, type),
pathModule._makeLong(path),
type);
};
fs.link = function(srcpath, dstpath, callback) {
callback = makeCallback(callback);
if (!nullCheck(srcpath, callback)) return;
if (!nullCheck(dstpath, callback)) return;
binding.link(pathModule._makeLong(srcpath),
pathModule._makeLong(dstpath),
callback);
};
fs.linkSync = function(srcpath, dstpath) {
nullCheck(srcpath);
nullCheck(dstpath);
return binding.link(pathModule._makeLong(srcpath),
pathModule._makeLong(dstpath));
};
fs.unlink = function(path, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.unlink(pathModule._makeLong(path), callback);
};
fs.unlinkSync = function(path) {
nullCheck(path);
return binding.unlink(pathModule._makeLong(path));
};
fs.fchmod = function(fd, mode, callback) {
binding.fchmod(fd, modeNum(mode), makeCallback(callback));
};
fs.fchmodSync = function(fd, mode) {
return binding.fchmod(fd, modeNum(mode));
};
if (constants.hasOwnProperty('O_SYMLINK')) {
fs.lchmod = function(path, mode, callback) {
callback = maybeCallback(callback);
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
if (err) {
callback(err);
return;
}
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
fs.fchmod(fd, mode, function(err) {
fs.close(fd, function(err2) {
callback(err || err2);
});
});
});
};
fs.lchmodSync = function(path, mode) {
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
// prefer to return the chmod error, if one occurs,
// but still try to close, and report closing errors if they occur.
var err, err2;
try {
var ret = fs.fchmodSync(fd, mode);
} catch (er) {
err = er;
}
try {
fs.closeSync(fd);
} catch (er) {
err2 = er;
}
if (err || err2) throw (err || err2);
return ret;
};
}
fs.chmod = function(path, mode, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.chmod(pathModule._makeLong(path),
modeNum(mode),
callback);
};
fs.chmodSync = function(path, mode) {
nullCheck(path);
return binding.chmod(pathModule._makeLong(path), modeNum(mode));
};
if (constants.hasOwnProperty('O_SYMLINK')) {
fs.lchown = function(path, uid, gid, callback) {
callback = maybeCallback(callback);
fs.open(path, constants.O_WRONLY | constants.O_SYMLINK, function(err, fd) {
if (err) {
callback(err);
return;
}
fs.fchown(fd, uid, gid, callback);
});
};
fs.lchownSync = function(path, uid, gid) {
var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK);
return fs.fchownSync(fd, uid, gid);
};
}
fs.fchown = function(fd, uid, gid, callback) {
binding.fchown(fd, uid, gid, makeCallback(callback));
};
fs.fchownSync = function(fd, uid, gid) {
return binding.fchown(fd, uid, gid);
};
fs.chown = function(path, uid, gid, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.chown(pathModule._makeLong(path), uid, gid, callback);
};
fs.chownSync = function(path, uid, gid) {
nullCheck(path);
return binding.chown(pathModule._makeLong(path), uid, gid);
};
// converts Date or number to a fractional UNIX timestamp
function toUnixTimestamp(time) {
if (typeof time == 'number') {
return time;
}
if (time instanceof Date) {
// convert to 123.456 UNIX timestamp
return time.getTime() / 1000;
}
throw new Error('Cannot parse time: ' + time);
}
// exported for unit tests, not for public consumption
fs._toUnixTimestamp = toUnixTimestamp;
fs.utimes = function(path, atime, mtime, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.utimes(pathModule._makeLong(path),
toUnixTimestamp(atime),
toUnixTimestamp(mtime),
callback);
};
fs.utimesSync = function(path, atime, mtime) {
nullCheck(path);
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);
binding.utimes(pathModule._makeLong(path), atime, mtime);
};
fs.futimes = function(fd, atime, mtime, callback) {
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);
binding.futimes(fd, atime, mtime, makeCallback(callback));
};
fs.futimesSync = function(fd, atime, mtime) {
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);
binding.futimes(fd, atime, mtime);
};
function writeAll(fd, buffer, offset, length, position, callback) {
callback = maybeCallback(arguments[arguments.length - 1]);
// write(fd, buffer, offset, length, position, callback)
fs.write(fd, buffer, offset, length, position, function(writeErr, written) {
if (writeErr) {
fs.close(fd, function() {
if (callback) callback(writeErr);
});
} else {
if (written === length) {
fs.close(fd, callback);
} else {
offset += written;
length -= written;
position += written;
writeAll(fd, buffer, offset, length, position, callback);
}
}
});
}
fs.writeFile = function(path, data, encoding_, callback) {
var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
assertEncoding(encoding);
callback = maybeCallback(arguments[arguments.length - 1]);
fs.open(path, 'w', 438 /*=0666*/, function(openErr, fd) {
if (openErr) {
if (callback) callback(openErr);
} else {
var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data,
encoding);
writeAll(fd, buffer, 0, buffer.length, 0, callback);
}
});
};
fs.writeFileSync = function(path, data, encoding) {
assertEncoding(encoding);
var fd = fs.openSync(path, 'w');
if (!Buffer.isBuffer(data)) {
data = new Buffer('' + data, encoding || 'utf8');
}
var written = 0;
var length = data.length;
try {
while (written < length) {
written += fs.writeSync(fd, data, written, length - written, written);
}
} finally {
fs.closeSync(fd);
}
};
fs.appendFile = function(path, data, encoding_, callback) {
var encoding = (typeof(encoding_) == 'string' ? encoding_ : 'utf8');
assertEncoding(encoding);
callback = maybeCallback(arguments[arguments.length - 1]);
fs.open(path, 'a', 438 /*=0666*/, function(err, fd) {
if (err) return callback(err);
var buffer = Buffer.isBuffer(data) ? data : new Buffer('' + data, encoding);
writeAll(fd, buffer, 0, buffer.length, null, callback);
});
};
fs.appendFileSync = function(path, data, encoding) {
assertEncoding(encoding);
var fd = fs.openSync(path, 'a');
if (!Buffer.isBuffer(data)) {
data = new Buffer('' + data, encoding || 'utf8');
}
var written = 0;
var position = null;
var length = data.length;
try {
while (written < length) {
written += fs.writeSync(fd, data, written, length - written, position);
position += written; // XXX not safe with multiple concurrent writers?
}
} finally {
fs.closeSync(fd);
}
};
function errnoException(errorno, syscall) {
// TODO make this more compatible with ErrnoException from src/node.cc
// Once all of Node is using this function the ErrnoException from
// src/node.cc should be removed.
var e = new Error(syscall + ' ' + errorno);
e.errno = e.code = errorno;
e.syscall = syscall;
return e;
}
function FSWatcher() {
EventEmitter.call(this);
var self = this;
var FSEvent = process.binding('fs_event_wrap').FSEvent;
this._handle = new FSEvent();
this._handle.owner = this;
this._handle.onchange = function(status, event, filename) {
if (status) {
self._handle.close();
self.emit('error', errnoException(errno, 'watch'));
} else {
self.emit('change', event, filename);
}
};
}
util.inherits(FSWatcher, EventEmitter);
FSWatcher.prototype.start = function(filename, persistent) {
nullCheck(filename);
var r = this._handle.start(pathModule._makeLong(filename), persistent);
if (r) {
this._handle.close();
throw errnoException(errno, 'watch');
}
};
FSWatcher.prototype.close = function() {
this._handle.close();
};
fs.watch = function(filename) {
nullCheck(filename);
var watcher;
var options;
var listener;
if ('object' == typeof arguments[1]) {
options = arguments[1];
listener = arguments[2];
} else {
options = {};
listener = arguments[1];
}
if (options.persistent === undefined) options.persistent = true;
watcher = new FSWatcher();
watcher.start(filename, options.persistent);
if (listener) {
watcher.addListener('change', listener);
}
return watcher;
};
// Stat Change Watchers
function StatWatcher() {
EventEmitter.call(this);
var self = this;
this._handle = new binding.StatWatcher();
// uv_fs_poll is a little more powerful than ev_stat but we curb it for
// the sake of backwards compatibility
var oldStatus = -1;
this._handle.onchange = function(current, previous, newStatus) {
if (oldStatus === -1 &&
newStatus === -1 &&
current.nlink === previous.nlink) return;
oldStatus = newStatus;
self.emit('change', current, previous);
};
this._handle.onstop = function() {
self.emit('stop');
};
}
util.inherits(StatWatcher, EventEmitter);
StatWatcher.prototype.start = function(filename, persistent, interval) {
nullCheck(filename);
this._handle.start(pathModule._makeLong(filename), persistent, interval);
};
StatWatcher.prototype.stop = function() {
this._handle.stop();
};
var statWatchers = {};
function inStatWatchers(filename) {
return Object.prototype.hasOwnProperty.call(statWatchers, filename) &&
statWatchers[filename];
}
fs.watchFile = function(filename) {
nullCheck(filename);
var stat;
var listener;
var options = {
// Poll interval in milliseconds. 5007 is what libev used to use. It's
// a little on the slow side but let's stick with it for now to keep
// behavioral changes to a minimum.
interval: 5007,
persistent: true
};
if ('object' == typeof arguments[1]) {
options = util._extend(options, arguments[1]);
listener = arguments[2];
} else {
listener = arguments[1];
}
if (!listener) {
throw new Error('watchFile requires a listener function');
}
if (inStatWatchers(filename)) {
stat = statWatchers[filename];
} else {
stat = statWatchers[filename] = new StatWatcher();
stat.start(filename, options.persistent, options.interval);
}
stat.addListener('change', listener);
return stat;
};
fs.unwatchFile = function(filename, listener) {
nullCheck(filename);
if (!inStatWatchers(filename)) return;
var stat = statWatchers[filename];
if (typeof listener === 'function') {
stat.removeListener('change', listener);
} else {
stat.removeAllListeners('change');
}
if (stat.listeners('change').length === 0) {
stat.stop();
statWatchers[filename] = undefined;
}
};
// Realpath
// Not using realpath(2) because it's bad.
// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
var normalize = pathModule.normalize;
// Regexp that finds the next partion of a (partial) path
// result is [base_with_slash, base], e.g. ['somedir/', 'somedir']
if (isWindows) {
var nextPartRe = /(.*?)(?:[\/\\]+|$)/g;
} else {
var nextPartRe = /(.*?)(?:[\/]+|$)/g;
}
// Regex to find the device root, including trailing slash. E.g. 'c:\\'.
if (isWindows) {
var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/;
} else {
var splitRootRe = /^[\/]*/;
}
fs.realpathSync = function realpathSync(p, cache) {
// make p is absolute
p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
return cache[p];
}
var original = p,
seenLinks = {},
knownHard = {};
// current character position in p
var pos;
// the partial path so far, including a trailing slash if any
var current;
// the partial path without a trailing slash (except when pointing at a root)
var base;
// the partial path scanned in the previous round, with slash
var previous;
start();
function start() {
// Skip over roots
var m = splitRootRe.exec(p);
pos = m[0].length;
current = m[0];
base = m[0];
previous = '';
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
fs.lstatSync(base);
knownHard[base] = true;
}
}
// walk down the path, swapping out linked pathparts for their real
// values
// NB: p.length changes.
while (pos < p.length) {
// find the next part
nextPartRe.lastIndex = pos;
var result = nextPartRe.exec(p);
previous = current;
current += result[0];
base = previous + result[1];
pos = nextPartRe.lastIndex;
// continue if not a symlink
if (knownHard[base] || (cache && cache[base] === base)) {
continue;
}
var resolvedLink;
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
// some known symbolic link. no need to stat again.
resolvedLink = cache[base];
} else {
var stat = fs.lstatSync(base);
if (!stat.isSymbolicLink()) {
knownHard[base] = true;
if (cache) cache[base] = base;
continue;
}
// read the link if it wasn't read before
// dev/ino always return 0 on windows, so skip the check.
var linkTarget = null;
if (!isWindows) {
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
if (seenLinks.hasOwnProperty(id)) {
linkTarget = seenLinks[id];
}
}
if (linkTarget === null) {
fs.statSync(base);
linkTarget = fs.readlinkSync(base);
}
resolvedLink = pathModule.resolve(previous, linkTarget);
// track this, if given a cache.
if (cache) cache[base] = resolvedLink;
if (!isWindows) seenLinks[id] = linkTarget;
}
// resolve the link, then start over
p = pathModule.resolve(resolvedLink, p.slice(pos));
start();
}
if (cache) cache[original] = p;
return p;
};
fs.realpath = function realpath(p, cache, cb) {
if (typeof cb !== 'function') {
cb = maybeCallback(cache);
cache = null;
}
// make p is absolute
p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
return process.nextTick(cb.bind(null, null, cache[p]));
}
var original = p,
seenLinks = {},
knownHard = {};
// current character position in p
var pos;
// the partial path so far, including a trailing slash if any
var current;
// the partial path without a trailing slash (except when pointing at a root)
var base;
// the partial path scanned in the previous round, with slash
var previous;
start();
function start() {
// Skip over roots
var m = splitRootRe.exec(p);
pos = m[0].length;
current = m[0];
base = m[0];
previous = '';
// On windows, check that the root exists. On unix there is no need.
if (isWindows && !knownHard[base]) {
fs.lstat(base, function(err) {
if (err) return cb(err);
knownHard[base] = true;
LOOP();
});
} else {
process.nextTick(LOOP);
}
}
// walk down the path, swapping out linked pathparts for their real
// values
function LOOP() {
// stop if scanned past end of path
if (pos >= p.length) {
if (cache) cache[original] = p;
return cb(null, p);
}
// find the next part
nextPartRe.lastIndex = pos;
var result = nextPartRe.exec(p);
previous = current;
current += result[0];
base = previous + result[1];
pos = nextPartRe.lastIndex;
// continue if not a symlink
if (knownHard[base] || (cache && cache[base] === base)) {
return process.nextTick(LOOP);
}
if (cache && Object.prototype.hasOwnProperty.call(cache, base)) {
// known symbolic link. no need to stat again.
return gotResolvedLink(cache[base]);
}
return fs.lstat(base, gotStat);
}
function gotStat(err, stat) {
if (err) return cb(err);
// if not a symlink, skip to the next path part
if (!stat.isSymbolicLink()) {
knownHard[base] = true;
if (cache) cache[base] = base;
return process.nextTick(LOOP);
}
// stat & read the link if not read before
// call gotTarget as soon as the link target is known
// dev/ino always return 0 on windows, so skip the check.
if (!isWindows) {
var id = stat.dev.toString(32) + ':' + stat.ino.toString(32);
if (seenLinks.hasOwnProperty(id)) {
return gotTarget(null, seenLinks[id], base);
}
}
fs.stat(base, function(err) {
if (err) return cb(err);
fs.readlink(base, function(err, target) {
if (!isWindows) seenLinks[id] = target;
gotTarget(err, target);
});
});
}
function gotTarget(err, target, base) {
if (err) return cb(err);
var resolvedLink = pathModule.resolve(previous, target);
if (cache) cache[base] = resolvedLink;
gotResolvedLink(resolvedLink);
}
function gotResolvedLink(resolvedLink) {
// resolve the link, then start over
p = pathModule.resolve(resolvedLink, p.slice(pos));
start();
}
};
var pool;
function allocNewPool() {
pool = new Buffer(kPoolSize);
pool.used = 0;
}
fs.createReadStream = function(path, options) {
return new ReadStream(path, options);
};
util.inherits(ReadStream, Readable);
fs.ReadStream = ReadStream;
function ReadStream(path, options) {
if (!(this instanceof ReadStream))
return new ReadStream(path, options);
// a little bit bigger buffer and water marks by default
options = util._extend({
bufferSize: 64 * 1024,
lowWaterMark: 16 * 1024,
highWaterMark: 64 * 1024
}, options || {});
Readable.call(this, options);
this.path = path;
this.fd = options.hasOwnProperty('fd') ? options.fd : null;
this.flags = options.hasOwnProperty('flags') ? options.flags : 'r';
this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/
this.start = options.hasOwnProperty('start') ? options.start : undefined;
this.end = options.hasOwnProperty('start') ? options.end : undefined;
this.pos = undefined;
if (this.start !== undefined) {
if ('number' !== typeof this.start) {
throw TypeError('start must be a Number');
}
if (this.end === undefined) {
this.end = Infinity;
} else if ('number' !== typeof this.end) {
throw TypeError('end must be a Number');
}
if (this.start > this.end) {
throw new Error('start must be <= end');
}
this.pos = this.start;
}
if (typeof this.fd !== 'number')
this.open();
this.on('end', function() {
this.destroy();
});
}
fs.FileReadStream = fs.ReadStream; // support the legacy name
ReadStream.prototype.open = function() {
var self = this;
fs.open(this.path, this.flags, this.mode, function(er, fd) {
if (er) {
self.destroy();
self.emit('error', er);
return;
}
self.fd = fd;
self.emit('open', fd);
// start the flow of data.
self.read();
});
};
ReadStream.prototype._read = function(n, cb) {
if (typeof this.fd !== 'number')
return this.once('open', function() {
this._read(n, cb);
});
if (this.destroyed)
return;
if (!pool || pool.length - pool.used < kMinPoolSpace) {
// discard the old pool. Can't add to the free list because
// users might have refernces to slices on it.
pool = null;
allocNewPool();
}
// Grab another reference to the pool in the case that while we're
// in the thread pool another read() finishes up the pool, and
// allocates a new one.
var thisPool = pool;
var toRead = Math.min(pool.length - pool.used, n);
var start = pool.used;
if (this.pos !== undefined)
toRead = Math.min(this.end - this.pos + 1, toRead);
// already read everything we were supposed to read!
// treat as EOF.
if (toRead <= 0)
return cb();
// the actual read.
var self = this;
fs.read(this.fd, pool, pool.used, toRead, this.pos, onread);
// move the pool positions, and internal position for reading.
if (this.pos !== undefined)
this.pos += toRead;
pool.used += toRead;
function onread(er, bytesRead) {
if (er) {
self.destroy();
return cb(er);
}
var b = null;
if (bytesRead > 0)
b = thisPool.slice(start, start + bytesRead);
cb(null, b);
}
};
ReadStream.prototype.destroy = function() {
if (this.destroyed)
return;
this.destroyed = true;
if ('number' === typeof this.fd)
this.close();
};
ReadStream.prototype.close = function(cb) {
if (cb)
this.once('close', cb);
if (this.closed || 'number' !== typeof this.fd) {
if ('number' !== typeof this.fd)
this.once('open', close);
return process.nextTick(this.emit.bind(this, 'close'));
}
this.closed = true;
var self = this;
close();
function close() {
fs.close(self.fd, function(er) {
if (er)
self.emit('error', er);
else
self.emit('close');
});
}
};
fs.createWriteStream = function(path, options) {
return new WriteStream(path, options);
};
util.inherits(WriteStream, Writable);
fs.WriteStream = WriteStream;
function WriteStream(path, options) {
if (!(this instanceof WriteStream))
return new WriteStream(path, options);
// a little bit bigger buffer and water marks by default
options = util._extend({
bufferSize: 64 * 1024,
lowWaterMark: 16 * 1024,
highWaterMark: 64 * 1024
}, options || {});
Writable.call(this, options);
this.path = path;
this.fd = null;
this.fd = options.hasOwnProperty('fd') ? options.fd : null;
this.flags = options.hasOwnProperty('flags') ? options.flags : 'w';
this.mode = options.hasOwnProperty('mode') ? options.mode : 438; /*=0666*/
this.start = options.hasOwnProperty('start') ? options.start : undefined;
this.pos = undefined;
this.bytesWritten = 0;
if (this.start !== undefined) {
if ('number' !== typeof this.start) {
throw TypeError('start must be a Number');
}
if (this.start < 0) {
throw new Error('start must be >= zero');
}
this.pos = this.start;
}
if ('number' !== typeof this.fd)
this.open();
// dispose on finish.
this.once('finish', this.close);
}
fs.FileWriteStream = fs.WriteStream; // support the legacy name
WriteStream.prototype.open = function() {
fs.open(this.path, this.flags, this.mode, function(er, fd) {
if (er) {
this.destroy();
this.emit('error', er);
return;
}
this.fd = fd;
this.emit('open', fd);
}.bind(this));
};
WriteStream.prototype._write = function(data, cb) {
if (!Buffer.isBuffer(data))
return this.emit('error', new Error('Invalid data'));
if (typeof this.fd !== 'number')
return this.once('open', this._write.bind(this, data, cb));
fs.write(this.fd, data, 0, data.length, this.pos, function(er, bytes) {
if (er) {
this.destroy();
return cb(er);
}
this.bytesWritten += bytes;
cb();
}.bind(this));
if (this.pos !== undefined)
this.pos += data.length;
};
WriteStream.prototype.destroy = ReadStream.prototype.destroy;
WriteStream.prototype.close = ReadStream.prototype.close;
// There is no shutdown() for files.
WriteStream.prototype.destroySoon = WriteStream.prototype.end;
// SyncWriteStream is internal. DO NOT USE.
// Temporary hack for process.stdout and process.stderr when piped to files.
function SyncWriteStream(fd) {
Stream.call(this);
this.fd = fd;
this.writable = true;
this.readable = false;
}
util.inherits(SyncWriteStream, Stream);
// Export
fs.SyncWriteStream = SyncWriteStream;
SyncWriteStream.prototype.write = function(data, arg1, arg2) {
var encoding, cb;
// parse arguments
if (arg1) {
if (typeof arg1 === 'string') {
encoding = arg1;
cb = arg2;
} else if (typeof arg1 === 'function') {
cb = arg1;
} else {
throw new Error('bad arg');
}
}
assertEncoding(encoding);
// Change strings to buffers. SLOW
if (typeof data == 'string') {
data = new Buffer(data, encoding);
}
fs.writeSync(this.fd, data, 0, data.length);
if (cb) {
process.nextTick(cb);
}
return true;
};
SyncWriteStream.prototype.end = function(data, arg1, arg2) {
if (data) {
this.write(data, arg1, arg2);
}
this.destroy();
};
SyncWriteStream.prototype.destroy = function() {
fs.closeSync(this.fd);
this.fd = null;
this.emit('close');
return true;
};
SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a duplex stream is just a stream that is both readable and writable.
// Since JS doesn't have multiple prototypal inheritance, this class
// prototypally inherits from Readable, and then parasitically from
// Writable.
module.exports = Duplex;
var util = require('util');
var Readable = require('./_stream_readable');
var Writable = require('./_stream_writable');
util.inherits(Duplex, Readable);
Object.keys(Writable.prototype).forEach(function(method) {
if (!Duplex.prototype[method])
Duplex.prototype[method] = Writable.prototype[method];
});
function Duplex(options) {
if (!(this instanceof Duplex))
return new Duplex(options);
Readable.call(this, options);
Writable.call(this, options);
if (options && options.readable === false)
this.readable = false;
if (options && options.writable === false)
this.writable = false;
this.allowHalfOpen = true;
if (options && options.allowHalfOpen === false)
this.allowHalfOpen = false;
this.once('end', onend);
}
// the no-half-open enforcer
function onend() {
// if we allow half-open state, or if the writable side ended,
// then we're ok.
if (this.allowHalfOpen || this._writableState.ended)
return;
// no more data can be written.
// But allow more writes to happen in this tick.
process.nextTick(this.end.bind(this));
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a passthrough stream.
// basically just the most minimal sort of Transform stream.
// Every written chunk gets output as-is.
module.exports = PassThrough;
var Transform = require('./_stream_transform');
var util = require('util');
util.inherits(PassThrough, Transform);
function PassThrough(options) {
if (!(this instanceof PassThrough))
return new PassThrough(options);
Transform.call(this, options);
}
PassThrough.prototype._transform = function(chunk, encoding, cb) {
cb(null, chunk);
};
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
module.exports = Readable;
Readable.ReadableState = ReadableState;
var EE = require('events').EventEmitter;
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
return emitter.listeners(type).length;
};
if (!global.setImmediate) global.setImmediate = function setImmediate(fn) {
return setTimeout(fn, 0);
};
if (!global.clearImmediate) global.clearImmediate = function clearImmediate(i) {
return clearTimeout(i);
};
var Stream = require('stream');
var util = require('util');
if (!util.isUndefined) {
var utilIs = require('core-util-is');
for (var f in utilIs) {
util[f] = utilIs[f];
}
}
var StringDecoder;
var debug;
if (util.debuglog)
debug = util.debuglog('stream');
else try {
debug = require('debuglog')('stream');
} catch (er) {
debug = function() {};
}
util.inherits(Readable, Stream);
function ReadableState(options, stream) {
options = options || {};
// the point at which it stops calling _read() to fill the buffer
// Note: 0 is a valid value, means "don't call _read preemptively ever"
var hwm = options.highWaterMark;
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.buffer = [];
this.length = 0;
this.pipes = null;
this.pipesCount = 0;
this.flowing = null;
this.ended = false;
this.endEmitted = false;
this.reading = false;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, because any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// whenever we return null, then we set a flag to say
// that we're awaiting a 'readable' event emission.
this.needReadable = false;
this.emittedReadable = false;
this.readableListening = false;
// object stream flag. Used to make read(n) ignore n and to
// make all the buffer merging and length checks go away
this.objectMode = !!options.objectMode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// when piping, we only care about 'readable' events that happen
// after read()ing all the bytes and not getting any pushback.
this.ranOut = false;
// the number of writers that are awaiting a drain event in .pipe()s
this.awaitDrain = 0;
// if true, a maybeReadMore has been scheduled
this.readingMore = false;
this.decoder = null;
this.encoding = null;
if (options.encoding) {
if (!StringDecoder)
StringDecoder = require('string_decoder').StringDecoder;
this.decoder = new StringDecoder(options.encoding);
this.encoding = options.encoding;
}
}
function Readable(options) {
if (!(this instanceof Readable))
return new Readable(options);
this._readableState = new ReadableState(options, this);
// legacy
this.readable = true;
Stream.call(this);
}
// Manually shove something into the read() buffer.
// This returns true if the highWaterMark has not been hit yet,
// similar to how Writable.write() returns true if you should
// write() some more.
Readable.prototype.push = function(chunk, encoding) {
var state = this._readableState;
if (util.isString(chunk) && !state.objectMode) {
encoding = encoding || state.defaultEncoding;
if (encoding !== state.encoding) {
chunk = new Buffer(chunk, encoding);
encoding = '';
}
}
return readableAddChunk(this, state, chunk, encoding, false);
};
// Unshift should *always* be something directly out of read()
Readable.prototype.unshift = function(chunk) {
var state = this._readableState;
return readableAddChunk(this, state, chunk, '', true);
};
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
var er = chunkInvalid(state, chunk);
if (er) {
stream.emit('error', er);
} else if (util.isNullOrUndefined(chunk)) {
state.reading = false;
if (!state.ended)
onEofChunk(stream, state);
} else if (state.objectMode || chunk && chunk.length > 0) {
if (state.ended && !addToFront) {
var e = new Error('stream.push() after EOF');
stream.emit('error', e);
} else if (state.endEmitted && addToFront) {
var e = new Error('stream.unshift() after end event');
stream.emit('error', e);
} else {
if (state.decoder && !addToFront && !encoding)
chunk = state.decoder.write(chunk);
if (!addToFront)
state.reading = false;
// if we want the data now, just emit it.
if (state.flowing && state.length === 0 && !state.sync) {
stream.emit('data', chunk);
stream.read(0);
} else {
// update the buffer info.
state.length += state.objectMode ? 1 : chunk.length;
if (addToFront)
state.buffer.unshift(chunk);
else
state.buffer.push(chunk);
if (state.needReadable)
emitReadable(stream);
}
maybeReadMore(stream, state);
}
} else if (!addToFront) {
state.reading = false;
}
return needMoreData(state);
}
// if it's past the high water mark, we can push in some more.
// Also, if we have no data yet, we can stand some
// more bytes. This is to work around cases where hwm=0,
// such as the repl. Also, if the push() triggered a
// readable event, and the user called read(largeNumber) such that
// needReadable was set, then we ought to push more, so that another
// 'readable' event will be triggered.
function needMoreData(state) {
return !state.ended &&
(state.needReadable ||
state.length < state.highWaterMark ||
state.length === 0);
}
// backwards compatibility.
Readable.prototype.setEncoding = function(enc) {
if (!StringDecoder)
StringDecoder = require('string_decoder').StringDecoder;
this._readableState.decoder = new StringDecoder(enc);
this._readableState.encoding = enc;
};
// Don't raise the hwm > 128MB
var MAX_HWM = 0x800000;
function roundUpToNextPowerOf2(n) {
if (n >= MAX_HWM) {
n = MAX_HWM;
} else {
// Get the next highest power of 2
n--;
for (var p = 1; p < 32; p <<= 1) n |= n >> p;
n++;
}
return n;
}
function howMuchToRead(n, state) {
if (state.length === 0 && state.ended)
return 0;
if (state.objectMode)
return n === 0 ? 0 : 1;
if (isNaN(n) || util.isNull(n)) {
// only flow one buffer at a time
if (state.flowing && state.buffer.length)
return state.buffer[0].length;
else
return state.length;
}
if (n <= 0)
return 0;
// If we're asking for more than the target buffer level,
// then raise the water mark. Bump up to the next highest
// power of 2, to prevent increasing it excessively in tiny
// amounts.
if (n > state.highWaterMark)
state.highWaterMark = roundUpToNextPowerOf2(n);
// don't have that much. return null, unless we've ended.
if (n > state.length) {
if (!state.ended) {
state.needReadable = true;
return 0;
} else
return state.length;
}
return n;
}
// you can override either this method, or the async _read(n) below.
Readable.prototype.read = function(n) {
debug('read', n);
var state = this._readableState;
var nOrig = n;
if (!util.isNumber(n) || n > 0)
state.emittedReadable = false;
// if we're doing read(0) to trigger a readable event, but we
// already have a bunch of data in the buffer, then just trigger
// the 'readable' event and move on.
if (n === 0 &&
state.needReadable &&
(state.length >= state.highWaterMark || state.ended)) {
debug('read: emitReadable', state.length, state.ended);
if (state.length === 0 && state.ended)
endReadable(this);
else
emitReadable(this);
return null;
}
n = howMuchToRead(n, state);
// if we've ended, and we're now clear, then finish it up.
if (n === 0 && state.ended) {
if (state.length === 0)
endReadable(this);
return null;
}
// All the actual chunk generation logic needs to be
// *below* the call to _read. The reason is that in certain
// synthetic stream cases, such as passthrough streams, _read
// may be a completely synchronous operation which may change
// the state of the read buffer, providing enough data when
// before there was *not* enough.
//
// So, the steps are:
// 1. Figure out what the state of things will be after we do
// a read from the buffer.
//
// 2. If that resulting state will trigger a _read, then call _read.
// Note that this may be asynchronous, or synchronous. Yes, it is
// deeply ugly to write APIs this way, but that still doesn't mean
// that the Readable class should behave improperly, as streams are
// designed to be sync/async agnostic.
// Take note if the _read call is sync or async (ie, if the read call
// has returned yet), so that we know whether or not it's safe to emit
// 'readable' etc.
//
// 3. Actually pull the requested chunks out of the buffer and return.
// if we need a readable event, then we need to do some reading.
var doRead = state.needReadable;
debug('need readable', doRead);
// if we currently have less than the highWaterMark, then also read some
if (state.length === 0 || state.length - n < state.highWaterMark) {
doRead = true;
debug('length less than watermark', doRead);
}
// however, if we've ended, then there's no point, and if we're already
// reading, then it's unnecessary.
if (state.ended || state.reading) {
doRead = false;
debug('reading or ended', doRead);
}
if (doRead) {
debug('do read');
state.reading = true;
state.sync = true;
// if the length is currently zero, then we *need* a readable event.
if (state.length === 0)
state.needReadable = true;
// call internal read method
this._read(state.highWaterMark);
state.sync = false;
}
// If _read pushed data synchronously, then `reading` will be false,
// and we need to re-evaluate how much data we can return to the user.
if (doRead && !state.reading)
n = howMuchToRead(nOrig, state);
var ret;
if (n > 0)
ret = fromList(n, state);
else
ret = null;
if (util.isNull(ret)) {
state.needReadable = true;
n = 0;
}
state.length -= n;
// If we have nothing in the buffer, then we want to know
// as soon as we *do* get something into the buffer.
if (state.length === 0 && !state.ended)
state.needReadable = true;
// If we tried to read() past the EOF, then emit end on the next tick.
if (nOrig !== n && state.ended && state.length === 0)
endReadable(this);
if (!util.isNull(ret))
this.emit('data', ret);
return ret;
};
function chunkInvalid(state, chunk) {
var er = null;
if (!util.isBuffer(chunk) &&
!util.isString(chunk) &&
!util.isNullOrUndefined(chunk) &&
!state.objectMode &&
!er) {
er = new TypeError('Invalid non-string/buffer chunk');
}
return er;
}
function onEofChunk(stream, state) {
if (state.decoder && !state.ended && state.decoder.end) {
var chunk = state.decoder.end();
if (chunk && chunk.length) {
state.buffer.push(chunk);
state.length += state.objectMode ? 1 : chunk.length;
}
}
state.ended = true;
// emit 'readable' now to make sure it gets picked up.
emitReadable(stream);
}
// Don't emit readable right away in sync mode, because this can trigger
// another read() call => stack overflow. This way, it might trigger
// a nextTick recursion warning, but that's not so bad.
function emitReadable(stream) {
var state = stream._readableState;
state.needReadable = false;
if (!state.emittedReadable) {
debug('emitReadable', state.flowing);
state.emittedReadable = true;
if (state.sync)
process.nextTick(function() {
emitReadable_(stream);
});
else
emitReadable_(stream);
}
}
function emitReadable_(stream) {
debug('emit readable');
stream.emit('readable');
flow(stream);
}
// at this point, the user has presumably seen the 'readable' event,
// and called read() to consume some data. that may have triggered
// in turn another _read(n) call, in which case reading = true if
// it's in progress.
// However, if we're not ended, or reading, and the length < hwm,
// then go ahead and try to read some more preemptively.
function maybeReadMore(stream, state) {
if (!state.readingMore) {
state.readingMore = true;
process.nextTick(function() {
maybeReadMore_(stream, state);
});
}
}
function maybeReadMore_(stream, state) {
var len = state.length;
while (!state.reading && !state.flowing && !state.ended &&
state.length < state.highWaterMark) {
debug('maybeReadMore read 0');
stream.read(0);
if (len === state.length)
// didn't get any data, stop spinning.
break;
else
len = state.length;
}
state.readingMore = false;
}
// abstract method. to be overridden in specific implementation classes.
// call cb(er, data) where data is <= n in length.
// for virtual (non-string, non-buffer) streams, "length" is somewhat
// arbitrary, and perhaps not very meaningful.
Readable.prototype._read = function(n) {
this.emit('error', new Error('not implemented'));
};
Readable.prototype.pipe = function(dest, pipeOpts) {
var src = this;
var state = this._readableState;
switch (state.pipesCount) {
case 0:
state.pipes = dest;
break;
case 1:
state.pipes = [state.pipes, dest];
break;
default:
state.pipes.push(dest);
break;
}
state.pipesCount += 1;
debug('pipe count=%d opts=%j', state.pipesCount, pipeOpts);
var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
dest !== process.stdout &&
dest !== process.stderr;
var endFn = doEnd ? onend : cleanup;
if (state.endEmitted)
process.nextTick(endFn);
else
src.once('end', endFn);
dest.on('unpipe', onunpipe);
function onunpipe(readable) {
debug('onunpipe');
if (readable === src) {
cleanup();
}
}
function onend() {
debug('onend');
dest.end();
}
// when the dest drains, it reduces the awaitDrain counter
// on the source. This would be more elegant with a .once()
// handler in flow(), but adding and removing repeatedly is
// too slow.
var ondrain = pipeOnDrain(src);
dest.on('drain', ondrain);
function cleanup() {
debug('cleanup');
// cleanup event handlers once the pipe is broken
dest.removeListener('close', onclose);
dest.removeListener('finish', onfinish);
dest.removeListener('drain', ondrain);
dest.removeListener('error', onerror);
dest.removeListener('unpipe', onunpipe);
src.removeListener('end', onend);
src.removeListener('end', cleanup);
src.removeListener('data', ondata);
// if the reader is waiting for a drain event from this
// specific writer, then it would cause it to never start
// flowing again.
// So, if this is awaiting a drain, then we just call it now.
// If we don't know, then assume that we are waiting for one.
if (state.awaitDrain &&
(!dest._writableState || dest._writableState.needDrain))
ondrain();
}
src.on('data', ondata);
function ondata(chunk) {
debug('ondata');
var ret = dest.write(chunk);
if (false === ret) {
debug('false write response, pause',
src._readableState.awaitDrain);
src._readableState.awaitDrain++;
src.pause();
}
}
// if the dest has an error, then stop piping into it.
// however, don't suppress the throwing behavior for this.
function onerror(er) {
debug('onerror', er);
unpipe();
dest.removeListener('error', onerror);
if (EE.listenerCount(dest, 'error') === 0)
dest.emit('error', er);
}
// This is a brutally ugly hack to make sure that our error handler
// is attached before any userland ones. NEVER DO THIS.
if (!dest._events.error)
dest.on('error', onerror);
else if (Array.isArray(dest._events.error))
dest._events.error.unshift(onerror);
else
dest._events.error = [onerror, dest._events.error];
// Both close and finish should trigger unpipe, but only once.
function onclose() {
dest.removeListener('finish', onfinish);
unpipe();
}
dest.once('close', onclose);
function onfinish() {
debug('onfinish');
dest.removeListener('close', onclose);
unpipe();
}
dest.once('finish', onfinish);
function unpipe() {
debug('unpipe');
src.unpipe(dest);
}
// tell the dest that it's being piped to
dest.emit('pipe', src);
// start the flow if it hasn't been started already.
if (!state.flowing) {
debug('pipe resume');
src.resume();
}
return dest;
};
function pipeOnDrain(src) {
return function() {
var state = src._readableState;
debug('pipeOnDrain', state.awaitDrain);
if (state.awaitDrain)
state.awaitDrain--;
if (state.awaitDrain === 0 && EE.listenerCount(src, 'data')) {
state.flowing = true;
flow(src);
}
};
}
Readable.prototype.unpipe = function(dest) {
var state = this._readableState;
// if we're not piping anywhere, then do nothing.
if (state.pipesCount === 0)
return this;
// just one destination. most common case.
if (state.pipesCount === 1) {
// passed in one, but it's not the right one.
if (dest && dest !== state.pipes)
return this;
if (!dest)
dest = state.pipes;
// got a match.
state.pipes = null;
state.pipesCount = 0;
state.flowing = false;
if (dest)
dest.emit('unpipe', this);
return this;
}
// slow case. multiple pipe destinations.
if (!dest) {
// remove all.
var dests = state.pipes;
var len = state.pipesCount;
state.pipes = null;
state.pipesCount = 0;
state.flowing = false;
for (var i = 0; i < len; i++)
dests[i].emit('unpipe', this);
return this;
}
// try to find the right one.
var i = state.pipes.indexOf(dest);
if (i === -1)
return this;
state.pipes.splice(i, 1);
state.pipesCount -= 1;
if (state.pipesCount === 1)
state.pipes = state.pipes[0];
dest.emit('unpipe', this);
return this;
};
// set up data events if they are asked for
// Ensure readable listeners eventually get something
Readable.prototype.on = function(ev, fn) {
var res = Stream.prototype.on.call(this, ev, fn);
// If listening to data, and it has not explicitly been paused,
// then call resume to start the flow of data on the next tick.
if (ev === 'data' && false !== this._readableState.flowing) {
this.resume();
}
if (ev === 'readable' && this.readable) {
var state = this._readableState;
if (!state.readableListening) {
state.readableListening = true;
state.emittedReadable = false;
state.needReadable = true;
if (!state.reading) {
var self = this;
process.nextTick(function() {
debug('readable nexttick read 0');
self.read(0);
});
} else if (state.length) {
emitReadable(this, state);
}
}
}
return res;
};
Readable.prototype.addListener = Readable.prototype.on;
// pause() and resume() are remnants of the legacy readable stream API
// If the user uses them, then switch into old mode.
Readable.prototype.resume = function() {
var state = this._readableState;
if (!state.flowing) {
debug('resume');
state.flowing = true;
if (!state.reading) {
debug('resume read 0');
this.read(0);
}
resume(this, state);
}
};
function resume(stream, state) {
if (!state.resumeScheduled) {
state.resumeScheduled = true;
process.nextTick(function() {
resume_(stream, state);
});
}
}
function resume_(stream, state) {
state.resumeScheduled = false;
stream.emit('resume');
flow(stream);
if (state.flowing && !state.reading)
stream.read(0);
}
Readable.prototype.pause = function() {
debug('call pause flowing=%j', this._readableState.flowing);
if (false !== this._readableState.flowing) {
debug('pause');
this._readableState.flowing = false;
this.emit('pause');
}
};
function flow(stream) {
var state = stream._readableState;
debug('flow', state.flowing);
if (state.flowing) {
do {
var chunk = stream.read();
} while (null !== chunk && state.flowing);
}
}
// wrap an old-style stream as the async data source.
// This is *not* part of the readable stream interface.
// It is an ugly unfortunate mess of history.
Readable.prototype.wrap = function(stream) {
var state = this._readableState;
var paused = false;
var self = this;
stream.on('end', function() {
debug('wrapped end');
if (state.decoder && !state.ended) {
var chunk = state.decoder.end();
if (chunk && chunk.length)
self.push(chunk);
}
self.push(null);
});
stream.on('data', function(chunk) {
debug('wrapped data');
if (state.decoder)
chunk = state.decoder.write(chunk);
if (!chunk || !state.objectMode && !chunk.length)
return;
var ret = self.push(chunk);
if (!ret) {
paused = true;
stream.pause();
}
});
// proxy all the other methods.
// important when wrapping filters and duplexes.
for (var i in stream) {
if (util.isFunction(stream[i]) && util.isUndefined(this[i])) {
this[i] = function(method) { return function() {
return stream[method].apply(stream, arguments);
}}(i);
}
}
// proxy certain important events.
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
events.forEach(function(ev) {
stream.on(ev, self.emit.bind(self, ev));
});
// when we try to consume some more bytes, simply unpause the
// underlying stream.
self._read = function(n) {
debug('wrapped _read', n);
if (paused) {
paused = false;
stream.resume();
}
};
return self;
};
// exposed for testing purposes only.
Readable._fromList = fromList;
// Pluck off n bytes from an array of buffers.
// Length is the combined lengths of all the buffers in the list.
function fromList(n, state) {
var list = state.buffer;
var length = state.length;
var stringMode = !!state.decoder;
var objectMode = !!state.objectMode;
var ret;
// nothing in the list, definitely empty.
if (list.length === 0)
return null;
if (length === 0)
ret = null;
else if (objectMode)
ret = list.shift();
else if (!n || n >= length) {
// read it all, truncate the array.
if (stringMode)
ret = list.join('');
else
ret = Buffer.concat(list, length);
list.length = 0;
} else {
// read just some of it.
if (n < list[0].length) {
// just take a part of the first list item.
// slice is the same for buffers and strings.
var buf = list[0];
ret = buf.slice(0, n);
list[0] = buf.slice(n);
} else if (n === list[0].length) {
// first list is a perfect match
ret = list.shift();
} else {
// complex case.
// we have enough to cover it, but it spans past the first buffer.
if (stringMode)
ret = '';
else
ret = new Buffer(n);
var c = 0;
for (var i = 0, l = list.length; i < l && c < n; i++) {
var buf = list[0];
var cpy = Math.min(n - c, buf.length);
if (stringMode)
ret += buf.slice(0, cpy);
else
buf.copy(ret, c, 0, cpy);
if (cpy < buf.length)
list[0] = buf.slice(cpy);
else
list.shift();
c += cpy;
}
}
}
return ret;
}
function endReadable(stream) {
var state = stream._readableState;
// If we get here before consuming all the bytes, then that is a
// bug in node. Should never happen.
if (state.length > 0)
throw new Error('endReadable called on non-empty stream');
if (!state.endEmitted) {
state.ended = true;
process.nextTick(function() {
// Check that we didn't get one last unshift.
if (!state.endEmitted && state.length === 0) {
state.endEmitted = true;
stream.readable = false;
stream.emit('end');
}
});
}
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// a transform stream is a readable/writable stream where you do
// something with the data. Sometimes it's called a "filter",
// but that's not a great name for it, since that implies a thing where
// some bits pass through, and others are simply ignored. (That would
// be a valid example of a transform, of course.)
//
// While the output is causally related to the input, it's not a
// necessarily symmetric or synchronous transformation. For example,
// a zlib stream might take multiple plain-text writes(), and then
// emit a single compressed chunk some time in the future.
//
// Here's how this works:
//
// The Transform stream has all the aspects of the readable and writable
// stream classes. When you write(chunk), that calls _write(chunk,cb)
// internally, and returns false if there's a lot of pending writes
// buffered up. When you call read(), that calls _read(n) until
// there's enough pending readable data buffered up.
//
// In a transform stream, the written data is placed in a buffer. When
// _read(n) is called, it transforms the queued up data, calling the
// buffered _write cb's as it consumes chunks. If consuming a single
// written chunk would result in multiple output chunks, then the first
// outputted bit calls the readcb, and subsequent chunks just go into
// the read buffer, and will cause it to emit 'readable' if necessary.
//
// This way, back-pressure is actually determined by the reading side,
// since _read has to be called to start processing a new chunk. However,
// a pathological inflate type of transform can cause excessive buffering
// here. For example, imagine a stream where every byte of input is
// interpreted as an integer from 0-255, and then results in that many
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
// 1kb of data being output. In this case, you could write a very small
// amount of input, and end up with a very large amount of output. In
// such a pathological inflating mechanism, there'd be no way to tell
// the system to stop doing the transform. A single 4MB write could
// cause the system to run out of memory.
//
// However, even in such a pathological case, only a single written chunk
// would be consumed, and then the rest would wait (un-transformed) until
// the results of the previous transformed chunk were consumed.
module.exports = Transform;
var Duplex = require('./_stream_duplex');
var util = require('util');
if (!util.isUndefined) {
var utilIs = require('core-util-is');
for (var f in utilIs) {
util[f] = utilIs[f];
}
}
util.inherits(Transform, Duplex);
function TransformState(options, stream) {
this.afterTransform = function(er, data) {
return afterTransform(stream, er, data);
};
this.needTransform = false;
this.transforming = false;
this.writecb = null;
this.writechunk = null;
}
function afterTransform(stream, er, data) {
var ts = stream._transformState;
ts.transforming = false;
var cb = ts.writecb;
if (!cb)
return stream.emit('error', new Error('no writecb in Transform class'));
ts.writechunk = null;
ts.writecb = null;
if (!util.isNullOrUndefined(data))
stream.push(data);
if (cb)
cb(er);
var rs = stream._readableState;
rs.reading = false;
if (rs.needReadable || rs.length < rs.highWaterMark) {
stream._read(rs.highWaterMark);
}
}
function Transform(options) {
if (!(this instanceof Transform))
return new Transform(options);
Duplex.call(this, options);
this._transformState = new TransformState(options, this);
// when the writable side finishes, then flush out anything remaining.
var stream = this;
// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
// we have implemented the _read method, and done the other things
// that Readable wants before the first _read call, so unset the
// sync guard flag.
this._readableState.sync = false;
this.once('prefinish', function() {
if (util.isFunction(this._flush))
this._flush(function(er) {
done(stream, er);
});
else
done(stream);
});
}
Transform.prototype.push = function(chunk, encoding) {
this._transformState.needTransform = false;
return Duplex.prototype.push.call(this, chunk, encoding);
};
// This is the part where you do stuff!
// override this function in implementation classes.
// 'chunk' is an input chunk.
//
// Call `push(newChunk)` to pass along transformed output
// to the readable side. You may call 'push' zero or more times.
//
// Call `cb(err)` when you are done with this chunk. If you pass
// an error, then that'll put the hurt on the whole operation. If you
// never call cb(), then you'll never get another chunk.
Transform.prototype._transform = function(chunk, encoding, cb) {
throw new Error('not implemented');
};
Transform.prototype._write = function(chunk, encoding, cb) {
var ts = this._transformState;
ts.writecb = cb;
ts.writechunk = chunk;
ts.writeencoding = encoding;
if (!ts.transforming) {
var rs = this._readableState;
if (ts.needTransform ||
rs.needReadable ||
rs.length < rs.highWaterMark)
this._read(rs.highWaterMark);
}
};
// Doesn't matter what the args are here.
// _transform does all the work.
// That we got here means that the readable side wants more data.
Transform.prototype._read = function(n) {
var ts = this._transformState;
if (!util.isNull(ts.writechunk) && ts.writecb && !ts.transforming) {
ts.transforming = true;
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
} else {
// mark that we need a transform, so that any data that comes in
// will get processed, now that we've asked for it.
ts.needTransform = true;
}
};
function done(stream, er) {
if (er)
return stream.emit('error', er);
// if there's nothing in the write buffer, then that means
// that nothing more will ever be provided
var ws = stream._writableState;
var ts = stream._transformState;
if (ws.length)
throw new Error('calling transform done when ws.length != 0');
if (ts.transforming)
throw new Error('calling transform done when still transforming');
return stream.push(null);
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// A bit simpler than readable streams.
// Implement an async ._write(chunk, cb), and it'll handle all
// the drain event emission and buffering.
module.exports = Writable;
Writable.WritableState = WritableState;
var util = require('util');
if (!util.isUndefined) {
var utilIs = require('core-util-is');
for (var f in utilIs) {
util[f] = utilIs[f];
}
}
var Stream = require('stream');
util.inherits(Writable, Stream);
function WriteReq(chunk, encoding, cb) {
this.chunk = chunk;
this.encoding = encoding;
this.callback = cb;
}
function WritableState(options, stream) {
options = options || {};
// the point at which write() starts returning false
// Note: 0 is a valid value, means that we always return false if
// the entire buffer is not flushed immediately on write()
var hwm = options.highWaterMark;
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
// object stream flag to indicate whether or not this stream
// contains buffers or objects.
this.objectMode = !!options.objectMode;
// cast to ints.
this.highWaterMark = ~~this.highWaterMark;
this.needDrain = false;
// at the start of calling end()
this.ending = false;
// when end() has been called, and returned
this.ended = false;
// when 'finish' is emitted
this.finished = false;
// should we decode strings into buffers before passing to _write?
// this is here so that some node-core streams can optimize string
// handling at a lower level.
var noDecode = options.decodeStrings === false;
this.decodeStrings = !noDecode;
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';
// not an actual buffer we keep track of, but a measurement
// of how much we're waiting to get pushed to some underlying
// socket or file.
this.length = 0;
// a flag to see when we're in the middle of a write.
this.writing = false;
// when true all writes will be buffered until .uncork() call
this.corked = 0;
// a flag to be able to tell if the onwrite cb is called immediately,
// or on a later tick. We set this to true at first, because any
// actions that shouldn't happen until "later" should generally also
// not happen before the first write call.
this.sync = true;
// a flag to know if we're processing previously buffered items, which
// may call the _write() callback in the same tick, so that we don't
// end up in an overlapped onwrite situation.
this.bufferProcessing = false;
// the callback that's passed to _write(chunk,cb)
this.onwrite = function(er) {
onwrite(stream, er);
};
// the callback that the user supplies to write(chunk,encoding,cb)
this.writecb = null;
// the amount that is being written when _write is called.
this.writelen = 0;
this.buffer = [];
// number of pending user-supplied write callbacks
// this must be 0 before 'finish' can be emitted
this.pendingcb = 0;
// emit prefinish if the only thing we're waiting for is _write cbs
// This is relevant for synchronous Transform streams
this.prefinished = false;
}
function Writable(options) {
// Writable ctor is applied to Duplexes, though they're not
// instanceof Writable, they're instanceof Readable.
if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex')))
return new Writable(options);
this._writableState = new WritableState(options, this);
// legacy.
this.writable = true;
Stream.call(this);
}
// Otherwise people can pipe Writable streams, which is just wrong.
Writable.prototype.pipe = function() {
this.emit('error', new Error('Cannot pipe. Not readable.'));
};
function writeAfterEnd(stream, state, cb) {
var er = new Error('write after end');
// TODO: defer error events consistently everywhere, not just the cb
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
}
// If we get something that is not a buffer, string, null, or undefined,
// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
function validChunk(stream, state, chunk, cb) {
var valid = true;
if (!util.isBuffer(chunk) &&
!util.isString(chunk) &&
!util.isNullOrUndefined(chunk) &&
!state.objectMode) {
var er = new TypeError('Invalid non-string/buffer chunk');
stream.emit('error', er);
process.nextTick(function() {
cb(er);
});
valid = false;
}
return valid;
}
Writable.prototype.write = function(chunk, encoding, cb) {
var state = this._writableState;
var ret = false;
if (util.isFunction(encoding)) {
cb = encoding;
encoding = null;
}
if (util.isBuffer(chunk))
encoding = 'buffer';
else if (!encoding)
encoding = state.defaultEncoding;
if (!util.isFunction(cb))
cb = function() {};
if (state.ended)
writeAfterEnd(this, state, cb);
else if (validChunk(this, state, chunk, cb)) {
state.pendingcb++;
ret = writeOrBuffer(this, state, chunk, encoding, cb);
}
return ret;
};
Writable.prototype.cork = function() {
var state = this._writableState;
state.corked++;
};
Writable.prototype.uncork = function() {
var state = this._writableState;
if (state.corked) {
state.corked--;
if (!state.writing &&
!state.corked &&
!state.finished &&
!state.bufferProcessing &&
state.buffer.length)
clearBuffer(this, state);
}
};
function decodeChunk(state, chunk, encoding) {
if (!state.objectMode &&
state.decodeStrings !== false &&
util.isString(chunk)) {
chunk = new Buffer(chunk, encoding);
}
return chunk;
}
// if we're already writing something, then just put this
// in the queue, and wait our turn. Otherwise, call _write
// If we return false, then we need a drain event, so set that flag.
function writeOrBuffer(stream, state, chunk, encoding, cb) {
chunk = decodeChunk(state, chunk, encoding);
if (util.isBuffer(chunk))
encoding = 'buffer';
var len = state.objectMode ? 1 : chunk.length;
state.length += len;
var ret = state.length < state.highWaterMark;
state.needDrain = !ret;
if (state.writing || state.corked)
state.buffer.push(new WriteReq(chunk, encoding, cb));
else
doWrite(stream, state, false, len, chunk, encoding, cb);
return ret;
}
function doWrite(stream, state, writev, len, chunk, encoding, cb) {
state.writelen = len;
state.writecb = cb;
state.writing = true;
state.sync = true;
if (writev)
stream._writev(chunk, state.onwrite);
else
stream._write(chunk, encoding, state.onwrite);
state.sync = false;
}
function onwriteError(stream, state, sync, er, cb) {
if (sync)
process.nextTick(function() {
state.pendingcb--;
cb(er);
});
else {
state.pendingcb--;
cb(er);
}
stream.emit('error', er);
}
function onwriteStateUpdate(state) {
state.writing = false;
state.writecb = null;
state.length -= state.writelen;
state.writelen = 0;
}
function onwrite(stream, er) {
var state = stream._writableState;
var sync = state.sync;
var cb = state.writecb;
onwriteStateUpdate(state);
if (er)
onwriteError(stream, state, sync, er, cb);
else {
// Check if we're actually ready to finish, but don't emit yet
var finished = needFinish(stream, state);
if (!finished &&
!state.corked &&
!state.bufferProcessing &&
state.buffer.length) {
clearBuffer(stream, state);
}
if (sync) {
process.nextTick(function() {
afterWrite(stream, state, finished, cb);
});
} else {
afterWrite(stream, state, finished, cb);
}
}
}
function afterWrite(stream, state, finished, cb) {
if (!finished)
onwriteDrain(stream, state);
state.pendingcb--;
cb();
finishMaybe(stream, state);
}
// Must force callback to be called on nextTick, so that we don't
// emit 'drain' before the write() consumer gets the 'false' return
// value, and has a chance to attach a 'drain' listener.
function onwriteDrain(stream, state) {
if (state.length === 0 && state.needDrain) {
state.needDrain = false;
stream.emit('drain');
}
}
// if there's something in the buffer waiting, then process it
function clearBuffer(stream, state) {
state.bufferProcessing = true;
if (stream._writev && state.buffer.length > 1) {
// Fast case, write everything using _writev()
var cbs = [];
for (var c = 0; c < state.buffer.length; c++)
cbs.push(state.buffer[c].callback);
// count the one we are adding, as well.
// TODO(isaacs) clean this up
state.pendingcb++;
doWrite(stream, state, true, state.length, state.buffer, '', function(err) {
for (var i = 0; i < cbs.length; i++) {
state.pendingcb--;
cbs[i](err);
}
});
// Clear buffer
state.buffer = [];
} else {
// Slow case, write chunks one-by-one
for (var c = 0; c < state.buffer.length; c++) {
var entry = state.buffer[c];
var chunk = entry.chunk;
var encoding = entry.encoding;
var cb = entry.callback;
var len = state.objectMode ? 1 : chunk.length;
doWrite(stream, state, false, len, chunk, encoding, cb);
// if we didn't call the onwrite immediately, then
// it means that we need to wait until it does.
// also, that means that the chunk and cb are currently
// being processed, so move the buffer counter past them.
if (state.writing) {
c++;
break;
}
}
if (c < state.buffer.length)
state.buffer = state.buffer.slice(c);
else
state.buffer.length = 0;
}
state.bufferProcessing = false;
}
Writable.prototype._write = function(chunk, encoding, cb) {
cb(new Error('not implemented'));
};
Writable.prototype._writev = null;
Writable.prototype.end = function(chunk, encoding, cb) {
var state = this._writableState;
if (util.isFunction(chunk)) {
cb = chunk;
chunk = null;
encoding = null;
} else if (util.isFunction(encoding)) {
cb = encoding;
encoding = null;
}
if (!util.isNullOrUndefined(chunk))
this.write(chunk, encoding);
// .end() fully uncorks
if (state.corked) {
state.corked = 1;
this.uncork();
}
// ignore unnecessary end() calls.
if (!state.ending && !state.finished)
endWritable(this, state, cb);
};
function needFinish(stream, state) {
return (state.ending &&
state.length === 0 &&
!state.finished &&
!state.writing);
}
function prefinish(stream, state) {
if (!state.prefinished) {
state.prefinished = true;
stream.emit('prefinish');
}
}
function finishMaybe(stream, state) {
var need = needFinish(stream, state);
if (need) {
if (state.pendingcb === 0) {
prefinish(stream, state);
state.finished = true;
stream.emit('finish');
} else
prefinish(stream, state);
}
return need;
}
function endWritable(stream, state, cb) {
state.ending = true;
finishMaybe(stream, state);
if (cb) {
if (state.finished)
process.nextTick(cb);
else
stream.once('finish', cb);
}
state.ended = true;
}
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
diff --git a/lib/util.js b/lib/util.js
index 9901a66..007fa10 100644
--- a/lib/util.js
+++ b/lib/util.js
@@ -19,425 +19,6 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
-var formatRegExp = /%[sdj%]/g;
-exports.format = function(f) {
- if (!isString(f)) {
- var objects = [];
- for (var i = 0; i < arguments.length; i++) {
- objects.push(inspect(arguments[i]));
- }
- return objects.join(' ');
- }
-
- var i = 1;
- var args = arguments;
- var len = args.length;
- var str = String(f).replace(formatRegExp, function(x) {
- if (x === '%%') return '%';
- if (i >= len) return x;
- switch (x) {
- case '%s': return String(args[i++]);
- case '%d': return Number(args[i++]);
- case '%j':
- try {
- return JSON.stringify(args[i++]);
- } catch (_) {
- return '[Circular]';
- }
- default:
- return x;
- }
- });
- for (var x = args[i]; i < len; x = args[++i]) {
- if (isNull(x) || !isObject(x)) {
- str += ' ' + x;
- } else {
- str += ' ' + inspect(x);
- }
- }
- return str;
-};
-
-
-// Mark that a method should not be used.
-// Returns a modified function which warns once by default.
-// If --no-deprecation is set, then it is a no-op.
-exports.deprecate = function(fn, msg) {
- // Allow for deprecating things in the process of starting up.
- if (isUndefined(global.process)) {
- return function() {
- return exports.deprecate(fn, msg).apply(this, arguments);
- };
- }
-
- if (process.noDeprecation === true) {
- return fn;
- }
-
- var warned = false;
- function deprecated() {
- if (!warned) {
- if (process.throwDeprecation) {
- throw new Error(msg);
- } else if (process.traceDeprecation) {
- console.trace(msg);
- } else {
- console.error(msg);
- }
- warned = true;
- }
- return fn.apply(this, arguments);
- }
-
- return deprecated;
-};
-
-
-var debugs = {};
-var debugEnviron;
-exports.debuglog = function(set) {
- if (isUndefined(debugEnviron))
- debugEnviron = process.env.NODE_DEBUG || '';
- set = set.toUpperCase();
- if (!debugs[set]) {
- if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
- var pid = process.pid;
- debugs[set] = function() {
- var msg = exports.format.apply(exports, arguments);
- console.error('%s %d: %s', set, pid, msg);
- };
- } else {
- debugs[set] = function() {};
- }
- }
- return debugs[set];
-};
-
-
-/**
- * Echos the value of a value. Trys to print the value out
- * in the best way possible given the different types.
- *
- * @param {Object} obj The object to print out.
- * @param {Object} opts Optional options object that alters the output.
- */
-/* legacy: obj, showHidden, depth, colors*/
-function inspect(obj, opts) {
- // default options
- var ctx = {
- seen: [],
- stylize: stylizeNoColor
- };
- // legacy...
- if (arguments.length >= 3) ctx.depth = arguments[2];
- if (arguments.length >= 4) ctx.colors = arguments[3];
- if (isBoolean(opts)) {
- // legacy...
- ctx.showHidden = opts;
- } else if (opts) {
- // got an "options" object
- exports._extend(ctx, opts);
- }
- // set default options
- if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
- if (isUndefined(ctx.depth)) ctx.depth = 2;
- if (isUndefined(ctx.colors)) ctx.colors = false;
- if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
- if (ctx.colors) ctx.stylize = stylizeWithColor;
- return formatValue(ctx, obj, ctx.depth);
-}
-exports.inspect = inspect;
-
-
-// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
-inspect.colors = {
- 'bold' : [1, 22],
- 'italic' : [3, 23],
- 'underline' : [4, 24],
- 'inverse' : [7, 27],
- 'white' : [37, 39],
- 'grey' : [90, 39],
- 'black' : [30, 39],
- 'blue' : [34, 39],
- 'cyan' : [36, 39],
- 'green' : [32, 39],
- 'magenta' : [35, 39],
- 'red' : [31, 39],
- 'yellow' : [33, 39]
-};
-
-// Don't use 'blue' not visible on cmd.exe
-inspect.styles = {
- 'special': 'cyan',
- 'number': 'yellow',
- 'boolean': 'yellow',
- 'undefined': 'grey',
- 'null': 'bold',
- 'string': 'green',
- 'date': 'magenta',
- // "name": intentionally not styling
- 'regexp': 'red'
-};
-
-
-function stylizeWithColor(str, styleType) {
- var style = inspect.styles[styleType];
-
- if (style) {
- return '\u001b[' + inspect.colors[style][0] + 'm' + str +
- '\u001b[' + inspect.colors[style][1] + 'm';
- } else {
- return str;
- }
-}
-
-
-function stylizeNoColor(str, styleType) {
- return str;
-}
-
-
-function arrayToHash(array) {
- var hash = {};
-
- array.forEach(function(val, idx) {
- hash[val] = true;
- });
-
- return hash;
-}
-
-
-function formatValue(ctx, value, recurseTimes) {
- // Provide a hook for user-specified inspect functions.
- // Check that value is an object with an inspect function on it
- if (ctx.customInspect &&
- value &&
- isFunction(value.inspect) &&
- // Filter out the util module, it's inspect function is special
- value.inspect !== exports.inspect &&
- // Also filter out any prototype objects using the circular check.
- !(value.constructor && value.constructor.prototype === value)) {
- var ret = value.inspect(recurseTimes, ctx);
- if (!isString(ret)) {
- ret = formatValue(ctx, ret, recurseTimes);
- }
- return ret;
- }
-
- // Primitive types cannot have properties
- var primitive = formatPrimitive(ctx, value);
- if (primitive) {
- return primitive;
- }
-
- // Look up the keys of the object.
- var keys = Object.keys(value);
- var visibleKeys = arrayToHash(keys);
-
- if (ctx.showHidden) {
- keys = Object.getOwnPropertyNames(value);
- }
-
- // Some type of object without properties can be shortcutted.
- if (keys.length === 0) {
- if (isFunction(value)) {
- var name = value.name ? ': ' + value.name : '';
- return ctx.stylize('[Function' + name + ']', 'special');
- }
- if (isRegExp(value)) {
- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
- }
- if (isDate(value)) {
- return ctx.stylize(Date.prototype.toString.call(value), 'date');
- }
- if (isError(value)) {
- return formatError(value);
- }
- }
-
- var base = '', array = false, braces = ['{', '}'];
-
- // Make Array say that they are Array
- if (isArray(value)) {
- array = true;
- braces = ['[', ']'];
- }
-
- // Make functions say that they are functions
- if (isFunction(value)) {
- var n = value.name ? ': ' + value.name : '';
- base = ' [Function' + n + ']';
- }
-
- // Make RegExps say that they are RegExps
- if (isRegExp(value)) {
- base = ' ' + RegExp.prototype.toString.call(value);
- }
-
- // Make dates with properties first say the date
- if (isDate(value)) {
- base = ' ' + Date.prototype.toUTCString.call(value);
- }
-
- // Make error with message first say the error
- if (isError(value)) {
- base = ' ' + formatError(value);
- }
-
- if (keys.length === 0 && (!array || value.length == 0)) {
- return braces[0] + base + braces[1];
- }
-
- if (recurseTimes < 0) {
- if (isRegExp(value)) {
- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
- } else {
- return ctx.stylize('[Object]', 'special');
- }
- }
-
- ctx.seen.push(value);
-
- var output;
- if (array) {
- output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
- } else {
- output = keys.map(function(key) {
- return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
- });
- }
-
- ctx.seen.pop();
-
- return reduceToSingleString(output, base, braces);
-}
-
-
-function formatPrimitive(ctx, value) {
- if (isUndefined(value))
- return ctx.stylize('undefined', 'undefined');
- if (isString(value)) {
- var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
- .replace(/'/g, "\\'")
- .replace(/\\"/g, '"') + '\'';
- return ctx.stylize(simple, 'string');
- }
- if (isNumber(value))
- return ctx.stylize('' + value, 'number');
- if (isBoolean(value))
- return ctx.stylize('' + value, 'boolean');
- // For some reason typeof null is "object", so special case here.
- if (isNull(value))
- return ctx.stylize('null', 'null');
-}
-
-
-function formatError(value) {
- return '[' + Error.prototype.toString.call(value) + ']';
-}
-
-
-function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
- var output = [];
- for (var i = 0, l = value.length; i < l; ++i) {
- if (hasOwnProperty(value, String(i))) {
- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
- String(i), true));
- } else {
- output.push('');
- }
- }
- keys.forEach(function(key) {
- if (!key.match(/^\d+$/)) {
- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
- key, true));
- }
- });
- return output;
-}
-
-
-function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
- var name, str, desc;
- desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
- if (desc.get) {
- if (desc.set) {
- str = ctx.stylize('[Getter/Setter]', 'special');
- } else {
- str = ctx.stylize('[Getter]', 'special');
- }
- } else {
- if (desc.set) {
- str = ctx.stylize('[Setter]', 'special');
- }
- }
- if (!hasOwnProperty(visibleKeys, key)) {
- name = '[' + key + ']';
- }
- if (!str) {
- if (ctx.seen.indexOf(desc.value) < 0) {
- if (isNull(recurseTimes)) {
- str = formatValue(ctx, desc.value, null);
- } else {
- str = formatValue(ctx, desc.value, recurseTimes - 1);
- }
- if (str.indexOf('\n') > -1) {
- if (array) {
- str = str.split('\n').map(function(line) {
- return ' ' + line;
- }).join('\n').substr(2);
- } else {
- str = '\n' + str.split('\n').map(function(line) {
- return ' ' + line;
- }).join('\n');
- }
- }
- } else {
- str = ctx.stylize('[Circular]', 'special');
- }
- }
- if (isUndefined(name)) {
- if (array && key.match(/^\d+$/)) {
- return str;
- }
- name = JSON.stringify('' + key);
- if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
- name = name.substr(1, name.length - 2);
- name = ctx.stylize(name, 'name');
- } else {
- name = name.replace(/'/g, "\\'")
- .replace(/\\"/g, '"')
- .replace(/(^"|"$)/g, "'");
- name = ctx.stylize(name, 'string');
- }
- }
-
- return name + ': ' + str;
-}
-
-
-function reduceToSingleString(output, base, braces) {
- var numLinesEst = 0;
- var length = output.reduce(function(prev, cur) {
- numLinesEst++;
- if (cur.indexOf('\n') >= 0) numLinesEst++;
- return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
- }, 0);
-
- if (length > 60) {
- return braces[0] +
- (base === '' ? '' : base + '\n ') +
- ' ' +
- output.join(',\n ') +
- ' ' +
- braces[1];
- }
-
- return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
-}
-
-
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
@@ -523,159 +104,3 @@ exports.isBuffer = isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}
-
-
-function pad(n) {
- return n < 10 ? '0' + n.toString(10) : n.toString(10);
-}
-
-
-var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
- 'Oct', 'Nov', 'Dec'];
-
-// 26 Feb 16:19:34
-function timestamp() {
- var d = new Date();
- var time = [pad(d.getHours()),
- pad(d.getMinutes()),
- pad(d.getSeconds())].join(':');
- return [d.getDate(), months[d.getMonth()], time].join(' ');
-}
-
-
-// log is just a thin wrapper to console.log that prepends a timestamp
-exports.log = function() {
- console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
-};
-
-
-/**
- * Inherit the prototype methods from one constructor into another.
- *
- * The Function.prototype.inherits from lang.js rewritten as a standalone
- * function (not on Function.prototype). NOTE: If this file is to be loaded
- * during bootstrapping this function needs to be rewritten using some native
- * functions as prototype setup using normal JavaScript does not work as
- * expected during bootstrapping (see mirror.js in r114903).
- *
- * @param {function} ctor Constructor function which needs to inherit the
- * prototype.
- * @param {function} superCtor Constructor function to inherit prototype from.
- */
-exports.inherits = function(ctor, superCtor) {
- ctor.super_ = superCtor;
- ctor.prototype = Object.create(superCtor.prototype, {
- constructor: {
- value: ctor,
- enumerable: false,
- writable: true,
- configurable: true
- }
- });
-};
-
-exports._extend = function(origin, add) {
- // Don't do anything if add isn't an object
- if (!add || !isObject(add)) return origin;
-
- var keys = Object.keys(add);
- var i = keys.length;
- while (i--) {
- origin[keys[i]] = add[keys[i]];
- }
- return origin;
-};
-
-function hasOwnProperty(obj, prop) {
- return Object.prototype.hasOwnProperty.call(obj, prop);
-}
-
-
-// Deprecated old stuff.
-
-exports.p = exports.deprecate(function() {
- for (var i = 0, len = arguments.length; i < len; ++i) {
- console.error(exports.inspect(arguments[i]));
- }
-}, 'util.p: Use console.error() instead');
-
-
-exports.exec = exports.deprecate(function() {
- return require('child_process').exec.apply(this, arguments);
-}, 'util.exec is now called `child_process.exec`.');
-
-
-exports.print = exports.deprecate(function() {
- for (var i = 0, len = arguments.length; i < len; ++i) {
- process.stdout.write(String(arguments[i]));
- }
-}, 'util.print: Use console.log instead');
-
-
-exports.puts = exports.deprecate(function() {
- for (var i = 0, len = arguments.length; i < len; ++i) {
- process.stdout.write(arguments[i] + '\n');
- }
-}, 'util.puts: Use console.log instead');
-
-
-exports.debug = exports.deprecate(function(x) {
- process.stderr.write('DEBUG: ' + x + '\n');
-}, 'util.debug: Use console.error instead');
-
-
-exports.error = exports.deprecate(function(x) {
- for (var i = 0, len = arguments.length; i < len; ++i) {
- process.stderr.write(arguments[i] + '\n');
- }
-}, 'util.error: Use console.error instead');
-
-
-exports.pump = exports.deprecate(function(readStream, writeStream, callback) {
- var callbackCalled = false;
-
- function call(a, b, c) {
- if (callback && !callbackCalled) {
- callback(a, b, c);
- callbackCalled = true;
- }
- }
-
- readStream.addListener('data', function(chunk) {
- if (writeStream.write(chunk) === false) readStream.pause();
- });
-
- writeStream.addListener('drain', function() {
- readStream.resume();
- });
-
- readStream.addListener('end', function() {
- writeStream.end();
- });
-
- readStream.addListener('close', function() {
- call();
- });
-
- readStream.addListener('error', function(err) {
- writeStream.end();
- call(err);
- });
-
- writeStream.addListener('error', function(err) {
- readStream.destroy();
- call(err);
- });
-}, 'util.pump(): Use readableStream.pipe() instead');
-
-
-var uv;
-exports._errnoException = function(err, syscall) {
- if (isUndefined(uv)) uv = process.binding('uv');
- var errname = uv.errname(err);
- var e = new Error(syscall + ' ' + errname);
- e.code = errname;
- e.errno = errname;
- e.syscall = syscall;
- return e;
-};
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
}
exports.isArray = isArray;
function isBoolean(arg) {
return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;
function isNull(arg) {
return arg === null;
}
exports.isNull = isNull;
function isNullOrUndefined(arg) {
return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;
function isNumber(arg) {
return typeof arg === 'number';
}
exports.isNumber = isNumber;
function isString(arg) {
return typeof arg === 'string';
}
exports.isString = isString;
function isSymbol(arg) {
return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;
function isUndefined(arg) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) && objectToString(e) === '[object Error]';
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null ||
typeof arg === 'boolean' ||
typeof arg === 'number' ||
typeof arg === 'string' ||
typeof arg === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
function isBuffer(arg) {
return arg instanceof Buffer;
}
exports.isBuffer = isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}
{
"name": "core-util-is",
"version": "1.0.0",
"description": "The `util.is*` functions introduced in Node v0.12.",
"main": "lib/util.js",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/core-util-is"
},
"keywords": [
"util",
"isBuffer",
"isArray",
"isNumber",
"isString",
"isRegExp",
"isThis",
"isThat",
"polyfill"
],
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/isaacs/core-util-is/issues"
},
"readme": "# core-util-is\n\nThe `util.is*` functions introduced in Node v0.12.\n",
"readmeFilename": "README.md",
"homepage": "https://github.com/isaacs/core-util-is",
"_id": "core-util-is@1.0.0",
"dist": {
"shasum": "63a6a30d96f26a8f3bc2424bf2e29e1c1d5db174"
},
"_from": "core-util-is@~1.0.0",
"_resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.0.tgz"
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// NOTE: These type checking functions intentionally don't use `instanceof`
// because it is fragile and can be easily faked with `Object.create()`.
function isArray(ar) {
return Array.isArray(ar);
}
exports.isArray = isArray;
function isBoolean(arg) {
return typeof arg === 'boolean';
}
exports.isBoolean = isBoolean;
function isNull(arg) {
return arg === null;
}
exports.isNull = isNull;
function isNullOrUndefined(arg) {
return arg == null;
}
exports.isNullOrUndefined = isNullOrUndefined;
function isNumber(arg) {
return typeof arg === 'number';
}
exports.isNumber = isNumber;
function isString(arg) {
return typeof arg === 'string';
}
exports.isString = isString;
function isSymbol(arg) {
return typeof arg === 'symbol';
}
exports.isSymbol = isSymbol;
function isUndefined(arg) {
return arg === void 0;
}
exports.isUndefined = isUndefined;
function isRegExp(re) {
return isObject(re) && objectToString(re) === '[object RegExp]';
}
exports.isRegExp = isRegExp;
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
exports.isObject = isObject;
function isDate(d) {
return isObject(d) && objectToString(d) === '[object Date]';
}
exports.isDate = isDate;
function isError(e) {
return isObject(e) && objectToString(e) === '[object Error]';
}
exports.isError = isError;
function isFunction(arg) {
return typeof arg === 'function';
}
exports.isFunction = isFunction;
function isPrimitive(arg) {
return arg === null ||
typeof arg === 'boolean' ||
typeof arg === 'number' ||
typeof arg === 'string' ||
typeof arg === 'symbol' || // ES6 symbol
typeof arg === 'undefined';
}
exports.isPrimitive = isPrimitive;
function isBuffer(arg) {
return arg instanceof Buffer;
}
exports.isBuffer = isBuffer;
function objectToString(o) {
return Object.prototype.toString.call(o);
}
var util = require('util');
module.exports = util.debuglog || debuglog;
var debugs = {};
var debugEnviron = process.env.NODE_DEBUG || '';
function debuglog(set) {
set = set.toUpperCase();
if (!debugs[set]) {
if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
var pid = process.pid;
debugs[set] = function() {
var msg = util.format.apply(exports, arguments);
console.error('%s %d: %s', set, pid, msg);
};
} else {
debugs[set] = function() {};
}
}
return debugs[set];
};
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
{
"name": "debuglog",
"version": "0.0.2",
"description": "backport of util.debuglog from node v0.11",
"license": "MIT",
"main": "debuglog.js",
"repository": {
"type": "git",
"url": "https://github.com/sam-github/node-debuglog.git"
},
"author": {
"name": "Sam Roberts",
"email": "sam@strongloop.com"
},
"engines": {
"node": "*"
},
"readme": "# debuglog - backport of util.debuglog() from node v0.11\n\nTo facilitate using the `util.debuglog()` function that will be available when\nnode v0.12 is released now, this is a copy extracted from the source.\n\n## require('debuglog')\n\nReturn `util.debuglog`, if it exists, otherwise it will return an internal copy\nof the implementation from node v0.11.\n\n## debuglog(section)\n\n* `section` {String} The section of the program to be debugged\n* Returns: {Function} The logging function\n\nThis is used to create a function which conditionally writes to stderr\nbased on the existence of a `NODE_DEBUG` environment variable. If the\n`section` name appears in that environment variable, then the returned\nfunction will be similar to `console.error()`. If not, then the\nreturned function is a no-op.\n\nFor example:\n\n```javascript\nvar debuglog = util.debuglog('foo');\n\nvar bar = 123;\ndebuglog('hello from foo [%d]', bar);\n```\n\nIf this program is run with `NODE_DEBUG=foo` in the environment, then\nit will output something like:\n\n FOO 3245: hello from foo [123]\n\nwhere `3245` is the process id. If it is not run with that\nenvironment variable set, then it will not print anything.\n\nYou may separate multiple `NODE_DEBUG` environment variables with a\ncomma. For example, `NODE_DEBUG=fs,net,tls`.\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/sam-github/node-debuglog/issues"
},
"homepage": "https://github.com/sam-github/node-debuglog",
"_id": "debuglog@0.0.2",
"dist": {
"shasum": "375665be49e2e92731234bff850b5a095a9f2dbf"
},
"_from": "debuglog@0.0.2",
"_resolved": "https://registry.npmjs.org/debuglog/-/debuglog-0.0.2.tgz"
}

debuglog - backport of util.debuglog() from node v0.11

To facilitate using the util.debuglog() function that will be available when node v0.12 is released now, this is a copy extracted from the source.

require('debuglog')

Return util.debuglog, if it exists, otherwise it will return an internal copy of the implementation from node v0.11.

debuglog(section)

  • section {String} The section of the program to be debugged
  • Returns: {Function} The logging function

This is used to create a function which conditionally writes to stderr based on the existence of a NODE_DEBUG environment variable. If the section name appears in that environment variable, then the returned function will be similar to console.error(). If not, then the returned function is a no-op.

For example:

var debuglog = util.debuglog('foo');

var bar = 123;
debuglog('hello from foo [%d]', bar);

If this program is run with NODE_DEBUG=foo in the environment, then it will output something like:

FOO 3245: hello from foo [123]

where 3245 is the process id. If it is not run with that environment variable set, then it will not print anything.

You may separate multiple NODE_DEBUG environment variables with a comma. For example, NODE_DEBUG=fs,net,tls.

{
"name": "readable-stream",
"version": "1.1.9",
"description": "An exploration of a new kind of readable streams for Node.js",
"main": "readable.js",
"dependencies": {
"core-util-is": "~1.0.0",
"debuglog": "0.0.2"
},
"devDependencies": {
"tap": "~0.2.6"
},
"scripts": {
"test": "tap test/simple/*.js"
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/readable-stream"
},
"keywords": [
"readable",
"stream",
"pipe"
],
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"license": "MIT",
"optionalDependencies": {
"debuglog": "0.0.2"
},
"readme": "# readable-stream\n\nA new class of streams for Node.js\n\nThis module provides the new Stream base classes introduced in Node\nv0.10, for use in Node v0.8. You can use it to have programs that\nhave to work with node v0.8, while being forward-compatible for v0.10\nand beyond. When you drop support for v0.8, you can remove this\nmodule, and only use the native streams.\n\nThis is almost exactly the same codebase as appears in Node v0.10.\nHowever:\n\n1. The exported object is actually the Readable class. Decorating the\n native `stream` module would be global pollution.\n2. In v0.10, you can safely use `base64` as an argument to\n `setEncoding` in Readable streams. However, in v0.8, the\n StringDecoder class has no `end()` method, which is problematic for\n Base64. So, don't use that, because it'll break and be weird.\n\nOther than that, the API is the same as `require('stream')` in v0.10,\nso the API docs are reproduced below.\n\n----------\n\n Stability: 2 - Unstable\n\nA stream is an abstract interface implemented by various objects in\nNode. For example a request to an HTTP server is a stream, as is\nstdout. Streams are readable, writable, or both. All streams are\ninstances of [EventEmitter][]\n\nYou can load the Stream base classes by doing `require('stream')`.\nThere are base classes provided for Readable streams, Writable\nstreams, Duplex streams, and Transform streams.\n\n## Compatibility\n\nIn earlier versions of Node, the Readable stream interface was\nsimpler, but also less powerful and less useful.\n\n* Rather than waiting for you to call the `read()` method, `'data'`\n events would start emitting immediately. If you needed to do some\n I/O to decide how to handle data, then you had to store the chunks\n in some kind of buffer so that they would not be lost.\n* The `pause()` method was advisory, rather than guaranteed. This\n meant that you still had to be prepared to receive `'data'` events\n even when the stream was in a paused state.\n\nIn Node v0.10, the Readable class described below was added. For\nbackwards compatibility with older Node programs, Readable streams\nswitch into \"old mode\" when a `'data'` event handler is added, or when\nthe `pause()` or `resume()` methods are called. The effect is that,\neven if you are not using the new `read()` method and `'readable'`\nevent, you no longer have to worry about losing `'data'` chunks.\n\nMost programs will continue to function normally. However, this\nintroduces an edge case in the following conditions:\n\n* No `'data'` event handler is added.\n* The `pause()` and `resume()` methods are never called.\n\nFor example, consider the following code:\n\n```javascript\n// WARNING! BROKEN!\nnet.createServer(function(socket) {\n\n // we add an 'end' method, but never consume the data\n socket.on('end', function() {\n // It will never get here.\n socket.end('I got your message (but didnt read it)\\n');\n });\n\n}).listen(1337);\n```\n\nIn versions of node prior to v0.10, the incoming message data would be\nsimply discarded. However, in Node v0.10 and beyond, the socket will\nremain paused forever.\n\nThe workaround in this situation is to call the `resume()` method to\ntrigger \"old mode\" behavior:\n\n```javascript\n// Workaround\nnet.createServer(function(socket) {\n\n socket.on('end', function() {\n socket.end('I got your message (but didnt read it)\\n');\n });\n\n // start the flow of data, discarding it.\n socket.resume();\n\n}).listen(1337);\n```\n\nIn addition to new Readable streams switching into old-mode, pre-v0.10\nstyle streams can be wrapped in a Readable class using the `wrap()`\nmethod.\n\n## Class: stream.Readable\n\n<!--type=class-->\n\nA `Readable Stream` has the following methods, members, and events.\n\nNote that `stream.Readable` is an abstract class designed to be\nextended with an underlying implementation of the `_read(size)`\nmethod. (See below.)\n\n### new stream.Readable([options])\n\n* `options` {Object}\n * `highWaterMark` {Number} The maximum number of bytes to store in\n the internal buffer before ceasing to read from the underlying\n resource. Default=16kb\n * `encoding` {String} If specified, then buffers will be decoded to\n strings using the specified encoding. Default=null\n * `objectMode` {Boolean} Whether this stream should behave\n as a stream of objects. Meaning that stream.read(n) returns\n a single value instead of a Buffer of size n\n\nIn classes that extend the Readable class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n### readable.\\_read(size)\n\n* `size` {Number} Number of bytes to read asynchronously\n\nNote: **This function should NOT be called directly.** It should be\nimplemented by child classes, and called by the internal Readable\nclass methods only.\n\nAll Readable stream implementations must provide a `_read` method\nto fetch data from the underlying resource.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\nWhen data is available, put it into the read queue by calling\n`readable.push(chunk)`. If `push` returns false, then you should stop\nreading. When `_read` is called again, you should start pushing more\ndata.\n\nThe `size` argument is advisory. Implementations where a \"read\" is a\nsingle call that returns data can use this to know how much data to\nfetch. Implementations where that is not relevant, such as TCP or\nTLS, may ignore this argument, and simply provide data whenever it\nbecomes available. There is no need, for example to \"wait\" until\n`size` bytes are available before calling `stream.push(chunk)`.\n\n### readable.push(chunk)\n\n* `chunk` {Buffer | null | String} Chunk of data to push into the read queue\n* return {Boolean} Whether or not more pushes should be performed\n\nNote: **This function should be called by Readable implementors, NOT\nby consumers of Readable subclasses.** The `_read()` function will not\nbe called again until at least one `push(chunk)` call is made. If no\ndata is available, then you MAY call `push('')` (an empty string) to\nallow a future `_read` call, without adding any data to the queue.\n\nThe `Readable` class works by putting data into a read queue to be\npulled out later by calling the `read()` method when the `'readable'`\nevent fires.\n\nThe `push()` method will explicitly insert some data into the read\nqueue. If it is called with `null` then it will signal the end of the\ndata.\n\nIn some cases, you may be wrapping a lower-level source which has some\nsort of pause/resume mechanism, and a data callback. In those cases,\nyou could wrap the low-level source object by doing something like\nthis:\n\n```javascript\n// source is an object with readStop() and readStart() methods,\n// and an `ondata` member that gets called when it has data, and\n// an `onend` member that gets called when the data is over.\n\nvar stream = new Readable();\n\nsource.ondata = function(chunk) {\n // if push() returns false, then we need to stop reading from source\n if (!stream.push(chunk))\n source.readStop();\n};\n\nsource.onend = function() {\n stream.push(null);\n};\n\n// _read will be called when the stream wants to pull more data in\n// the advisory size argument is ignored in this case.\nstream._read = function(n) {\n source.readStart();\n};\n```\n\n### readable.unshift(chunk)\n\n* `chunk` {Buffer | null | String} Chunk of data to unshift onto the read queue\n* return {Boolean} Whether or not more pushes should be performed\n\nThis is the corollary of `readable.push(chunk)`. Rather than putting\nthe data at the *end* of the read queue, it puts it at the *front* of\nthe read queue.\n\nThis is useful in certain use-cases where a stream is being consumed\nby a parser, which needs to \"un-consume\" some data that it has\noptimistically pulled out of the source.\n\n```javascript\n// A parser for a simple data protocol.\n// The \"header\" is a JSON object, followed by 2 \\n characters, and\n// then a message body.\n//\n// Note: This can be done more simply as a Transform stream. See below.\n\nfunction SimpleProtocol(source, options) {\n if (!(this instanceof SimpleProtocol))\n return new SimpleProtocol(options);\n\n Readable.call(this, options);\n this._inBody = false;\n this._sawFirstCr = false;\n\n // source is a readable stream, such as a socket or file\n this._source = source;\n\n var self = this;\n source.on('end', function() {\n self.push(null);\n });\n\n // give it a kick whenever the source is readable\n // read(0) will not consume any bytes\n source.on('readable', function() {\n self.read(0);\n });\n\n this._rawHeader = [];\n this.header = null;\n}\n\nSimpleProtocol.prototype = Object.create(\n Readable.prototype, { constructor: { value: SimpleProtocol }});\n\nSimpleProtocol.prototype._read = function(n) {\n if (!this._inBody) {\n var chunk = this._source.read();\n\n // if the source doesn't have data, we don't have data yet.\n if (chunk === null)\n return this.push('');\n\n // check if the chunk has a \\n\\n\n var split = -1;\n for (var i = 0; i < chunk.length; i++) {\n if (chunk[i] === 10) { // '\\n'\n if (this._sawFirstCr) {\n split = i;\n break;\n } else {\n this._sawFirstCr = true;\n }\n } else {\n this._sawFirstCr = false;\n }\n }\n\n if (split === -1) {\n // still waiting for the \\n\\n\n // stash the chunk, and try again.\n this._rawHeader.push(chunk);\n this.push('');\n } else {\n this._inBody = true;\n var h = chunk.slice(0, split);\n this._rawHeader.push(h);\n var header = Buffer.concat(this._rawHeader).toString();\n try {\n this.header = JSON.parse(header);\n } catch (er) {\n this.emit('error', new Error('invalid simple protocol data'));\n return;\n }\n // now, because we got some extra data, unshift the rest\n // back into the read queue so that our consumer will see it.\n var b = chunk.slice(split);\n this.unshift(b);\n\n // and let them know that we are done parsing the header.\n this.emit('header', this.header);\n }\n } else {\n // from there on, just provide the data to our consumer.\n // careful not to push(null), since that would indicate EOF.\n var chunk = this._source.read();\n if (chunk) this.push(chunk);\n }\n};\n\n// Usage:\nvar parser = new SimpleProtocol(source);\n// Now parser is a readable stream that will emit 'header'\n// with the parsed header data.\n```\n\n### readable.wrap(stream)\n\n* `stream` {Stream} An \"old style\" readable stream\n\nIf you are using an older Node library that emits `'data'` events and\nhas a `pause()` method that is advisory only, then you can use the\n`wrap()` method to create a Readable stream that uses the old stream\nas its data source.\n\nFor example:\n\n```javascript\nvar OldReader = require('./old-api-module.js').OldReader;\nvar oreader = new OldReader;\nvar Readable = require('stream').Readable;\nvar myReader = new Readable().wrap(oreader);\n\nmyReader.on('readable', function() {\n myReader.read(); // etc.\n});\n```\n\n### Event: 'readable'\n\nWhen there is data ready to be consumed, this event will fire.\n\nWhen this event emits, call the `read()` method to consume the data.\n\n### Event: 'end'\n\nEmitted when the stream has received an EOF (FIN in TCP terminology).\nIndicates that no more `'data'` events will happen. If the stream is\nalso writable, it may be possible to continue writing.\n\n### Event: 'data'\n\nThe `'data'` event emits either a `Buffer` (by default) or a string if\n`setEncoding()` was used.\n\nNote that adding a `'data'` event listener will switch the Readable\nstream into \"old mode\", where data is emitted as soon as it is\navailable, rather than waiting for you to call `read()` to consume it.\n\n### Event: 'error'\n\nEmitted if there was an error receiving data.\n\n### Event: 'close'\n\nEmitted when the underlying resource (for example, the backing file\ndescriptor) has been closed. Not all streams will emit this.\n\n### readable.setEncoding(encoding)\n\nMakes the `'data'` event emit a string instead of a `Buffer`. `encoding`\ncan be `'utf8'`, `'utf16le'` (`'ucs2'`), `'ascii'`, or `'hex'`.\n\nThe encoding can also be set by specifying an `encoding` field to the\nconstructor.\n\n### readable.read([size])\n\n* `size` {Number | null} Optional number of bytes to read.\n* Return: {Buffer | String | null}\n\nNote: **This function SHOULD be called by Readable stream users.**\n\nCall this method to consume data once the `'readable'` event is\nemitted.\n\nThe `size` argument will set a minimum number of bytes that you are\ninterested in. If not set, then the entire content of the internal\nbuffer is returned.\n\nIf there is no data to consume, or if there are fewer bytes in the\ninternal buffer than the `size` argument, then `null` is returned, and\na future `'readable'` event will be emitted when more is available.\n\nCalling `stream.read(0)` will always return `null`, and will trigger a\nrefresh of the internal buffer, but otherwise be a no-op.\n\n### readable.pipe(destination, [options])\n\n* `destination` {Writable Stream}\n* `options` {Object} Optional\n * `end` {Boolean} Default=true\n\nConnects this readable stream to `destination` WriteStream. Incoming\ndata on this stream gets written to `destination`. Properly manages\nback-pressure so that a slow destination will not be overwhelmed by a\nfast readable stream.\n\nThis function returns the `destination` stream.\n\nFor example, emulating the Unix `cat` command:\n\n process.stdin.pipe(process.stdout);\n\nBy default `end()` is called on the destination when the source stream\nemits `end`, so that `destination` is no longer writable. Pass `{ end:\nfalse }` as `options` to keep the destination stream open.\n\nThis keeps `writer` open so that \"Goodbye\" can be written at the\nend.\n\n reader.pipe(writer, { end: false });\n reader.on(\"end\", function() {\n writer.end(\"Goodbye\\n\");\n });\n\nNote that `process.stderr` and `process.stdout` are never closed until\nthe process exits, regardless of the specified options.\n\n### readable.unpipe([destination])\n\n* `destination` {Writable Stream} Optional\n\nUndo a previously established `pipe()`. If no destination is\nprovided, then all previously established pipes are removed.\n\n### readable.pause()\n\nSwitches the readable stream into \"old mode\", where data is emitted\nusing a `'data'` event rather than being buffered for consumption via\nthe `read()` method.\n\nCeases the flow of data. No `'data'` events are emitted while the\nstream is in a paused state.\n\n### readable.resume()\n\nSwitches the readable stream into \"old mode\", where data is emitted\nusing a `'data'` event rather than being buffered for consumption via\nthe `read()` method.\n\nResumes the incoming `'data'` events after a `pause()`.\n\n\n## Class: stream.Writable\n\n<!--type=class-->\n\nA `Writable` Stream has the following methods, members, and events.\n\nNote that `stream.Writable` is an abstract class designed to be\nextended with an underlying implementation of the\n`_write(chunk, encoding, cb)` method. (See below.)\n\n### new stream.Writable([options])\n\n* `options` {Object}\n * `highWaterMark` {Number} Buffer level when `write()` starts\n returning false. Default=16kb\n * `decodeStrings` {Boolean} Whether or not to decode strings into\n Buffers before passing them to `_write()`. Default=true\n\nIn classes that extend the Writable class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n### writable.\\_write(chunk, encoding, callback)\n\n* `chunk` {Buffer | String} The chunk to be written. Will always\n be a buffer unless the `decodeStrings` option was set to `false`.\n* `encoding` {String} If the chunk is a string, then this is the\n encoding type. Ignore chunk is a buffer. Note that chunk will\n **always** be a buffer unless the `decodeStrings` option is\n explicitly set to `false`.\n* `callback` {Function} Call this function (optionally with an error\n argument) when you are done processing the supplied chunk.\n\nAll Writable stream implementations must provide a `_write` method to\nsend data to the underlying resource.\n\nNote: **This function MUST NOT be called directly.** It should be\nimplemented by child classes, and called by the internal Writable\nclass methods only.\n\nCall the callback using the standard `callback(error)` pattern to\nsignal that the write completed successfully or with an error.\n\nIf the `decodeStrings` flag is set in the constructor options, then\n`chunk` may be a string rather than a Buffer, and `encoding` will\nindicate the sort of string that it is. This is to support\nimplementations that have an optimized handling for certain string\ndata encodings. If you do not explicitly set the `decodeStrings`\noption to `false`, then you can safely ignore the `encoding` argument,\nand assume that `chunk` will always be a Buffer.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\n\n### writable.write(chunk, [encoding], [callback])\n\n* `chunk` {Buffer | String} Data to be written\n* `encoding` {String} Optional. If `chunk` is a string, then encoding\n defaults to `'utf8'`\n* `callback` {Function} Optional. Called when this chunk is\n successfully written.\n* Returns {Boolean}\n\nWrites `chunk` to the stream. Returns `true` if the data has been\nflushed to the underlying resource. Returns `false` to indicate that\nthe buffer is full, and the data will be sent out in the future. The\n`'drain'` event will indicate when the buffer is empty again.\n\nThe specifics of when `write()` will return false, is determined by\nthe `highWaterMark` option provided to the constructor.\n\n### writable.end([chunk], [encoding], [callback])\n\n* `chunk` {Buffer | String} Optional final data to be written\n* `encoding` {String} Optional. If `chunk` is a string, then encoding\n defaults to `'utf8'`\n* `callback` {Function} Optional. Called when the final chunk is\n successfully written.\n\nCall this method to signal the end of the data being written to the\nstream.\n\n### Event: 'drain'\n\nEmitted when the stream's write queue empties and it's safe to write\nwithout buffering again. Listen for it when `stream.write()` returns\n`false`.\n\n### Event: 'close'\n\nEmitted when the underlying resource (for example, the backing file\ndescriptor) has been closed. Not all streams will emit this.\n\n### Event: 'finish'\n\nWhen `end()` is called and there are no more chunks to write, this\nevent is emitted.\n\n### Event: 'pipe'\n\n* `source` {Readable Stream}\n\nEmitted when the stream is passed to a readable stream's pipe method.\n\n### Event 'unpipe'\n\n* `source` {Readable Stream}\n\nEmitted when a previously established `pipe()` is removed using the\nsource Readable stream's `unpipe()` method.\n\n## Class: stream.Duplex\n\n<!--type=class-->\n\nA \"duplex\" stream is one that is both Readable and Writable, such as a\nTCP socket connection.\n\nNote that `stream.Duplex` is an abstract class designed to be\nextended with an underlying implementation of the `_read(size)`\nand `_write(chunk, encoding, callback)` methods as you would with a Readable or\nWritable stream class.\n\nSince JavaScript doesn't have multiple prototypal inheritance, this\nclass prototypally inherits from Readable, and then parasitically from\nWritable. It is thus up to the user to implement both the lowlevel\n`_read(n)` method as well as the lowlevel `_write(chunk, encoding, cb)` method\non extension duplex classes.\n\n### new stream.Duplex(options)\n\n* `options` {Object} Passed to both Writable and Readable\n constructors. Also has the following fields:\n * `allowHalfOpen` {Boolean} Default=true. If set to `false`, then\n the stream will automatically end the readable side when the\n writable side ends and vice versa.\n\nIn classes that extend the Duplex class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n## Class: stream.Transform\n\nA \"transform\" stream is a duplex stream where the output is causally\nconnected in some way to the input, such as a zlib stream or a crypto\nstream.\n\nThere is no requirement that the output be the same size as the input,\nthe same number of chunks, or arrive at the same time. For example, a\nHash stream will only ever have a single chunk of output which is\nprovided when the input is ended. A zlib stream will either produce\nmuch smaller or much larger than its input.\n\nRather than implement the `_read()` and `_write()` methods, Transform\nclasses must implement the `_transform()` method, and may optionally\nalso implement the `_flush()` method. (See below.)\n\n### new stream.Transform([options])\n\n* `options` {Object} Passed to both Writable and Readable\n constructors.\n\nIn classes that extend the Transform class, make sure to call the\nconstructor so that the buffering settings can be properly\ninitialized.\n\n### transform.\\_transform(chunk, encoding, callback)\n\n* `chunk` {Buffer | String} The chunk to be transformed. Will always\n be a buffer unless the `decodeStrings` option was set to `false`.\n* `encoding` {String} If the chunk is a string, then this is the\n encoding type. (Ignore if `decodeStrings` chunk is a buffer.)\n* `callback` {Function} Call this function (optionally with an error\n argument) when you are done processing the supplied chunk.\n\nNote: **This function MUST NOT be called directly.** It should be\nimplemented by child classes, and called by the internal Transform\nclass methods only.\n\nAll Transform stream implementations must provide a `_transform`\nmethod to accept input and produce output.\n\n`_transform` should do whatever has to be done in this specific\nTransform class, to handle the bytes being written, and pass them off\nto the readable portion of the interface. Do asynchronous I/O,\nprocess things, and so on.\n\nCall `transform.push(outputChunk)` 0 or more times to generate output\nfrom this input chunk, depending on how much data you want to output\nas a result of this chunk.\n\nCall the callback function only when the current chunk is completely\nconsumed. Note that there may or may not be output as a result of any\nparticular input chunk.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\n### transform.\\_flush(callback)\n\n* `callback` {Function} Call this function (optionally with an error\n argument) when you are done flushing any remaining data.\n\nNote: **This function MUST NOT be called directly.** It MAY be implemented\nby child classes, and if so, will be called by the internal Transform\nclass methods only.\n\nIn some cases, your transform operation may need to emit a bit more\ndata at the end of the stream. For example, a `Zlib` compression\nstream will store up some internal state so that it can optimally\ncompress the output. At the end, however, it needs to do the best it\ncan with what is left, so that the data will be complete.\n\nIn those cases, you can implement a `_flush` method, which will be\ncalled at the very end, after all the written data is consumed, but\nbefore emitting `end` to signal the end of the readable side. Just\nlike with `_transform`, call `transform.push(chunk)` zero or more\ntimes, as appropriate, and call `callback` when the flush operation is\ncomplete.\n\nThis method is prefixed with an underscore because it is internal to\nthe class that defines it, and should not be called directly by user\nprograms. However, you **are** expected to override this method in\nyour own extension classes.\n\n### Example: `SimpleProtocol` parser\n\nThe example above of a simple protocol parser can be implemented much\nmore simply by using the higher level `Transform` stream class.\n\nIn this example, rather than providing the input as an argument, it\nwould be piped into the parser, which is a more idiomatic Node stream\napproach.\n\n```javascript\nfunction SimpleProtocol(options) {\n if (!(this instanceof SimpleProtocol))\n return new SimpleProtocol(options);\n\n Transform.call(this, options);\n this._inBody = false;\n this._sawFirstCr = false;\n this._rawHeader = [];\n this.header = null;\n}\n\nSimpleProtocol.prototype = Object.create(\n Transform.prototype, { constructor: { value: SimpleProtocol }});\n\nSimpleProtocol.prototype._transform = function(chunk, encoding, done) {\n if (!this._inBody) {\n // check if the chunk has a \\n\\n\n var split = -1;\n for (var i = 0; i < chunk.length; i++) {\n if (chunk[i] === 10) { // '\\n'\n if (this._sawFirstCr) {\n split = i;\n break;\n } else {\n this._sawFirstCr = true;\n }\n } else {\n this._sawFirstCr = false;\n }\n }\n\n if (split === -1) {\n // still waiting for the \\n\\n\n // stash the chunk, and try again.\n this._rawHeader.push(chunk);\n } else {\n this._inBody = true;\n var h = chunk.slice(0, split);\n this._rawHeader.push(h);\n var header = Buffer.concat(this._rawHeader).toString();\n try {\n this.header = JSON.parse(header);\n } catch (er) {\n this.emit('error', new Error('invalid simple protocol data'));\n return;\n }\n // and let them know that we are done parsing the header.\n this.emit('header', this.header);\n\n // now, because we got some extra data, emit this first.\n this.push(b);\n }\n } else {\n // from there on, just provide the data to our consumer as-is.\n this.push(b);\n }\n done();\n};\n\nvar parser = new SimpleProtocol();\nsource.pipe(parser)\n\n// Now parser is a readable stream that will emit 'header'\n// with the parsed header data.\n```\n\n\n## Class: stream.PassThrough\n\nThis is a trivial implementation of a `Transform` stream that simply\npasses the input bytes across to the output. Its purpose is mainly\nfor examples and testing, but there are occasionally use cases where\nit can come in handy.\n\n\n[EventEmitter]: events.html#events_class_events_eventemitter\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/isaacs/readable-stream/issues"
},
"homepage": "https://github.com/isaacs/readable-stream",
"_id": "readable-stream@1.1.9",
"dist": {
"shasum": "cd1d3c09a557707f726a63cb185babab12a21535"
},
"_from": "readable-stream@~1.1.9",
"_resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.9.tgz"
}
exports = module.exports = require('./lib/_stream_readable.js');
exports.Stream = require('stream');
exports.Readable = exports;
exports.Writable = require('./lib/_stream_writable.js');
exports.Duplex = require('./lib/_stream_duplex.js');
exports.Transform = require('./lib/_stream_transform.js');
exports.PassThrough = require('./lib/_stream_passthrough.js');

readable-stream

A new class of streams for Node.js

This module provides the new Stream base classes introduced in Node v0.10, for use in Node v0.8. You can use it to have programs that have to work with node v0.8, while being forward-compatible for v0.10 and beyond. When you drop support for v0.8, you can remove this module, and only use the native streams.

This is almost exactly the same codebase as appears in Node v0.10. However:

  1. The exported object is actually the Readable class. Decorating the native stream module would be global pollution.
  2. In v0.10, you can safely use base64 as an argument to setEncoding in Readable streams. However, in v0.8, the StringDecoder class has no end() method, which is problematic for Base64. So, don't use that, because it'll break and be weird.

Other than that, the API is the same as require('stream') in v0.10, so the API docs are reproduced below.


Stability: 2 - Unstable

A stream is an abstract interface implemented by various objects in Node. For example a request to an HTTP server is a stream, as is stdout. Streams are readable, writable, or both. All streams are instances of EventEmitter

You can load the Stream base classes by doing require('stream'). There are base classes provided for Readable streams, Writable streams, Duplex streams, and Transform streams.

Compatibility

In earlier versions of Node, the Readable stream interface was simpler, but also less powerful and less useful.

  • Rather than waiting for you to call the read() method, 'data' events would start emitting immediately. If you needed to do some I/O to decide how to handle data, then you had to store the chunks in some kind of buffer so that they would not be lost.
  • The pause() method was advisory, rather than guaranteed. This meant that you still had to be prepared to receive 'data' events even when the stream was in a paused state.

In Node v0.10, the Readable class described below was added. For backwards compatibility with older Node programs, Readable streams switch into "old mode" when a 'data' event handler is added, or when the pause() or resume() methods are called. The effect is that, even if you are not using the new read() method and 'readable' event, you no longer have to worry about losing 'data' chunks.

Most programs will continue to function normally. However, this introduces an edge case in the following conditions:

  • No 'data' event handler is added.
  • The pause() and resume() methods are never called.

For example, consider the following code:

// WARNING!  BROKEN!
net.createServer(function(socket) {

  // we add an 'end' method, but never consume the data
  socket.on('end', function() {
    // It will never get here.
    socket.end('I got your message (but didnt read it)\n');
  });

}).listen(1337);

In versions of node prior to v0.10, the incoming message data would be simply discarded. However, in Node v0.10 and beyond, the socket will remain paused forever.

The workaround in this situation is to call the resume() method to trigger "old mode" behavior:

// Workaround
net.createServer(function(socket) {

  socket.on('end', function() {
    socket.end('I got your message (but didnt read it)\n');
  });

  // start the flow of data, discarding it.
  socket.resume();

}).listen(1337);

In addition to new Readable streams switching into old-mode, pre-v0.10 style streams can be wrapped in a Readable class using the wrap() method.

Class: stream.Readable

A Readable Stream has the following methods, members, and events.

Note that stream.Readable is an abstract class designed to be extended with an underlying implementation of the _read(size) method. (See below.)

new stream.Readable([options])

  • options {Object}
    • highWaterMark {Number} The maximum number of bytes to store in the internal buffer before ceasing to read from the underlying resource. Default=16kb
    • encoding {String} If specified, then buffers will be decoded to strings using the specified encoding. Default=null
    • objectMode {Boolean} Whether this stream should behave as a stream of objects. Meaning that stream.read(n) returns a single value instead of a Buffer of size n

In classes that extend the Readable class, make sure to call the constructor so that the buffering settings can be properly initialized.

readable._read(size)

  • size {Number} Number of bytes to read asynchronously

Note: This function should NOT be called directly. It should be implemented by child classes, and called by the internal Readable class methods only.

All Readable stream implementations must provide a _read method to fetch data from the underlying resource.

This method is prefixed with an underscore because it is internal to the class that defines it, and should not be called directly by user programs. However, you are expected to override this method in your own extension classes.

When data is available, put it into the read queue by calling readable.push(chunk). If push returns false, then you should stop reading. When _read is called again, you should start pushing more data.

The size argument is advisory. Implementations where a "read" is a single call that returns data can use this to know how much data to fetch. Implementations where that is not relevant, such as TCP or TLS, may ignore this argument, and simply provide data whenever it becomes available. There is no need, for example to "wait" until size bytes are available before calling stream.push(chunk).

readable.push(chunk)

  • chunk {Buffer | null | String} Chunk of data to push into the read queue
  • return {Boolean} Whether or not more pushes should be performed

Note: This function should be called by Readable implementors, NOT by consumers of Readable subclasses. The _read() function will not be called again until at least one push(chunk) call is made. If no data is available, then you MAY call push('') (an empty string) to allow a future _read call, without adding any data to the queue.

The Readable class works by putting data into a read queue to be pulled out later by calling the read() method when the 'readable' event fires.

The push() method will explicitly insert some data into the read queue. If it is called with null then it will signal the end of the data.

In some cases, you may be wrapping a lower-level source which has some sort of pause/resume mechanism, and a data callback. In those cases, you could wrap the low-level source object by doing something like this:

// source is an object with readStop() and readStart() methods,
// and an `ondata` member that gets called when it has data, and
// an `onend` member that gets called when the data is over.

var stream = new Readable();

source.ondata = function(chunk) {
  // if push() returns false, then we need to stop reading from source
  if (!stream.push(chunk))
    source.readStop();
};

source.onend = function() {
  stream.push(null);
};

// _read will be called when the stream wants to pull more data in
// the advisory size argument is ignored in this case.
stream._read = function(n) {
  source.readStart();
};

readable.unshift(chunk)

  • chunk {Buffer | null | String} Chunk of data to unshift onto the read queue
  • return {Boolean} Whether or not more pushes should be performed

This is the corollary of readable.push(chunk). Rather than putting the data at the end of the read queue, it puts it at the front of the read queue.

This is useful in certain use-cases where a stream is being consumed by a parser, which needs to "un-consume" some data that it has optimistically pulled out of the source.

// A parser for a simple data protocol.
// The "header" is a JSON object, followed by 2 \n characters, and
// then a message body.
//
// Note: This can be done more simply as a Transform stream.  See below.

function SimpleProtocol(source, options) {
  if (!(this instanceof SimpleProtocol))
    return new SimpleProtocol(options);

  Readable.call(this, options);
  this._inBody = false;
  this._sawFirstCr = false;

  // source is a readable stream, such as a socket or file
  this._source = source;

  var self = this;
  source.on('end', function() {
    self.push(null);
  });

  // give it a kick whenever the source is readable
  // read(0) will not consume any bytes
  source.on('readable', function() {
    self.read(0);
  });

  this._rawHeader = [];
  this.header = null;
}

SimpleProtocol.prototype = Object.create(
  Readable.prototype, { constructor: { value: SimpleProtocol }});

SimpleProtocol.prototype._read = function(n) {
  if (!this._inBody) {
    var chunk = this._source.read();

    // if the source doesn't have data, we don't have data yet.
    if (chunk === null)
      return this.push('');

    // check if the chunk has a \n\n
    var split = -1;
    for (var i = 0; i < chunk.length; i++) {
      if (chunk[i] === 10) { // '\n'
        if (this._sawFirstCr) {
          split = i;
          break;
        } else {
          this._sawFirstCr = true;
        }
      } else {
        this._sawFirstCr = false;
      }
    }

    if (split === -1) {
      // still waiting for the \n\n
      // stash the chunk, and try again.
      this._rawHeader.push(chunk);
      this.push('');
    } else {
      this._inBody = true;
      var h = chunk.slice(0, split);
      this._rawHeader.push(h);
      var header = Buffer.concat(this._rawHeader).toString();
      try {
        this.header = JSON.parse(header);
      } catch (er) {
        this.emit('error', new Error('invalid simple protocol data'));
        return;
      }
      // now, because we got some extra data, unshift the rest
      // back into the read queue so that our consumer will see it.
      var b = chunk.slice(split);
      this.unshift(b);

      // and let them know that we are done parsing the header.
      this.emit('header', this.header);
    }
  } else {
    // from there on, just provide the data to our consumer.
    // careful not to push(null), since that would indicate EOF.
    var chunk = this._source.read();
    if (chunk) this.push(chunk);
  }
};

// Usage:
var parser = new SimpleProtocol(source);
// Now parser is a readable stream that will emit 'header'
// with the parsed header data.

readable.wrap(stream)

  • stream {Stream} An "old style" readable stream

If you are using an older Node library that emits 'data' events and has a pause() method that is advisory only, then you can use the wrap() method to create a Readable stream that uses the old stream as its data source.

For example:

var OldReader = require('./old-api-module.js').OldReader;
var oreader = new OldReader;
var Readable = require('stream').Readable;
var myReader = new Readable().wrap(oreader);

myReader.on('readable', function() {
  myReader.read(); // etc.
});

Event: 'readable'

When there is data ready to be consumed, this event will fire.

When this event emits, call the read() method to consume the data.

Event: 'end'

Emitted when the stream has received an EOF (FIN in TCP terminology). Indicates that no more 'data' events will happen. If the stream is also writable, it may be possible to continue writing.

Event: 'data'

The 'data' event emits either a Buffer (by default) or a string if setEncoding() was used.

Note that adding a 'data' event listener will switch the Readable stream into "old mode", where data is emitted as soon as it is available, rather than waiting for you to call read() to consume it.

Event: 'error'

Emitted if there was an error receiving data.

Event: 'close'

Emitted when the underlying resource (for example, the backing file descriptor) has been closed. Not all streams will emit this.

readable.setEncoding(encoding)

Makes the 'data' event emit a string instead of a Buffer. encoding can be 'utf8', 'utf16le' ('ucs2'), 'ascii', or 'hex'.

The encoding can also be set by specifying an encoding field to the constructor.

readable.read([size])

  • size {Number | null} Optional number of bytes to read.
  • Return: {Buffer | String | null}

Note: This function SHOULD be called by Readable stream users.

Call this method to consume data once the 'readable' event is emitted.

The size argument will set a minimum number of bytes that you are interested in. If not set, then the entire content of the internal buffer is returned.

If there is no data to consume, or if there are fewer bytes in the internal buffer than the size argument, then null is returned, and a future 'readable' event will be emitted when more is available.

Calling stream.read(0) will always return null, and will trigger a refresh of the internal buffer, but otherwise be a no-op.

readable.pipe(destination, [options])

  • destination {Writable Stream}
  • options {Object} Optional
    • end {Boolean} Default=true

Connects this readable stream to destination WriteStream. Incoming data on this stream gets written to destination. Properly manages back-pressure so that a slow destination will not be overwhelmed by a fast readable stream.

This function returns the destination stream.

For example, emulating the Unix cat command:

process.stdin.pipe(process.stdout);

By default end() is called on the destination when the source stream emits end, so that destination is no longer writable. Pass { end: false } as options to keep the destination stream open.

This keeps writer open so that "Goodbye" can be written at the end.

reader.pipe(writer, { end: false });
reader.on("end", function() {
  writer.end("Goodbye\n");
});

Note that process.stderr and process.stdout are never closed until the process exits, regardless of the specified options.

readable.unpipe([destination])

  • destination {Writable Stream} Optional

Undo a previously established pipe(). If no destination is provided, then all previously established pipes are removed.

readable.pause()

Switches the readable stream into "old mode", where data is emitted using a 'data' event rather than being buffered for consumption via the read() method.

Ceases the flow of data. No 'data' events are emitted while the stream is in a paused state.

readable.resume()

Switches the readable stream into "old mode", where data is emitted using a 'data' event rather than being buffered for consumption via the read() method.

Resumes the incoming 'data' events after a pause().

Class: stream.Writable

A Writable Stream has the following methods, members, and events.

Note that stream.Writable is an abstract class designed to be extended with an underlying implementation of the _write(chunk, encoding, cb) method. (See below.)

new stream.Writable([options])

  • options {Object}
    • highWaterMark {Number} Buffer level when write() starts returning false. Default=16kb
    • decodeStrings {Boolean} Whether or not to decode strings into Buffers before passing them to _write(). Default=true

In classes that extend the Writable class, make sure to call the constructor so that the buffering settings can be properly initialized.

writable._write(chunk, encoding, callback)

  • chunk {Buffer | String} The chunk to be written. Will always be a buffer unless the decodeStrings option was set to false.
  • encoding {String} If the chunk is a string, then this is the encoding type. Ignore chunk is a buffer. Note that chunk will always be a buffer unless the decodeStrings option is explicitly set to false.
  • callback {Function} Call this function (optionally with an error argument) when you are done processing the supplied chunk.

All Writable stream implementations must provide a _write method to send data to the underlying resource.

Note: This function MUST NOT be called directly. It should be implemented by child classes, and called by the internal Writable class methods only.

Call the callback using the standard callback(error) pattern to signal that the write completed successfully or with an error.

If the decodeStrings flag is set in the constructor options, then chunk may be a string rather than a Buffer, and encoding will indicate the sort of string that it is. This is to support implementations that have an optimized handling for certain string data encodings. If you do not explicitly set the decodeStrings option to false, then you can safely ignore the encoding argument, and assume that chunk will always be a Buffer.

This method is prefixed with an underscore because it is internal to the class that defines it, and should not be called directly by user programs. However, you are expected to override this method in your own extension classes.

writable.write(chunk, [encoding], [callback])

  • chunk {Buffer | String} Data to be written
  • encoding {String} Optional. If chunk is a string, then encoding defaults to 'utf8'
  • callback {Function} Optional. Called when this chunk is successfully written.
  • Returns {Boolean}

Writes chunk to the stream. Returns true if the data has been flushed to the underlying resource. Returns false to indicate that the buffer is full, and the data will be sent out in the future. The 'drain' event will indicate when the buffer is empty again.

The specifics of when write() will return false, is determined by the highWaterMark option provided to the constructor.

writable.end([chunk], [encoding], [callback])

  • chunk {Buffer | String} Optional final data to be written
  • encoding {String} Optional. If chunk is a string, then encoding defaults to 'utf8'
  • callback {Function} Optional. Called when the final chunk is successfully written.

Call this method to signal the end of the data being written to the stream.

Event: 'drain'

Emitted when the stream's write queue empties and it's safe to write without buffering again. Listen for it when stream.write() returns false.

Event: 'close'

Emitted when the underlying resource (for example, the backing file descriptor) has been closed. Not all streams will emit this.

Event: 'finish'

When end() is called and there are no more chunks to write, this event is emitted.

Event: 'pipe'

  • source {Readable Stream}

Emitted when the stream is passed to a readable stream's pipe method.

Event 'unpipe'

  • source {Readable Stream}

Emitted when a previously established pipe() is removed using the source Readable stream's unpipe() method.

Class: stream.Duplex

A "duplex" stream is one that is both Readable and Writable, such as a TCP socket connection.

Note that stream.Duplex is an abstract class designed to be extended with an underlying implementation of the _read(size) and _write(chunk, encoding, callback) methods as you would with a Readable or Writable stream class.

Since JavaScript doesn't have multiple prototypal inheritance, this class prototypally inherits from Readable, and then parasitically from Writable. It is thus up to the user to implement both the lowlevel _read(n) method as well as the lowlevel _write(chunk, encoding, cb) method on extension duplex classes.

new stream.Duplex(options)

  • options {Object} Passed to both Writable and Readable constructors. Also has the following fields:
    • allowHalfOpen {Boolean} Default=true. If set to false, then the stream will automatically end the readable side when the writable side ends and vice versa.

In classes that extend the Duplex class, make sure to call the constructor so that the buffering settings can be properly initialized.

Class: stream.Transform

A "transform" stream is a duplex stream where the output is causally connected in some way to the input, such as a zlib stream or a crypto stream.

There is no requirement that the output be the same size as the input, the same number of chunks, or arrive at the same time. For example, a Hash stream will only ever have a single chunk of output which is provided when the input is ended. A zlib stream will either produce much smaller or much larger than its input.

Rather than implement the _read() and _write() methods, Transform classes must implement the _transform() method, and may optionally also implement the _flush() method. (See below.)

new stream.Transform([options])

  • options {Object} Passed to both Writable and Readable constructors.

In classes that extend the Transform class, make sure to call the constructor so that the buffering settings can be properly initialized.

transform._transform(chunk, encoding, callback)

  • chunk {Buffer | String} The chunk to be transformed. Will always be a buffer unless the decodeStrings option was set to false.
  • encoding {String} If the chunk is a string, then this is the encoding type. (Ignore if decodeStrings chunk is a buffer.)
  • callback {Function} Call this function (optionally with an error argument) when you are done processing the supplied chunk.

Note: This function MUST NOT be called directly. It should be implemented by child classes, and called by the internal Transform class methods only.

All Transform stream implementations must provide a _transform method to accept input and produce output.

_transform should do whatever has to be done in this specific Transform class, to handle the bytes being written, and pass them off to the readable portion of the interface. Do asynchronous I/O, process things, and so on.

Call transform.push(outputChunk) 0 or more times to generate output from this input chunk, depending on how much data you want to output as a result of this chunk.

Call the callback function only when the current chunk is completely consumed. Note that there may or may not be output as a result of any particular input chunk.

This method is prefixed with an underscore because it is internal to the class that defines it, and should not be called directly by user programs. However, you are expected to override this method in your own extension classes.

transform._flush(callback)

  • callback {Function} Call this function (optionally with an error argument) when you are done flushing any remaining data.

Note: This function MUST NOT be called directly. It MAY be implemented by child classes, and if so, will be called by the internal Transform class methods only.

In some cases, your transform operation may need to emit a bit more data at the end of the stream. For example, a Zlib compression stream will store up some internal state so that it can optimally compress the output. At the end, however, it needs to do the best it can with what is left, so that the data will be complete.

In those cases, you can implement a _flush method, which will be called at the very end, after all the written data is consumed, but before emitting end to signal the end of the readable side. Just like with _transform, call transform.push(chunk) zero or more times, as appropriate, and call callback when the flush operation is complete.

This method is prefixed with an underscore because it is internal to the class that defines it, and should not be called directly by user programs. However, you are expected to override this method in your own extension classes.

Example: SimpleProtocol parser

The example above of a simple protocol parser can be implemented much more simply by using the higher level Transform stream class.

In this example, rather than providing the input as an argument, it would be piped into the parser, which is a more idiomatic Node stream approach.

function SimpleProtocol(options) {
  if (!(this instanceof SimpleProtocol))
    return new SimpleProtocol(options);

  Transform.call(this, options);
  this._inBody = false;
  this._sawFirstCr = false;
  this._rawHeader = [];
  this.header = null;
}

SimpleProtocol.prototype = Object.create(
  Transform.prototype, { constructor: { value: SimpleProtocol }});

SimpleProtocol.prototype._transform = function(chunk, encoding, done) {
  if (!this._inBody) {
    // check if the chunk has a \n\n
    var split = -1;
    for (var i = 0; i < chunk.length; i++) {
      if (chunk[i] === 10) { // '\n'
        if (this._sawFirstCr) {
          split = i;
          break;
        } else {
          this._sawFirstCr = true;
        }
      } else {
        this._sawFirstCr = false;
      }
    }

    if (split === -1) {
      // still waiting for the \n\n
      // stash the chunk, and try again.
      this._rawHeader.push(chunk);
    } else {
      this._inBody = true;
      var h = chunk.slice(0, split);
      this._rawHeader.push(h);
      var header = Buffer.concat(this._rawHeader).toString();
      try {
        this.header = JSON.parse(header);
      } catch (er) {
        this.emit('error', new Error('invalid simple protocol data'));
        return;
      }
      // and let them know that we are done parsing the header.
      this.emit('header', this.header);

      // now, because we got some extra data, emit this first.
      this.push(b);
    }
  } else {
    // from there on, just provide the data to our consumer as-is.
    this.push(b);
  }
  done();
};

var parser = new SimpleProtocol();
source.pipe(parser)

// Now parser is a readable stream that will emit 'header'
// with the parsed header data.

Class: stream.PassThrough

This is a trivial implementation of a Transform stream that simply passes the input bytes across to the output. Its purpose is mainly for examples and testing, but there are occasionally use cases where it can come in handy.

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var path = require('path');
var assert = require('assert');
exports.testDir = path.dirname(__filename);
exports.fixturesDir = path.join(exports.testDir, 'fixtures');
exports.libDir = path.join(exports.testDir, '../lib');
exports.tmpDir = path.join(exports.testDir, 'tmp');
exports.PORT = +process.env.NODE_COMMON_PORT || 12346;
if (process.platform === 'win32') {
exports.PIPE = '\\\\.\\pipe\\libuv-test';
} else {
exports.PIPE = exports.tmpDir + '/test.sock';
}
var util = require('util');
for (var i in util) exports[i] = util[i];
//for (var i in exports) global[i] = exports[i];
function protoCtrChain(o) {
var result = [];
for (; o; o = o.__proto__) { result.push(o.constructor); }
return result.join();
}
exports.indirectInstanceOf = function(obj, cls) {
if (obj instanceof cls) { return true; }
var clsChain = protoCtrChain(cls.prototype);
var objChain = protoCtrChain(obj);
return objChain.slice(-clsChain.length) === clsChain;
};
exports.ddCommand = function(filename, kilobytes) {
if (process.platform === 'win32') {
var p = path.resolve(exports.fixturesDir, 'create-file.js');
return '"' + process.argv[0] + '" "' + p + '" "' +
filename + '" ' + (kilobytes * 1024);
} else {
return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes;
}
};
exports.spawnCat = function(options) {
var spawn = require('child_process').spawn;
if (process.platform === 'win32') {
return spawn('more', [], options);
} else {
return spawn('cat', [], options);
}
};
exports.spawnPwd = function(options) {
var spawn = require('child_process').spawn;
if (process.platform === 'win32') {
return spawn('cmd.exe', ['/c', 'cd'], options);
} else {
return spawn('pwd', [], options);
}
};
// Turn this off if the test should not check for global leaks.
exports.globalCheck = true;
process.on('exit', function() {
if (!exports.globalCheck) return;
var knownGlobals = [setTimeout,
setInterval,
setImmediate,
clearTimeout,
clearInterval,
clearImmediate,
console,
Buffer,
process,
global];
if (global.gc) {
knownGlobals.push(gc);
}
if (global.DTRACE_HTTP_SERVER_RESPONSE) {
knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
knownGlobals.push(DTRACE_NET_STREAM_END);
knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
knownGlobals.push(DTRACE_NET_SOCKET_READ);
knownGlobals.push(DTRACE_NET_SOCKET_WRITE);
}
if (global.COUNTER_NET_SERVER_CONNECTION) {
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION);
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE);
knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST);
knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE);
knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST);
knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE);
}
if (global.ArrayBuffer) {
knownGlobals.push(ArrayBuffer);
knownGlobals.push(Int8Array);
knownGlobals.push(Uint8Array);
knownGlobals.push(Uint8ClampedArray);
knownGlobals.push(Int16Array);
knownGlobals.push(Uint16Array);
knownGlobals.push(Int32Array);
knownGlobals.push(Uint32Array);
knownGlobals.push(Float32Array);
knownGlobals.push(Float64Array);
knownGlobals.push(DataView);
}
for (var x in global) {
var found = false;
for (var y in knownGlobals) {
if (global[x] === knownGlobals[y]) {
found = true;
break;
}
}
if (!found) {
console.error('Unknown global: %s', x);
assert.ok(false, 'Unknown global found');
}
}
});
var mustCallChecks = [];
function runCallChecks(exitCode) {
if (exitCode !== 0) return;
var failed = mustCallChecks.filter(function(context) {
return context.actual !== context.expected;
});
failed.forEach(function(context) {
console.log('Mismatched %s function calls. Expected %d, actual %d.',
context.name,
context.expected,
context.actual);
console.log(context.stack.split('\n').slice(2).join('\n'));
});
if (failed.length) process.exit(1);
}
exports.mustCall = function(fn, expected) {
if (typeof expected !== 'number') expected = 1;
var context = {
expected: expected,
actual: 0,
stack: (new Error).stack,
name: fn.name || '<anonymous>'
};
// add the exit listener only once to avoid listener leak warnings
if (mustCallChecks.length === 0) process.on('exit', runCallChecks);
mustCallChecks.push(context);
return function() {
context.actual++;
return fn.apply(this, arguments);
};
};
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
var assert = require('assert');
var http = require('http');
var net = require('net');
var stream = require('readable-stream');
var PORT = require('../common.js').PORT;
var server = http.createServer(function (req, res) {
res.end('ok');
server.close(function() {
console.log('ok');
});
}).listen(PORT, 'localhost', function () {
var client = net.connect(PORT);
client.write(
"GET / HTTP/1.1\r\n" +
"Host: localhost\r\n\r\n")
client.end();
});
var assert = require('assert');
var http = require('http');
var net = require('net');
var stream = require('readable-stream');
var PORT = require('../common.js').PORT;
var server = http.createServer(function (req, res) {
res.end('ok');
server.close(function() {
console.log('ok');
});
}).listen(PORT, 'localhost', function () {
var client = net.connect(PORT);
client.write(
"GET / HTTP/1.1\r\n" +
"Host: localhost\r\n\r\n")
client.end();
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var stream = require('../../');
var str = 'asdfasdfasdfasdfasdf';
var r = new stream.Readable({
highWaterMark: 5,
encoding: 'utf8'
});
var reads = 0;
var eofed = false;
var ended = false;
r._read = function(n) {
if (reads === 0) {
setTimeout(function() {
r.push(str);
});
reads++;
} else if (reads === 1) {
var ret = r.push(str);
assert.equal(ret, false);
reads++;
} else {
assert(!eofed);
eofed = true;
r.push(null);
}
};
r.on('end', function() {
ended = true;
});
// push some data in to start.
// we've never gotten any read event at this point.
var ret = r.push(str);
// should be false. > hwm
assert(!ret);
var chunk = r.read();
assert.equal(chunk, str);
chunk = r.read();
assert.equal(chunk, null);
r.once('readable', function() {
// this time, we'll get *all* the remaining data, because
// it's been added synchronously, as the read WOULD take
// us below the hwm, and so it triggered a _read() again,
// which synchronously added more, which we then return.
chunk = r.read();
assert.equal(chunk, str + str);
chunk = r.read();
assert.equal(chunk, null);
});
process.on('exit', function() {
assert(eofed);
assert(ended);
assert.equal(reads, 2);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var gotEnd = false;
// Make sure we don't miss the end event for paused 0-length streams
var Readable = require('../../').Readable;
var stream = new Readable();
var calledRead = false;
stream._read = function() {
assert(!calledRead);
calledRead = true;
this.push(null);
};
stream.on('data', function() {
throw new Error('should not ever get data');
});
stream.pause();
setTimeout(function() {
stream.on('end', function() {
gotEnd = true;
});
stream.resume();
});
process.on('exit', function() {
assert(gotEnd);
assert(calledRead);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Readable = require('../../lib/_stream_readable');
var Writable = require('../../lib/_stream_writable');
var util = require('util');
util.inherits(TestReadable, Readable);
function TestReadable(opt) {
if (!(this instanceof TestReadable))
return new TestReadable(opt);
Readable.call(this, opt);
this._ended = false;
}
TestReadable.prototype._read = function(n) {
if (this._ended)
this.emit('error', new Error('_read called twice'));
this._ended = true;
this.push(null);
};
util.inherits(TestWritable, Writable);
function TestWritable(opt) {
if (!(this instanceof TestWritable))
return new TestWritable(opt);
Writable.call(this, opt);
this._written = [];
}
TestWritable.prototype._write = function(chunk, encoding, cb) {
this._written.push(chunk);
cb();
};
// this one should not emit 'end' until we read() from it later.
var ender = new TestReadable();
var enderEnded = false;
// what happens when you pipe() a Readable that's already ended?
var piper = new TestReadable();
// pushes EOF null, and length=0, so this will trigger 'end'
piper.read();
setTimeout(function() {
ender.on('end', function() {
enderEnded = true;
});
assert(!enderEnded);
var c = ender.read();
assert.equal(c, null);
var w = new TestWritable();
var writableFinished = false;
w.on('finish', function() {
writableFinished = true;
});
piper.pipe(w);
process.on('exit', function() {
assert(enderEnded);
assert(writableFinished);
console.log('ok');
});
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Stream = require('../../').Stream;
(function testErrorListenerCatches() {
var source = new Stream();
var dest = new Stream();
source.pipe(dest);
var gotErr = null;
source.on('error', function(err) {
gotErr = err;
});
var err = new Error('This stream turned into bacon.');
source.emit('error', err);
assert.strictEqual(gotErr, err);
})();
(function testErrorWithoutListenerThrows() {
var source = new Stream();
var dest = new Stream();
source.pipe(dest);
var err = new Error('This stream turned into bacon.');
var gotErr = null;
try {
source.emit('error', err);
} catch (e) {
gotErr = e;
}
assert.strictEqual(gotErr, err);
})();
(function testErrorWithRemovedListenerThrows() {
var EE = require('events').EventEmitter;
var R = Stream.Readable;
var W = Stream.Writable;
var r = new R;
var w = new W;
var removed = false;
var didTest = false;
process.on('exit', function() {
assert(didTest);
console.log('ok');
});
r._read = function() {
setTimeout(function() {
assert(removed);
assert.throws(function() {
w.emit('error', new Error('fail'));
});
didTest = true;
});
};
w.on('error', myOnError);
r.pipe(w);
w.removeListener('error', myOnError);
removed = true;
function myOnError(er) {
throw new Error('this should not happen');
}
})();
(function testErrorWithRemovedListenerThrows() {
var EE = require('events').EventEmitter;
var R = Stream.Readable;
var W = Stream.Writable;
var r = new R;
var w = new W;
var removed = false;
var didTest = false;
var caught = false;
process.on('exit', function() {
assert(didTest);
console.log('ok');
});
r._read = function() {
setTimeout(function() {
assert(removed);
w.emit('error', new Error('fail'));
didTest = true;
});
};
w.on('error', myOnError);
w._write = function() {};
r.pipe(w);
// Removing some OTHER random listener should not do anything
w.removeListener('error', function() {});
removed = true;
function myOnError(er) {
assert(!caught);
caught = true;
}
})();
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var stream = require('../../');
var assert = require('assert');
var util = require('util');
function Writable() {
this.writable = true;
stream.Stream.call(this);
}
util.inherits(Writable, stream.Stream);
function Readable() {
this.readable = true;
stream.Stream.call(this);
}
util.inherits(Readable, stream.Stream);
var passed = false;
var w = new Writable();
w.on('pipe', function(src) {
passed = true;
});
var r = new Readable();
r.pipe(w);
assert.ok(passed);
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var Readable = require('../../').Readable;
var assert = require('assert');
var s = new Readable({
highWaterMark: 20,
encoding: 'ascii'
});
var list = ['1', '2', '3', '4', '5', '6'];
s._read = function (n) {
var one = list.shift();
if (!one) {
s.push(null);
} else {
var two = list.shift();
s.push(one);
s.push(two);
}
};
var v = s.read(0);
// ACTUALLY [1, 3, 5, 6, 4, 2]
process.on("exit", function () {
assert.deepEqual(s._readableState.buffer,
['1', '2', '3', '4', '5', '6']);
console.log("ok");
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Readable = require('../../').Readable;
var util = require('util');
util.inherits(MyStream, Readable);
function MyStream(options) {
Readable.call(this, options);
this._chunks = 3;
}
MyStream.prototype._read = function(n) {
switch (this._chunks--) {
case 0:
return this.push(null);
case 1:
return setTimeout(function() {
this.push('last chunk');
}.bind(this), 100);
case 2:
return this.push('second to last chunk');
case 3:
return process.nextTick(function() {
this.push('first chunk');
}.bind(this));
default:
throw new Error('?');
}
};
var ms = new MyStream();
var results = [];
ms.on('readable', function() {
var chunk;
while (null !== (chunk = ms.read()))
results.push(chunk + '');
});
var expect = [ 'first chunksecond to last chunk', 'last chunk' ];
process.on('exit', function() {
assert.equal(ms._chunks, -1);
assert.deepEqual(results, expect);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Readable = require('../../').Readable;
(function first() {
// First test, not reading when the readable is added.
// make sure that on('readable', ...) triggers a readable event.
var r = new Readable({
highWaterMark: 3
});
var _readCalled = false;
r._read = function(n) {
_readCalled = true;
};
// This triggers a 'readable' event, which is lost.
r.push(new Buffer('blerg'));
var caughtReadable = false;
setTimeout(function() {
// we're testing what we think we are
assert(!r._readableState.reading);
r.on('readable', function() {
caughtReadable = true;
});
});
process.on('exit', function() {
// we're testing what we think we are
assert(!_readCalled);
assert(caughtReadable);
console.log('ok 1');
});
})();
(function second() {
// second test, make sure that readable is re-emitted if there's
// already a length, while it IS reading.
var r = new Readable({
highWaterMark: 3
});
var _readCalled = false;
r._read = function(n) {
_readCalled = true;
};
// This triggers a 'readable' event, which is lost.
r.push(new Buffer('bl'));
var caughtReadable = false;
setTimeout(function() {
// assert we're testing what we think we are
assert(r._readableState.reading);
r.on('readable', function() {
caughtReadable = true;
});
});
process.on('exit', function() {
// we're testing what we think we are
assert(_readCalled);
assert(caughtReadable);
console.log('ok 2');
});
})();
(function third() {
// Third test, not reading when the stream has not passed
// the highWaterMark but *has* reached EOF.
var r = new Readable({
highWaterMark: 30
});
var _readCalled = false;
r._read = function(n) {
_readCalled = true;
};
// This triggers a 'readable' event, which is lost.
r.push(new Buffer('blerg'));
r.push(null);
var caughtReadable = false;
setTimeout(function() {
// assert we're testing what we think we are
assert(!r._readableState.reading);
r.on('readable', function() {
caughtReadable = true;
});
});
process.on('exit', function() {
// we're testing what we think we are
assert(!_readCalled);
assert(caughtReadable);
console.log('ok 3');
});
})();
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
// this test verifies that passing a huge number to read(size)
// will push up the highWaterMark, and cause the stream to read
// more data continuously, but without triggering a nextTick
// warning or RangeError.
var Readable = require('../../').Readable;
// throw an error if we trigger a nextTick warning.
process.throwDeprecation = true;
var stream = new Readable({ highWaterMark: 2 });
var reads = 0;
var total = 5000;
stream._read = function(size) {
reads++;
size = Math.min(size, total);
total -= size;
if (size === 0)
stream.push(null);
else
stream.push(new Buffer(size));
};
var depth = 0;
function flow(stream, size, callback) {
depth += 1;
var chunk = stream.read(size);
if (!chunk)
stream.once('readable', flow.bind(null, stream, size, callback));
else
callback(chunk);
depth -= 1;
console.log('flow(' + depth + '): exit');
}
flow(stream, 5000, function() {
console.log('complete (' + depth + ')');
});
process.on('exit', function(code) {
assert.equal(reads, 2);
// we pushed up the high water mark
assert.equal(stream._readableState.highWaterMark, 8192);
// length is 0 right now, because we pulled it all out.
assert.equal(stream._readableState.length, 0);
assert(!code);
assert.equal(depth, 0);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
// This test verifies that stream.unshift(Buffer(0)) or
// stream.unshift('') does not set state.reading=false.
var Readable = require('../../').Readable;
var r = new Readable();
var nChunks = 10;
var chunk = new Buffer(10);
chunk.fill('x');
r._read = function(n) {
setTimeout(function() {
r.push(--nChunks === 0 ? null : chunk);
});
};
var readAll = false;
var seen = [];
r.on('readable', function() {
var chunk;
while (chunk = r.read()) {
seen.push(chunk.toString());
// simulate only reading a certain amount of the data,
// and then putting the rest of the chunk back into the
// stream, like a parser might do. We just fill it with
// 'y' so that it's easy to see which bits were touched,
// and which were not.
var putBack = new Buffer(readAll ? 0 : 5);
putBack.fill('y');
readAll = !readAll;
r.unshift(putBack);
}
});
var expect =
[ 'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy',
'xxxxxxxxxx',
'yyyyy' ];
r.on('end', function() {
assert.deepEqual(seen, expect);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
// This test verifies that:
// 1. unshift() does not cause colliding _read() calls.
// 2. unshift() after the 'end' event is an error, but after the EOF
// signalling null, it is ok, and just creates a new readable chunk.
// 3. push() after the EOF signaling null is an error.
// 4. _read() is not called after pushing the EOF null chunk.
var stream = require('../../');
var hwm = 10;
var r = stream.Readable({ highWaterMark: hwm });
var chunks = 10;
var t = (chunks * 5);
var data = new Buffer(chunks * hwm + Math.ceil(hwm / 2));
for (var i = 0; i < data.length; i++) {
var c = 'asdf'.charCodeAt(i % 4);
data[i] = c;
}
var pos = 0;
var pushedNull = false;
r._read = function(n) {
assert(!pushedNull, '_read after null push');
// every third chunk is fast
push(!(chunks % 3));
function push(fast) {
assert(!pushedNull, 'push() after null push');
var c;
if (pos >= data.length)
c = null;
else {
if (n + pos > data.length)
n = data.length - pos;
c = data.slice(pos, pos + n);
}
pushedNull = c === null;
if (fast) {
pos += n;
r.push(c);
if (c === null) pushError();
} else {
setTimeout(function() {
pos += n;
r.push(c);
if (c === null) pushError();
});
}
}
};
function pushError() {
assert.throws(function() {
r.push(new Buffer(1));
});
}
var w = stream.Writable();
var written = [];
w._write = function(chunk, encoding, cb) {
written.push(chunk.toString());
cb();
};
var ended = false;
r.on('end', function() {
assert(!ended, 'end emitted more than once');
assert.throws(function() {
r.unshift(new Buffer(1));
});
ended = true;
w.end();
});
r.on('readable', function() {
var chunk;
while (null !== (chunk = r.read(10))) {
w.write(chunk);
if (chunk.length > 4)
r.unshift(new Buffer('1234'));
}
});
var finished = false;
w.on('finish', function() {
finished = true;
// each chunk should start with 1234, and then be asfdasdfasdf...
// The first got pulled out before the first unshift('1234'), so it's
// lacking that piece.
assert.equal(written[0], 'asdfasdfas');
var asdf = 'd';
console.error('0: %s', written[0]);
for (var i = 1; i < written.length; i++) {
console.error('%s: %s', i.toString(32), written[i]);
assert.equal(written[i].slice(0, 4), '1234');
for (var j = 4; j < written[i].length; j++) {
var c = written[i].charAt(j);
assert.equal(c, asdf);
switch (asdf) {
case 'a': asdf = 's'; break;
case 's': asdf = 'd'; break;
case 'd': asdf = 'f'; break;
case 'f': asdf = 'a'; break;
}
}
}
});
process.on('exit', function() {
assert.equal(written.length, 18);
assert(ended, 'stream ended');
assert(finished, 'stream finished');
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var stream = require('../../');
var queue = [];
for (var decode = 0; decode < 2; decode++) {
for (var uncork = 0; uncork < 2; uncork++) {
for (var multi = 0; multi < 2; multi++) {
queue.push([!!decode, !!uncork, !!multi]);
}
}
}
run();
function run() {
var t = queue.pop();
if (t)
test(t[0], t[1], t[2], run);
else
console.log('ok');
}
function test(decode, uncork, multi, next) {
console.log('# decode=%j uncork=%j multi=%j', decode, uncork, multi);
var counter = 0;
var expectCount = 0;
function cnt(msg) {
expectCount++;
var expect = expectCount;
var called = false;
return function(er) {
if (er)
throw er;
called = true;
counter++;
assert.equal(counter, expect);
};
}
var w = new stream.Writable({ decodeStrings: decode });
w._write = function(chunk, e, cb) {
assert(false, 'Should not call _write');
};
var expectChunks = decode ?
[{ encoding: 'buffer',
chunk: [104, 101, 108, 108, 111, 44, 32] },
{ encoding: 'buffer', chunk: [119, 111, 114, 108, 100] },
{ encoding: 'buffer', chunk: [33] },
{ encoding: 'buffer',
chunk: [10, 97, 110, 100, 32, 116, 104, 101, 110, 46, 46, 46] },
{ encoding: 'buffer',
chunk: [250, 206, 190, 167, 222, 173, 190, 239, 222, 202, 251, 173] }] :
[{ encoding: 'ascii', chunk: 'hello, ' },
{ encoding: 'utf8', chunk: 'world' },
{ encoding: 'buffer', chunk: [33] },
{ encoding: 'binary', chunk: '\nand then...' },
{ encoding: 'hex', chunk: 'facebea7deadbeefdecafbad' }];
var actualChunks;
w._writev = function(chunks, cb) {
actualChunks = chunks.map(function(chunk) {
return {
encoding: chunk.encoding,
chunk: Buffer.isBuffer(chunk.chunk) ?
Array.prototype.slice.call(chunk.chunk) : chunk.chunk
};
});
cb();
};
w.cork();
w.write('hello, ', 'ascii', cnt('hello'));
w.write('world', 'utf8', cnt('world'));
if (multi)
w.cork();
w.write(new Buffer('!'), 'buffer', cnt('!'));
w.write('\nand then...', 'binary', cnt('and then'));
if (multi)
w.uncork();
w.write('facebea7deadbeefdecafbad', 'hex', cnt('hex'));
if (uncork)
w.uncork();
w.end(cnt('end'));
w.on('finish', function() {
// make sure finish comes after all the write cb
cnt('finish')();
assert.deepEqual(expectChunks, actualChunks);
next();
});
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var R = require('../../lib/_stream_readable');
var assert = require('assert');
var util = require('util');
var EE = require('events').EventEmitter;
function TestReader(n) {
R.apply(this);
this._buffer = new Buffer(n || 100);
this._buffer.fill('x');
this._pos = 0;
this._bufs = 10;
}
util.inherits(TestReader, R);
TestReader.prototype._read = function(n) {
var max = this._buffer.length - this._pos;
n = Math.max(n, 0);
var toRead = Math.min(n, max);
if (toRead === 0) {
// simulate the read buffer filling up with some more bytes some time
// in the future.
setTimeout(function() {
this._pos = 0;
this._bufs -= 1;
if (this._bufs <= 0) {
// read them all!
if (!this.ended)
this.push(null);
} else {
// now we have more.
// kinda cheating by calling _read, but whatever,
// it's just fake anyway.
this._read(n);
}
}.bind(this), 10);
return;
}
var ret = this._buffer.slice(this._pos, this._pos + toRead);
this._pos += toRead;
this.push(ret);
};
/////
function TestWriter() {
EE.apply(this);
this.received = [];
this.flush = false;
}
util.inherits(TestWriter, EE);
TestWriter.prototype.write = function(c) {
this.received.push(c.toString());
this.emit('write', c);
return true;
};
TestWriter.prototype.end = function(c) {
if (c) this.write(c);
this.emit('end', this.received);
};
////////
// tiny node-tap lookalike.
var tests = [];
var count = 0;
function test(name, fn) {
count++;
tests.push([name, fn]);
}
function run() {
var next = tests.shift();
if (!next)
return console.error('ok');
var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
ok: assert,
equal: assert.equal,
end: function () {
count--;
run();
}
});
}
// ensure all tests have run
process.on("exit", function () {
assert.equal(count, 0);
});
process.nextTick(run);
test('a most basic test', function(t) {
var r = new TestReader(20);
var reads = [];
var expect = [ 'x',
'xx',
'xxx',
'xxxx',
'xxxxx',
'xxxxxxxxx',
'xxxxxxxxxx',
'xxxxxxxxxxxx',
'xxxxxxxxxxxxx',
'xxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxxxxxxx',
'xxxxxxxxxxxxxxxxxxxxx' ];
r.on('end', function() {
t.same(reads, expect);
t.end();
});
var readSize = 1;
function flow() {
var res;
while (null !== (res = r.read(readSize++))) {
reads.push(res.toString());
}
r.once('readable', flow);
}
flow();
});
test('pipe', function(t) {
var r = new TestReader(5);
var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ]
var w = new TestWriter;
var flush = true;
w.on('end', function(received) {
t.same(received, expect);
t.end();
});
r.pipe(w);
});
[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) {
test('unpipe', function(t) {
var r = new TestReader(5);
// unpipe after 3 writes, then write to another stream instead.
var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ];
expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ];
var w = [ new TestWriter(), new TestWriter() ];
var writes = SPLIT;
w[0].on('write', function() {
if (--writes === 0) {
r.unpipe();
t.equal(r._readableState.pipes, null);
w[0].end();
r.pipe(w[1]);
t.equal(r._readableState.pipes, w[1]);
}
});
var ended = 0;
var ended0 = false;
var ended1 = false;
w[0].on('end', function(results) {
t.equal(ended0, false);
ended0 = true;
ended++;
t.same(results, expect[0]);
});
w[1].on('end', function(results) {
t.equal(ended1, false);
ended1 = true;
ended++;
t.equal(ended, 2);
t.same(results, expect[1]);
t.end();
});
r.pipe(w[0]);
});
});
// both writers should get the same exact data.
test('multipipe', function(t) {
var r = new TestReader(5);
var w = [ new TestWriter, new TestWriter ];
var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ];
var c = 2;
w[0].on('end', function(received) {
t.same(received, expect, 'first');
if (--c === 0) t.end();
});
w[1].on('end', function(received) {
t.same(received, expect, 'second');
if (--c === 0) t.end();
});
r.pipe(w[0]);
r.pipe(w[1]);
});
[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) {
test('multi-unpipe', function(t) {
var r = new TestReader(5);
// unpipe after 3 writes, then write to another stream instead.
var expect = [ 'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx',
'xxxxx' ];
expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ];
var w = [ new TestWriter(), new TestWriter(), new TestWriter() ];
var writes = SPLIT;
w[0].on('write', function() {
if (--writes === 0) {
r.unpipe();
w[0].end();
r.pipe(w[1]);
}
});
var ended = 0;
w[0].on('end', function(results) {
ended++;
t.same(results, expect[0]);
});
w[1].on('end', function(results) {
ended++;
t.equal(ended, 2);
t.same(results, expect[1]);
t.end();
});
r.pipe(w[0]);
r.pipe(w[2]);
});
});
test('back pressure respected', function (t) {
function noop() {}
var r = new R({ objectMode: true });
r._read = noop;
var counter = 0;
r.push(["one"]);
r.push(["two"]);
r.push(["three"]);
r.push(["four"]);
r.push(null);
var w1 = new R();
w1.write = function (chunk) {
console.error('w1.emit("close")');
assert.equal(chunk[0], "one");
w1.emit("close");
process.nextTick(function () {
r.pipe(w2);
r.pipe(w3);
})
};
w1.end = noop;
r.pipe(w1);
var expected = ["two", "two", "three", "three", "four", "four"];
var w2 = new R();
w2.write = function (chunk) {
console.error('w2 write', chunk, counter);
assert.equal(chunk[0], expected.shift());
assert.equal(counter, 0);
counter++;
if (chunk[0] === "four") {
return true;
}
setTimeout(function () {
counter--;
console.error("w2 drain");
w2.emit("drain");
}, 10);
return false;
}
w2.end = noop;
var w3 = new R();
w3.write = function (chunk) {
console.error('w3 write', chunk, counter);
assert.equal(chunk[0], expected.shift());
assert.equal(counter, 1);
counter++;
if (chunk[0] === "four") {
return true;
}
setTimeout(function () {
counter--;
console.error("w3 drain");
w3.emit("drain");
}, 50);
return false;
};
w3.end = function () {
assert.equal(counter, 2);
assert.equal(expected.length, 0);
t.end();
};
});
test('read(0) for ended streams', function (t) {
var r = new R();
var written = false;
var ended = false;
r._read = function (n) {};
r.push(new Buffer("foo"));
r.push(null);
var v = r.read(0);
assert.equal(v, null);
var w = new R();
w.write = function (buffer) {
written = true;
assert.equal(ended, false);
assert.equal(buffer.toString(), "foo")
};
w.end = function () {
ended = true;
assert.equal(written, true);
t.end();
};
r.pipe(w);
})
test('sync _read ending', function (t) {
var r = new R();
var called = false;
r._read = function (n) {
r.push(null);
};
r.once('end', function () {
called = true;
})
r.read();
process.nextTick(function () {
assert.equal(called, true);
t.end();
})
});
test('adding readable triggers data flow', function(t) {
var r = new R({ highWaterMark: 5 });
var onReadable = false;
var readCalled = 0;
r._read = function(n) {
if (readCalled++ === 2)
r.push(null);
else
r.push(new Buffer('asdf'));
};
var called = false;
r.on('readable', function() {
onReadable = true;
r.read();
});
r.on('end', function() {
t.equal(readCalled, 3);
t.ok(onReadable);
t.end();
});
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var R = require('../../lib/_stream_readable');
var assert = require('assert');
var util = require('util');
var EE = require('events').EventEmitter;
var ondataCalled = 0;
function TestReader() {
R.apply(this);
this._buffer = new Buffer(100);
this._buffer.fill('x');
this.on('data', function() {
ondataCalled++;
});
}
util.inherits(TestReader, R);
TestReader.prototype._read = function(n) {
this.push(this._buffer);
this._buffer = new Buffer(0);
};
var reader = new TestReader();
setImmediate(function() {
assert.equal(ondataCalled, 1);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var stream = require('../../');
var Buffer = require('buffer').Buffer;
var r = new stream.Readable();
r._read = function(size) {
r.push(new Buffer(size));
};
var w = new stream.Writable();
w._write = function(data, encoding, cb) {
cb(null);
};
r.pipe(w);
// This might sound unrealistic, but it happens in net.js. When
// `socket.allowHalfOpen === false`, EOF will cause `.destroySoon()` call which
// ends the writable side of net.Socket.
w.end();
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var assert = require('assert');
// If everything aligns so that you do a read(n) of exactly the
// remaining buffer, then make sure that 'end' still emits.
var READSIZE = 100;
var PUSHSIZE = 20;
var PUSHCOUNT = 1000;
var HWM = 50;
var Readable = require('../../').Readable;
var r = new Readable({
highWaterMark: HWM
});
var rs = r._readableState;
r._read = push;
r.on('readable', function() {
//console.error('>> readable');
do {
//console.error(' > read(%d)', READSIZE);
var ret = r.read(READSIZE);
//console.error(' < %j (%d remain)', ret && ret.length, rs.length);
} while (ret && ret.length === READSIZE);
//console.error('<< after read()',
// ret && ret.length,
// rs.needReadable,
// rs.length);
});
var endEmitted = false;
r.on('end', function() {
endEmitted = true;
//console.error('end');
});
var pushes = 0;
function push() {
if (pushes > PUSHCOUNT)
return;
if (pushes++ === PUSHCOUNT) {
//console.error(' push(EOF)');
return r.push(null);
}
//console.error(' push #%d', pushes);
if (r.push(new Buffer(PUSHSIZE)))
setTimeout(push);
}
// start the flow
var ret = r.read(0);
process.on('exit', function() {
assert.equal(pushes, PUSHCOUNT + 1);
assert(endEmitted);
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var Readable = require('../../lib/_stream_readable');
var Writable = require('../../lib/_stream_writable');
var assert = require('assert');
// tiny node-tap lookalike.
var tests = [];
var count = 0;
function test(name, fn) {
count++;
tests.push([name, fn]);
}
function run() {
var next = tests.shift();
if (!next)
return console.error('ok');
var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
equal: assert.equal,
end: function() {
count--;
run();
}
});
}
// ensure all tests have run
process.on('exit', function() {
assert.equal(count, 0);
});
process.nextTick(run);
function toArray(callback) {
var stream = new Writable({ objectMode: true });
var list = [];
stream.write = function(chunk) {
list.push(chunk);
};
stream.end = function() {
callback(list);
};
return stream;
}
function fromArray(list) {
var r = new Readable({ objectMode: true });
r._read = noop;
list.forEach(function(chunk) {
r.push(chunk);
});
r.push(null);
return r;
}
function noop() {}
test('can read objects from stream', function(t) {
var r = fromArray([{ one: '1'}, { two: '2' }]);
var v1 = r.read();
var v2 = r.read();
var v3 = r.read();
assert.deepEqual(v1, { one: '1' });
assert.deepEqual(v2, { two: '2' });
assert.deepEqual(v3, null);
t.end();
});
test('can pipe objects into stream', function(t) {
var r = fromArray([{ one: '1'}, { two: '2' }]);
r.pipe(toArray(function(list) {
assert.deepEqual(list, [
{ one: '1' },
{ two: '2' }
]);
t.end();
}));
});
test('read(n) is ignored', function(t) {
var r = fromArray([{ one: '1'}, { two: '2' }]);
var value = r.read(2);
assert.deepEqual(value, { one: '1' });
t.end();
});
test('can read objects from _read (sync)', function(t) {
var r = new Readable({ objectMode: true });
var list = [{ one: '1'}, { two: '2' }];
r._read = function(n) {
var item = list.shift();
r.push(item || null);
};
r.pipe(toArray(function(list) {
assert.deepEqual(list, [
{ one: '1' },
{ two: '2' }
]);
t.end();
}));
});
test('can read objects from _read (async)', function(t) {
var r = new Readable({ objectMode: true });
var list = [{ one: '1'}, { two: '2' }];
r._read = function(n) {
var item = list.shift();
process.nextTick(function() {
r.push(item || null);
});
};
r.pipe(toArray(function(list) {
assert.deepEqual(list, [
{ one: '1' },
{ two: '2' }
]);
t.end();
}));
});
test('can read strings as objects', function(t) {
var r = new Readable({
objectMode: true
});
r._read = noop;
var list = ['one', 'two', 'three'];
list.forEach(function(str) {
r.push(str);
});
r.push(null);
r.pipe(toArray(function(array) {
assert.deepEqual(array, list);
t.end();
}));
});
test('read(0) for object streams', function(t) {
var r = new Readable({
objectMode: true
});
r._read = noop;
r.push('foobar');
r.push(null);
var v = r.read(0);
r.pipe(toArray(function(array) {
assert.deepEqual(array, ['foobar']);
t.end();
}));
});
test('falsey values', function(t) {
var r = new Readable({
objectMode: true
});
r._read = noop;
r.push(false);
r.push(0);
r.push('');
r.push(null);
r.pipe(toArray(function(array) {
assert.deepEqual(array, [false, 0, '']);
t.end();
}));
});
test('high watermark _read', function(t) {
var r = new Readable({
highWaterMark: 6,
objectMode: true
});
var calls = 0;
var list = ['1', '2', '3', '4', '5', '6', '7', '8'];
r._read = function(n) {
calls++;
};
list.forEach(function(c) {
r.push(c);
});
var v = r.read();
assert.equal(calls, 0);
assert.equal(v, '1');
var v2 = r.read();
assert.equal(v2, '2');
var v3 = r.read();
assert.equal(v3, '3');
assert.equal(calls, 1);
t.end();
});
test('high watermark push', function(t) {
var r = new Readable({
highWaterMark: 6,
objectMode: true
});
r._read = function(n) {};
for (var i = 0; i < 6; i++) {
var bool = r.push(i);
assert.equal(bool, i === 5 ? false : true);
}
t.end();
});
test('can write objects to stream', function(t) {
var w = new Writable({ objectMode: true });
w._write = function(chunk, encoding, cb) {
assert.deepEqual(chunk, { foo: 'bar' });
cb();
};
w.on('finish', function() {
t.end();
});
w.write({ foo: 'bar' });
w.end();
});
test('can write multiple objects to stream', function(t) {
var w = new Writable({ objectMode: true });
var list = [];
w._write = function(chunk, encoding, cb) {
list.push(chunk);
cb();
};
w.on('finish', function() {
assert.deepEqual(list, [0, 1, 2, 3, 4]);
t.end();
});
w.write(0);
w.write(1);
w.write(2);
w.write(3);
w.write(4);
w.end();
});
test('can write strings as objects', function(t) {
var w = new Writable({
objectMode: true
});
var list = [];
w._write = function(chunk, encoding, cb) {
list.push(chunk);
process.nextTick(cb);
};
w.on('finish', function() {
assert.deepEqual(list, ['0', '1', '2', '3', '4']);
t.end();
});
w.write('0');
w.write('1');
w.write('2');
w.write('3');
w.write('4');
w.end();
});
test('buffers finish until cb is called', function(t) {
var w = new Writable({
objectMode: true
});
var called = false;
w._write = function(chunk, encoding, cb) {
assert.equal(chunk, 'foo');
process.nextTick(function() {
called = true;
cb();
});
};
w.on('finish', function() {
assert.equal(called, true);
t.end();
});
w.write('foo');
w.end();
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var stream = require('../../');
(function testErrorListenerCatches() {
var count = 1000;
var source = new stream.Readable();
source._read = function(n) {
n = Math.min(count, n);
count -= n;
source.push(new Buffer(n));
};
var unpipedDest;
source.unpipe = function(dest) {
unpipedDest = dest;
stream.Readable.prototype.unpipe.call(this, dest);
};
var dest = new stream.Writable();
dest._write = function(chunk, encoding, cb) {
cb();
};
source.pipe(dest);
var gotErr = null;
dest.on('error', function(err) {
gotErr = err;
});
var unpipedSource;
dest.on('unpipe', function(src) {
unpipedSource = src;
});
var err = new Error('This stream turned into bacon.');
dest.emit('error', err);
assert.strictEqual(gotErr, err);
assert.strictEqual(unpipedSource, source);
assert.strictEqual(unpipedDest, dest);
})();
(function testErrorWithoutListenerThrows() {
var count = 1000;
var source = new stream.Readable();
source._read = function(n) {
n = Math.min(count, n);
count -= n;
source.push(new Buffer(n));
};
var unpipedDest;
source.unpipe = function(dest) {
unpipedDest = dest;
stream.Readable.prototype.unpipe.call(this, dest);
};
var dest = new stream.Writable();
dest._write = function(chunk, encoding, cb) {
cb();
};
source.pipe(dest);
var unpipedSource;
dest.on('unpipe', function(src) {
unpipedSource = src;
});
var err = new Error('This stream turned into bacon.');
var gotErr = null;
try {
dest.emit('error', err);
} catch (e) {
gotErr = e;
}
assert.strictEqual(gotErr, err);
assert.strictEqual(unpipedSource, source);
assert.strictEqual(unpipedDest, dest);
})();
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var assert = require('assert');
var util = require('util');
var stream = require('../../');
var Read = function() {
stream.Readable.call(this);
};
util.inherits(Read, stream.Readable);
Read.prototype._read = function(size) {
this.push('x');
this.push(null);
};
var Write = function() {
stream.Writable.call(this);
};
util.inherits(Write, stream.Writable);
Write.prototype._write = function(buffer, encoding, cb) {
this.emit('error', new Error('boom'));
this.emit('alldone');
};
var read = new Read();
var write = new Write();
write.once('error', function(err) {});
write.once('alldone', function(err) {
console.log('ok');
});
process.on('exit', function(c) {
console.error('error thrown even with listener');
});
read.pipe(write);
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var stream = require('../../');
var Readable = stream.Readable;
var Writable = stream.Writable;
var assert = require('assert');
var util = require('util');
var EE = require('events').EventEmitter;
// a mock thing a bit like the net.Socket/tcp_wrap.handle interaction
var stream = new Readable({
highWaterMark: 16,
encoding: 'utf8'
});
var source = new EE;
stream._read = function() {
console.error('stream._read');
readStart();
};
var ended = false;
stream.on('end', function() {
ended = true;
});
source.on('data', function(chunk) {
var ret = stream.push(chunk);
console.error('data', stream._readableState.length);
if (!ret)
readStop();
});
source.on('end', function() {
stream.push(null);
});
var reading = false;
function readStart() {
console.error('readStart');
reading = true;
}
function readStop() {
console.error('readStop');
reading = false;
process.nextTick(function() {
var r = stream.read();
if (r !== null)
writer.write(r);
});
}
var writer = new Writable({
decodeStrings: false
});
var written = [];
var expectWritten =
[ 'asdfgasdfgasdfgasdfg',
'asdfgasdfgasdfgasdfg',
'asdfgasdfgasdfgasdfg',
'asdfgasdfgasdfgasdfg',
'asdfgasdfgasdfgasdfg',
'asdfgasdfgasdfgasdfg' ];
writer._write = function(chunk, encoding, cb) {
console.error('WRITE %s', chunk);
written.push(chunk);
process.nextTick(cb);
};
writer.on('finish', finish);
// now emit some chunks.
var chunk = "asdfg";
var set = 0;
readStart();
data();
function data() {
assert(reading);
source.emit('data', chunk);
assert(reading);
source.emit('data', chunk);
assert(reading);
source.emit('data', chunk);
assert(reading);
source.emit('data', chunk);
assert(!reading);
if (set++ < 5)
setTimeout(data, 10);
else
end();
}
function finish() {
console.error('finish');
assert.deepEqual(written, expectWritten);
console.log('ok');
}
function end() {
source.emit('end');
assert(!reading);
writer.end(stream.read());
setTimeout(function() {
assert(ended);
});
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Readable = require('../../').Readable;
var r = new Readable();
var N = 256 * 1024;
// Go ahead and allow the pathological case for this test.
// Yes, it's an infinite loop, that's the point.
process.maxTickDepth = N + 2;
var reads = 0;
r._read = function(n) {
var chunk = reads++ === N ? null : new Buffer(1);
r.push(chunk);
};
r.on('readable', function onReadable() {
if (!(r._readableState.length % 256))
console.error('readable', r._readableState.length);
r.read(N * 2);
});
var ended = false;
r.on('end', function onEnd() {
ended = true;
});
r.read(0);
process.on('exit', function() {
assert(ended);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Readable = require('../../').Readable;
test1();
function test1() {
var r = new Readable();
// should not end when we get a Buffer(0) or '' as the _read result
// that just means that there is *temporarily* no data, but to go
// ahead and try again later.
//
// note that this is very unusual. it only works for crypto streams
// because the other side of the stream will call read(0) to cycle
// data through openssl. that's why we set the timeouts to call
// r.read(0) again later, otherwise there is no more work being done
// and the process just exits.
var buf = new Buffer(5);
buf.fill('x');
var reads = 5;
r._read = function(n) {
switch (reads--) {
case 0:
return r.push(null); // EOF
case 1:
return r.push(buf);
case 2:
setTimeout(r.read.bind(r, 0), 10);
return r.push(new Buffer(0)); // Not-EOF!
case 3:
setTimeout(r.read.bind(r, 0), 10);
return process.nextTick(function() {
return r.push(new Buffer(0));
});
case 4:
setTimeout(r.read.bind(r, 0), 10);
return setTimeout(function() {
return r.push(new Buffer(0));
});
case 5:
return setTimeout(function() {
return r.push(buf);
});
default:
throw new Error('unreachable');
}
};
var results = [];
function flow() {
var chunk;
while (null !== (chunk = r.read()))
results.push(chunk + '');
}
r.on('readable', flow);
r.on('end', function() {
results.push('EOF');
});
flow();
process.on('exit', function() {
assert.deepEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]);
console.log('ok');
});
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var assert = require('assert');
var common = require('../common.js');
var fromList = require('../../lib/_stream_readable')._fromList;
// tiny node-tap lookalike.
var tests = [];
var count = 0;
function test(name, fn) {
count++;
tests.push([name, fn]);
}
function run() {
var next = tests.shift();
if (!next)
return console.error('ok');
var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
equal: assert.equal,
end: function () {
count--;
run();
}
});
}
// ensure all tests have run
process.on("exit", function () {
assert.equal(count, 0);
});
process.nextTick(run);
test('buffers', function(t) {
// have a length
var len = 16;
var list = [ new Buffer('foog'),
new Buffer('bark'),
new Buffer('bazy'),
new Buffer('kuel') ];
// read more than the first element.
var ret = fromList(6, { buffer: list, length: 16 });
t.equal(ret.toString(), 'foogba');
// read exactly the first element.
ret = fromList(2, { buffer: list, length: 10 });
t.equal(ret.toString(), 'rk');
// read less than the first element.
ret = fromList(2, { buffer: list, length: 8 });
t.equal(ret.toString(), 'ba');
// read more than we have.
ret = fromList(100, { buffer: list, length: 6 });
t.equal(ret.toString(), 'zykuel');
// all consumed.
t.same(list, []);
t.end();
});
test('strings', function(t) {
// have a length
var len = 16;
var list = [ 'foog',
'bark',
'bazy',
'kuel' ];
// read more than the first element.
var ret = fromList(6, { buffer: list, length: 16, decoder: true });
t.equal(ret, 'foogba');
// read exactly the first element.
ret = fromList(2, { buffer: list, length: 10, decoder: true });
t.equal(ret, 'rk');
// read less than the first element.
ret = fromList(2, { buffer: list, length: 8, decoder: true });
t.equal(ret, 'ba');
// read more than we have.
ret = fromList(100, { buffer: list, length: 6, decoder: true });
t.equal(ret, 'zykuel');
// all consumed.
t.same(list, []);
t.end();
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Stream = require('../../');
var Readable = Stream.Readable;
var r = new Readable();
var N = 256;
var reads = 0;
r._read = function(n) {
return r.push(++reads === N ? null : new Buffer(1));
};
var rended = false;
r.on('end', function() {
rended = true;
});
var w = new Stream();
w.writable = true;
var writes = 0;
var buffered = 0;
w.write = function(c) {
writes += c.length;
buffered += c.length;
process.nextTick(drain);
return false;
};
function drain() {
assert(buffered <= 2);
buffered = 0;
w.emit('drain');
}
var wended = false;
w.end = function() {
wended = true;
};
// Just for kicks, let's mess with the drain count.
// This verifies that even if it gets negative in the
// pipe() cleanup function, we'll still function properly.
r.on('readable', function() {
w.emit('drain');
});
r.pipe(w);
process.on('exit', function() {
assert(rended);
assert(wended);
console.error('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var assert = require('assert');
var common = require('../common.js');
var Readable = require('../../lib/_stream_readable');
var len = 0;
var chunks = new Array(10);
for (var i = 1; i <= 10; i++) {
chunks[i-1] = new Buffer(i);
len += i;
}
var test = new Readable();
var n = 0;
test._read = function(size) {
var chunk = chunks[n++];
setTimeout(function() {
test.push(chunk);
});
};
test.on('end', thrower);
function thrower() {
throw new Error('this should not happen!');
}
var bytesread = 0;
test.on('readable', function() {
var b = len - bytesread - 1;
var res = test.read(b);
if (res) {
bytesread += res.length;
console.error('br=%d len=%d', bytesread, len);
setTimeout(next);
}
test.read(0);
});
test.read(0);
function next() {
// now let's make 'end' happen
test.removeListener('end', thrower);
var endEmitted = false;
process.on('exit', function() {
assert(endEmitted, 'end should be emitted by now');
});
test.on('end', function() {
endEmitted = true;
});
// one to get the last byte
var r = test.read();
assert(r);
assert.equal(r.length, 1);
r = test.read();
assert.equal(r, null);
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Readable = require('../../lib/_stream_readable');
var EE = require('events').EventEmitter;
var oldStream = new EE();
oldStream.pause = function(){};
oldStream.resume = function(){};
var newStream = new Readable().wrap(oldStream);
var ended = false;
newStream
.on('readable', function(){})
.on('end', function(){ ended = true; });
oldStream.emit('end');
process.on('exit', function(){
assert.ok(ended);
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var Readable = require('../../lib/_stream_readable');
var Writable = require('../../lib/_stream_writable');
var EE = require('events').EventEmitter;
var testRuns = 0, completedRuns = 0;
function runTest(highWaterMark, objectMode, produce) {
testRuns++;
var old = new EE;
var r = new Readable({ highWaterMark: highWaterMark, objectMode: objectMode });
assert.equal(r, r.wrap(old));
var ended = false;
r.on('end', function() {
ended = true;
});
old.pause = function() {
console.error('old.pause()');
old.emit('pause');
flowing = false;
};
old.resume = function() {
console.error('old.resume()');
old.emit('resume');
flow();
};
var flowing;
var chunks = 10;
var oldEnded = false;
var expected = [];
function flow() {
flowing = true;
while (flowing && chunks-- > 0) {
var item = produce();
expected.push(item);
console.log('old.emit', chunks, flowing);
old.emit('data', item);
console.log('after emit', chunks, flowing);
}
if (chunks <= 0) {
oldEnded = true;
console.log('old end', chunks, flowing);
old.emit('end');
}
}
var w = new Writable({ highWaterMark: highWaterMark * 2, objectMode: objectMode });
var written = [];
w._write = function(chunk, encoding, cb) {
console.log('_write', chunk);
written.push(chunk);
setTimeout(cb);
};
w.on('finish', function() {
completedRuns++;
performAsserts();
});
r.pipe(w);
flow();
function performAsserts() {
assert(ended);
assert(oldEnded);
assert.deepEqual(written, expected);
}
}
runTest(100, false, function(){ return new Buffer(100); });
runTest(10, false, function(){ return new Buffer('xxxxxxxxxx'); });
runTest(1, true, function(){ return { foo: 'bar' }; });
process.on('exit', function() {
assert.equal(testRuns, completedRuns);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var assert = require('assert');
var R = require('../../lib/_stream_readable');
var util = require('util');
// tiny node-tap lookalike.
var tests = [];
var count = 0;
function test(name, fn) {
count++;
tests.push([name, fn]);
}
function run() {
var next = tests.shift();
if (!next)
return console.error('ok');
var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
equal: assert.equal,
end: function () {
count--;
run();
}
});
}
// ensure all tests have run
process.on("exit", function () {
assert.equal(count, 0);
});
process.nextTick(run);
/////
util.inherits(TestReader, R);
function TestReader(n, opts) {
R.call(this, opts);
this.pos = 0;
this.len = n || 100;
}
TestReader.prototype._read = function(n) {
setTimeout(function() {
if (this.pos >= this.len) {
// double push(null) to test eos handling
this.push(null);
return this.push(null);
}
n = Math.min(n, this.len - this.pos);
if (n <= 0) {
// double push(null) to test eos handling
this.push(null);
return this.push(null);
}
this.pos += n;
var ret = new Buffer(n);
ret.fill('a');
console.log("this.push(ret)", ret)
return this.push(ret);
}.bind(this), 1);
};
test('setEncoding utf8', function(t) {
var tr = new TestReader(100);
tr.setEncoding('utf8');
var out = [];
var expect =
[ 'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa' ];
tr.on('readable', function flow() {
var chunk;
while (null !== (chunk = tr.read(10)))
out.push(chunk);
});
tr.on('end', function() {
t.same(out, expect);
t.end();
});
});
test('setEncoding hex', function(t) {
var tr = new TestReader(100);
tr.setEncoding('hex');
var out = [];
var expect =
[ '6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161' ];
tr.on('readable', function flow() {
var chunk;
while (null !== (chunk = tr.read(10)))
out.push(chunk);
});
tr.on('end', function() {
t.same(out, expect);
t.end();
});
});
test('setEncoding hex with read(13)', function(t) {
var tr = new TestReader(100);
tr.setEncoding('hex');
var out = [];
var expect =
[ "6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"16161" ];
tr.on('readable', function flow() {
console.log("readable once")
var chunk;
while (null !== (chunk = tr.read(13)))
out.push(chunk);
});
tr.on('end', function() {
console.log("END")
t.same(out, expect);
t.end();
});
});
test('setEncoding base64', function(t) {
var tr = new TestReader(100);
tr.setEncoding('base64');
var out = [];
var expect =
[ 'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYQ==' ];
tr.on('readable', function flow() {
var chunk;
while (null !== (chunk = tr.read(10)))
out.push(chunk);
});
tr.on('end', function() {
t.same(out, expect);
t.end();
});
});
test('encoding: utf8', function(t) {
var tr = new TestReader(100, { encoding: 'utf8' });
var out = [];
var expect =
[ 'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa',
'aaaaaaaaaa' ];
tr.on('readable', function flow() {
var chunk;
while (null !== (chunk = tr.read(10)))
out.push(chunk);
});
tr.on('end', function() {
t.same(out, expect);
t.end();
});
});
test('encoding: hex', function(t) {
var tr = new TestReader(100, { encoding: 'hex' });
var out = [];
var expect =
[ '6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161',
'6161616161' ];
tr.on('readable', function flow() {
var chunk;
while (null !== (chunk = tr.read(10)))
out.push(chunk);
});
tr.on('end', function() {
t.same(out, expect);
t.end();
});
});
test('encoding: hex with read(13)', function(t) {
var tr = new TestReader(100, { encoding: 'hex' });
var out = [];
var expect =
[ "6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"1616161616161",
"6161616161616",
"16161" ];
tr.on('readable', function flow() {
var chunk;
while (null !== (chunk = tr.read(13)))
out.push(chunk);
});
tr.on('end', function() {
t.same(out, expect);
t.end();
});
});
test('encoding: base64', function(t) {
var tr = new TestReader(100, { encoding: 'base64' });
var out = [];
var expect =
[ 'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYWFhYWFh',
'YWFhYWFhYW',
'FhYQ==' ];
tr.on('readable', function flow() {
var chunk;
while (null !== (chunk = tr.read(10)))
out.push(chunk);
});
tr.on('end', function() {
t.same(out, expect);
t.end();
});
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var assert = require('assert');
var common = require('../common.js');
var PassThrough = require('../../').PassThrough;
var Transform = require('../../').Transform;
// tiny node-tap lookalike.
var tests = [];
var count = 0;
function test(name, fn) {
count++;
tests.push([name, fn]);
}
function run() {
var next = tests.shift();
if (!next)
return console.error('ok');
var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
equal: assert.equal,
ok: assert,
end: function () {
count--;
run();
}
});
}
// ensure all tests have run
process.on("exit", function () {
assert.equal(count, 0);
});
process.nextTick(run);
/////
test('writable side consumption', function(t) {
var tx = new Transform({
highWaterMark: 10
});
var transformed = 0;
tx._transform = function(chunk, encoding, cb) {
transformed += chunk.length;
tx.push(chunk);
cb();
};
for (var i = 1; i <= 10; i++) {
tx.write(new Buffer(i));
}
tx.end();
t.equal(tx._readableState.length, 10);
t.equal(transformed, 10);
t.equal(tx._transformState.writechunk.length, 5);
t.same(tx._writableState.buffer.map(function(c) {
return c.chunk.length;
}), [6, 7, 8, 9, 10]);
t.end();
});
test('passthrough', function(t) {
var pt = new PassThrough();
pt.write(new Buffer('foog'));
pt.write(new Buffer('bark'));
pt.write(new Buffer('bazy'));
pt.write(new Buffer('kuel'));
pt.end();
t.equal(pt.read(5).toString(), 'foogb');
t.equal(pt.read(5).toString(), 'arkba');
t.equal(pt.read(5).toString(), 'zykue');
t.equal(pt.read(5).toString(), 'l');
t.end();
});
test('object passthrough', function (t) {
var pt = new PassThrough({ objectMode: true });
pt.write(1);
pt.write(true);
pt.write(false);
pt.write(0);
pt.write('foo');
pt.write('');
pt.write({ a: 'b'});
pt.end();
t.equal(pt.read(), 1);
t.equal(pt.read(), true);
t.equal(pt.read(), false);
t.equal(pt.read(), 0);
t.equal(pt.read(), 'foo');
t.equal(pt.read(), '');
t.same(pt.read(), { a: 'b'});
t.end();
});
test('simple transform', function(t) {
var pt = new Transform;
pt._transform = function(c, e, cb) {
var ret = new Buffer(c.length);
ret.fill('x');
pt.push(ret);
cb();
};
pt.write(new Buffer('foog'));
pt.write(new Buffer('bark'));
pt.write(new Buffer('bazy'));
pt.write(new Buffer('kuel'));
pt.end();
t.equal(pt.read(5).toString(), 'xxxxx');
t.equal(pt.read(5).toString(), 'xxxxx');
t.equal(pt.read(5).toString(), 'xxxxx');
t.equal(pt.read(5).toString(), 'x');
t.end();
});
test('simple object transform', function(t) {
var pt = new Transform({ objectMode: true });
pt._transform = function(c, e, cb) {
pt.push(JSON.stringify(c));
cb();
};
pt.write(1);
pt.write(true);
pt.write(false);
pt.write(0);
pt.write('foo');
pt.write('');
pt.write({ a: 'b'});
pt.end();
t.equal(pt.read(), '1');
t.equal(pt.read(), 'true');
t.equal(pt.read(), 'false');
t.equal(pt.read(), '0');
t.equal(pt.read(), '"foo"');
t.equal(pt.read(), '""');
t.equal(pt.read(), '{"a":"b"}');
t.end();
});
test('async passthrough', function(t) {
var pt = new Transform;
pt._transform = function(chunk, encoding, cb) {
setTimeout(function() {
pt.push(chunk);
cb();
}, 10);
};
pt.write(new Buffer('foog'));
pt.write(new Buffer('bark'));
pt.write(new Buffer('bazy'));
pt.write(new Buffer('kuel'));
pt.end();
pt.on('finish', function() {
t.equal(pt.read(5).toString(), 'foogb');
t.equal(pt.read(5).toString(), 'arkba');
t.equal(pt.read(5).toString(), 'zykue');
t.equal(pt.read(5).toString(), 'l');
t.end();
});
});
test('assymetric transform (expand)', function(t) {
var pt = new Transform;
// emit each chunk 2 times.
pt._transform = function(chunk, encoding, cb) {
setTimeout(function() {
pt.push(chunk);
setTimeout(function() {
pt.push(chunk);
cb();
}, 10)
}, 10);
};
pt.write(new Buffer('foog'));
pt.write(new Buffer('bark'));
pt.write(new Buffer('bazy'));
pt.write(new Buffer('kuel'));
pt.end();
pt.on('finish', function() {
t.equal(pt.read(5).toString(), 'foogf');
t.equal(pt.read(5).toString(), 'oogba');
t.equal(pt.read(5).toString(), 'rkbar');
t.equal(pt.read(5).toString(), 'kbazy');
t.equal(pt.read(5).toString(), 'bazyk');
t.equal(pt.read(5).toString(), 'uelku');
t.equal(pt.read(5).toString(), 'el');
t.end();
});
});
test('assymetric transform (compress)', function(t) {
var pt = new Transform;
// each output is the first char of 3 consecutive chunks,
// or whatever's left.
pt.state = '';
pt._transform = function(chunk, encoding, cb) {
if (!chunk)
chunk = '';
var s = chunk.toString();
setTimeout(function() {
this.state += s.charAt(0);
if (this.state.length === 3) {
pt.push(new Buffer(this.state));
this.state = '';
}
cb();
}.bind(this), 10);
};
pt._flush = function(cb) {
// just output whatever we have.
pt.push(new Buffer(this.state));
this.state = '';
cb();
};
pt.write(new Buffer('aaaa'));
pt.write(new Buffer('bbbb'));
pt.write(new Buffer('cccc'));
pt.write(new Buffer('dddd'));
pt.write(new Buffer('eeee'));
pt.write(new Buffer('aaaa'));
pt.write(new Buffer('bbbb'));
pt.write(new Buffer('cccc'));
pt.write(new Buffer('dddd'));
pt.write(new Buffer('eeee'));
pt.write(new Buffer('aaaa'));
pt.write(new Buffer('bbbb'));
pt.write(new Buffer('cccc'));
pt.write(new Buffer('dddd'));
pt.end();
// 'abcdeabcdeabcd'
pt.on('finish', function() {
t.equal(pt.read(5).toString(), 'abcde');
t.equal(pt.read(5).toString(), 'abcde');
t.equal(pt.read(5).toString(), 'abcd');
t.end();
});
});
// this tests for a stall when data is written to a full stream
// that has empty transforms.
test('complex transform', function(t) {
var count = 0;
var saved = null;
var pt = new Transform({highWaterMark:3});
pt._transform = function(c, e, cb) {
if (count++ === 1)
saved = c;
else {
if (saved) {
pt.push(saved);
saved = null;
}
pt.push(c);
}
cb();
};
pt.once('readable', function() {
process.nextTick(function() {
pt.write(new Buffer('d'));
pt.write(new Buffer('ef'), function() {
pt.end();
t.end();
});
t.equal(pt.read().toString(), 'abcdef');
t.equal(pt.read(), null);
});
});
pt.write(new Buffer('abc'));
});
test('passthrough event emission', function(t) {
var pt = new PassThrough();
var emits = 0;
pt.on('readable', function() {
var state = pt._readableState;
console.error('>>> emit readable %d', emits);
emits++;
});
var i = 0;
pt.write(new Buffer('foog'));
console.error('need emit 0');
pt.write(new Buffer('bark'));
console.error('should have emitted readable now 1 === %d', emits);
t.equal(emits, 1);
t.equal(pt.read(5).toString(), 'foogb');
t.equal(pt.read(5) + '', 'null');
console.error('need emit 1');
pt.write(new Buffer('bazy'));
console.error('should have emitted, but not again');
pt.write(new Buffer('kuel'));
console.error('should have emitted readable now 2 === %d', emits);
t.equal(emits, 2);
t.equal(pt.read(5).toString(), 'arkba');
t.equal(pt.read(5).toString(), 'zykue');
t.equal(pt.read(5), null);
console.error('need emit 2');
pt.end();
t.equal(emits, 3);
t.equal(pt.read(5).toString(), 'l');
t.equal(pt.read(5), null);
console.error('should not have emitted again');
t.equal(emits, 3);
t.end();
});
test('passthrough event emission reordered', function(t) {
var pt = new PassThrough;
var emits = 0;
pt.on('readable', function() {
console.error('emit readable', emits)
emits++;
});
pt.write(new Buffer('foog'));
console.error('need emit 0');
pt.write(new Buffer('bark'));
console.error('should have emitted readable now 1 === %d', emits);
t.equal(emits, 1);
t.equal(pt.read(5).toString(), 'foogb');
t.equal(pt.read(5), null);
console.error('need emit 1');
pt.once('readable', function() {
t.equal(pt.read(5).toString(), 'arkba');
t.equal(pt.read(5), null);
console.error('need emit 2');
pt.once('readable', function() {
t.equal(pt.read(5).toString(), 'zykue');
t.equal(pt.read(5), null);
pt.once('readable', function() {
t.equal(pt.read(5).toString(), 'l');
t.equal(pt.read(5), null);
t.equal(emits, 4);
t.end();
});
pt.end();
});
pt.write(new Buffer('kuel'));
});
pt.write(new Buffer('bazy'));
});
test('passthrough facaded', function(t) {
console.error('passthrough facaded');
var pt = new PassThrough;
var datas = [];
pt.on('data', function(chunk) {
datas.push(chunk.toString());
});
pt.on('end', function() {
t.same(datas, ['foog', 'bark', 'bazy', 'kuel']);
t.end();
});
pt.write(new Buffer('foog'));
setTimeout(function() {
pt.write(new Buffer('bark'));
setTimeout(function() {
pt.write(new Buffer('bazy'));
setTimeout(function() {
pt.write(new Buffer('kuel'));
setTimeout(function() {
pt.end();
}, 10);
}, 10);
}, 10);
}, 10);
});
test('object transform (json parse)', function(t) {
console.error('json parse stream');
var jp = new Transform({ objectMode: true });
jp._transform = function(data, encoding, cb) {
try {
jp.push(JSON.parse(data));
cb();
} catch (er) {
cb(er);
}
};
// anything except null/undefined is fine.
// those are "magic" in the stream API, because they signal EOF.
var objects = [
{ foo: 'bar' },
100,
"string",
{ nested: { things: [ { foo: 'bar' }, 100, "string" ] } }
];
var ended = false;
jp.on('end', function() {
ended = true;
});
objects.forEach(function(obj) {
jp.write(JSON.stringify(obj));
var res = jp.read();
t.same(res, obj);
});
jp.end();
// read one more time to get the 'end' event
jp.read();
process.nextTick(function() {
t.ok(ended);
t.end();
})
});
test('object transform (json stringify)', function(t) {
console.error('json parse stream');
var js = new Transform({ objectMode: true });
js._transform = function(data, encoding, cb) {
try {
js.push(JSON.stringify(data));
cb();
} catch (er) {
cb(er);
}
};
// anything except null/undefined is fine.
// those are "magic" in the stream API, because they signal EOF.
var objects = [
{ foo: 'bar' },
100,
"string",
{ nested: { things: [ { foo: 'bar' }, 100, "string" ] } }
];
var ended = false;
js.on('end', function() {
ended = true;
});
objects.forEach(function(obj) {
js.write(obj);
var res = js.read();
t.equal(res, JSON.stringify(obj));
});
js.end();
// read one more time to get the 'end' event
js.read();
process.nextTick(function() {
t.ok(ended);
t.end();
})
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var assert = require('assert');
var stream = require('../../');
var crypto = require('crypto');
var util = require('util');
function TestWriter() {
stream.Writable.call(this);
}
util.inherits(TestWriter, stream.Writable);
TestWriter.prototype._write = function (buffer, encoding, callback) {
console.log('write called');
// super slow write stream (callback never called)
};
var dest = new TestWriter();
function TestReader(id) {
stream.Readable.call(this);
this.reads = 0;
}
util.inherits(TestReader, stream.Readable);
TestReader.prototype._read = function (size) {
this.reads += 1;
this.push(crypto.randomBytes(size));
};
var src1 = new TestReader();
var src2 = new TestReader();
src1.pipe(dest);
src1.once('readable', function () {
process.nextTick(function () {
src2.pipe(dest);
src2.once('readable', function () {
process.nextTick(function () {
src1.unpipe(dest);
});
});
});
});
process.on('exit', function () {
assert.equal(src1.reads, 2);
assert.equal(src2.reads, 2);
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var assert = require('assert');
var stream = require('../../');
var chunk = new Buffer('hallo');
var util = require('util');
function TestWriter() {
stream.Writable.call(this);
}
util.inherits(TestWriter, stream.Writable);
TestWriter.prototype._write = function(buffer, encoding, callback) {
callback(null);
};
var dest = new TestWriter();
// Set this high so that we'd trigger a nextTick warning
// and/or RangeError if we do maybeReadMore wrong.
function TestReader() {
stream.Readable.call(this, { highWaterMark: 0x10000 });
}
util.inherits(TestReader, stream.Readable);
TestReader.prototype._read = function(size) {
this.push(chunk);
};
var src = new TestReader();
for (var i = 0; i < 10; i++) {
src.pipe(dest);
src.unpipe(dest);
}
assert.equal(src.listeners('end').length, 0);
assert.equal(src.listeners('readable').length, 0);
assert.equal(dest.listeners('unpipe').length, 0);
assert.equal(dest.listeners('drain').length, 0);
assert.equal(dest.listeners('error').length, 0);
assert.equal(dest.listeners('close').length, 0);
assert.equal(dest.listeners('finish').length, 0);
console.error(src._readableState);
process.on('exit', function() {
src._readableState.buffer.length = 0;
console.error(src._readableState);
assert(src._readableState.length >= src._readableState.highWaterMark);
console.log('ok');
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common.js');
var W = require('../../').Writable;
var D = require('../../').Duplex;
var assert = require('assert');
var util = require('util');
util.inherits(TestWriter, W);
function TestWriter() {
W.apply(this, arguments);
this.buffer = [];
this.written = 0;
}
TestWriter.prototype._write = function(chunk, encoding, cb) {
// simulate a small unpredictable latency
setTimeout(function() {
this.buffer.push(chunk.toString());
this.written += chunk.length;
cb();
}.bind(this), Math.floor(Math.random() * 10));
};
var chunks = new Array(50);
for (var i = 0; i < chunks.length; i++) {
chunks[i] = new Array(i + 1).join('x');
}
// tiny node-tap lookalike.
var tests = [];
var count = 0;
function test(name, fn) {
count++;
tests.push([name, fn]);
}
function run() {
var next = tests.shift();
if (!next)
return console.error('ok');
var name = next[0];
var fn = next[1];
console.log('# %s', name);
fn({
same: assert.deepEqual,
equal: assert.equal,
end: function () {
count--;
run();
}
});
}
// ensure all tests have run
process.on("exit", function () {
assert.equal(count, 0);
});
process.nextTick(run);
test('write fast', function(t) {
var tw = new TestWriter({
highWaterMark: 100
});
tw.on('finish', function() {
t.same(tw.buffer, chunks, 'got chunks in the right order');
t.end();
});
chunks.forEach(function(chunk) {
// screw backpressure. Just buffer it all up.
tw.write(chunk);
});
tw.end();
});
test('write slow', function(t) {
var tw = new TestWriter({
highWaterMark: 100
});
tw.on('finish', function() {
t.same(tw.buffer, chunks, 'got chunks in the right order');
t.end();
});
var i = 0;
(function W() {
tw.write(chunks[i++]);
if (i < chunks.length)
setTimeout(W, 10);
else
tw.end();
})();
});
test('write backpressure', function(t) {
var tw = new TestWriter({
highWaterMark: 50
});
var drains = 0;
tw.on('finish', function() {
t.same(tw.buffer, chunks, 'got chunks in the right order');
t.equal(drains, 17);
t.end();
});
tw.on('drain', function() {
drains++;
});
var i = 0;
(function W() {
do {
var ret = tw.write(chunks[i++]);
} while (ret !== false && i < chunks.length);
if (i < chunks.length) {
assert(tw._writableState.length >= 50);
tw.once('drain', W);
} else {
tw.end();
}
})();
});
test('write bufferize', function(t) {
var tw = new TestWriter({
highWaterMark: 100
});
var encodings =
[ 'hex',
'utf8',
'utf-8',
'ascii',
'binary',
'base64',
'ucs2',
'ucs-2',
'utf16le',
'utf-16le',
undefined ];
tw.on('finish', function() {
t.same(tw.buffer, chunks, 'got the expected chunks');
});
chunks.forEach(function(chunk, i) {
var enc = encodings[ i % encodings.length ];
chunk = new Buffer(chunk);
tw.write(chunk.toString(enc), enc);
});
t.end();
});
test('write no bufferize', function(t) {
var tw = new TestWriter({
highWaterMark: 100,
decodeStrings: false
});
tw._write = function(chunk, encoding, cb) {
assert(typeof chunk === 'string');
chunk = new Buffer(chunk, encoding);
return TestWriter.prototype._write.call(this, chunk, encoding, cb);
};
var encodings =
[ 'hex',
'utf8',
'utf-8',
'ascii',
'binary',
'base64',
'ucs2',
'ucs-2',
'utf16le',
'utf-16le',
undefined ];
tw.on('finish', function() {
t.same(tw.buffer, chunks, 'got the expected chunks');
});
chunks.forEach(function(chunk, i) {
var enc = encodings[ i % encodings.length ];
chunk = new Buffer(chunk);
tw.write(chunk.toString(enc), enc);
});
t.end();
});
test('write callbacks', function (t) {
var callbacks = chunks.map(function(chunk, i) {
return [i, function(er) {
callbacks._called[i] = chunk;
}];
}).reduce(function(set, x) {
set['callback-' + x[0]] = x[1];
return set;
}, {});
callbacks._called = [];
var tw = new TestWriter({
highWaterMark: 100
});
tw.on('finish', function() {
process.nextTick(function() {
t.same(tw.buffer, chunks, 'got chunks in the right order');
t.same(callbacks._called, chunks, 'called all callbacks');
t.end();
});
});
chunks.forEach(function(chunk, i) {
tw.write(chunk, callbacks['callback-' + i]);
});
tw.end();
});
test('end callback', function (t) {
var tw = new TestWriter();
tw.end(function () {
t.end();
});
});
test('end callback with chunk', function (t) {
var tw = new TestWriter();
tw.end(new Buffer('hello world'), function () {
t.end();
});
});
test('end callback with chunk and encoding', function (t) {
var tw = new TestWriter();
tw.end('hello world', 'ascii', function () {
t.end();
});
});
test('end callback after .write() call', function (t) {
var tw = new TestWriter();
tw.write(new Buffer('hello world'));
tw.end(function () {
t.end();
});
});
test('end callback called after write callback', function (t) {
var tw = new TestWriter();
var writeCalledback = false;
tw.write(new Buffer('hello world'), function() {
writeCalledback = true;
});
tw.end(function () {
t.equal(writeCalledback, true);
t.end();
});
});
test('encoding should be ignored for buffers', function(t) {
var tw = new W();
var hex = '018b5e9a8f6236ffe30e31baf80d2cf6eb';
tw._write = function(chunk, encoding, cb) {
t.equal(chunk.toString('hex'), hex);
t.end();
};
var buf = new Buffer(hex, 'hex');
tw.write(buf, 'binary');
});
test('writables are not pipable', function(t) {
var w = new W();
w._write = function() {};
var gotError = false;
w.on('error', function(er) {
gotError = true;
});
w.pipe(process.stdout);
assert(gotError);
t.end();
});
test('duplexes are pipable', function(t) {
var d = new D();
d._read = function() {};
d._write = function() {};
var gotError = false;
d.on('error', function(er) {
gotError = true;
});
d.pipe(process.stdout);
assert(!gotError);
t.end();
});
test('end(chunk) two times is an error', function(t) {
var w = new W();
w._write = function() {};
var gotError = false;
w.on('error', function(er) {
gotError = true;
t.equal(er.message, 'write after end');
});
w.end('this is the end');
w.end('and so is this');
process.nextTick(function() {
assert(gotError);
t.end();
});
});
test('dont end while writing', function(t) {
var w = new W();
var wrote = false;
w._write = function(chunk, e, cb) {
assert(!this.writing);
wrote = true;
this.writing = true;
setTimeout(function() {
this.writing = false;
cb();
});
};
w.on('finish', function() {
assert(wrote);
t.end();
});
w.write(Buffer(0));
w.end();
});
test('finish does not come before write cb', function(t) {
var w = new W();
var writeCb = false;
w._write = function(chunk, e, cb) {
setTimeout(function() {
writeCb = true;
cb();
}, 10);
};
w.on('finish', function() {
assert(writeCb);
t.end();
});
w.write(Buffer(0));
w.end();
});
test('finish does not come before sync _write cb', function(t) {
var w = new W();
var writeCb = false;
w._write = function(chunk, e, cb) {
cb();
};
w.on('finish', function() {
assert(writeCb);
t.end();
});
w.write(Buffer(0), function(er) {
writeCb = true;
});
w.end();
});
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var common = require('../common');
var assert = require('assert');
var stream = require('../../');
var Readable = stream.Readable;
var Writable = stream.Writable;
var totalChunks = 100;
var chunkSize = 99;
var expectTotalData = totalChunks * chunkSize;
var expectEndingData = expectTotalData;
var r = new Readable({ highWaterMark: 1000 });
var chunks = totalChunks;
r._read = function(n) {
if (!(chunks % 2))
setImmediate(push);
else if (!(chunks % 3))
process.nextTick(push);
else
push();
};
var totalPushed = 0;
function push() {
var chunk = chunks-- > 0 ? new Buffer(chunkSize) : null;
if (chunk) {
totalPushed += chunk.length;
chunk.fill('x');
}
r.push(chunk);
}
read100();
// first we read 100 bytes
function read100() {
readn(100, onData);
}
function readn(n, then) {
console.error('read %d', n);
expectEndingData -= n;
;(function read() {
var c = r.read(n);
if (!c)
r.once('readable', read);
else {
assert.equal(c.length, n);
assert(!r._readableState.flowing);
then();
}
})();
}
// then we listen to some data events
function onData() {
expectEndingData -= 100;
console.error('onData');
var seen = 0;
r.on('data', function od(c) {
seen += c.length;
if (seen >= 100) {
// seen enough
r.removeListener('data', od);
r.pause();
if (seen > 100) {
// oh no, seen too much!
// put the extra back.
var diff = seen - 100;
r.unshift(c.slice(c.length - diff));
console.error('seen too much', seen, diff);
}
// Nothing should be lost in between
setImmediate(pipeLittle);
}
});
}
// Just pipe 200 bytes, then unshift the extra and unpipe
function pipeLittle() {
expectEndingData -= 200;
console.error('pipe a little');
var w = new Writable();
var written = 0;
w.on('finish', function() {
assert.equal(written, 200);
setImmediate(read1234);
});
w._write = function(chunk, encoding, cb) {
written += chunk.length;
if (written >= 200) {
r.unpipe(w);
w.end();
cb();
if (written > 200) {
var diff = written - 200;
written -= diff;
r.unshift(chunk.slice(chunk.length - diff));
}
} else {
setImmediate(cb);
}
};
r.pipe(w);
}
// now read 1234 more bytes
function read1234() {
readn(1234, resumePause);
}
function resumePause() {
console.error('resumePause');
// don't read anything, just resume and re-pause a whole bunch
r.resume();
r.pause();
r.resume();
r.pause();
r.resume();
r.pause();
r.resume();
r.pause();
r.resume();
r.pause();
setImmediate(pipe);
}
function pipe() {
console.error('pipe the rest');
var w = new Writable();
var written = 0;
w._write = function(chunk, encoding, cb) {
written += chunk.length;
cb();
};
w.on('finish', function() {
console.error('written', written, totalPushed);
assert.equal(written, expectEndingData);
assert.equal(totalPushed, expectTotalData);
console.log('ok');
});
r.pipe(w);
}
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var Transform = require('./lib/_stream_transform.js');
var binding = process.binding('zlib');
var util = require('util');
var assert = require('assert').ok;
// zlib doesn't provide these, so kludge them in following the same
// const naming scheme zlib uses.
binding.Z_MIN_WINDOWBITS = 8;
binding.Z_MAX_WINDOWBITS = 15;
binding.Z_DEFAULT_WINDOWBITS = 15;
// fewer than 64 bytes per chunk is stupid.
// technically it could work with as few as 8, but even 64 bytes
// is absurdly low. Usually a MB or more is best.
binding.Z_MIN_CHUNK = 64;
binding.Z_MAX_CHUNK = Infinity;
binding.Z_DEFAULT_CHUNK = (16 * 1024);
binding.Z_MIN_MEMLEVEL = 1;
binding.Z_MAX_MEMLEVEL = 9;
binding.Z_DEFAULT_MEMLEVEL = 8;
binding.Z_MIN_LEVEL = -1;
binding.Z_MAX_LEVEL = 9;
binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION;
// expose all the zlib constants
Object.keys(binding).forEach(function(k) {
if (k.match(/^Z/)) exports[k] = binding[k];
});
// translation table for return codes.
exports.codes = {
Z_OK: binding.Z_OK,
Z_STREAM_END: binding.Z_STREAM_END,
Z_NEED_DICT: binding.Z_NEED_DICT,
Z_ERRNO: binding.Z_ERRNO,
Z_STREAM_ERROR: binding.Z_STREAM_ERROR,
Z_DATA_ERROR: binding.Z_DATA_ERROR,
Z_MEM_ERROR: binding.Z_MEM_ERROR,
Z_BUF_ERROR: binding.Z_BUF_ERROR,
Z_VERSION_ERROR: binding.Z_VERSION_ERROR
};
Object.keys(exports.codes).forEach(function(k) {
exports.codes[exports.codes[k]] = k;
});
exports.Deflate = Deflate;
exports.Inflate = Inflate;
exports.Gzip = Gzip;
exports.Gunzip = Gunzip;
exports.DeflateRaw = DeflateRaw;
exports.InflateRaw = InflateRaw;
exports.Unzip = Unzip;
exports.createDeflate = function(o) {
return new Deflate(o);
};
exports.createInflate = function(o) {
return new Inflate(o);
};
exports.createDeflateRaw = function(o) {
return new DeflateRaw(o);
};
exports.createInflateRaw = function(o) {
return new InflateRaw(o);
};
exports.createGzip = function(o) {
return new Gzip(o);
};
exports.createGunzip = function(o) {
return new Gunzip(o);
};
exports.createUnzip = function(o) {
return new Unzip(o);
};
// Convenience methods.
// compress/decompress a string or buffer in one step.
exports.deflate = function(buffer, callback) {
zlibBuffer(new Deflate(), buffer, callback);
};
exports.gzip = function(buffer, callback) {
zlibBuffer(new Gzip(), buffer, callback);
};
exports.deflateRaw = function(buffer, callback) {
zlibBuffer(new DeflateRaw(), buffer, callback);
};
exports.unzip = function(buffer, callback) {
zlibBuffer(new Unzip(), buffer, callback);
};
exports.inflate = function(buffer, callback) {
zlibBuffer(new Inflate(), buffer, callback);
};
exports.gunzip = function(buffer, callback) {
zlibBuffer(new Gunzip(), buffer, callback);
};
exports.inflateRaw = function(buffer, callback) {
zlibBuffer(new InflateRaw(), buffer, callback);
};
function zlibBuffer(engine, buffer, callback) {
var buffers = [];
var nread = 0;
engine.on('error', onError);
engine.on('end', onEnd);
engine.end(buffer);
flow();
function flow() {
var chunk;
while (null !== (chunk = engine.read())) {
buffers.push(chunk);
nread += chunk.length;
}
engine.once('readable', flow);
}
function onError(err) {
engine.removeListener('end', onEnd);
engine.removeListener('readable', flow);
callback(err);
}
function onEnd() {
var buf = Buffer.concat(buffers, nread);
buffers = [];
callback(null, buf);
}
}
// generic zlib
// minimal 2-byte header
function Deflate(opts) {
if (!(this instanceof Deflate)) return new Deflate(opts);
Zlib.call(this, opts, binding.DEFLATE);
}
function Inflate(opts) {
if (!(this instanceof Inflate)) return new Inflate(opts);
Zlib.call(this, opts, binding.INFLATE);
}
// gzip - bigger header, same deflate compression
function Gzip(opts) {
if (!(this instanceof Gzip)) return new Gzip(opts);
Zlib.call(this, opts, binding.GZIP);
}
function Gunzip(opts) {
if (!(this instanceof Gunzip)) return new Gunzip(opts);
Zlib.call(this, opts, binding.GUNZIP);
}
// raw - no header
function DeflateRaw(opts) {
if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
Zlib.call(this, opts, binding.DEFLATERAW);
}
function InflateRaw(opts) {
if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
Zlib.call(this, opts, binding.INFLATERAW);
}
// auto-detect header.
function Unzip(opts) {
if (!(this instanceof Unzip)) return new Unzip(opts);
Zlib.call(this, opts, binding.UNZIP);
}
// the Zlib class they all inherit from
// This thing manages the queue of requests, and returns
// true or false if there is anything in the queue when
// you call the .write() method.
function Zlib(opts, mode) {
this._opts = opts = opts || {};
this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;
Transform.call(this, opts);
// means a different thing there.
this._readableState.chunkSize = null;
if (opts.chunkSize) {
if (opts.chunkSize < exports.Z_MIN_CHUNK ||
opts.chunkSize > exports.Z_MAX_CHUNK) {
throw new Error('Invalid chunk size: ' + opts.chunkSize);
}
}
if (opts.windowBits) {
if (opts.windowBits < exports.Z_MIN_WINDOWBITS ||
opts.windowBits > exports.Z_MAX_WINDOWBITS) {
throw new Error('Invalid windowBits: ' + opts.windowBits);
}
}
if (opts.level) {
if (opts.level < exports.Z_MIN_LEVEL ||
opts.level > exports.Z_MAX_LEVEL) {
throw new Error('Invalid compression level: ' + opts.level);
}
}
if (opts.memLevel) {
if (opts.memLevel < exports.Z_MIN_MEMLEVEL ||
opts.memLevel > exports.Z_MAX_MEMLEVEL) {
throw new Error('Invalid memLevel: ' + opts.memLevel);
}
}
if (opts.strategy) {
if (opts.strategy != exports.Z_FILTERED &&
opts.strategy != exports.Z_HUFFMAN_ONLY &&
opts.strategy != exports.Z_RLE &&
opts.strategy != exports.Z_FIXED &&
opts.strategy != exports.Z_DEFAULT_STRATEGY) {
throw new Error('Invalid strategy: ' + opts.strategy);
}
}
if (opts.dictionary) {
if (!Buffer.isBuffer(opts.dictionary)) {
throw new Error('Invalid dictionary: it should be a Buffer instance');
}
}
this._binding = new binding.Zlib(mode);
var self = this;
this._hadError = false;
this._binding.onerror = function(message, errno) {
// there is no way to cleanly recover.
// continuing only obscures problems.
self._binding = null;
self._hadError = true;
var error = new Error(message);
error.errno = errno;
error.code = exports.codes[errno];
self.emit('error', error);
};
this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS,
opts.level || exports.Z_DEFAULT_COMPRESSION,
opts.memLevel || exports.Z_DEFAULT_MEMLEVEL,
opts.strategy || exports.Z_DEFAULT_STRATEGY,
opts.dictionary);
this._buffer = new Buffer(this._chunkSize);
this._offset = 0;
this._closed = false;
this.once('end', this.close);
}
util.inherits(Zlib, Transform);
Zlib.prototype.reset = function reset() {
return this._binding.reset();
};
Zlib.prototype._flush = function(output, callback) {
var rs = this._readableState;
var self = this;
this._transform(null, output, function(er) {
if (er)
return callback(er);
// now a weird thing happens... it could be that you called flush
// but everything had already actually been consumed, but it wasn't
// enough to get over the Readable class's lowWaterMark.
// In that case, we emit 'readable' now to make sure it's consumed.
if (rs.length &&
rs.length < rs.lowWaterMark &&
!rs.ended &&
rs.needReadable)
self.emit('readable');
callback();
});
};
Zlib.prototype.flush = function(callback) {
var ws = this._writableState;
var ts = this._transformState;
if (ws.writing) {
ws.needDrain = true;
var self = this;
this.once('drain', function() {
self._flush(ts.output, callback);
});
return;
}
this._flush(ts.output, callback || function() {});
};
Zlib.prototype.close = function(callback) {
if (callback)
process.nextTick(callback);
if (this._closed)
return;
this._closed = true;
this._binding.close();
var self = this;
process.nextTick(function() {
self.emit('close');
});
};
Zlib.prototype._transform = function(chunk, output, cb) {
var flushFlag;
var ws = this._writableState;
var ending = ws.ending || ws.ended;
var last = ending && (!chunk || ws.length === chunk.length);
if (chunk !== null && !Buffer.isBuffer(chunk))
return cb(new Error('invalid input'));
// If it's the last chunk, or a final flush, we use the Z_FINISH flush flag.
// If it's explicitly flushing at some other time, then we use
// Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression
// goodness.
if (last)
flushFlag = binding.Z_FINISH;
else if (chunk === null)
flushFlag = binding.Z_FULL_FLUSH;
else
flushFlag = binding.Z_NO_FLUSH;
var availInBefore = chunk && chunk.length;
var availOutBefore = this._chunkSize - this._offset;
var inOff = 0;
var req = this._binding.write(flushFlag,
chunk, // in
inOff, // in_off
availInBefore, // in_len
this._buffer, // out
this._offset, //out_off
availOutBefore); // out_len
req.buffer = chunk;
req.callback = callback;
var self = this;
function callback(availInAfter, availOutAfter, buffer) {
if (self._hadError)
return;
var have = availOutBefore - availOutAfter;
assert(have >= 0, 'have should not go down');
if (have > 0) {
var out = self._buffer.slice(self._offset, self._offset + have);
self._offset += have;
// serve some output to the consumer.
output(out);
}
// exhausted the output buffer, or used all the input create a new one.
if (availOutAfter === 0 || self._offset >= self._chunkSize) {
availOutBefore = self._chunkSize;
self._offset = 0;
self._buffer = new Buffer(self._chunkSize);
}
if (availOutAfter === 0) {
// Not actually done. Need to reprocess.
// Also, update the availInBefore to the availInAfter value,
// so that if we have to hit it a third (fourth, etc.) time,
// it'll have the correct byte counts.
inOff += (availInBefore - availInAfter);
availInBefore = availInAfter;
var newReq = self._binding.write(flushFlag,
chunk,
inOff,
availInBefore,
self._buffer,
self._offset,
self._chunkSize);
newReq.callback = callback; // this same function
newReq.buffer = chunk;
return;
}
// finished with the chunk.
cb();
}
};
util.inherits(Deflate, Zlib);
util.inherits(Inflate, Zlib);
util.inherits(Gzip, Zlib);
util.inherits(Gunzip, Zlib);
util.inherits(DeflateRaw, Zlib);
util.inherits(InflateRaw, Zlib);
util.inherits(Unzip, Zlib);
module.exports = ByteCounter;
var Writable = require('readable-stream').Writable;
var util = require('util');
util.inherits(ByteCounter, Writable);
function ByteCounter(options) {
Writable.call(this, options);
this.bytes = 0;
}
ByteCounter.prototype._write = function(chunk, encoding, cb) {
this.bytes += chunk.length;
this.emit('progress');
cb();
};
{
"name": "stream-counter",
"version": "0.2.0",
"description": "keeps track of how many bytes have been written to a stream",
"main": "index.js",
"scripts": {
"test": "node test/test.js"
},
"repository": {
"type": "git",
"url": "git://github.com/superjoe30/node-stream-counter.git"
},
"author": {
"name": "Andrew Kelley",
"email": "superjoe30@gmail.com"
},
"license": "BSD",
"engines": {
"node": ">=0.8.0"
},
"dependencies": {
"readable-stream": "~1.1.8"
},
"readme": "# stream-counter\n\nKeep track of how many bytes have been written to a stream.\n\n## Usage\n\n```js\nvar StreamCounter = require('stream-counter');\nvar counter = new StreamCounter();\ncounter.on('progress', function() {\n console.log(\"progress\", counter.bytes);\n});\nfs.createReadStream('foo.txt').pipe(counter);\n```\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/superjoe30/node-stream-counter/issues"
},
"homepage": "https://github.com/superjoe30/node-stream-counter",
"_id": "stream-counter@0.2.0",
"dist": {
"shasum": "386414bae32938a8f7a9b6d94545a03d2260f606"
},
"_from": "stream-counter@~0.2.0",
"_resolved": "https://registry.npmjs.org/stream-counter/-/stream-counter-0.2.0.tgz"
}

stream-counter

Keep track of how many bytes have been written to a stream.

Usage

var StreamCounter = require('stream-counter');
var counter = new StreamCounter();
counter.on('progress', function() {
  console.log("progress", counter.bytes);
});
fs.createReadStream('foo.txt').pipe(counter);
var ByteCounter = require('../');
var fs = require('fs');
var path = require('path');
var assert = require('assert');
var counter = new ByteCounter();
var remainingTests = 2;
counter.once('progress', function() {
assert.strictEqual(counter.bytes, 5);
remainingTests -= 1;
});
var is = fs.createReadStream(path.join(__dirname, 'test.txt'));
is.pipe(counter);
is.on('end', function() {
remainingTests -= 1;
assert.strictEqual(counter.bytes, 5);
});
process.on('exit', function() {
assert.strictEqual(remainingTests, 0);
});
{
"name": "multiparty",
"version": "2.2.0",
"description": "multipart/form-data parser which supports streaming",
"repository": {
"type": "git",
"url": "git@github.com:superjoe30/node-multiparty.git"
},
"keywords": [
"file",
"upload",
"formidable",
"stream",
"s3"
],
"devDependencies": {
"findit": "0.1.1",
"hashish": "0.0.4",
"mocha": "~1.8.2",
"request": "~2.16.6",
"mkdirp": "~0.3.5",
"superagent": "~0.14.1"
},
"scripts": {
"test": "ulimit -n 500 && mocha --timeout 4000 --reporter spec --recursive test/test.js"
},
"engines": {
"node": ">=0.8.0"
},
"license": "MIT",
"dependencies": {
"readable-stream": "~1.1.9",
"stream-counter": "~0.2.0"
},
"readme": "[![Build Status](https://travis-ci.org/superjoe30/node-multiparty.png?branch=master)](https://travis-ci.org/superjoe30/node-multiparty)\n# multiparty\n\nParse http requests with content-type `multipart/form-data`, also known as file uploads.\n\nSee also [busboy](https://github.com/mscdex/busboy) - a\n[faster](https://github.com/mscdex/dicer/wiki/Benchmarks) alternative\nwhich may be worth looking into.\n\n### Why the fork?\n\n * This module uses the Node.js v0.10 streams properly, *even in Node.js v0.8*\n * It will not create a temp file for you unless you want it to.\n * Counts bytes and does math to help you figure out the `Content-Length` of\n each part.\n * You can easily stream uploads to s3 with\n [knox](https://github.com/LearnBoost/knox), for [example](examples/s3.js).\n * Less bugs. This code is simpler, has all deprecated functionality removed,\n has cleaner tests, and does not try to do anything beyond multipart stream\n parsing.\n\n## Installation\n\n```\nnpm install multiparty\n```\n\n## Usage\n\n * See [examples](examples).\n\nParse an incoming `multipart/form-data` request.\n\n```js\nvar multiparty = require('multiparty')\n , http = require('http')\n , util = require('util')\n\nhttp.createServer(function(req, res) {\n if (req.url === '/upload' && req.method === 'POST') {\n // parse a file upload\n var form = new multiparty.Form();\n\n form.parse(req, function(err, fields, files) {\n res.writeHead(200, {'content-type': 'text/plain'});\n res.write('received upload:\\n\\n');\n res.end(util.inspect({fields: fields, files: files}));\n });\n\n return;\n }\n\n // show a file upload form\n res.writeHead(200, {'content-type': 'text/html'});\n res.end(\n '<form action=\"/upload\" enctype=\"multipart/form-data\" method=\"post\">'+\n '<input type=\"text\" name=\"title\"><br>'+\n '<input type=\"file\" name=\"upload\" multiple=\"multiple\"><br>'+\n '<input type=\"submit\" value=\"Upload\">'+\n '</form>'\n );\n}).listen(8080);\n```\n\n## API\n\n### multiparty.Form\n```js\nvar form = new multiparty.Form(options)\n```\nCreates a new form. Options:\n\n * `encoding` - sets encoding for the incoming form fields. Defaults to `utf8`.\n * `maxFieldSize` - Limits the amount of memory a field (not a file) can\n allocate in bytes. If this value is exceeded, an `error` event is emitted.\n The default size is 2MB.\n * `maxFields` - Limits the number of fields that will be parsed before\n emitting an `error` event. A file counts as a field in this case.\n Defaults to 1000.\n * `autoFields` - Enables `field` events. This is automatically set to `true`\n if you add a `field` listener.\n * `autoFiles` - Enables `file` events. This is automatically set to `true`\n if you add a `file` listener.\n * `uploadDir` - Only relevant when `autoFiles` is `true`. The directory for\n placing file uploads in. You can move them later using `fs.rename()`.\n Defaults to `os.tmpDir()`.\n * `hash` - Only relevant when `autoFiles` is `true`. If you want checksums\n calculated for incoming files, set this to either `sha1` or `md5`.\n Defaults to off.\n\n#### form.parse(request, [cb])\n\nParses an incoming node.js `request` containing form data. If `cb` is\nprovided, `autoFields` and `autoFiles` are set to `true` and all fields and\nfiles are collected and passed to the callback:\n\n```js\nform.parse(req, function(err, fieldsObject, filesObject, fieldsList, filesList) {\n // ...\n});\n```\n\nIt is often convenient to access a field or file by name. In this situation,\nuse `fieldsObject` or `filesObject`. However sometimes, as in the case of a\n`<input type=\"file\" multiple=\"multiple\">` the multipart stream will contain\nmultiple files of the same input name, and you are interested in all of them.\nIn this case, use `filesList`.\n\nAnother example is when you do not care what the field name of a file is; you\nare merely interested in a single upload. In this case, set `maxFields` to 1\n(assuming no other fields expected besides the file) and use `filesList[0]`.\n\n#### form.bytesReceived\n\nThe amount of bytes received for this form so far.\n\n#### form.bytesExpected\n\nThe expected number of bytes in this form.\n\n### Events\n\n#### 'error' (err)\n\nYou definitely want to handle this event. If not your server *will* crash when\nusers submit bogus multipart requests!\n\n#### 'part' (part)\n\nEmitted when a part is encountered in the request. `part` is a\n`ReadableStream`. It also has the following properties:\n\n * `headers` - the headers for this part. For example, you may be interested\n in `content-type`.\n * `name` - the field name for this part\n * `filename` - only if the part is an incoming file\n * `byteOffset` - the byte offset of this part in the request body\n * `byteCount` - assuming that this is the last part in the request,\n this is the size of this part in bytes. You could use this, for\n example, to set the `Content-Length` header if uploading to S3.\n If the part had a `Content-Length` header then that value is used\n here instead.\n\n#### 'aborted'\n\nEmitted when the request is aborted. This event will be followed shortly\nby an `error` event. In practice you do not need to handle this event.\n\n#### 'progress' (bytesReceived, bytesExpected)\n\n#### 'close'\n\nEmitted after all parts have been parsed and emitted. Not emitted if an `error`\nevent is emitted. This is typically when you would send your response.\n\n#### 'file' (name, file)\n\n**By default multiparty will not touch your hard drive.** But if you add this\nlistener, multiparty automatically sets `form.autoFiles` to `true` and will\nstream uploads to disk for you. \n\n * `name` - the field name for this file\n * `file` - an object with these properties:\n - `fieldName` - same as `name` - the field name for this file\n - `originalFilename` - the filename that the user reports for the file\n - `path` - the absolute path of the uploaded file on disk\n - `headers` - the HTTP headers that were sent along with this file\n - `size` - size of the file in bytes\n\nIf you set the `form.hash` option, then `file` will also contain a `hash`\nproperty which is the checksum of the file.\n\n#### 'field' (name, value)\n\n * `name` - field name\n * `value` - string field value\n\n",
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/superjoe30/node-multiparty/issues"
},
"homepage": "https://github.com/superjoe30/node-multiparty",
"_id": "multiparty@2.2.0",
"dist": {
"shasum": "504f139df8a89c7e6bc65417f8cb73b37f047a18"
},
"_from": "multiparty@2.2.0",
"_resolved": "https://registry.npmjs.org/multiparty/-/multiparty-2.2.0.tgz"
}

Build Status

multiparty

Parse http requests with content-type multipart/form-data, also known as file uploads.

See also busboy - a faster alternative which may be worth looking into.

Why the fork?

  • This module uses the Node.js v0.10 streams properly, even in Node.js v0.8
  • It will not create a temp file for you unless you want it to.
  • Counts bytes and does math to help you figure out the Content-Length of each part.
  • You can easily stream uploads to s3 with knox, for example.
  • Less bugs. This code is simpler, has all deprecated functionality removed, has cleaner tests, and does not try to do anything beyond multipart stream parsing.

Installation

npm install multiparty

Usage

Parse an incoming multipart/form-data request.

var multiparty = require('multiparty')
  , http = require('http')
  , util = require('util')

http.createServer(function(req, res) {
  if (req.url === '/upload' && req.method === 'POST') {
    // parse a file upload
    var form = new multiparty.Form();

    form.parse(req, function(err, fields, files) {
      res.writeHead(200, {'content-type': 'text/plain'});
      res.write('received upload:\n\n');
      res.end(util.inspect({fields: fields, files: files}));
    });

    return;
  }

  // show a file upload form
  res.writeHead(200, {'content-type': 'text/html'});
  res.end(
    '<form action="/upload" enctype="multipart/form-data" method="post">'+
    '<input type="text" name="title"><br>'+
    '<input type="file" name="upload" multiple="multiple"><br>'+
    '<input type="submit" value="Upload">'+
    '</form>'
  );
}).listen(8080);

API

multiparty.Form

var form = new multiparty.Form(options)

Creates a new form. Options:

  • encoding - sets encoding for the incoming form fields. Defaults to utf8.
  • maxFieldSize - Limits the amount of memory a field (not a file) can allocate in bytes. If this value is exceeded, an error event is emitted. The default size is 2MB.
  • maxFields - Limits the number of fields that will be parsed before emitting an error event. A file counts as a field in this case. Defaults to 1000.
  • autoFields - Enables field events. This is automatically set to true if you add a field listener.
  • autoFiles - Enables file events. This is automatically set to true if you add a file listener.
  • uploadDir - Only relevant when autoFiles is true. The directory for placing file uploads in. You can move them later using fs.rename(). Defaults to os.tmpDir().
  • hash - Only relevant when autoFiles is true. If you want checksums calculated for incoming files, set this to either sha1 or md5. Defaults to off.

form.parse(request, [cb])

Parses an incoming node.js request containing form data. If cb is provided, autoFields and autoFiles are set to true and all fields and files are collected and passed to the callback:

form.parse(req, function(err, fieldsObject, filesObject, fieldsList, filesList) {
  // ...
});

It is often convenient to access a field or file by name. In this situation, use fieldsObject or filesObject. However sometimes, as in the case of a <input type="file" multiple="multiple"> the multipart stream will contain multiple files of the same input name, and you are interested in all of them. In this case, use filesList.

Another example is when you do not care what the field name of a file is; you are merely interested in a single upload. In this case, set maxFields to 1 (assuming no other fields expected besides the file) and use filesList[0].

form.bytesReceived

The amount of bytes received for this form so far.

form.bytesExpected

The expected number of bytes in this form.

Events

'error' (err)

You definitely want to handle this event. If not your server will crash when users submit bogus multipart requests!

'part' (part)

Emitted when a part is encountered in the request. part is a ReadableStream. It also has the following properties:

  • headers - the headers for this part. For example, you may be interested in content-type.
  • name - the field name for this part
  • filename - only if the part is an incoming file
  • byteOffset - the byte offset of this part in the request body
  • byteCount - assuming that this is the last part in the request, this is the size of this part in bytes. You could use this, for example, to set the Content-Length header if uploading to S3. If the part had a Content-Length header then that value is used here instead.

'aborted'

Emitted when the request is aborted. This event will be followed shortly by an error event. In practice you do not need to handle this event.

'progress' (bytesReceived, bytesExpected)

'close'

Emitted after all parts have been parsed and emitted. Not emitted if an error event is emitted. This is typically when you would send your response.

'file' (name, file)

By default multiparty will not touch your hard drive. But if you add this listener, multiparty automatically sets form.autoFiles to true and will stream uploads to disk for you.

  • name - the field name for this file
  • file - an object with these properties:
    • fieldName - same as name - the field name for this file
    • originalFilename - the filename that the user reports for the file
    • path - the absolute path of the uploaded file on disk
    • headers - the HTTP headers that were sent along with this file
    • size - size of the file in bytes

If you set the form.hash option, then file will also contain a hash property which is the checksum of the file.

'field' (name, value)

  • name - field name
  • value - string field value
var assert = require('assert')
, Form = require('../').Form
, boundary = '-----------------------------168072824752491622650073'
, mb = 100
, buffer = createMultipartBuffer(boundary, mb * 1024 * 1024)
var callbacks = {
partBegin: -1,
partEnd: -1,
headerField: -1,
headerValue: -1,
partData: -1,
end: -1,
};
var form = new Form({ boundary: boundary });
hijack('onParseHeaderField', function() {
callbacks.headerField++;
});
hijack('onParseHeaderValue', function() {
callbacks.headerValue++;
});
hijack('onParsePartBegin', function() {
callbacks.partBegin++;
});
hijack('onParsePartData', function() {
callbacks.partData++;
});
hijack('onParsePartEnd', function() {
callbacks.partEnd++;
});
form.on('finish', function() {
callbacks.end++;
});
var start = new Date();
form.write(buffer, function(err) {
var duration = new Date() - start;
assert.ifError(err);
var mbPerSec = (mb / (duration / 1000)).toFixed(2);
console.log(mbPerSec+' mb/sec');
});
process.on('exit', function() {
for (var k in callbacks) {
assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);
}
});
function createMultipartBuffer(boundary, size) {
var head =
'--'+boundary+'\r\n' +
'content-disposition: form-data; name="field1"\r\n' +
'\r\n'
, tail = '\r\n--'+boundary+'--\r\n'
, buffer = new Buffer(size);
buffer.write(head, 'ascii', 0);
buffer.write(tail, 'ascii', buffer.length - tail.length);
return buffer;
}
function hijack(name, fn) {
var oldFn = form[name];
form[name] = function() {
fn();
return oldFn.apply(this, arguments);
};
}
�PNG

IHDR$$���tEXtSoftwareAdobe ImageReadyq�e<IDATx�̘{HG�g��#pj���Ĉ�����M5D� ��W��?b�PhӢi�[i)�H-�<H$TZ�I�z��Mz�|��*�+��zV���f�����y�:�s����^�}���lH�ҵ�ܯ0�O�$�i����?T8i� LT��$�Ɏ�P��m����9a��?�^
���'��44!���j�h����LII���zU>�O����?>>~�رcw`�qs����>&��A0����eOEE�ɨ���*�JO�HA����YYXX0vww�wttX`y�����r
��`�0x�MMM�����)A(-˕��Ǝ[�n=�%��¹EPj31��_�p!����磣�K�:�G<ຬ���������ݻg�9�&_�1�%0/^�---���tI�Â����ž-(((}��ihh��E�O�Oj��������744�F����tyyy9f�y`~~��E�����C�ot ���gB��5�����~�K � ��ى?�!�mmm�_�F�iO��Gzzzyeee
L#�5�E VD¼����va��KAR�}�i���LsX��"� ?���P����"��K�.ek4���Ɔ�Qo�Y1����<B�A������ȸ'N�m2��8S��9���"�c����}D��8�� ݽ=E��V���S�_�n߾}�F�iH���^��?�.�Q��,Jz/����Q�7Ps��|�^=�`V�?�E__���L@� ]3��!�����M���& 86\$)���$ �s�e�b~*hb��n�"PF�.t��wp�8����d��W2�
Ѫu�޴��|(a4�͖i�?���|nz�D��O���2 �LhyɁ�kr���I��� Dֆ*onn�Rh�1M�rC;9�xa���^������=���~����W�̏xz���h?�����O��8s挑��:������|����������'��x�u�@Z�=�ag6i��D �n r�����폡�{ ��,��ĭ�vZ�󺡆�
� r8�f�� �Ӊ�n7��P����O�b-Pުey��BSSS����j��a U X��EP�5x){]]��P�`�ب����S�j���I�C�V!��ಁi�����`P���h����e�06f*�)�� �aOO�������������(����*�r�\D�|G�ԠQ���i��`�ZY�㖛L�}�ggg����&И1;;��N���0�+�@�Z��0E�Y�V Tg�TV
c�j!{�Z��D\_�Y5Bq5���������0�-�W��T����1WUU��������H���� nhmoo� ��xZZZbBB�N ô�a�R@���������/e�a���`��� ~��Ф��E�U�X�������4@ݽ�k `q�� \mkk� �]��s��Z!�V@�zC�H��*;�U*���B�X��;1::�D��E�������W�u��۫9��`�Tt� �6��u�k�d��sUj�y3�` N�����>%��~RS�
u~+�Ⱦ����^�4_�$�d@���>���xA/:<IEND�B`�
�h�!O���n�0`�}���e��D1�ދ8j��V3c|�� �mYve�-�wS���@�ʽ�u�I�D�,��̗b�v��*�2�b!2��>���U�Ľ4ut^Y�WZ]������G]f��FՔ>�t��ZW�8P��Y�K� ��ӊZ2�R� �����^>B
[2�p�3�.�!�hSi�ǒ�!�)��n��gC~�G�5����.��΢-����q�El��x1��J�1��3�[����~���W��ĺ�q��6ӽLSu���]�/���ċ�(
�PNG

IHDR_���tEXtSoftwareAdobe ImageReadyq�e<"iTXtXML:com.adobe.xmp<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.0-c060 61.134777, 2010/02/12-17:32:00 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CS5 Macintosh" xmpMM:InstanceID="xmp.iid:07183A72DD7211E1AA8EE44390402C24" xmpMM:DocumentID="xmp.did:07183A73DD7211E1AA8EE44390402C24"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:07183A70DD7211E1AA8EE44390402C24" stRef:documentID="xmp.did:07183A71DD7211E1AA8EE44390402C24"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>k�m�IDATx�bq����bb��K��V��ZIEND�B`�

Connect build status

Connect is an extensible HTTP server framework for node, providing high performance "plugins" known as middleware.

Connect is bundled with over 20 commonly used middleware, including a logger, session support, cookie parser, and more. Be sure to view the 2.x documentation.

var connect = require('connect')
  , http = require('http');

var app = connect()
  .use(connect.favicon())
  .use(connect.logger('dev'))
  .use(connect.static('public'))
  .use(connect.directory('public'))
  .use(connect.cookieParser())
  .use(connect.session({ secret: 'my secret here' }))
  .use(function(req, res){
    res.end('Hello from Connect!\n');
  });

http.createServer(app).listen(3000);

Middleware

Running Tests

first:

$ npm install -d

then:

$ make test

Contributors

https://github.com/senchalabs/connect/graphs/contributors

Node Compatibility

Connect < 1.x is compatible with node 0.2.x

Connect 1.x is compatible with node 0.4.x

Connect 2.x is compatible with node 0.6.x

Connect (master) is compatible with node 0.8.x

CLA

http://sencha.com/cla

License

View the LICENSE file. The Silk icons used by the directory middleware created by/copyright of FAMFAMFAM.

express logo

Fast, unopinionated, minimalist web framework for node.

Build Status Gittip

var express = require('express');
var app = express();

app.get('/', function(req, res){
  res.send('Hello World');
});

app.listen(3000);

Installation

$ npm install -g express

Quick Start

The quickest way to get started with express is to utilize the executable express(1) to generate an application as shown below:

Create the app:

$ npm install -g express
$ express /tmp/foo && cd /tmp/foo

Install dependencies:

$ npm install

Start the server:

$ node app

Features

  • Built on Connect
  • Robust routing
  • HTTP helpers (redirection, caching, etc)
  • View system supporting 14+ template engines
  • Content negotiation
  • Focus on high performance
  • Environment based configuration
  • Executable for generating applications quickly
  • High test coverage

Philosophy

The Express philosophy is to provide small, robust tooling for HTTP servers, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs.

Built on Connect, you can use only what you need, and nothing more. Applications can be as big or as small as you like, even a single file. Express does not force you to use any specific ORM or template engine. With support for over 14 template engines via Consolidate.js, you can quickly craft your perfect framework.

More Information

Viewing Examples

Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:

$ git clone git://github.com/visionmedia/express.git --depth 1
$ cd express
$ npm install

Then run whichever tests you want:

$ node examples/content-negotiation

You can also view live examples here:

Running Tests

To run the test suite, first invoke the following command within the repo, installing the development dependencies:

$ npm install

Then run the tests:

$ make test

Contributors

https://github.com/visionmedia/express/graphs/contributors

License

(The MIT License)

Copyright (c) 2009-2012 TJ Holowaychuk <tj@vision-media.ca>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

�PNG

IHDR�)��`bKGD������� ��IDATx��_�5ϓԟ~�ew�������.��E�HXI�*!x���&�� �z���'4Q�1�`�$&D61 b�(� %Y���&*"����������̟���93�9�y�~�o�����鮮�����4����7�� ���Hr:��C��S�獮�����c��\,}Mb��<h��t+����J�^}�����������b�%�[W�W�����M0�� �߿� M��P�Lm�; �t�+4>� ��Z?�(�6y�_�X9��%2��+��f���k�t2�WI_mw�P��l�| d��1$��O��勱�1Js��tM�����?��e<Z�o��m^0ȃ6_h��v�P�P~Hf���٨����K��O�k��Y��Y���4�'�O��TY� �IF}b͟����>_
m!�9��?�J؞��F����r��>ou ���VՇ���[�8ׄX=)�Uu�k�IЧ�1��i�~y�k��Qߞbv��sΐ֜[�BضvѬo �m|�g�5Oۛo����_���������'��L�!5z#��+�yN����W쟉�e��u
���?&������r��}Ì8��X��#��g���XO�_y&�$���JA)�}�KW�IJ㿱3�*w��F��Ħ��Y��iz���'��n�z�K��|(v���_�'�)�Z�j'�Y���Qӑ,�)��Si��\� �����t�n�����œ\��r���/R��r�[����i�p�E��ϒ$�?M�LC���ukͮ/����������|%�?������達��H�����Sß���۳���Rv�U���� ԧ�9��/9��`u�uۥ�Bs�����&$��a�(��/����Y'A����T�&�HN'���w}�r�����ҧvU^�*#�!�7��Y(5����j%���<|�u�;�������^�$mm k�������C����`�y��}X+^V����ɚˊj�=���{?��4�&��;˼:����!�c���]��MO�z.h ��7�t�^���t�3�oc�,�_t�Ӏ�=�� O��s��O�''''ׇ㛝�Ӧ��$-�����-�y <_5]!?�/�!8`��I�,҂#텡�' /�?0��TcE��8 �B�\d�+`���k��WX�pi~�>9T����r��ay�#[c]���{����?�E��SK9a�qL��t����<�:{�}�7�/�rG��� sH_d�L��"��Q�M��I�� �w��+�L�xG����p��<��8�'Ϛ/V��K����_�Y�_*�jb���>#O���� �����]-vLWܡID�|m�
v;;_Yt��*=~!��/�u�9��Ӎ룀ΐ��y���)���� �Z{�z^�h�O'�p�������tl��Wk������y��7���y_'1V2�p ��۝WAr��v��I}!��Cr"j�~N\�O)^ݶғE"��v�\�wb��v;N��_�s�_��z��<yt𕹓���#��GE�P#��1v<�0���vM��u���&���6�5��kv��>"�z�F�����&۸C<Կm�il���s, boϥE(���O�X��'�O�"T��S�5d'��X9[���ӱNSd3-q�����j�89J��� ]z�� k� �!mN���x��F��8�5 �3����H�f4,z�90�۱�1:���D� ���<�c�1/u�d�Ø�m*�
��Bz�@�(�(�%����T�����%=�j�U�5��3?���%�)|^��e~\�ߔ�b��v -�� ��%��"���������2>JGvrrrr:{v1�_�v:\��"��/�. �j�b��H�ydvI��� f^�Ϫ��Io�>h!'Ɏ��uaӦ�WԶ�ә$������$��=d�����#t`�ߕjtc�,��0z���f-z7����Yh݃.�� ��%){V>z�.4����vzͩdlD�l��/m��l�5=��"k���5�؋��T݇W�sH����`�[�嬙�_�� k�n�� ]�_<���#{�L�OX��[{��������qzg��~|��sx<v��u�~��ۮ4>��מ6�\>�Eq�!���D{%֬��᭙>ft���V�.�D��'��w\��젂浚�i�$��X<5�ָ�6�a�d#�Lf�k���OS4tG鐷�������fzǕ��-] �Ҹ�N�M����}��r9����+��|y���”N�.Y��v*�&�� ��낫���
.y7���2̀��� �j�px�W�����q����gs�x}S��j����=�wN�z~c�엤_�>9��a^x[�l����3z��lzF/�L���=h�{a�_2��{�G�>tO~�#�l���������A?��!j���F�;X�`�7�g���VUBy��߆m���|���Ѳ����Ӌc�Y��E�ׇi������$��l�=�E�J��I�A���C?ՃZ��ބ�X��Z�c�Z�ɲ����Cpt?X>�䓓 88�5�)ƚ����E�����ij{��g�-�ޙ߭O��7;��*�k�|e�Z
���NNNNNN/��y�P'''���^��w����m4tT��9Ѥ��
E
�?��́��̋4͔�Sۻf0N��z��Ä�L[}R���s�����h� �z��$��^��� �#XP^sS�hBb����;�o�+t�p=�^Srw���� {Q�2D����t�7���&#oQ�O��t}���>ڠ�a��H�S�2Ͽ��Tk׋�;�������^��= ڑ�����=몯O�G�9��H ��B��X�9E����1���У���k2��\����'�J�u�n���yJ~dž~gi�5���>2r
� 7������wOC��Q�齹WN {R�Ǹ����/I�vrrrrrz&C�ecs�1 �/f�pz����g_=HN�t{�0@�7|VGj ȿ������g���:ޭ�{��9遳䳓����,�(�ҿV��?��4��Hf>'�W%�Ev����s�����䜧�Ȋ�Ncb5��F���c�߶g7�8k2�$.�6�Ѝ��Z���3�� �t�XU2i�xE_)�B4)�SՏP�F��-��1�����F��f�~�Ȥ�/rP�.��`��Eĕ��
�w��><Gh�B-�G��ž�B/�����o�;~�X�Y�����hn����J�mzҤ-�j������z a_����������"� ���t''''�J�Q���Kb/>gsa�tv���g*ޏ��`�A'����9GJ@�m�vS�݇��̣�%%຿p��������Y�Y�]�h'''''�g2��h�s}��q,��S� 0G�x��x�1C�m=��#�����E@�|�ҿV=)���Ă�>��#�x�� �>槤3�i�D�~�b������:�$�l���<����l���4⬽����>�Y��
���<��%#��C������u}[-���%'��9�m}<�W>p�[�����`�1�i�:�a}�N 8rFIf�֤c��?c��>���!�O__u�����3@j�OD^��_W/��6��']��x�?rP���{�w}�@3�;��� �����2r}J~����x�XQ�������� �p���;\�s����u٘���.��c�����]i��|S��c&�A�p!�+HORmE��(H�_[���O����W[l��R[��w¥i���<Is�Ѿ<�z }��c�� �����Ji�MX��#����R�)� =�����Af�cΰ�H�k���Җ0}d�)������Y��Y�>����0���� Z>;��>Z�Մ���D}S=���SS,�_T�@1-�R1�θO�ؽݾ"i�!�����`O���bW����4v}�R���x�?�N�t��~��Wf�� �~�z9�b��\ �7��t禆�/�-Z�rz\@��������W����>����Oz�M%iL�����r�Dҩ�K�}1�BJ�Q�л%����X�W�W��@F=o� s�R�'>GZ���2_O���~�����-J��V,�}0�eٹ���nT����o�3�pY��/ �7�qS]���ӡk����ᐂ]c:D'�/���<Eu\U�X��j� �ٔub�����$LV�83��6�x�` �L���_�R\�]�V�m_]NI2���i�'�M��"�nO�$ b5����ǮDɤ?m&=*�ۀ�@��7�ӌ�[�2y$U�t���؉��7�qcM���&7�i��F2)'�!Љ��S�1�OB�J!��a1pd�������b�"�eX��m�F�O�m��ٽ����%�s���P�!h���a��X���<� �vK�-@96n�)�Es��|8�9b.e�yx������ZfF�NIv@HUG.Hֵl��ĥ �X��S�ud�lͼ>��[��0GB���X-jZ2��lYY��S?��64�h*�k�z�%��ZFH���Dm�Tu�5g��K� RjKz���FR��w'$�8������Z�)�D�L��['n�����������6m9�?l��z!��i�f�ս��.\׀���֫����j�j�I.K�M�4���1��8͵�!�#�-�t*���7�50 ���|�XW��:;̖�d���ˆ%}�uB Y[��3|ui_�r�z������ b/J�qft\a�f�.$��Ua���� �^���JR���M+(�_wlr'��?:�b7�;��Y�1�1_Ѥ{���:�.l����곯���5���� �jr@��yNo�S�?��!��'� �qoˆ�˜�u�1$���������~�Ÿ�}ϫ��V����O~MM��C�����-�U}R�Ǯ��Hh�P�Sy��v��f�a�U�ӱhk��sM~hp���Ҧ�����b!���h��� ���L�E �3�D�7��L=ݪ3��cW��
n4{2��%?M ��ӵ�Z"X�Ën�bq�d���e�z��30��d��x+d_�:t S es{h<N�A�/:��qZ|:�̆Y�o,�f���J��v�[�W���
�h玿V������"�͗�=�7GԮw��x�#~!xe��4^��!���!�Bظ���h��2�ʷ�z�OqZo����`�Vn�>h`گ��K������8�I�)+NV�P}P?'� �%)��Z��ǬV�A�u�B7��.uW�� �<Ԩ B]}j5Id��⯙���~��G&�s's��-4����뻹KU���5�Tʩ�Xgn��:Lr��q��5�S�� C��R��դA�c�h��b�Y\�iŚb)Ӟ���z��
���õ *;���m�m�u]�f3)O&|�g�x��_���Ɵ��l>� \ ^P���z�ď�/���b����2�����F�묖����Z�ɳXK�a��,��Mػ>���w"�t�����]!���.Y�j#ivH�Pcф����1'(��G� b9#Q^tW�z��w ���\���g~�w���a ��� �R07�H�����Ʋi��p��^�����@��\�˦K}M3
��#�Z5�S1Ӂv�_U�6�@��ѫ`;w��v��������� 9_��e'�'�N�_N �K g~��m���w��{��OU���u������?�0^�U�qIE��R>�P�K���u�gK�g� ��Y����������/�������N�W������9}���u��_3q��� ���b����O�<��)B"`cw��zk�C{� �Qa��_��3�g��s��������W��|�2T)�3�+�%��"�Z�+�-�3f��j[�q�`p<�X3�f�XNm<�,?���r���``x�Z����t*H �u���6z���S>3 (4B���m�&a���®�:��
�� >�� ?1,O)�L�h���e��R� tD�Y%�}�w6���:в��.$�ĀF�EA��:�ՙ���Y���XM�B��0��PS����g���؉����m�֡ne��>O�o�X��M�׀[i�� ��[��ӱB{ ��l� �1#'�z�+�Cy�����U��N2���4S�� fl�w�������+� +��X��|''��Y��Q���N%
�s!V(����2�C�iq���Ծ%��"W?./ٜ����S,s��"KV��R��D�g�nW�C�Aج��s����Ey=�s4��*��8¬}�e��|
�v��ז�n�χ㜵�x����G:p_��߱�o]��u�;�������YҏRD�r>��1g�<�ϡ��k��)*��������g�����SF@�1��G@+�������3�S�����|:�6(Y>5����&�6:���_{1�iI��=�o�����m=�E�Q��?�ji>p/z+s.���iI�����,;ƌ:�-�A(�$��rB��lֆ�k�;��'/(�~k��R����"rY���Ά�����I�ɵV���`V��Q�e;_�ۍ��-pz����7����W�UϴaڬB�MG>�PȯX����Ii�)z�m�YDv%�c���/�}2sV�D�zu_�Q�� �w���������
=�����F������i��R�m��>\ȱ U�-
�3{�o.��wI��#�U?;�6o �c _��g՗ˁ8��e)��0�l)��T�s�Yd�A�1u2%�Ç�~� �_�[�e��1�c4&���f�9^���M�>U���;��kN,=���ow��[�����)(K䓯� `�����^|6����5�9���|�ŝ������ ����*�kh�*��c�f]~Ҹ�F4�ŋ��4��^4#x�x'����r�sJ>k������x���`�|آ�" ��E5�ʎ,]���%�≠��Xi[Z�.�b�Dz��k����R�6�T��óQ����5�5��Y�нD��� ���7F'˼��1��+�L|}#�l+��w��\j�ي���.*��Fͻ�OE4=�m�"��1�[���O�c��
���i�6��t�2��\�����ʆ�r9R���=��v�����i&Q�?7/����>0M�q�H=�C��G��ܹ�y�۳�x�,�"��_��=.�����{u�Y�}����xQ��k�B{����:�w"�����;���؝~�/�z��� �������l]�AL��cu@?Y�£��/�-���߻��nP2�O���R�#�"T8{ם��������A���rPgFs^���y��]�W�>���m_0�n<��d�o�9p��5BĨ���B,�tp�ݢ�"�{� 8R��h��$��oT�so� /jf���ԡ)��eέ�]���D��B���.�5y�ko�l��Mky���0��;�� ժ��NNNNNN���22�O�� 8999���eힺ�s(��ج�ۈ����-���hoG����Lե^<j�-���Dg ���ͱ�>��2B$��*X�qZ�<�� X�a�^k�_�,.�c�-Fx��J MI�T�F+��X���� V� e�A�9�?���q�z�c�����Sd���R��� A���{�\�u�7��0E�`NNNNNN�PWbo���NNNNN�Ȼ�w<{��v�Ю� %���/�Io��TD)U�y�W��������0�cf�U1^<��q�D � /�Kϱ?k[/��q�z��獍卜���v��� �$��vrrr��F�;�>g��Z������/�~�h�l~�7/� u��1H ��Y�*v��Q�Q�m��_o���?��b�:�������7������5���S�*���5���\궨C 6�sىը/՞H�ELG��5};T0J��A���([��d�`Ȱ��fۡv 64���M�x^#*��3t��h�|����H�d�R!(�0���`����(C.w��$f�@6T(X�S��0Y*�T������t��Ӣ�ǒWT�9g�n�}�+v����D%UM���q3ؗqO�;W�.����)�rV��l�,=Dʚ�f͵�d@�*���`J�t��E�0Z������]X��|x��f)?���gK��,(�/�����y1T�+�k*[�_zօ;�������>,����Oȇ�~''''�����T:���rķ�H��W���"q�����QV0q�YR*�4ˊ�Jwwu�Kl�W�?�aƄ
��.�=�O�X���s<� i���9��Ee3lPҁ�A>�"��:}>�4�.��&�����qq9�WIG\�Fc���`7V��{�'w@;999}�U�5��9=�����;�z �9�bt�������#���{����9�#�� �!�l������޾��wR(��/3v3�ݟ��?��l�������hf�ZJN�k#��ҫ]������9�ɉ���D� "���Ŧ�1x2வd���* �y��Y������=�s�"cN�S=�!=�b��h���%�^���K�'�o�~��szo/B��-�@y ����[���ˁM����>:~�бƻ�9t<�Y� #'?��P��1si�K�N�ٿ���9����t�"ʖ�K��g��R��ݽ~:g;GVVz��x���䳃"�YQ����md�)��Pz����|�^8/�O�a~%'�5��뮦�c,2��fo�^�!��]���\uѺp#�Լ���� �`�����0&��DZ�M5n�ߔ�i��C�}nթ�`u���p���|� U�0Sѥ;=|�|�?E�;���Ap�F�#G�y�b����M�džM��ueG�
o��)��R�#� �7�/�����������E|��I:R��о��/�o?�Y�O_5i��aJ�n͢�Ԩ�t�r�2m����W��NV� �m�'�NW�^���?h�iڬůl; �wjtZ�b����W�,�s
T�t��W#wBu�h�BY�?��\�|�?@m�.9�\g8�e�K`;B�)�y��i���[���k�Nh�N�)��z;"X�~�u�֨�0�#�!����ӑA����3�7�+������߆k����9�;O�\�L7�Ϯ�\ 5����}��;D�? 9}��D��gِ9���9�;4�]�Ϳ�Ώ�Z�b*u�~H�L���7�&e �R/�ӬnA�5����������H´״qL��T�Qz*��՞ٛ/*�C�I�t�TG��[Y�OP�4:,��4>�L��d�f~}�'
���j�`��8�B��?�����L�E�b��i�̿��~9�yT�R�r}�9�������zY q����Yfelu,�T�ġY��-͡i�e K�3bn65���RZ�V'A��6I��vi}_�-�E.��b�/���B��E!�p�c���`������V�,S a�>��!�z��Z���[�6��YN��C˘����3�:�4UjoAR��׸3l͒}\� ��h��skḛ`��5�p:D�ii�H�!�݉�����q����AgCb �FZ�z�O�38Q%$����������+Hmz����������M��Ԥ�*�ּ��'m��y�-��.��/�U{N� ����:��3?S�^�-">���|-ҩZ�-��i�����r������ȴ��l�`u@�%Z���U��W�Y���G�{=6��M�x�鵁vq�HŕLńw�ѩ�����扫>���8е��PZ���ФPz����] ʖ���ؑ����p}}��Tf�� '�+����-�����̥�}�I��̣ߏ�:V�N�G�o+$H��l���\cs�4�������o��w�~.�������
�"���qt����V������U��O�DEall��A:⾎����z(�1�!��L���RX~Y�C8�5�P�Ϸ���O�m���'�}� � �i�\1�y��U�O*%���9��\��"�>h�Ú���7h��ٱR:ֈ��)��c��U��z�x^Y{c�>������\Ǹƛ���0�k�/�E�=���
�M�YM���9�-.�Z��ܾ�ci�ےB(�i�Y�����:�&OǶ�/��C��c�ʓ%������*��Y��i��DeK������'����Y?�V��� G��6��[?#��IP�������Y���Q��"�m!��?�T�J���'
�?U��ք��]�N���x�ն�K�����u���Ut�QAܦ:Ê7�:Ѩ�<�T[i�[� N�c��RQ�?̰�l�Rű���?��7����Hl���x@M�����pX�~iF���K�P2�Z|���w}k?���C�~z���1�Ǻw�x(���V��u8���k�k5�E�8Փ� E��>�e��B���"�{���`�M'z^`��1JǦmC�Sa{j9'۳h����',a"��D��[L�V��*l:�l���gٶ
W��y��l.�;�|�^��j7�n\�P�\k��/�?�QY�w��ޘ����~�N��z ����e�W���W=y:Ǫд֟JY�C�S��+��Y6����Iz�
��BH޽1�m�5�C���������Fy'$5������������t��R�JFC��`�ß5?��������t���i��� u:Ȑ�m�̿�CK۳��y��Wq��V���|q������$�+忺����c1��C�?�[ �}�-'��y+� �GMJ��dj���¾.��*yU%�e�7����^���'���_�G��>��a�ö�k�����|�~F֠�����u����?Q��&lX��� \򯠨[z��̠�x��U�֤��|c���-�3��Dۋ
� ���4�l�6�i|
?�J��!�#@�-^#�{��1�N��%)Z�7%?�A�h&�֏8Fu��G�P��Q��Qד�=���[^%��ߣ��[~h\�ZN(.���C5�[�kI�=eC���� �f�A���į%a�
?�L�Xp����/��y��v�ah�gt�9Z9�-���,=(!�|ͷe�_WԳ:��J�6�w��2�!�P�`��^���B��ZՌ�nփ#w@;99999}d�ee+<&���~׫����Txs�d��M~ �ΧP��j�����o��{��+��"���֢pR���$-?�r�er�ޑ҉�eQ�VD'
�f�/G�n7Z:�X>h�,ҡ��sӇ��z�iְT|���uV��A�$ȨU����5�`�a�{K99]9N�vrrrr����;�pB2Nh-���@�x8�hR��O�iD!��?w��i\� h1]5�1��.��*��>��T�c�+�>����h�������i~��h[�th|h�"#�8�6j�ǵ�������,��زu>�}��K�(�Jk΄�Akmdn<b �|4��fq(CͯF([ǣ��C��?Ү�,�ˇ�ϧ��Sψ[G���F�<L���x'��u�Q��r�ɹJD��"���Oq �������PD�-�;�`��g\�^8�n,u��g��E-��D�$Zw��������~ �/>��U����z�����#h��hǀvrrrrr����ϟ�7�~�����k̎W=�`��ث��W(��Ja �U�9}��ә�����h��(�G�a�?t2I[��ǔS�v�Ӡ?IO���'d ��M�M��I����"����z�r<��&mOR�Y�9��|�b(�#��{����75�L�����Y�9+����~9al�c�c��6�^��"�r����nRT�����^}��#�]\���C�O���^���G6��E.)�[�:C� �^��<km-V�Ջi}� �y���K�1�Z�Fw��Ùo�_5�v�$u�L?\�������@)�,(�SpBv �6
��t��c0����U�~Nq wr�����sZm�a�n�z��%Z6��A�Z~�|\�����V� �K09����U骾��Ki-������*�T�������A3R+e\�x9�;�_���l���|T�Gec=��7�����}.O�k�YW��Ձu^}���������Q�H�Q(f��ː�*+��W�s�8>��3��������H��{V�m� "��~�Oy�u ��5F���8��ݴ����6)
�rxޫ���@s�e�R����7�>���UȦ�Q�l����i �?G�Q�C~V�����O[��=��~*�G���F�1 zC�>25��+�5��LW�K�R~ z5��;���S�)|���6=#L�l�_�V��<�"�p~�mo�)g������;_o-$e4
��NNNNNN�b���Ph��q�8999}j���BMf���3?L����o�cb�d�O@�!p�ܬ �>�u��:'u�����!~@�:B9�>,���_4��w�������2;S����{��V%'E�M���( ���9g�|��l_s�'-�E�:�o/=�����U��3��@��\5��V��!2W�m����0@o��T�L�I�w�k]����N<��n9�vrrrrr���0��{��wz��n����>�N !X"(�Dԋ��bVw���F�G�u�.���}��h���(��F( � ����F8�jr!Yk3��ٖ���]�� ?����JOYF[��:�_�,nuA�����~�)Q.�j�R�u�w�z�"w@;999}��E��G���E����{g���i�h�`��\�F�w��A��v����UA&9ĥ텺ݽ�E��^c}�[���c� ��B9Y�Ũ�.�y}��]R����ʿ��s $�xև��mzȨ�k<��뼋�N_j�Bp����V���J���o?�5 �z��a�O�����˽����J
�l"�K�@
hP��O��C�.����g�/���}�AO���ظ�G47o�_[��u�z��{����뺦��.�-xD�7��o
�kw�Kv���_����=}F�< k�x�'/1M:�&dM[�S��r}B��n�\�T��� ��v�N?jҘ_��I����]���-Y/����������Ӈw_>���N��w'''���2�<� z�+���_�p�0C��"?Y����p�x��]4t*�xm����v����M'25���.ճ�Y!�^�v�������h'''��b�y��o������!u$<a����l>�\py8����rs�#�=z�:>��G@������"��U��Qi?�m����e�;��k�I�v:� Q�� �m�� ��a�Oh��#�%�(�f��Z�]�爀f� -Y��H��
�|��J��A�_QeKŀ�p���Q���чMĴn�)v���G�p�p$��^X�{W��k�)�����/�|=�ؿ�~�� �n��2��[�ˮ���R�~�����h:�$��1y��;jt�<����l]�JV�?@��:�#���]�yٲ�XH��<�Ĺl���=���p,=����;�{�j�z�>;n�~zd�M��<���"�i蕔ԥ�j�1jL#]�$�:�� �
l�R�sG%��Ul������Bq�Xʦ�!�z�S ���9+R4<,�i�B�Vs�2@{�Y�v�5���_;\������2� �ĆiR�ЛA��0tƮW��{u�^8����tv~�kÐ�gF�*�gR�/��RF�DU�W Z}���F@�S����D���!�p��H�w�� T&RL!W)�0@M��1iښ�n�q;X��SN����f�ݚ�"����(��mV�����O؀��k��Ԧ���+���z���9|#:�K���6��(��� �m���]��[����|��O�����9�V�v�^ �J@��{ �;!�*C��PƅcJ�ó�FlE�#ƄlXW��mH��S��Y ��^!m����b�4D�b�D"av��)�1�lj:{���q,t��6���,mA��-�[c���Ԭժ[YG/�R .Iy�)�G���ΐדj�Wf>כhm���fv��!Lc��esj3�a<�Gi���
���4_O5��I@2cV�ã��������/� ��+l�K�J���3ԭ����(����H��s
����:�iҬ'�ͪ�t��Ģo���)|��j{j�둂�-�4��<�J����de3���ֲ�:��~���m����i��� ��൬���*M�m�� �o���Ze��z1��x�kΜ�e4j2_�<Ljo��[�h*��YW��׃�բ�V<�c���6�2eD����9F� !ֻ(��Iv̽�t�N�2z�_ \���W���nޕIb�t-rYN���4�6(��`a��)%����zο 8 $�c�O�(�f���\��[u�tj��U:q��L��r�tR�r}���:�]�,]!����M������y�-���[ٟX�!}]�_��P 5�����fOγ��_PV��e���U�/;�q�Ϫ$ �����E?�Mq�z��D�1pN�h�V#�])N-�����oK}L�`�2��2� p�t�W��zS���;~j�c(%̛��d����u�杙MCʆHi@�p����_Ю̙�Q�f��ӡ�-T�~{1k�6�~�� ��)��avs�]'uw��]C^������C�Ư�~��m]F������́�H��P}b8
5yݎr�*, �e 0 �)�=n�h�(��;\���H
�r�Φ��{�����2}�NS>�@�H�����B�������P��k&�'}����~ XT~h���Rz��R;��vC~�z��_�����;8�l¸��[AV�"��8O��;��o����CۋS�w���*{{�ı��)Jc�L���b��:Sy��sd�y:���-0�A
Vq@���XZ�S�)��� #����c=p�=�j�?NGj��,-��?���/���X�VfP ���Ȝ���������ޟ!P��k��5gA�6Gv�&ȍ�ˆ���� X��S��b�� �J� Q-�N\>0̲j�8����HJ}����O�]�&s����N:��[�Im��o�� �u�f�Eq:�m��P�S�z��� C��1�&0��6{�����W�� ���ĵa�
�n�W��-)�3e\�m��oA�mA3�I��<%��� ��s_�j�…��d��'kos��9�����W\_dAkέ�F�t:9=���m�� �����������W��oQ:�]�ă�*���l��V�u\!�#��^���ҺfN�b'~�̙>����
"��!yw:�O���6`R�5*[�#w����%�B�I�s�n�S�xj�~��!�MZ#@ټ@O1��+«p����t���K��B�*�O 察�z~��r�|��J�ίE�5F�eA"�.i �]�Ź�^���6M,n�������NH/�-��Y
ĵK��u����k�3G�z���J띞pF�8�Z�゠��P�j��L��v�C�������ӧ����N�؃Ս�������$x����{-�����qj��oE��r�;��.�\ñb?�DL��-0�*Hzܡ�I�m� 1;��w�z��r=��S8��9k=U��ő�� ��;��NNNNN�z �^���I�%r��k���^�P�E1B��XF�ﭏ��CEG7��N{����^��P�th\�]��u��_��p1�W20c�@�0�
^��{w�qo�gɏͿ� �����ui��{�~<8l��
x� ��sĜX� f��#�О d�Z��������_~Y�b@�ؽ��Qʼn��8_�a� (,@�)!���s�n����P�Ӗ�7|o��X�Ŧ�IiCF)<��Mc��oZ yޢ�����,��+�K��s�>���M:O��C��Jצc S^���҃�����6 �G�����}~J����|^�W�y��<�VO�q>������G��srrrrrr���c�N�ُ^'�''���ø���Nж����¸ɝW�t���_����Ֆ�_���ևiX�}׸�b�s�vpО�o������#�N?{}Q���圜����(�������h''''�k]9J�s���5��|;������br�s�^-:�A���#��C}�{?l4;�p�zf��"n��ⅻ1�(b�X�)r�%����3E@3n �;�rh��
ip��3\<Z���H�1�{ ����S�D4���^�O@[>�p�-�>�� �����lb2�R� ��.$+B�m*P�3��h8��~�r��vl�:�r�.COf}*�Ϡ�M=��OUJR:��C�h�(W=M:���rT~������i����9]����´ � ���h''''''��Tr��Л]]�X�.��!'�a��;C���?58�S��Ν���������5��� �n:�p6�;��8g��O�1���*��g��x���o�"DZ>����?�tZ���E^nRqr+��
�b����&2��BD����!��������NOM�D���F!O�>F,dU���S^w����p[�,���r���������DP��>9���S���0ύ4?��k���i~l��H/]�n8[P��À~ }�*���^����C��r�g/B�<=و��q��9"�RU!��IR���H�{Eh�A׊y�y�ȋKT�dh�r݌m�hF_��A U�@����x���!�bTNk�nʜ��C���^���������3��S{���C�t� �o�Z��s�'���#�7�]a .�˸��z��^5]s�Ϧ^���sF��s@k[ ߵ>�����4��k����ʩ<,������ͯ�����B�~ �ȃ��O*�~�����<�cO�w򇡪����O⧬���Jd�����S�vHd�����٫V���jw��2��Pl8�[�#o5�>�x� 8�b��ԗ�,BA�8�@<�A?���8�CjCK����P���G��r�6=SFL��9��i{R"��1NE�����D�AO砚keD�J��q>M�ګ�{,�#�w�Ě�.��yAY�B� ��� l����%��prrrrrr�����$81�>M}>��U''��R��:{ �J?E�'GV6��u$��]T���NU�[�+���)�'lNA�0I ?�2��PVH#n� )���5�g����-_��.�I�%�>5Z(�� �"� )[��g�#�!�#��Sd�lCL.5\�&��!��sSU� ���/� ���V~�5�K���p]�ڜN��wpl}D��@�ϖ�є���,뾇�;�������>��@��ҟA��Tu���*�կ�����t��/��C�]��A���sՄ�l�O������ؙ�.����>щ���h�2��P�1&�X�B�����<�9$�@F��hԈ�d�H)g�|���:�#_���z'�����R�`kw����NNNNN6o����@��j�e@N��Z@: ^�L�N-_�� SŠtYw �x��B��ۋ�zo� ������0��qc-�<�]�M2�{J{�A���;��M�܏gaٙqn������P�3 r�@������F_m�X�4�����<�(,���Xi�l+�>iG��(U�W(?���V���˂��,L���� T h� 2(���450=�>�T0q�减��`4�q�w ���4��b4�Fh,Ҷ�8&{S�*?��&sJ�b�"p-1{#���o�a��T��sܞ�7�)���%P��� �z���˦V\�/�����1�ܳ������
Z��T��Ԕ{i^0Y��·�899999�[�A�N���G��A;99=j��!:��X���Zw�Y?�w쑋:�{�������� ���N�]�N�N!L�]�����9?���9=LL�_���'��vrrrr��Z*rt��ꯠ|�@�������x�ȧ�Gn>�{G@W�9=z���t���G#���z��>���payt�e<Z�=,"����yv��+�X�%��Zԓ�x6�Ӄ<�#��C� j� �;:L���F�q|���#���T��s�2����"�σ�acOj�-���P�I��O��JcG�)X����y� A�8�1��eR: �ôBBK��Μ
t�"{�n̏`\��^%�μ���r�9�!�i��F��s;�����_���@���_���>�Ѻ�z��c�:Yl�Pb�G����^��t@�MO��/�"'\W[6M��&o�(��,���e~��'���N��+(���UJ�SA���C��y��V�e�i����6r�\D�?BwHKc/Ѱݢ�$��o��@��a���H:z*߫�e���_�99M���XJ�^�@t���.o�EX3>��z��a�� � ��{Q$��ԧZa�73�57��j��
�7Z���=��9;�ya/��O�i�Y�y�o�8Z�o�$m'_�P3������|��Tq�����1׳>�W2�����b�t���k� ?���
���d���T�񫵧���sx�L��A�k�bE��i�"�m�S*��5Z����I��.����>��F��S�j(�NK��u�K�fx�Z�LAw2?/�kh�g�n��6Y��jy��^Z���h�t�]�ҵ��;�BL�C��
�c+6����T�J�/' � �}[�b��(�q[��p0�RF��ѳ���;|�R�M�S��~�o4�`w�_� 2\`����#��;�{%0v�����l������&�o�&jB�h�ϴN��T�>�hl�� �t;-O4Y���Zj�|� \3���'�/�P`�1t/��2^?I��'3� ��3�4���*���0�BU\��P|�۩� '�E���G�ýv�;.��j���6=p�,���ٛqrmј��2lଅsβ�g��(��q,K �UD)$k���xe��i3��/;c'�$(�!�)S�ۡl�Z�;�'N����"9 -������s�fgqR�28V���HV����o}����z궞F�ۚ�^/�(Y���D��9�Y(.M�E���b�Te�d�'lL6��zk��4���"�J��C�/��8US)��ҷ(�e�I���ST�������t/�����T��s�$�"y>B�+�{�f��ޜFR�/�B�Asmz�?я= �K�CJ'k}�eG�� q}��+���-]�OV�m~������]O�=�MUζt�FKZ��5��I�����G�b���%����Z��GHo��"�X}�vMd��+I.�Ӄ�m�����8���"�N�w��v}ܐܿJ{�HГ�_��hl�#n���֥�z���|_,�w^%R����o]5S�[x.��9L�Kj׷s��뷻l;��+ʿ~ &��}}�9���x�!{�F=����V ��gm9t#ʘ�NF�\��Ea��V c:��k<ſ��˵ⓌbM�i8�/m]E�{�E���,�Y�k�� Ϲ��[���Rd��h��qA�E����;��j¯�:�����A�p�ĩ��nW��T�> ����j�v�W��Ľ]!��+�d�c$]!�w��5�yP��@#��`�-A맯a����}>+����������gGa�@5�B�k=�h@��)��Ӻ�\�s�V_�2^Q���rE�*,#*�D�l?#�a�o�ۇH�� ���ǦεA���ؾ�j^�8�E���ºym��1��,�q# �(�֛p���y
����|m�ĉ,P���j�4��!8�urҦI�3��ǃ���pG��� ����o�J�.�T;D{5j5�q���. V2���uΕ��
�`Ey
��ްPW+��T�Z�hكA3�8��fŰ�>ФUv�y�3�g2e�T�I����R�Ak绶^���?�� \��4{���v�j�qhA����o�qzWy����Yj:=����8 ��C̅�2Vda��^w������3�t��� ���� ��q�\� Ik��޳h�N5䳠r���ԇ��<ռW�O7���7N�[����n���=�w�O�]CX�LPH�����B �2W��=���Sa�)�2Ԋ��������U�O�j�A����2cĘv��Ds����%v�Y�iIgp�C^�����cprrrrrrr6a?�'�''''����os�S~���:�ϛN��N��� ���ߣ�>`b�<�n
��J�e���帰�c�4�y��{�򿸦���NNNNN��O�������=@YK�����{�=G��5B���a���h�vH �[�[����9��0�!�aۯ=��*o�B����kȏ���q��U��\�q����J��;`� m`q��T�V��mQx:'�M �Q�V��a
���PDW��J #W���� Ѣᧆݠ�#t�f�,�l�1䈱�G��B�� 84�W��h�S�f&T�FB�k�gg�f�,�h�/�cU�dU҄'���l���V�|_ ;fy�Y���,�L&�>�yFB�������M҆հ���<�t,���y�v~��7����3�;aAd!v�G��q$��� �~R'�d�"g���tT�
�����NNNNNNN����t���''''�w���f�Ճ�}��4� �1�/�_e�%��naW����W ͍�n��dw0淖Z9 {�� ��]���ˆ?�����NNNNN��ϳ"d��0����3;L�ߋe�]�����0����-]�����#����#�w��G@�[��~�h���(u6�dM��'�} D@�n��he~y��f~ �AߝA��gG@���9�4��vxe��P"��z��j�j�� S-�o�# ��S��^D�j^q��!nQ|���Q�����N��4�K9u�F��\<���K����JR}���)By��\�@x��ߨ ��w���px����^U��ק�=!�a�AY��-��#��������F�i��;=C�89999�I���-�G�{�ϛO�mز��w�:�p�T��w�8= ���#  �[J��9�<��BcyT��>?���:ZTNO��eU#�MO������d���m���{�������#��1伹Dj_E+lͯ=N�1�H�[�;�5POSD$}T�Q��E���,��t�<Y�?_ߙީ�E��j��r����J�O5>���]<� s��ܯd_�8�rT�B���]���S��د�g��g�!�bj��|j�:���r�@`c�/hR�r��@�V(��O\'Z#�i^/6�j>B�xt�ќ����mt‚�* �50�ݴE^�ZFJ_������p��K,5�[K�t��?e] ���8b^�
�Ik�p���7���t���x�����n��kh����{h����lni�X�g3�ޫ�W��E���M�����ZH���Ï������z�0�g��̌`�[:�]g�W��������N[Ӟb�5����*ü+����֏u�QN�3����c��6�^��g��'��}�� 8>�|������f��A��}����H1xJ��8���z��o��Ή�λl�~,ސ��˘�S��M��&�ǩ�i,ٱ���ƾ�l�;�M����Yj��"���\�z���_���n(Ưz�J���Ԥ��z��L�!�� ݶ;J��ǽ :�<�������I0. m�����wxe����Dž��5��^�������Y���a+>o~V��=dI&���j9M ��
X�_��t���cI�ͯF�"#U���8�I��[�=���O�'��ap�:
�� ���;갲�?���%�꽓[�Oh����������c����)�� ��W��OlJkWl�e�t�55��N�v麬��-�1����������փ��{�z���3� '��d.a�,�����f��˫�/�7ߕ�̱�����21�F�����?���^ܠ�ݦgD��FNu��t�d�Eu���x~�D���D�sķ��=�J�]4�x��������������b1ݚ�o4i�h9]���E��J�E�W� �T�/�jeA�m�%�����[{�-�د�9�����U����1��rޅ���1w�i�2.� P��f #���'h�?�`�*��=Ϳ�)D)g{_Vz�l.,,�u��WT :�� T��K@p,����CmD5j �f���AfJ��oK`cm�KiE|�G�YJh�L!�y��D�"� ��M�1�M,�R^�
� F�a���丨*�~7��f$hv��U
�?(#9��#�p���H��Is�p�����C�/:�P��V����|U���.�����wz��ȡ���������j��t�{�ӏNN�y\89}������A�N�w���r~J��H��J������pO,��C�-����چ�c4L�d_���ֿ9�,�۶����Yz��ß�G@�ߵ59p����өz���K8�9�P7}�?/�>�8������2�>{t����#���y���;,��~4�e�&�qͥ+�M�<K�[�U��'�k ]�D@��R�!��#�ώ����X`������k��bO�����?ӊ���Q�����8�N���`L+㨴osD�7^Dۺ��L� ���%xzC7��`K�S����|�֢L_⟃a=2�y$|;l�ߴ�!*���'�,!�� ���,�|��u���1�����Ӆ�[}
�&��<8���u}�]ǝ��+����(�_���>jAwm{�o;���F���^�_0> q��������� ���?ԣK�f��z�è�5�BO�9����S�����Ԩ�å����vBw��B�m��x2�tȳEN���?!� �C��`'�m�s̥�<��O��& r��e J}�^�M��"_������~��^AW#��r�̀]wr�N��j��}Xh~ҮIV(�� �~�a���!}��?�a¦p���#��#�m�7�`����÷��uHo7�?K�
���ЄB:��7�F��_�sw4U3�
"���Xz�*����y���rdd�(�g���d6'�E�)���9-�m�^{�x;ݲ�Ң#�/w�K�ϴP�;M�m��<��XO��zs��Fs
$�OwI��F���C�͏�R����M"�֠({�L�B�c�����4��_�l[���ū�3�좽bHb��I���!�g��of�r�E
X#>�撍t���`Y}X&%}p؆�[�?5�*n)u�����,���l�kv���xo,�t>�]����*v�^D�{G�!m�k��y�k�]�aQ�=�#�1y�� �B�Ųh�����Bl�����C��Mŷ�-�c��~o�9� x��piA������a,���oc��n�!�qX��@��q@�h/}T7�'k�!��*�Oєz۸�'j���0�
JC��[���96�"ùf�sa�
����go6��+7Q�O넔g+|!\��FuS3� l�cь�A��b�AG�i���[#�p��r�;����7i�Cy�ւR� �e�h"A��B�ν�=pp���W�J y�
Ѷ�T�^c.�OKxi��iJ��&�h�GxC��5-1����v�U�n��Z�о�L��L��d�$A��YR��|e�q���餤�^:��%>�\/� �s}��QX��a���Kq����>7��튯����Sʶ#�R�5���#GT��� ��+�S
l��j�/�*�TşU�N�pFSۿ��q@��q�ZY���Ho��'Rܽ.|�����r��_�(�����M9r�kݸ8�Q�nzr��{š|oӏ�����̃]��_��7�E>��V����o��W�u�vE����Ղ �]�k#d}[�'����c��7\Z�^���㥸,�Ru�Y�b�����t��U�K4�,�y~Wƅ�1t`��-����L��fe3;|(�A�}Z6���c�_:�k�~��W�\���JW�F�-���w�͟քx�t -�lڇ-P=��
j��{�@'�{�V8���aT��>F���3�\�_��]���X��e�C&�ܿ�Z���o�ܲ�6|bǜEW��+��}B�Z��{�˲!6v���v���.6+x�Xp�m�G>�c����]��l��-��L:Z��Su�۲�e=���2�,.S.,��!r9�S�~(���-Pw5#,�J�U���V�(��S��=�Ӂ(�� ,��-�x�ܢu4 掲�ճ�c�'����q��6!�lߨb�W��O�L�qM�19A��G�ǖr73�C�;�X.�����(���2��7I��� �`_�4�vi�0�omR�&Z�������P ;��>�������+��Jqd��<�oU��'��fXՎ�LQ�68~�a|�� �b��^8쵿��O�b�T�urRfIN.�&.��|����a�`��!�l����
���Gd�y�qt ��Y?�����};��(���i�>���ŧ�]��a���S�֙`2'��l��PB�'G��s��#vB��lpOa�y��=�6~����~��9(��oBI���X婁�H�w����|\n����EF���d��>W(o-��cۄ�ӯ[�;�
B������1?w;999999]d�>��Ng����˳���E3��{��}��(\3:����3���_/O0���>W����ʃ��}�*����{��sQ9_\3:9999��-����/�5���R~�����t�zC�i�t~ȉ �jd���{�u�*��z�{�xA�?f�e+e>��q ?;Hɧ�o��~�V-���;'���\������埸EF�!8��BZj�&@p(� ��{��l�j� �:چ޶�F� ��~KPƗ>�O���EE� �{G({����~�vx�6) ����F�^(ōCph1���;��k�yJ;�FI����+f��n��8�7��8��� ��c�^����bj��)�����r(c,�>��� �y4�i����$�͆y�җHscA��
��H�O`�� �e\������L����6��5���ߺ��vrrrrrr2�)������������t����ȓ��x��z�������0 T'/��� �: ��% �Px�0� �^�(��G��pfk}`�?��}{�����NNNNN6S���'"�s�/~ֆ1���s,,�#������#����#���Ώ�n���y�#���#�q"�Iq+H�J~��^D����$Q�G��Q7��7���#���6s�ہ�.@�J�q�!�CI�3鋗y+g ���@��y���5
31 *������U�S�ڷ�C�#������%*�fY:-l��gU,ӗ(���&ܘ a�j�)� ���-�&��]å��]r~��2�g�?��W���u��^S��m=��NNNNNNNC>����������t�,��fG5� W�#A �5��peP��H�M�
�>з�����O*�h����Ѝ���`Uν��'�q ��)m@#�1�˜��/���LM~J9�����9�M�S ����3����U��/���'T� ��އ ރ���O��h>����������8dht|���22�h��ý�[[99�Y\��>�T��v�8���gӱ�X�|�/h��҆�۵��W�ҟS oK"1�G �6,��D��W�p =��8_�|�<,(����=1�9���Wfa �-�3�l�<G���j$�r����I7�:y&lhuS1@(���?�w,��=��7� @��/�'�>sĻ�^-��>�H��@*ڵ_�nl]���"��X{�!8^E���i3�<N���W�3~�~������(�K}���A �������>��d��!n���_� F��^~�p���)�.���|A����;:�﬏PN���t�|���C�w ����^��Af= ���q�aH�ra�� Ϳ7R�,�!Ng ���>��u�i�7gx�=
�T?l����cO�.F X���
�K�?�:O!C���_�������=4oO!��FZ�4�_�?�rYz��������]�;�2P)���\��ܿ��m�&�l��2ޓQ�%#�T�����^[����*V1����x�r}Qe�h''''''���8��y�χNNNNN����՝������b!���.�;�@#�șb��V�gݼD����2Z{W[>/���|%zZ(M{K��0��x�??�; �\!�<��Z�!&W f������@�.����W��nz����L�vrrr�\�3���y?�o�����o���?S�OJ�����"��/�B4Cy��6���+i�NR�fz���س�H�YH
/�t�N�������*�ZHy�ʄ��R9>ʱq���W[�B�P�yf^S�i[z-�a�h�� ��oY�����I�����3��=KU���Y������)c��q�a�]x�a��~8d'�I쵐췋L���W)\��A ��p���O��hG����<�[�G�.?�X �b����&#;d��s�S I�U��M�]ů��.�����%^rP� Ϲ�aZ�y@�gH<'V�������ɂ`����̹��T�ԞQ Q�'P=��!�A�fn�k@$�^V�Q�P����*{j������?:c�9@���p��m(f8*!C
Pu<f��)�%mgc�%Df�B ��I
�vn�Gi��mJ�|�9�^�MѮN��'HX&e���e� ���'0���z�(����]wL>wR�KM��i��)�<��ݾ�]�u�O��?������������O��O��3Lpg�,��NNNN��[��/�;��/���Ÿ���O���ן�s?�?����Ͳ��\�$w�{��;��|���nQ��Q�t���2�����s@W��a�Cy�H9��Q*��h��{���]Љ�Y���8��vy��J�{�v0.5�>�]mMO6G��8˯h����jIiH����PwT���8m"�@rmjXE��xb���mu@���c�+ h�������z��\I�'/9� _����;�5y`s�c�?�:|�Y���d@�AC";���HG@�$d�W���:��˃}t�w@�U��=>yB���/ע�i�>���B���{ss�\{�B� f�U�m}���G�rBԱ?�����:ag��N�dҟ;&y���\���V*��:)o]_�P���wP% TW�����S�.��C'�.�j}T�u1<��D˦���?�{��{��~�/����s�������?����?���?C�v�s_0TabsY�Ԍ�,/�g�҉Au9�vrrr���3���~������C?����?�����O�����?��-3?��`tuw]��d�~�)�N�T:5��l2�~��F��y�AVE��X�A �:� �R���4^��nY��P|��#;��-�"~��u#�u$p �m�<�#j[G�榿,��I ����"���8�m����l�X?��R���fA ��d������s@c�>ے;)�el�#ʃ��X [�0��iV�����(��[j��n�s�oU~j��[4!���B������V�n{q�s~�H���a[ƈ ;��f�F�Z�!{�v"�qftl~q@�z�Es@�y�ba�)���D@7hpg'�jܦ�~�'�����J��T�r��‎��P ��*�G�~��籫�$ �叝��or�k05 ΛcUe_m�j��'+잤�%������v �<�ؾT\� "x
JŪ�T��n٤e�քW���U�8��� oиyp��[���� ���_�O��?����O��_��-<I�s�w@;999}X������G����O~�?���_�����i�o�7T�ԋ �3���C �q�|T���P�:�,T�b臲���Ȑ� # ue�|��yD�˞�t�dY�$�1wG��!8��Z�j��o�/f�"荐 �9��h�'E^�v�s9�M����B�_��`m?C�I\�)�-�[l#��Xl�V!8���ޘ���P�̍}�V�D�u�g�4`&l�)�̎�G9A-�‡M�)� �!bue�;�0h-�9�XѱK�V �E ��./HC��a��(� �
�&��;�)�7�hf)��������
��O)B�z�S��a@w9��/��4�{S^[���ӗ�18DZ}�'�����Бs~dxj�!��H���� �� �֠��2UϢX*�e^�R)��nc�sz>M|��P�:���r�(ټa0���z�+���O~������_������M?��������?�0�N���F@��=�Y�����\� ���w�Z���+�ʇ�|CK�k���"�.���z��������ӥ���f��������ʯ��/� ���=��w(�g*�&�ק�������4��6�3�J87���~�wRV������r`���Y4J �S�o`Rus�U�qG���k�M?���Q(D��}mL��bj ����Y[�`u��<�q�8A��f�'m�ζ�F�u$�g�#������q�#:��$��3�,C�}��H�=�җ8M�t�kpwc3���b+�J�D J]/��-��#��p���P��jD��o� � ,.f�����I�� hɲ�j���O���������k�̿�7�����������F�����jH������3�����2Mi�Jf��e�b
i�:�TaC/��'��o��B��wZw{{{��#^7yK��/���I����L�>���̆&asн�jO�Фb�y�(�3�Ol$�1 s:��eJY���:�h��.@4b���c}�#%?���u���� ��o��������K���f�e �1�:��t��h���m���6�'��{��M[X��Ms�4��8e �� W���j��}�ؘ#�*��$��*�g_j��4�^� ��R.�ki�rdbwqB���ԣؔ'�:�XMD�pL�?�\N�Wӥo��𓿣ԇ���i�ƺ^�Q��t�f�;Ry+��3�<�4n�U�@;�P9�ú4�z˞��m�BufZi1�cS77���G��ف���ĥ���r�WQyViP�x�~�3v`WV���³�ljE\(��O$� g��/���SD��V*F&�IhlO����u+��`��M�T�7G0���p��HJ]VXٷ0B i:Q�^�6Ka�]Zb��9B�����E���Cn�4�ӷ�3��'� ���أ�H����PI��<f�`ϜD�dh���Kj���1���_�K�������W��_��?�Ϳ��~�P8�7̓�Y�Wq�����g�*�*M Xq>�{�_��8|�r9��7 ��N���޻2������+�8^�m=�������G�N�K�����L5��M��ڿ0����w�����.���9�g�:}�ʼn}ӯ�<�յ��;^���n�)W���m:FžΏ�a�۾����Ŀ����7��?�G0$�$�C7ÓN��}$� :3/��^�3
��4agi_�-v�i'��f8�;���b#ک0�U���(���/X@ˀ���z�+�^��@V3��~�N�v<`�Kw����y�9����t�+�(��Ԫ�x\��q��5T�����º�1�ʱ0��|P�������a��0X�%K���1���k�ޱۘ<:0�ե��tq��1PA���oBQ=�.>�j6A���g��0@x����j�>%���T��Ԑ}Z�<�l׍|Z�E#�V�Iɽ�G��jG8�Kڢ���a�]���X\U�1�^ %W�0�4�������
��9O�H���Q�(��N"���|��F ��Aءr4�+*�\���W��߈��U�Wj>X����R-$B��GY�4�p=�zs��\7m'�ҊgQ���H�N��rt3����m�f! � 9$Ә�8��Z�BP��P'�E������?���_����������/ͻ�S\w���/<�uzi�/p7�r"��7-+_��Z�D��f� �p�_��������� ��>��K;��W���X������������o��?�G�a,��*�I�������j����#��w�8���nϸ�v�E�-$�/���ob#4$G�?;6�Ў��8��cr�ŕo�+��7:���
�fC#�����.?��f�f�zB��:x[����B�4W���/4���me����st�X��:k��Ϧi(Jzu�� TCWh�.k�|�8?��������U�ܯ������e�r��B NNNNN����������?�W?�pz��d�mς��w:�UM3DkR �vr�V>M�b��4>�^��q�Q�x���yΐp�ޞ�SzE��=ҎǏ� ⢏�^����&0 �M]h���Q��!��Ak���pstSsO��H�� �o+8����?���w��_�O�2ʼn��PWw�2_\�9999} �;~�����/����9+�.Y��l �1��4�i���i軟��#M*�R:ic�1���� vo���8-&W;�J޹� =�eS���G�� 3dA0�o�h`s�w�v��d�?�A�-DH�;��0���z��L�o �;��>�����<DiA���F�EDW+�) p5���W�k��� E+f�� ��?H��D��"� ������~��8;�ц�H�#�A���иF�u�I��WCz��9��4H�ڪ��P�.T�d� 2�A�X���k�3��F><d�X`@$jRĪ�����3��%!rT0f�S�G�#3��l��_��B���_@�q��r��E),��9�o8YP���&�eVt �����s� ��O[�<��L��>2:�R߱t��W��s����������_���?�&P�Ϯ�����ӭ��#�����>}�w}���_��~ӿ�pz(a���A/�{��|��
Z� ��R��.��;�wrr�������l'>�e�@�}V��@�0�"��u�Ik���iftW=�k��)���w� %]����ʽ����mN6Fس�K��U�:0�� �: �E�P����w��?���}Gl�� �����1���?��������'�Ng[��sݥ0�O������j^��C��,E�HqѬ��wX���������ٻ�8�����{�r��n��6`�)�wB�-I�$$��BH�R����C!!`Z��i�ㆻ�.˖dI�����6uw�t'K��w>��fggg޼y����y��<Wc2�9U�t&��w} a���/2�cT�'��1''�,��=�%>� �$oO� ĉo!�A U멊��Ƈ��_<o �lN>̥�,A:I
��C�=APp%��j%�g�w�5Ё�@�\�#���qs!��W�˱+(@�<��1WS�$��P5�h��@V^}^Cq �'�@��
/,��0-Ÿ݀�;*
G'�|�W#(}(ꕉ�Q� �N�p ����B@FgC{F�h[�5�G��ҐKqoo���hoQ6݈M�� ��_d'�l?]� �Ѹ wc2����l�y�ݘt�v܁Q�n8Eԉu�{���s���O�c=������F���OV�n�d�� /dj�q����v[7un���QǏ����Tܟ5۝\��,H<�
(���rJJ�t�Io<���)�K��6��_A��ڷ�&�4��O�;�!wʜ+�;P0�$�]�>���Wsο{��"���L�1U����IC��l
^L ln��{MDפ�>��*ݯ'�d�0���mo�fp����X�&���rD�_ˆD~]sA��4��1]S��#=.�~�0{��!ԏܟ5�:�3s�Յ��B�(laYz�I�u�̶�g}�ڇ��=�@�C@��!��d�i�ѓ��]����K��'��h ��L��~���je�@���,���j8L�<=�D���9]���u��bH�Ϫ1�>��Z�2����_��Q�k�|��u��#0H���VODMi�U b�֕��i��e�[2��Hw����1�L�(~���d9 O����t�F\|p�G�+��vJ $K Þ�RW�eyP������u�.. \��tyv@����c�� �,�Htl?�
í� x��޾��g�╃' ���&Y;����2[��A,|��̕~�O�
q*g�qo�����}[O�W���m|�G�HG�9~��zFy���Mp�#Lt�'�`aC�� ��_`y��ē� ˏ�ŧ}�]߉e��� @{���1��<ۇ��$�D�:�O�����6�1��� ��� uZ AU�G5(�ڡ� �30���2��|/p���c�d��%ȷ(8� ��Z�g���Ӆ�.
J���녍�a�r�۰䃦c����>������-�v��!($��x��0fmyD)�q�nAq� � Ԝ'#�ο`�j톎i�ų������(�����B�qȠ�P-���>����]��^�nX��=�� f��r��
Ƞ�]*�ѷ)�����mY�>��A0���i^T���1ċ�#��bc�� O?��-K7�����Q<c��S���ov��:��
h�S���_,�<h����ʿ�A?�v@n�xЊ�*�sT���?_���W���$ >蠚C'� ���ܿ�����-4x��ǖ��n�lه/�x.����D��8�����K�#�td�3�kc-���.�I�#���"����U��B�e]Y�xJ���I�����I��,Ì��K��<|��+�W/|y��O��j~��SÏIG @۱;��!�;H=W�J����\��f:�&2σ����t=�Y�KW�$�[���Ge~�㖮��KS+'��
(��6e���֦��C��,���6�� ��U�AKQh�z](l�σ�?���o _�zb������ѿi��׎=�����7g��o�Ѩ�����[�;���P@��gL
�LK�SŠ �~�d� ������6���� �!?��滀� ��\�m�saШ`��V*��(y�ύ��R��[<�d��C�I�=�Nú�؞VPQPVSf>��_�f'�B��`��LK5ʐ�#.%�ɺXC7p�p7�'�Y�(�v����/�����wvt�e����M$����:��
h`SIM�����_n��"ĥ��<�������#�Z��E�碆�/����� 8��t�,p���|�_w�"D���1��`1t�����K� -��*�� �B�ۛ����ۙ��� ��GX=߫w�5��~���w�j1q�ڰ���}��\9�/JT������ Z�j� >ĭL`q#E\�ht��r�z!�!Zp"���%�z\���Aֆ��9�����w��Ԓ�\�u������f�F���h��� ��!Cl�?�Z�qh�Ĉ
���r �����D���&�E�ȤǨ!���|���Q<"�� �2���C��h���\�k�k���[H�r�:$�;ÄTxBD���>�uɱ�;j�舦����;�!�@�� *�'����'@\wL�40X��%�H�$�pHi�h���rl�vQ�SO{���;�t"��Nq`Ԩ��$�9�� l$��@�6P5P\�d\��eYæ�����5 m ���t�[=�q�r��:��
h`SnIakCS��+"TC����~x�o}}�����H��y�֟�t�0Oɠ���������o����~��ߟ+� �������@�I�ɓ�<g����/��r�a�����Y3�p����8���g�_2������3T5?��Ig�_<���{�X>g�P@���g�cY�w��4a~Y�Ȝ�A|���q�i�U��Y���Y�]3z��R> ���cG^���[�mz�W��%))�_�^TV�~�'�m��u���ݿ^
R�V���&���tjs�b�iO?��j�� �|� �=Na4=5#��+ͺ�ǹ1ꁜ �j/�ݘ0�&�,g̔<j�^�ʑ�k(�M���:�4���(r�v�]*��C'�=��i[f~f�ܖ�$�kj�P@_J�����=��L1.�|*��A�͘: ��DJ�c�"�[/�)X"��})���&��E�<����j�G��vG�ϴ�2������8�A��q9! .^=h�V'��#�')9��?=<��Ce/>l�ay�e�f�Ax>:F��\W�~ǣ��5�zԸ�ڑ��G��dno���jԸ�a#5��ƫM� ?�衯� �tQe��< �a��_�LL;t��0Q�[ƃJ��xx/`�i'�8Q��n��D �ӡ�P$� �e�k���&��dΓ�������8��Kz�4��i����WĜ-:��=2���‰Ҩ����d���^'������j���<a�GK5�����x�a}SfNV v5b� m�u|�����z&Z^P��G3a2�8�_ϥGol�(S���_�e�f&�$���;���T�]�qܜ�I4�t��V �m�D�?���e�lͳ#(I7`� �i�����<�M�H{u#�T�a����c�l=������K��=]=y�9�_�g�q?��yS�׿��f����7������:舭�D�K}0��K2%�<�g}�%��o�� �6��&P@ ����wWW�<�E������<�W�/o��"Q�`Ex;ڹ�'w�����Aз��j/�ZV~ޭO>T2�*�����=t�����z�ǯ���0E�sbg`�G��C f��߻Ͷ>�h��Y3����ׅU�C>�v���<����^X�<JSzw�J 4�w�;���`�py����ʭj�b�ݗO*�ṪA�-:�3�\I'H��I����ݾ�_�2C?S�gMb}�$�+�]�$H�m
'VȠ��6���u��{<N�|�&��1O��3� �����O_����\6�!#+�سO����Ό]k�MSA*��� �u�l4��_� �m�¦f@3��ۺ�e�|6jq��Ң����ƶ�� ̋6h��l �w�-����@�E���,��Y�_�bc)
�Abbh�.�fh��"c�2p�A���Ԣ���(�VL�� ;ș�������� P���ٛ���u�'�nl�B�������'[#�Qj����^� �j�����5�����
hb�D{��.���e]�"h�0sHL<��e�P�C$v���A5�b}@��7F��B@���枽�1�1[~}�����L| P��cv������b����C�
IL|���7��'元����/�W �7�o�cx��O���N4���wN����o�"�8]�<�5J�=��J�qƨo|��(�_6�-7�͑��s�U������?^w���.��љ�"���&B��1�/��*�{��8)#�eeM��b��7�����x��.6� *�z:�%Fg��1�h���Z|��f����ǡv�EKf� �����`T3'��2>��q1�<?ڛ{�7�m�Jk�.�bIE�Z�xIf����}̀��F�/�k(!6i��m����Ȕ`�lEH,# 5o�+�z�O��d:1 S�sV`�����hb�[Н��\נ��@���Mf��CbQ�[�xj�(�m�}H�(0�3��{s���̅�E��������pڙ�ӳ2tV����� ���l<]B���&Gk�F 嶪��fa��~
8����H�ݶ�%ժ�W-����|��8i�EDž/6,]���_��7��%�l��NG���{� ap�A\y��0���BLLs:�Py�K
��@�yD[��7�>4'DyDs_�U�[�k�'fs(���Y��D�g�R�k���C�Z�f��G�%�(�*�\����l�N�����x+�~����I����V6ZC|}�AB�fCY m������r��N+��ߠֿѡ�Ht��8n�T��F}\O�ߵ$�}��b�x��}>P�j/E�F�p��[����X������P�͛zt%�xFڛ��gg[��A�">P'Lq�-�5)��ldW�(�֤�w�����[ �'��s"_.h��h.� vrzV�-�=8x\$����_<���z:w�k����9{�uPқ�о`(q������Q����������Y�|���:�&���*kri���t��X�1r$����z�\Yc�����/�������w��0�~#5�.
�o�s%�kj����kTPi1*a5Ab($C�Q�f�-1|(C.�d�V+�=k�@M\�f�$�3��W�����@ZE��&��N0�.HND��CCJS�a�5+nY������lAb:�9z��*&!�6�R&G�K�t5i(�@�C�{�a�؂�9^��AG�~������->w���{��G�)��4 �r����|e��X�Pdl�h��d{HH��2"LR}@O�\߅�F�(�i|kB���u��Јz320�{evn��%�5o�F�_��US2��q�ZD�pyΑ�ICZZ�2���XG��љ��ن�����xLg���̧��0/,,�Y҂ks۷��~�=tǛ����ŀ�f�,�|-ZS~�$�[K|�@���>�L} 9g�V!�FE���MQ�Y��>��� ����_������;d�S�R����a�<h�������׮[<s֊y �l��8����hٴ�g�/,�Ι/V��/~wѕu��P=
�?��I��\Z3�xPuGK�+��X�p�˻v�tQC�RR�8�����W�]XUռqcݪU�/Y�ƛ�%�x��#��><�� ��pwWW��M���B���"3T�9r�����).�-.ߵw��� u�W���w�����'�qf���U�Fg��l�Ըn�ǯ��w��;���g�A�a�kG���;�|Hni@�y����k7,Y��������3Ω{P�1���-�6ԯY���O��z]�&��G�8��p�������¢}�6�o�bՋ�����g� Ȕe�^{��C�W��'�|f���*F�.��gW�ζֺ���.������<_��݇�zV�����|ΛL�Jkjoz�y-ry�_���������������g���"u8k��S
ʫrKJ���ӹ��e�-�?������۫(�P}�K�����1��j��q�-X4�=T��G�p�ФA�Պ�I�*�� ��� ��HC��=��-Z��y9�ņU_Ԍ���;阩�f�'����MOI��۰⋚�#�G�t�JN:��)���+�V�nkhnظu����/�1E�g벃�9<�׋����3�� ��᷷�/�7�=��ߑ�JJ�p�ac�N�/+�-�߷w_g[������6|<s��=���INI�t�Qգ���Te�d�nmn�Ҵr��uKָ6� J��pQ��h�@YMٰ�#�F�.��.����ڻg_{��� �>0�(�𳧜|ݩ�/̝���D��MJ
4�ઑU���K���ݵw玝�k��.^�j�J��5e�?� \�������_�;�}Rh� �=.�,?�(wߞp3v�liٲr�G�}�/� >� ;� 6( ��!�|��W�V0j�(��� �>AI���(���)���}���A
(��
(�>\��X;�i�Φ�"�Ϗ�iء���׮���ov�m�e��8�ʻZV3�Nz������Ι��3~w��n3x銹 ��ڕ��^2dPAE���F����
���Ѻ�~�̯�]y���\�,�� &�vʴK.|�?���J����v�e���j�H;e�ر�ϔ��;쬳����ڛ[�������������e�x�����?��Ɵb��.�����)$955#'B����c���Οe�:0��ƍ ;���[7m4.��+-�hiR�ߑS��p�s���=��ܵ�K��_ZS{᝿�1�N�=.�9�� &�~Ό_������?��KO�ޏ�6II->*�f��- �_��T���KzzV~Aٰ�2mtB)�����/�t�)�iY�Ńkj'���#���\�詜y��4n�"�{5�q�R�P����z���m\��� 7��I�IG�v�'��߷������6ܟ{��g=�� wݦ1��T��[����S*j�?�O�p�a�<�l��vM�L��J
������Bb�HN�L/�.�9xԇ�}����5g~����Rgb98�9������'_ݳ������agN9��ә�pU�k�C!���h-
� ���s+GV�RJf^f�Ҳ�2Ot�ʯ���pS�����<i}�+ͻ�WW��uKJ�J+T\{h��z� ���A�bHh�\�A���i�O����ivC��1� E��P X8m:*�'��&4�� �G�c��|���{T2��
(���xQԛ�^��į��ö����>&���T)��se�ԗ&lFN�-�>X�>7D���r�>�9��>��ac��شa���;�F�vH��i��������ȼg׮�?�㵈��� _|�)�v�Uo��E�)@{|��"V���=�k�u��V�QTY�UU~�Ǟ����s�3#k��]�����R��7�[׺ek��a�CG=�����k�Ѽq#3�[7o)(/o\��{ǎ�Ύ��ҪѣsKJ�u�~�5 k�-y�Mr�I��Y߿�i��muu�v��.(,�(ߺr��șv�eg}ߴS���l�ؼaCauu�Кp%��5xrV�k��ݛ���v��>y��-����5;۶5oX���/,24��,�8~���9O�r���ֱ�a����LNM3Z�y�ڶ�[Jkj� סv򔛟z���nٴ�y`�i�p���u��:�
��*�W΃�.��u_�/Ѵ� ��C�|��LM�0���������ť��G��x!��!\����wŻo5�_K�[ӆ�F9I�)��}�a}޻��n���/V�ed��V�ݺy#*b����5��ZN��X��W����;�)��QRN��2��y������^H�<��A����^Q;(p%�L9PLZTi�'� ���YuI _�?^�� ��&A���I��^�YJ�ꏜ����G�2D$������[E�"=3;b�޷w_kc�yN>vjN~��c�|4g.s��SM��o���u���dg����skƎ����S�͙�����������$����#���>����MwN���V�䔤�n��a}޻{O��� 뷤f����To�k6M��@��0꒟^o�ZG��/6wwt� ���џx�)i)/��/���E ��A4����M��5A뭛��7n�ݽ+3?+�$�am��z"�3i����������M�[67�/r�r�j�7|�AY4JEUE��yI��ׯ���u���GeRr��w\lX����[�E}ú�ԌԜœ�ڊ֭�2���Nؠ�wa��,�2C�oJǑ1�� �/ �r,Xq'j�����;�����S00H����4 �����D�n"ۍ
whjQ��(|���Ymz��P�����
(��
��H�Z��A��y�=��!�Ƅ��֬���o�ܾC�95=����P�I_{��7�����*���㏽������q���~��5�;�f�qӦ���'+�.�������ӷ޶A'�U�{ˆ�$����[V/Xd;�;:�x���Ȱ>o^��S?����t�-�v�}����{�n�ƛ�g͸��q�\X���{�퇟sv���o���YB���~��?��\�,;lVA��7�h\����g��I��mÕ�r�'\}FN��n����Ɵ8�ֈ-�lX���{q���h��;�ܾ���z߯���zO����n�5��T{���Fׯ^E����?�ð>o]���;n�}�Kkj/����Ճ�5?��?y�7�����S�~�L ��W�?��3�1���Ϻ���ܸv� �� i�N��IJN��X9w��pQA+F�)�����߻��o]�V��,���@����g��8���8�я/3�Q��ʗF��A�a�����RRSR�"SvOWd����������r����'�ܴ����Eܟw��������m����D�%%'�v����~���������oŕe�z]aYqzV�W����B�+��嵃ʆTE�}�������?�r����g�����������s����D�>yܹ�^���:��C��^��A�cL���k�K��y�-#�� %
yJ
�v����iC㋿�u�s�,#+#��.���^v�e���Κ��9Kg/%�^QVSm�����h[}��gfs��a�% /8o�ei` $��F~�B�N (�D^�d�8og&?������s�l�ɏ����.�X�p�P'�����
(��
�/WF ���eJS"�z������|8{6�q}�� �k"�
��M����ӏ��-+���*7��0�y� ��o����ڟ5���[���o<����9�k�v������DΓ�9j�a&�6j�/2W/��P��Kf�
�%h떭�_���ch��!S�����]yY��Vr�ƍ]C{s�݉���?xյ�T��v��'�����a:l���/��w��"nV��ed~N�w���YN�����<�ƥ�>���t��ۙ���;���ڰd S�ü�g�s����3�����~̕W������Ã���!������Crb���g�Ϟe�:�������G]rUQ�`-޽��o=
�a�hް��߼��#rxyؤ#:����꣎��l�M9?ܕ5�,۶n~��+l��:��vv�o�`Qi ��$�{+jM���f���~3=���ۚ ���Y-��/�a"T)�Uܪ|��_����_*�_X�\�Ct#�N���!�=VZ��D��l�"n��z��M �Ȓ�2�5o2o�6?��Oq�P��d� �}��,@5��-J�% �@��N=�Ը 4i��$�����m�S}�6O��n�c�?G �$�۶/[�I�"��h��C�ޙt�i��>�=w��]����]��MO�SN�^XV�>���埢�g�Z����}=;#��q#�N9��� �� �P���X>��X>����;h]D@��m�nG_xr~i�Wz���M]�
�H��5.��?o6���DZu@�C��lme8 �����O�#��agQT�sm�o{��o��B2���N��n����u�ݗ�F�� _Z�`�|:;�՘�$���Y[��H4�h���l��ھE7�����EotB��yt;�H�`[��������7D�MQ�� I{����ba�kv]#�o�n+��[��V��Q/j�O��I/�(��6���G�O"<���E�t@P@�'6k�ͫtA+�[\X=r�q���]|� :���Z���?=����+f`�����]]�+�G���23GO=�̟WZR<�:b�^��v ���]�s�����3O';׶G��ȣ�՛�����q=����<+\��OM?���J��x���Ż{�?ܳ ���}��tC�*fB��S�^u��=�҄SNS-m��Cz���O��mF�|�ɿ��˶dOG�����y�����Ͻĸ��؃���;P�������1&)%5�D��R>�O�3�^Ąi��hi��&�&#oT&Lp�N�9f��.���7���G�:�Ζ��tĉ�D'� g��ʆӴ]���7ոx��7��Ù�vv/|}��s��ޜ���iY�ˇU'�&�9�*Ԕ��]�<������ظ,RNp�Zeĺޥ�,�3L��ݧ��ߧ��d�d\��ˋ����Kf-���ۼ5�c[�9��HJMr���p�L7�OSPQ� �{:���lL��V�T^����e���|��(�2�#��O{��Í.igq��X�y�! (�~E X~��#�N��X���
����egCMS�oONL{
��?M���/[ԯ���ja#�z?�O��O�����ݛ=%z�n)������Mh�۴�(sw{�Aӏ����5�O�˅U䍟Κ}�qG�/&���TGc����T���ӷ�>󖛵hXB;1-3��"�l���_6K�n��7O�9�Q6lّ�ejFFQuu^iivAAOgg� ;��?e��](��0=�۵��{͂��� �.~��++G��|ι�t���QXU}��~y�)���˟�l�注�͛��ܒR2=5=#���h��ߙ%�w٬�O��;����ڸ�%Y���aF��4�9���+���ڙ��]V;��=<��?n^�$A漀�� "s�ԡ��1h6�x\�uF=|�2�H# 2��n��=gMꅚ!(ҬcE^�/�E6mfd�!{,���敋���4�bH���#ׯXN<hʤ��������ݝ��-]ZQ�4g��\�D�}ŇKhG`�I�/X<����pqu����";��[�qw������!UW�y��g^ٲr�{h$���E�����#&�e��\��:�By��Q}� ��?u����L���`u'�k����Լ�<�UW/\�7J�L�J����J�Ft�sW��ׅ�[�uw��̴��eW��9O����&�C�x�`�OE�=�h��;#��ct�1#.l�$�D �ţ���?*N��꜆HM�=������� Q�A��� "�}�n�N�!`�� l 8���6��������c���11��T7 �;��q����؍�w,b�`u &� 0N�J�������jI?���lwtlk����]w�/G19���+.�1����U�Pe�0(0+?�_��R��������m��v�;��߷/)9y‰ӟ��o�ݦG�iG��/��ю�fc������tD��eÆ ���Q���ۛ���dd��nו#GN��CN>)%=]��dB�(
}hs��*�׬~��߼���M?���_W<8�h�#/��=��[�-��w�p+9�%dzI�0�ڛ�-)|���%�
��
��wh��Ϙ�xH�Y�fiW�8�D}�8�Q�;�g�s�Ewݛ��R=���{�u�ů�X��w�숡d`6�Ԣ��zv�ȱ6��G>����U��A�͋�����l�ߋ4�}��Bcaj�z�
~�2�;�%�uP��4������%��MQ#vS�ױ�ͬ��]�Q����S֣�r�����|Ff�qa��4�9c'M_L=�8�}���j�p� �zǮ��-�8-3}WW�9U�/�޶=�'�;�_�w����o��������� Y��c��]/?������䤪�C���w[뚖�=��w?�y#��dP�Q�����o�ȥMR�R��vt�F�����wWkf��9�M<y���es����$Ӆ�;�_��� |a��GU_���[��.~s�w��ڹ+��L�2�+D�^Ў�~�v�@�ٯ�A GǟZ�d��4�{.����=��r�f� ��E�0)���La5���y�^�yP@P�6k��ю������bhx���|�����@�p>)�> ZOg��E�]9g�N��~c��YC�ܹq�r�!��KG�*����-�4ݮ;[[�7��N 亐�Ԩ�<�;{�ϒY�c�<˭��}G\��i�}{�,}k��_8��g��������2�E�ݽ{�޽Z4��^Pn�L�6���|� *��˓��VZ��w�Es�r�E��Y�E�JjN���o}v� ��NrZZ\�1���r�4�<p�ң��׀JB'��-���@�P|�J� І5٠��[6���1�?���bظQ��cU���}��b�e����"f�B�b��!�篎�V������%�����|���DW��t���}���/?39���;t���'�e��(�<C�׺�yE`֚<�����//0[��S��-��%G]p�UU���ڏ�<�ݿ�Y��l��⓿q�-O�z�U�'G@9�����XH�&�f�kk&&5z�;��h:2�#S�/(���Sɐ5>&�~�Ǣ���:�_����Œ0�L�)��(��
(���pM�h����N���k=�V�x���4)9y����<��������h5��m��YY!���WӺ���c�E`?댥oF�1j'j��W�?��G�i�HMO���5��������h���n �Bم�Z[��������O�{��͟}�����.)9��[Gyd 擶�z�"��,��W �z��o��#��Wwڈ#��{�i�]/';͖�)rkɬ�B+}|��մ;g��ɿ�^� ������Kmu�����TTM>��CO�ZzvNrZڑ_3b��O|���==1�cp,���L�����!��ˉ^e��E>�� LF�KK�U�~q��� ��w7l`�sjƌ_L9�؜|s�_��;d�=�-ٙۛ���V 5˺�f6 �!2�e���[ڈ�d�S+�X��B��������N:yڄ㧦ge$��L=���?�?سkw8O��tۺ��[~��4��╝m�6����t�ǯ���~���c.96=;}��oʵ�����w~Y���;�C�7&�&y�Q#&�|��ٵ�� ��N�h9#��g:R Ρ]��� �� �k=B�af�T!�塑����~�0�xIo�"������� �ڙ�0[�K�����P@�]��ܕL��t| Ȼ9=��Ӗ$yLv�����]�>��������|�m��ִ!������d�d��7����5C�22|��Ն���~����1GM�+�>4�X�׏^}����e��}���6vwu�M�7�lAy�8<@^y���۵}{#���&ML��Φ�>{�o��;���]��]��-�6��Ց�4͜I;y�Z6���+-KNM���B�.eR�n)�+�3W��G9|(jD1�SX�&��eIIIš䕖�-�c{�ś ��6Z���m�INM��m[MN�/+�P����6Xg6q������
S���Jw�����g?������S� �X2����+�!����*I+�&��Ip���@:7���4��f���Q�f��i���U��m�BW_V=�Ø��"��K�!�ST�8b��� ����������C.��(L��+d#A��`�I�c���z���֯Xݼ5�z��I��G�/֯X�\WO��^K�g�d�����0t�@R(�㸵�� y�y��H���Νݝ�Gg��9+/�v�%@o,`4#Ι�������O����w������F�*�~�Y�u� i[TU���Jt��U��;�~���V l�8���}P6]�&mu��>�$B(Ϗi�߻������f,���<����G᩶y��f��?����۵����ל��s�?�8���;��QH7,�v���@!_9��F�c̶>��� fa0����ζ�߮s�@4�΃�$^u7%��q����0贃�H�� �.�
(��
(���6`m�1Z��[𞾣�?X����5����~k,� �����9�
��أ�*� "�y�-��1�� ��a ;ZZ�,�P؏.4�S��������{v46�8���7r�)�6؉C:�|���!K�{�ɞ����F��&�|J<���>B=���RJzz/�j��t5���w�0�A'�]вq�j�]�tVA�,��:I)�c�M�,���\ؗ� ���q����]=<�آ�i�9��#z)�
�<_�<���8��<���6ՀlZ�3 �/��CZ� {������Y�Ӑ��7�e���mY��-�g���(�V()4� �)��qS�ȭu��T�f�K�+<� 8Ʋ8;\��/���kfm�<Ҝq�;ww�2x�����d��0���ۻv��N�ɣ�P�������G>����3��T�w�s�/zu�9�N����A�]��������n������<<�FMt� �}1^���e��3z�>�}N]X�F B�9�x��P@ �ő��߿[b�{�KG�rt{̱�-������b�/>^��m?��G�c�<����ZE�xŸ8������y��d�|����e�� �6��d�ǯ�i�|A��j��5C���b�׏^�/��×^5.N�������̼�㮼�\ʾ����;ڍĬ�|��Y |�_�>�~���9�i�j-�P����?�>�׮[��&���f=���%���^2׊�~3)5��IFnޑ�^mdX�ڋ"����׵��r�1Z�Ѳ�M���������`��uF�QS�1�A��,�����(>�r�!7�>ø7��B�*h�N��!UO�C�󩢧��'��5���Ct�I��ɸ=��ROg�ע<!)�*�� ���� �O�M�s����b�]|H�M��F��9)��v�668I���XUO�yV���}��#��h?h��4K��x@�|���K�:,؊��u+V3%��m����&����sΩI)�̤���5����O�]@�l�Nj����y�d��0��~��1k�9G���/�c�SO��씴�!D���>i�� 5���6\���B4���lΧFڱ�mU���_�^�����F�7�U6���y�%o~b6ci��e���3���y���z9��SB��/P�4�F��`���F;��6h�O��'�xR��f?��2��mUV^�Λ��ʏ&�w���A�h�#�o:����W
��Zg4�����|k�~@}M��;�ĵ�O҃㷅�o_��J#F������t��>wkJ�h0������Kg�����4R�]��S������C�uE͵EU����Cƍu�5E/� �k\��Fc�ݽ�g��Sp�/~jZ�_yM�]9���<��a\^5o��Y���w����-[��������ևAᔛ�|"3/����Kfδںj�q1��3ʇ����RDѻO��usx$�����c�!����C�p�s3���ª��$�KÅ����z��F��+�C���Smu[��D����Xnq��S8���<��iɍ�~�l���u�sr�1'T�OƳ/Rc5���Q�!î��?J����_�D>�[�|����������2�QM�[eU��*G�s�@���:���|�~�~��1���$��ńq�CbuD�g�P5��Q�at��[U��O�^f�7�tV���LH(]�Xt�X�Z��0��Z��
`���^���b��_O�Rm4������� ��?��~��^�L_����͑�ŕeW��;9��vO�S���֌���k�ϧ���u���nԢn��r]�1…��r*� �9��3�3.Z�4�ط����+j�.����T �t{fN��H�5&%s:r��#&�y>������*���ח .a'�|�b�yz��%������n�05#�i�����
����2��ln�� OSg4� MX{5
lC�.0c�%�� Y���e�$fh����.��d(�Š�#���ʌr볬��Q'h���9FB���=����AȊ P����Ř8�>��0�D�� ��uG��V�( %7�N�v�+���r�,{��������ba�G���X�����s_\[�i��$Wl�(+~�Sϐ!|��DK���< I�A���ct�Ƭ�?du"��"���b��D�^KW3Z��օ� (!��������=<{�߉��x���
y6:��"j�)*���v�������ڽ (��*���_���������޹/�\9���k"����r�/^�d��y�|�7�]�˟��B�Ǎ���O��5�l�ҹ�-��(���tȐu��<x��H*`�#�����'~�����ښ��wu��u.B}�% �thgk���ܒ��9E&ZBw{��{~c9�ݴo��p����}rjꐃ�㿯6�[ߺeKy���!
���9�׿!�zÒO�֯/6,+?��g����i�g�[�����QS�0-�/e��wϞ������`rJjaU� }dGcC�ڵ�:;Ӳ����ʆ��+za{r��Uէ����G�������u� 1���h�`����O>����I8�K�}{����{.���ᖬ{ЭϽּa][ݖҚڢj�%;;^�ïeF��K�kVl^����$%'�OOn\��ζּ����CÓݽ�γ�k��?�{�O�?�l؈����e������¢���9��>���?x��K���E�؃o��K��7nۺI߿?5#�xp -��z4�Y����M%'_s����}�ն-���h��p�?\|J�c�v�Y?�Ygk˶��M ݝ��J�+���+>x[6@���I6D���Q�E�X��`L��V���� ����&V<�r��ע �>�]�\v6��T� ��J��e�<�q�=��C�ʌ������%-Պ�*]�ͿHۑLU�\u��' _[�0ס.�(j&ts�z��j կ�u�?�� -P\0�{����XO{Pښ�a�N�=���e�}mv��� g�N���u��}�^깋o�>9%��vȷwgk}C[SkIUyay������=�����}i��?�V��j�Л~��ֆ���f]��[���<��/�lkokl�h�޽�����������UV.�Ԏ_���g��^:�ۗ���a����{;Z�ooh�j�����)�+�,ټr�3w>d�)H�ׄ|fЭ � ^��Խ'z.�ӵk��3���y�ҡe��SGk{�֖�Ξ���Š����{/�����/�7���^�_V�ڍg�|�y��v��3�sN�ζ�m-��2 � ����{��0���6xY�pG|� %U#��!�l �7���kg2B�U�M�עa �
���(?�ʅ�6'�v�
��� �#�r�,`j��:�o� ����4c!�MN�`"��� ;@�I�����ֿ�`֣�! �l��r��Z�m��vH`�2��s7LZNLo�G-��f|s6��\R'h|�\�����B]�YȖh����_!� �v�
����4Hp�+�1�ю��(;kȎ�!͌(��7H����������)���|��ԭ�/�@����L/t��D��X�A>���p�t'X�h�AD�̸��#�8dܘ�J������vٞh|�E/�ָ~����U>�&�!EՕ�Yf��a��ii�����W�]r���W^��� �C`.������wg[͓�۾X���+����_T�JJ�9"��3}��G������-�"v��'o��M�>�[R�E�x��7��Z�~�'����~r�� �D
/�ă�<���4^�7 ͡P���"��Ƥ��xw�˿����[�\ ,����������������->2�q~��×��YGk�|�
.-3�O���ᒇM<��UX9��>���ٜ�m�[����E��DH���?dμ�����g�?�O�����ef%���Վ D�"���C�����D�Qs(U���A��V�������?�[~���=��T>FU"� n�8M@D>J��r����Pה��RC�K�.8�rJ)�!N�� �v� ��ː�2��ʈG��_�����Qf���A�n`��3�s,�ڒ���Z��Ӷ�jPd��֋�"������:�a ������E ��⑳��0݆��b@K�1 �ӆ�����AǸo��k���_��e��BI��AU�]���׼��3���A���-]�⟞<���e�'�$�oD�m̪C#sDa^��g[�΢�/�&5�������鬛//�. ���?�-%��ȗ�F��D�ߏ�Q$\�ێ��^ Q�]2ݻ�e;����s���¿��?d�����v�ݵ��r��W�S�=~���~�E�GE��s�r�A3��t������O���ꐓ��0 �hi�v�F�Rȥk�j5Z���K��#�L�%Vi�|���̜�H@kC� ϴ*����Q�q��p�j�2Ďs[���dߋMp_��t�P���)�b�#���GB�?h��������|�ރ^zB���������}�����Ϥge� �>�G�<{�=F�M�W���+?猪Q#+����IIO�h���ܲu՚e��s�_���y���PH߿��o ����7;�� �pPymmVA^V~^fnnw{G����ׯ��`���.�״n�����s�|�AU�GVV�l�\�j��%�.yc��M������'�v�!��TP^�[ZJJ������޹m�����͛�|�ylҲi�#7�0��S�<�lXm�КԌ�p���;vn�ްf���m\��{!����ُ>2��c<�r�����ܜ}���77u475�[��K��hl���iް��o]3񌳫�T1rtAEe��M _�ڴt�Yo�fr�_���_r�U7�Y<hhjf�����[W/xg�6;[ݪ����K=����J�֖ �m-�6lZ�IOG;�6~�q��g�k��.,������ζ���a�����Fn�b�#߼�+�/�YT=$-\���H5}�sG� ^x����jԸ� ��$#'̇�;��4�]��k3׭�E��Y@qK������= �h��ݠ^ʛ�u;�]C�
���/X>~�V����1oB���[K�F��s@����€���bN%������_�w��S�j�� �_\����q��ͫ�._�1�IA0���4n�z�qG�TW�?Yy����{v�?]�-�7lٺ� b��;ڶW��ؠ �ҳ�z:wvn�hZ�e�y����Ұv�?�w�q�� �,T^\]����s{G�Ӵ�n��Ϥ��;��O]i���� �_�=q�_'�phYMy���갲��ѵmk���7�t�x��e��_�p���Sn8m��M{v�Y���m�+#6�œ�쌞����;�5,~� �2�l�i{:3.����6�x�R�'�8�y2�1M-<d�ג�"ɷ�g����+�ۧ�Wt3��b�ۙ�/K�,��0.�V��I�C� A������I��GT���J�8 ɼ�ԞI^;N��ӓ�-�ˣ\�_d:y^��8M�������j������?�\1o�xn��9�d�ﰑ�d� ���U#e�Eg4�t�|�yP�!'��Bq��,e
������k/h�#�>��d�/��<pA�����W;#2u�_�~O,�hȳ�@��?��E���C��|��e|�m�4��s z܎�lL���B!Ҥ`��d���9��Ԍ������?�1p��W��2�^zqm����s�Q4�At*�W��m�^ԻR�R��n���� .���A�ʖ"5�P�O�ctP���j`HU����٤���ӑ����"Q2�A"@�?�8���'ĕ�
���&���� >�T_1��)=�$�;UE'��tcZЁ��:^o�G!Q[��d��`z9נ%�31��c�-M 9����O�����M�(�{��\,R�4���_�䮲�!���-6���Ѯ��I�Է�HK-�brP�PS�t�a6�E��cq?'����29���L�x� �Gpm�D\� @p��.?��ў��E���ޱ2�H0�F�o��+�.dž�ԍ$�X��Y~��/(l��C�_�{Y�� %;�ݎ�д��V<t�{+�q��^����4aNS�r�H(�T9(��(��
(��G��386Ƿ�1�vf�܌�hh�Ё�ߑ�D����ѽ��
View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment