first post

This commit is contained in:
2012-05-25 09:03:56 -04:00
commit 6a753904b7
609 changed files with 252648 additions and 0 deletions

9
Nodejs-Socketio-Mysql-Demo/node_modules/.bin/wd generated vendored Normal file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
if [ -x "`dirname "$0"`/node" ]; then
"`dirname "$0"`/node" "`dirname "$0"`/../wd/lib/bin.js" "$@"
ret=$?
else
node "`dirname "$0"`/../wd/lib/bin.js" "$@"
ret=$?
fi
exit $ret

6
Nodejs-Socketio-Mysql-Demo/node_modules/.bin/wd.cmd generated vendored Normal file
View File

@@ -0,0 +1,6 @@
:: Created by npm, please don't edit manually.
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\wd\lib\bin.js" %*
) ELSE (
node "%~dp0\..\wd\lib\bin.js" %*
)

View File

@@ -0,0 +1,4 @@
/node_modules
/test/config.js
*.swo
*.un~

View File

@@ -0,0 +1,4 @@
language: node_js
node_js:
- 0.4
- 0.6

19
Nodejs-Socketio-Mysql-Demo/node_modules/mysql/License generated vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2010 Felix Geisendörfer (felix@debuggable.com)
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.

13
Nodejs-Socketio-Mysql-Demo/node_modules/mysql/Makefile generated vendored Normal file
View File

@@ -0,0 +1,13 @@
SHELL := /bin/bash
NODE = node
test:
@$(NODE) test/run.js
benchmark-node-mysql:
@find benchmark/node-mysql/*.js | xargs -n 1 -t node
benchmark-php:
@find benchmark/php/*.php | xargs -n 1 -t php
benchmark-all: benchmark-node-mysql benchmark-php
benchmark: benchmark-node-mysql
.PHONY: test benchmark

306
Nodejs-Socketio-Mysql-Demo/node_modules/mysql/Readme.md generated vendored Normal file
View File

@@ -0,0 +1,306 @@
# node-mysql
[![Build Status](https://secure.travis-ci.org/felixge/node-mysql.png?branch=master)](http://travis-ci.org/felixge/node-mysql)
## Purpose
A pure node.js JavaScript Client implementing the [MySQL protocol](http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol).
## Support this module
If you like this module, check out and spread the word about our service
[transloadit.com][]. We provide file uploading and encoding functionality to
other applications, and have performed billions of queries with this module so
far.
[transloadit.com]: http://transloadit.com/
## Installation
```
npm install mysql
```
**Important**: If you are upgrading from 0.9.1 or below, there have been
backwards incompatible changes in the API. Please read the [upgrading guide][].
[upgrading guide]: https://github.com/felixge/node-mysql/wiki/Upgrading-to-0.9.2+
## Usage
``` javascript
var mysql = require('mysql');
var TEST_DATABASE = 'nodejs_mysql_test';
var TEST_TABLE = 'test';
var client = mysql.createClient({
user: 'root',
password: 'root',
});
client.query('CREATE DATABASE '+TEST_DATABASE, function(err) {
if (err && err.number != mysql.ERROR_DB_CREATE_EXISTS) {
throw err;
}
});
// If no callback is provided, any errors will be emitted as `'error'`
// events by the client
client.query('USE '+TEST_DATABASE);
client.query(
'CREATE TEMPORARY TABLE '+TEST_TABLE+
'(id INT(11) AUTO_INCREMENT, '+
'title VARCHAR(255), '+
'text TEXT, '+
'created DATETIME, '+
'PRIMARY KEY (id))'
);
client.query(
'INSERT INTO '+TEST_TABLE+' '+
'SET title = ?, text = ?, created = ?',
['super cool', 'this is a nice text', '2010-08-16 10:00:23']
);
var query = client.query(
'INSERT INTO '+TEST_TABLE+' '+
'SET title = ?, text = ?, created = ?',
['another entry', 'because 2 entries make a better test', '2010-08-16 12:42:15']
);
client.query(
'SELECT * FROM '+TEST_TABLE,
function selectCb(err, results, fields) {
if (err) {
throw err;
}
console.log(results);
console.log(fields);
client.end();
}
);
```
## API
### mysql.createClient([options])
Creates a new client instance. Any client property can be set using the
`options` object.
### client.host = 'localhost'
The host to connect to.
### client.port = 3306
The port to connect to.
### client.user = null
The username to authenticate as.
### client.password = null
The password to use.
### client.database = null
The name of the database to connect to (optional).
### client.debug = false
Prints incoming and outgoing packets, useful for development / testing purposes.
### client.flags = Client.defaultFlags
Connection flags send to the server.
### client.query(sql, [params, cb])
Sends a `sql` query to the server. `'?'` characters can be used as placeholders
for an array of `params` that will be safely escaped before sending the final
query.
This method returns a `Query` object which can be used to stream incoming row
data.
**Warning:** `sql` statements with multiple queries separated by semicolons
are not supported yet.
### client.ping([cb])
Sends a ping command to the server.
### client.useDatabase(database, [cb])
Same as issuing a `'USE <database>'` query.
### client.statistics([cb])
Returns some server statistics provided by MySql.
### client.format(sql, params)
Allows to safely insert a list of `params` into a `sql` string using the
placeholder mechanism described above.
### client.escape(val)
Escapes a single `val` for use inside of a sql string.
### client.destroy()
Forces the client connection / socket to be destroyed right away.
### client.end([cb])
Schedule a COM_QUIT packet for closing the connection. All currently queued
queries will still execute before the graceful termination of the connection
is attempted.
### client event: 'error' (err)
When the client has no callback / delegate for an error, it is emitted with this
event instead.
### new mysql.Query()
Query objects are not meant to be invoked manually. To get a query object, use
the `client.query` API.
### query event: 'error' (err)
Emitted when mysql returns an error packet for the query.
### query event: 'field' (field)
Emitted upon receiving a field packet from mysql.
### query event: 'row' (row)
Emitted upon receiving a row. An option for streaming the contents of the row
itself will be made available soon.
### query event: 'end' ([result])
Emitted once the query is finished. In case there is no result set, a `result`
parameter is provided which contains the information from the mysql OK packet.
## FAQ
### How do I compile this module?
This module is written entirely in JavaScript. There is no dependency on external
C libraries such as libmysql. That means you don't have to compile this module
at all.
### How can I retrieve the id from the last inserted record?
client.query('INSERT INTO my_table SET title = ?', function(err, info) {
console.log(info.insertId);
});
### How can I find out the number of rows affected by the last query?
client.query('UPDATE my_table SET title = ?', function(err, info) {
console.log(info.affectedRows);
});
## Todo
At this point the module is ready to be tried out, but a lot of things are yet to be done:
* Implement retry
* Pause / resume
* Remaining mysql commands
* Prepared Statements
* Packet's > 16 MB
* Compression
* Decide how to handle queries with multiple statements
* Transactions
A stop-gap solution to support multiple statements and transactions is
available. Check it out here: http://github.com/bminer/node-mysql-queues
## Contributors
[Click here][contributors] for a full list of contributors.
[contributors]: https://github.com/felixge/node-mysql/contributors
## Sponsors
* [Joyent](http://www.joyent.com/) - Main sponsor, you should check out their [node.js hosting](https://no.de/).
* [pinkbike.com](http://pinkbike.com/) - The most awesome biking site there is
This is a rather large project requiring a significant amount of my limited resources.
If your company could benefit from a well-engineered non-blocking mysql driver, and
wants to support this project, I would greatly appriciate any sponsorship you may be
able to provide. All sponsors will get lifetime display in this readme, priority
support on problems, and votes on roadmap decisions. If you are interested, contact
me at [felix@debuggable.com](mailto:felix@debuggable.com) for details.
Of course I'm also happy about code contributions. If you're interested in
working on features, just get in touch so we can talk about API design and
testing.
[transloadit]: http://transloadit.com/
## Changelog
### v0.9.6
* Escape array values so they produce sql arrays (Roger Castells, Colin Smith)
* docs: mention mysql transaction stop gap solution (Blake Miner)
* docs: Mention affectedRows in FAQ (Michael Baldwin)
### v0.9.5
* Fix #142 Driver stalls upon reconnect attempt that's immediately closed
* Add travis build
* Switch to urun as a test runner
* Switch to utest for unit tests
* Remove fast-or-slow dependency for tests
* Split integration tests into individual files again
### v0.9.4
* Expose package.json as `mysql.PACKAGE` (#104)
### v0.9.3
* Set default `client.user` to root
* Fix #91: Client#format should not mutate params array
* Fix #94: TypeError in client.js
* Parse decimals as string (vadimg)
### v0.9.2
* The underlaying socket connection is now managed implicitly rather than explicitly.
* Check the [upgrading guide][] for a full list of changes.
### v0.9.1
* Fix issue #49 / `client.escape()` throwing exceptions on objects. (Nick Payne)
* Drop < v0.4.x compatibility. From now on you need node v0.4.x to use this module.
### Older releases
These releases were done before starting to maintain the above Changelog:
* [v0.9.0](https://github.com/felixge/node-mysql/compare/v0.8.0...v0.9.0)
* [v0.8.0](https://github.com/felixge/node-mysql/compare/v0.7.0...v0.8.0)
* [v0.7.0](https://github.com/felixge/node-mysql/compare/v0.6.0...v0.7.0)
* [v0.6.0](https://github.com/felixge/node-mysql/compare/v0.5.0...v0.6.0)
* [v0.5.0](https://github.com/felixge/node-mysql/compare/v0.4.0...v0.5.0)
* [v0.4.0](https://github.com/felixge/node-mysql/compare/v0.3.0...v0.4.0)
* [v0.3.0](https://github.com/felixge/node-mysql/compare/v0.2.0...v0.3.0)
* [v0.2.0](https://github.com/felixge/node-mysql/compare/v0.1.0...v0.2.0)
* [v0.1.0](https://github.com/felixge/node-mysql/commits/v0.1.0)
## License
node-mysql is licensed under the MIT license.

View File

@@ -0,0 +1,42 @@
require('../../test/common');
var Client = require('mysql/client'),
client = Client(TEST_CONFIG);
client.connect();
client.query('CREATE DATABASE '+TEST_DB, function(err) {
if (err && err.number != Client.ERROR_DB_CREATE_EXISTS) {
throw err;
}
});
client.query('USE '+TEST_DB);
client.query('DROP TABLE IF EXISTS '+TEST_TABLE);
client.query(
'CREATE TABLE '+TEST_TABLE+' ('+
'id INT(11) AUTO_INCREMENT, '+
'title VARCHAR(255), '+
'text TEXT, '+
'created DATETIME, '+
'PRIMARY KEY (id));',
function(err) {
if (err) throw err;
var start = +new Date, inserts = 0, total = 10000;
function insertOne() {
client.query('INSERT INTO '+TEST_TABLE+' SET title = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"', function() {
inserts++;
if (inserts < total) {
insertOne();
} else {
var duration = (+new Date - start) / 1000,
insertsPerSecond = inserts / duration;
console.log('%d inserts / second', insertsPerSecond.toFixed(2));
console.log('%d ms', +new Date - start);
client.end();
}
});
}
insertOne();
}
);

View File

@@ -0,0 +1,39 @@
// Last v8 profile when running this test for 500k rows:
// https://gist.github.com/f85c38010c038e5efe2e
var common = require('../../test/common');
var Client = require('../../lib/client'),
client = common.createClient(),
rows = 0;
client.typeCast = false;
client.query('CREATE DATABASE '+common.TEST_DB, function(err) {
//if (err && err.number != Client.ERROR_DB_CREATE_EXISTS) {
//throw err;
//}
});
client.query('USE '+common.TEST_DB);
var selectStart = +new Date;
function query() {
client
.query('SELECT * FROM '+common.TEST_TABLE)
.on('row', function(row) {
rows++;
})
.on('end', function() {
if (rows < 10000) {
query();
return;
}
var duration = (+new Date - selectStart) / 1000,
rowsPerSecond = rows / duration;
console.log('%d rows / second', rowsPerSecond.toFixed(2));
console.log('%d ms', +new Date - selectStart);
client.end();
});
}
query();

View File

@@ -0,0 +1,40 @@
<?php
$INSERTS = 10000;
$config = array(
'host' => 'localhost',
'port' => 3306,
'user' => 'root',
'password' => 'root',
'db' => 'node_mysql_test',
'table' => 'post',
);
extract($config);
$connection = mysql_connect($host, $user, $password);
mysql_query('USE '.$db, $connection);
mysql_query('CREATE TEMPORARY TABLE '.$table.' ('.
'id INT(11) AUTO_INCREMENT, '.
'title VARCHAR(255), '.
'text TEXT, '.
'created DATETIME, '.
'PRIMARY KEY (id));', $connection);
$start = microtime(true);
for ($i = 0; $i < $INSERTS; $i++) {
mysql_query('INSERT INTO '.$table.' SET title = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";', $connection);
}
$duration = (microtime(true) - $start);
$insertsPerSecond = $INSERTS / $duration;
echo sprintf("%d inserts / second\n", $insertsPerSecond);
echo sprintf("%d ms\n", $duration * 1000);
$start = microtime(true);
$q = mysql_query('SELECT * FROM '.$table);
while ($a = mysql_fetch_assoc($q)) {
}
$duration = (microtime(true) - $start);
$rowsPerSecond = $INSERTS / $duration;
echo sprintf("%d rows / second\n", $rowsPerSecond);
echo sprintf("%d ms\n", $duration * 1000);
?>

View File

@@ -0,0 +1 @@
module.exports = require('./lib/mysql');

View File

@@ -0,0 +1,164 @@
var Buffer = require('buffer').Buffer;
var crypto = require('crypto');
function sha1(msg) {
var hash = crypto.createHash('sha1');
hash.update(msg);
// hash.digest() does not output buffers yet
return hash.digest('binary');
};
exports.sha1 = sha1;
function xor(a, b) {
a = new Buffer(a, 'binary');
b = new Buffer(b, 'binary');
var result = new Buffer(a.length);
for (var i = 0; i < a.length; i++) {
result[i] = (a[i] ^ b[i]);
}
return result;
};
exports.xor = xor;
exports.token = function(password, scramble) {
if (!password) {
return new Buffer(0);
}
var stage1 = sha1(password);
var stage2 = sha1(stage1);
var stage3 = sha1(scramble.toString('binary') + stage2);
return xor(stage3, stage1);
};
// This is a port of sql/password.c:hash_password which needs to be used for
// pre-4.1 passwords.
exports.hashPassword = function(password) {
var nr = [0x5030, 0x5735],
add = 7,
nr2 = [0x1234, 0x5671],
result = new Buffer(8);
if (typeof password == 'string'){
password = new Buffer(password);
}
for (var i = 0; i < password.length; i++) {
var c = password[i];
if (c == 32 || c == 9) {
// skip space in password
continue;
}
// nr^= (((nr & 63)+add)*c)+ (nr << 8);
// nr = xor(nr, add(mul(add(and(nr, 63), add), c), shl(nr, 8)))
nr = this.xor32(nr, this.add32(this.mul32(this.add32(this.and32(nr, [0,63]), [0,add]), [0,c]), this.shl32(nr, 8)));
// nr2+=(nr2 << 8) ^ nr;
// nr2 = add(nr2, xor(shl(nr2, 8), nr))
nr2 = this.add32(nr2, this.xor32(this.shl32(nr2, 8), nr));
// add+=tmp;
add += c;
}
this.int31Write(result, nr, 0);
this.int31Write(result, nr2, 4);
return result;
};
exports.randomInit = function(seed1, seed2) {
return {
max_value: 0x3FFFFFFF,
max_value_dbl: 0x3FFFFFFF,
seed1: seed1 % 0x3FFFFFFF,
seed2: seed2 % 0x3FFFFFFF
};
};
exports.myRnd = function(r){
r.seed1 = (r.seed1 * 3 + r.seed2) % r.max_value;
r.seed2 = (r.seed1 + r.seed2 + 33) % r.max_value;
return r.seed1 / r.max_value_dbl;
};
exports.scramble323 = function(message, password) {
var to = new Buffer(8),
hashPass = this.hashPassword(password),
hashMessage = this.hashPassword(message.slice(0, 8)),
seed1 = this.int32Read(hashPass, 0) ^ this.int32Read(hashMessage, 0),
seed2 = this.int32Read(hashPass, 4) ^ this.int32Read(hashMessage, 4),
r = this.randomInit(seed1, seed2);
for (var i = 0; i < 8; i++){
to[i] = Math.floor(this.myRnd(r) * 31) + 64;
}
var extra = (Math.floor(this.myRnd(r) * 31));
for (var i = 0; i < 8; i++){
to[i] ^= extra;
}
return to;
};
exports.fmt32 = function(x){
var a = x[0].toString(16),
b = x[1].toString(16);
if (a.length == 1) a = '000'+a;
if (a.length == 2) a = '00'+a;
if (a.length == 3) a = '0'+a;
if (b.length == 1) b = '000'+b;
if (b.length == 2) b = '00'+b;
if (b.length == 3) b = '0'+b;
return '' + a + '/' + b;
};
exports.xor32 = function(a,b){
return [a[0] ^ b[0], a[1] ^ b[1]];
};
exports.add32 = function(a,b){
var w1 = a[1] + b[1],
w2 = a[0] + b[0] + ((w1 & 0xFFFF0000) >> 16);
return [w2 & 0xFFFF, w1 & 0xFFFF];
};
exports.mul32 = function(a,b){
// based on this example of multiplying 32b ints using 16b
// http://www.dsprelated.com/showmessage/89790/1.php
var w1 = a[1] * b[1],
w2 = (((a[1] * b[1]) >> 16) & 0xFFFF) + ((a[0] * b[1]) & 0xFFFF) + (a[1] * b[0] & 0xFFFF);
return [w2 & 0xFFFF, w1 & 0xFFFF];
};
exports.and32 = function(a,b){
return [a[0] & b[0], a[1] & b[1]];
};
exports.shl32 = function(a,b){
// assume b is 16 or less
var w1 = a[1] << b,
w2 = (a[0] << b) | ((w1 & 0xFFFF0000) >> 16);
return [w2 & 0xFFFF, w1 & 0xFFFF];
};
exports.int31Write = function(buffer, number, offset) {
buffer[offset] = (number[0] >> 8) & 0x7F;
buffer[offset + 1] = (number[0]) & 0xFF;
buffer[offset + 2] = (number[1] >> 8) & 0xFF;
buffer[offset + 3] = (number[1]) & 0xFF;
};
exports.int32Read = function(buffer, offset){
return (buffer[offset] << 24)
+ (buffer[offset+1] << 16)
+ (buffer[offset+2] << 8)
+ (buffer[offset+3]);
};

View File

@@ -0,0 +1,461 @@
var util = require('util');
var Socket = require('net').Socket;
var auth = require('./auth');
var constants = require('./constants');
var Parser = require('./parser');
var OutgoingPacket = require('./outgoing_packet');
var Query = require('./query');
var EventEmitter = require('events').EventEmitter;
function Client() {
if (!(this instanceof Client) || arguments.length) {
throw new Error('deprecated: use mysql.createClient() instead');
}
EventEmitter.call(this);
this.host = 'localhost';
this.port = 3306;
this.user = 'root';
this.password = null;
this.database = '';
this.typeCast = true;
this.flags = Client.defaultFlags;
this.maxPacketSize = 0x01000000;
this.charsetNumber = constants.UTF8_UNICODE_CI;
this.debug = false;
this.ending = false;
this.connected = false;
this._greeting = null;
this._queue = [];
this._socket = null;
this._parser = null;
};
util.inherits(Client, EventEmitter);
module.exports = Client;
Client.prototype.connect = function() {
throw new Error('deprecated: connect() is now done automatically.');
};
Client.prototype._connect = function() {
this.destroy();
var socket = this._socket = new Socket();
var parser = this._parser = new Parser();
var self = this;
socket
.on('error', this._connectionErrorHandler())
.on('data', parser.write.bind(parser))
.on('end', function() {
if (self.ending) {
// @todo destroy()?
self.connected = false;
self.ending = false;
if (self._queue.length) {
self._connect();
}
return;
}
if (!self.connected) {
this.emit('error', new Error('reconnection attempt failed before connection was fully set up'));
return;
}
self._connect();
})
.connect(this.port, this.host);
parser.on('packet', this._handlePacket.bind(this));
};
Client.prototype.query = function(sql, params, cb) {
if (Array.isArray(params)) {
sql = this.format(sql, params);
} else {
cb = arguments[1];
}
var query = new Query({
typeCast: this.typeCast,
sql: sql
});
var self = this;
if (cb) {
var rows = [], fields = {};
query
.on('error', function(err) {
cb(err);
self._dequeue();
})
.on('field', function(field) {
fields[field.name] = field;
})
.on('row', function(row) {
rows.push(row);
})
.on('end', function(result) {
if (result) {
cb(null, result);
} else {
cb(null, rows, fields);
}
self._dequeue();
});
} else {
query
.on('error', function(err) {
if (query.listeners('error').length <= 1) {
self.emit('error', err);
}
self._dequeue();
})
.on('end', function(result) {
self._dequeue();
});
}
this._enqueue(function query() {
var packet = new OutgoingPacket(1 + Buffer.byteLength(sql, 'utf-8'));
packet.writeNumber(1, constants.COM_QUERY);
packet.write(sql, 'utf-8');
self.write(packet);
}, query);
return query;
};
Client.prototype.write = function(packet) {
if (this.debug) {
console.log('-> %s', packet.buffer.inspect());
}
this._socket.write(packet.buffer);
};
Client.prototype.format = function(sql, params) {
var escape = this.escape;
params = params.concat();
sql = sql.replace(/\?/g, function() {
if (params.length == 0) {
throw new Error('too few parameters given');
}
return escape(params.shift());
});
if (params.length) {
throw new Error('too many parameters given');
}
return sql;
};
Client.prototype.escape = function(val) {
var escape = this.escape;
if (val === undefined || val === null) {
return 'NULL';
}
switch (typeof val) {
case 'boolean': return (val) ? 'true' : 'false';
case 'number': return val+'';
}
if (Array.isArray(val)) {
var sanitized = val.map( function( v ) { return escape( v ); } );
return "'" + sanitized.join( "','" ) + "'";
}
if (typeof val === 'object') {
val = (typeof val.toISOString === 'function')
? val.toISOString()
: val.toString();
}
val = val.replace(/[\0\n\r\b\t\\\'\"\x1a]/g, function(s) {
switch(s) {
case "\0": return "\\0";
case "\n": return "\\n";
case "\r": return "\\r";
case "\b": return "\\b";
case "\t": return "\\t";
case "\x1a": return "\\Z";
default: return "\\"+s;
}
});
return "'"+val+"'";
};
Client.prototype.ping = function(cb) {
var self = this;
this._enqueue(function ping() {
var packet = new OutgoingPacket(1);
packet.writeNumber(1, constants.COM_PING);
self.write(packet);
}, cb);
};
Client.prototype.statistics = function(cb) {
var self = this;
this._enqueue(function statistics() {
var packet = new OutgoingPacket(1);
packet.writeNumber(1, constants.COM_STATISTICS);
self.write(packet);
}, cb);
};
Client.prototype.useDatabase = function(database, cb) {
var self = this;
this._enqueue(function useDatabase() {
var packet = new OutgoingPacket(1 + Buffer.byteLength(database, 'utf-8'));
packet.writeNumber(1, constants.COM_INIT_DB);
packet.write(database, 'utf-8');
self.write(packet);
}, cb);
};
Client.prototype.destroy = function() {
if (this._socket) {
this._socket.destroy();
}
this._socket = null;
this._parser = null;
this.connected = false;
}
Client.prototype.end = function(cb) {
var self = this;
this.ending = true;
this._enqueue(function end() {
var packet = new OutgoingPacket(1);
packet.writeNumber(1, constants.COM_QUIT);
self.write(packet);
// @todo handle clean shut down properly
if (cb) {
self._socket.on('end', cb);
}
self._dequeue();
}, cb);
};
Client.prototype._enqueue = function(fn, delegate) {
if (!this._socket) {
this._connect();
}
this._queue.push({fn: fn, delegate: delegate});
if (this._queue.length === 1 && this.connected) {
fn();
}
};
Client.prototype._dequeue = function() {
this._queue.shift();
if (!this._queue.length) {
return;
}
if (!this.connected) {
this._connect();
return;
}
this._queue[0].fn();
};
Client.prototype._handlePacket = function(packet) {
if (this.debug) {
this._debugPacket(packet);
}
if (packet.type == Parser.GREETING_PACKET) {
this._sendAuth(packet);
return;
}
if (packet.type == Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET) {
this._sendOldAuth(this._greeting);
return;
}
if (!this.connected) {
if (packet.type != Parser.ERROR_PACKET) {
this.connected = true;
if (this._queue.length) this._queue[0].fn();
return;
}
this._connectionErrorHandler()(Client._packetToUserObject(packet));
return;
}
// @TODO Simplify the code below and above as well
var type = packet.type;
var task = this._queue[0];
var delegate = (task)
? task.delegate
: null;
if (delegate instanceof Query) {
delegate._handlePacket(packet);
return;
}
if (type != Parser.ERROR_PACKET) {
this.connected = true;
if (delegate) {
delegate(null, Client._packetToUserObject(packet));
}
} else {
packet = Client._packetToUserObject(packet);
if (delegate) {
delegate(packet);
} else {
this.emit('error', packet);
}
}
this._dequeue();
};
Client.prototype._connectionErrorHandler = function() {
return function(err) {
this.destroy();
var task = this._queue[0];
var delegate = (task)
? task.delegate
: null;
if (delegate instanceof Query) {
delegate.emit('error', err);
return;
}
if (!delegate) {
this.emit('error', err);
} else {
delegate(err);
this._queue.shift();
}
if (this._queue.length) {
this._connect();
}
}.bind(this);
};
Client.prototype._sendAuth = function(greeting) {
var token = auth.token(this.password, greeting.scrambleBuffer);
var packetSize = (
4 + 4 + 1 + 23 +
this.user.length + 1 +
token.length + 1 +
this.database.length + 1
);
var packet = new OutgoingPacket(packetSize, greeting.number+1);
packet.writeNumber(4, this.flags);
packet.writeNumber(4, this.maxPacketSize);
packet.writeNumber(1, this.charsetNumber);
packet.writeFiller(23);
packet.writeNullTerminated(this.user);
packet.writeLengthCoded(token);
packet.writeNullTerminated(this.database);
this.write(packet);
// Keep a reference to the greeting packet. We might receive a
// USE_OLD_PASSWORD_PROTOCOL_PACKET as a response, in which case we will need
// the greeting packet again. See _sendOldAuth()
this._greeting = greeting;
};
Client._packetToUserObject = function(packet) {
var userObject = (packet.type == Parser.ERROR_PACKET)
? new Error()
: {};
for (var key in packet) {
var newKey = key;
if (key == 'type' || key == 'number' || key == 'length' || key == 'received') {
continue;
}
if (key == 'errorMessage') {
newKey = 'message';
} else if (key == 'errorNumber') {
newKey = 'number';
}
userObject[newKey] = packet[key];
}
return userObject;
};
Client.prototype._debugPacket = function(packet) {
var packetName = null;
for (var key in Parser) {
if (!key.match(/_PACKET$/)) {
continue;
}
if (Parser[key] == packet.type) {
packetName = key;
break;
}
}
console.log('<- %s: %j', packetName, packet);
};
Client.prototype._sendOldAuth = function(greeting) {
var token = auth.scramble323(greeting.scrambleBuffer, this.password);
var packetSize = (
token.length + 1
);
var packet = new OutgoingPacket(packetSize, greeting.number+3);
// I could not find any official documentation for this, but from sniffing
// the mysql command line client, I think this is the right way to send the
// scrambled token after receiving the USE_OLD_PASSWORD_PROTOCOL_PACKET.
packet.write(token);
packet.writeFiller(1);
this.write(packet);
};
Client.defaultFlags =
constants.CLIENT_LONG_PASSWORD
| constants.CLIENT_FOUND_ROWS
| constants.CLIENT_LONG_FLAG
| constants.CLIENT_CONNECT_WITH_DB
| constants.CLIENT_ODBC
| constants.CLIENT_LOCAL_FILES
| constants.CLIENT_IGNORE_SPACE
| constants.CLIENT_PROTOCOL_41
| constants.CLIENT_INTERACTIVE
| constants.CLIENT_IGNORE_SIGPIPE
| constants.CLIENT_TRANSACTIONS
| constants.CLIENT_RESERVED
| constants.CLIENT_SECURE_CONNECTION
| constants.CLIENT_MULTI_STATEMENTS
| constants.CLIENT_MULTI_RESULTS;

View File

@@ -0,0 +1,673 @@
var hashish = require('hashish');
// Connections Flags
hashish.update(exports, {
CLIENT_LONG_PASSWORD : 1,
CLIENT_FOUND_ROWS : 2,
CLIENT_LONG_FLAG : 4,
CLIENT_CONNECT_WITH_DB : 8,
CLIENT_NO_SCHEMA : 16,
CLIENT_COMPRESS : 32,
CLIENT_ODBC : 64,
CLIENT_LOCAL_FILES : 128,
CLIENT_IGNORE_SPACE : 256,
CLIENT_PROTOCOL_41 : 512,
CLIENT_INTERACTIVE : 1024,
CLIENT_SSL : 2048,
CLIENT_IGNORE_SIGPIPE : 4096,
CLIENT_TRANSACTIONS : 8192,
CLIENT_RESERVED : 16384,
CLIENT_SECURE_CONNECTION : 32768,
CLIENT_MULTI_STATEMENTS : 65536,
CLIENT_MULTI_RESULTS : 131072,
});
// Commands
hashish.update(exports, {
COM_SLEEP : 0x00,
COM_QUIT : 0x01,
COM_INIT_DB : 0x02,
COM_QUERY : 0x03,
COM_FIELD_LIST : 0x04,
COM_CREATE_DB : 0x05,
COM_DROP_DB : 0x06,
COM_REFRESH : 0x07,
COM_SHUTDOWN : 0x08,
COM_STATISTICS : 0x09,
COM_PROCESS_INFO : 0x0a,
COM_CONNECT : 0x0b,
COM_PROCESS_KILL : 0x0c,
COM_DEBUG : 0x0d,
COM_PING : 0x0e,
COM_TIME : 0x0f,
COM_DELAYED_INSERT : 0x10,
COM_CHANGE_USER : 0x11,
COM_BINLOG_DUMP : 0x12,
COM_TABLE_DUMP : 0x13,
COM_CONNECT_OUT : 0x14,
COM_REGISTER_SLAVE : 0x15,
COM_STMT_PREPARE : 0x16,
COM_STMT_EXECUTE : 0x17,
COM_STMT_SEND_LONG_DATA : 0x18,
COM_STMT_CLOSE : 0x19,
COM_STMT_RESET : 0x1a,
COM_SET_OPTION : 0x1b,
COM_STMT_FETCH : 0x1c,
});
// Collations / Charsets
hashish.update(exports, {
BIG5_CHINESE_CI : 1,
LATIN2_CZECH_CS : 2,
DEC8_SWEDISH_CI : 3,
CP850_GENERAL_CI : 4,
LATIN1_GERMAN1_CI : 5,
HP8_ENGLISH_CI : 6,
KOI8R_GENERAL_CI : 7,
LATIN1_SWEDISH_CI : 8,
LATIN2_GENERAL_CI : 9,
SWE7_SWEDISH_CI : 10,
ASCII_GENERAL_CI : 11,
UJIS_JAPANESE_CI : 12,
SJIS_JAPANESE_CI : 13,
CP1251_BULGARIAN_CI : 14,
LATIN1_DANISH_CI : 15,
HEBREW_GENERAL_CI : 16,
TIS620_THAI_CI : 18,
EUCKR_KOREAN_CI : 19,
LATIN7_ESTONIAN_CS : 20,
LATIN2_HUNGARIAN_CI : 21,
KOI8U_GENERAL_CI : 22,
CP1251_UKRAINIAN_CI : 23,
GB2312_CHINESE_CI : 24,
GREEK_GENERAL_CI : 25,
CP1250_GENERAL_CI : 26,
LATIN2_CROATIAN_CI : 27,
GBK_CHINESE_CI : 28,
CP1257_LITHUANIAN_CI : 29,
LATIN5_TURKISH_CI : 30,
LATIN1_GERMAN2_CI : 31,
ARMSCII8_GENERAL_CI : 32,
UTF8_GENERAL_CI : 33,
CP1250_CZECH_CS : 34,
UCS2_GENERAL_CI : 35,
CP866_GENERAL_CI : 36,
KEYBCS2_GENERAL_CI : 37,
MACCE_GENERAL_CI : 38,
MACROMAN_GENERAL_CI : 39,
CP852_GENERAL_CI : 40,
LATIN7_GENERAL_CI : 41,
LATIN7_GENERAL_CS : 42,
MACCE_BIN : 43,
CP1250_CROATIAN_CI : 44,
LATIN1_BIN : 47,
LATIN1_GENERAL_CI : 48,
LATIN1_GENERAL_CS : 49,
CP1251_BIN : 50,
CP1251_GENERAL_CI : 51,
CP1251_GENERAL_CS : 52,
MACROMAN_BIN : 53,
CP1256_GENERAL_CI : 57,
CP1257_BIN : 58,
CP1257_GENERAL_CI : 59,
BINARY : 63,
ARMSCII8_BIN : 64,
ASCII_BIN : 65,
CP1250_BIN : 66,
CP1256_BIN : 67,
CP866_BIN : 68,
DEC8_BIN : 69,
GREEK_BIN : 70,
HEBREW_BIN : 71,
HP8_BIN : 72,
KEYBCS2_BIN : 73,
KOI8R_BIN : 74,
KOI8U_BIN : 75,
LATIN2_BIN : 77,
LATIN5_BIN : 78,
LATIN7_BIN : 79,
CP850_BIN : 80,
CP852_BIN : 81,
SWE7_BIN : 82,
UTF8_BIN : 83,
BIG5_BIN : 84,
EUCKR_BIN : 85,
GB2312_BIN : 86,
GBK_BIN : 87,
SJIS_BIN : 88,
TIS620_BIN : 89,
UCS2_BIN : 90,
UJIS_BIN : 91,
GEOSTD8_GENERAL_CI : 92,
GEOSTD8_BIN : 93,
LATIN1_SPANISH_CI : 94,
CP932_JAPANESE_CI : 95,
CP932_BIN : 96,
EUCJPMS_JAPANESE_CI : 97,
EUCJPMS_BIN : 98,
CP1250_POLISH_CI : 99,
UCS2_UNICODE_CI : 128,
UCS2_ICELANDIC_CI : 129,
UCS2_LATVIAN_CI : 130,
UCS2_ROMANIAN_CI : 131,
UCS2_SLOVENIAN_CI : 132,
UCS2_POLISH_CI : 133,
UCS2_ESTONIAN_CI : 134,
UCS2_SPANISH_CI : 135,
UCS2_SWEDISH_CI : 136,
UCS2_TURKISH_CI : 137,
UCS2_CZECH_CI : 138,
UCS2_DANISH_CI : 139,
UCS2_LITHUANIAN_CI : 140,
UCS2_SLOVAK_CI : 141,
UCS2_SPANISH2_CI : 142,
UCS2_ROMAN_CI : 143,
UCS2_PERSIAN_CI : 144,
UCS2_ESPERANTO_CI : 145,
UCS2_HUNGARIAN_CI : 146,
UTF8_UNICODE_CI : 192,
UTF8_ICELANDIC_CI : 193,
UTF8_LATVIAN_CI : 194,
UTF8_ROMANIAN_CI : 195,
UTF8_SLOVENIAN_CI : 196,
UTF8_POLISH_CI : 197,
UTF8_ESTONIAN_CI : 198,
UTF8_SPANISH_CI : 199,
UTF8_SWEDISH_CI : 200,
UTF8_TURKISH_CI : 201,
UTF8_CZECH_CI : 202,
UTF8_DANISH_CI : 203,
UTF8_LITHUANIAN_CI : 204,
UTF8_SLOVAK_CI : 205,
UTF8_SPANISH2_CI : 206,
UTF8_ROMAN_CI : 207,
UTF8_PERSIAN_CI : 208,
UTF8_ESPERANTO_CI : 209,
UTF8_HUNGARIAN_CI : 210,
});
// Error numbers
// from: http://dev.mysql.com/doc/refman/5.0/en/error-messages-server.html
hashish.update(exports, {
ERROR_HASHCHK : 1000,
ERROR_NISAMCHK : 1001,
ERROR_NO : 1002,
ERROR_YES : 1003,
ERROR_CANT_CREATE_FILE : 1004,
ERROR_CANT_CREATE_TABLE : 1005,
ERROR_CANT_CREATE_DB : 1006,
ERROR_DB_CREATE_EXISTS : 1007,
ERROR_DB_DROP_EXISTS : 1008,
ERROR_DB_DROP_DELETE : 1009,
ERROR_DB_DROP_RMDIR : 1010,
ERROR_CANT_DELETE_FILE : 1011,
ERROR_CANT_FIND_SYSTEM_REC : 1012,
ERROR_CANT_GET_STAT : 1013,
ERROR_CANT_GET_WD : 1014,
ERROR_CANT_LOCK : 1015,
ERROR_CANT_OPEN_FILE : 1016,
ERROR_FILE_NOT_FOUND : 1017,
ERROR_CANT_READ_DIR : 1018,
ERROR_CANT_SET_WD : 1019,
ERROR_CHECKREAD : 1020,
ERROR_DISK_FULL : 1021,
ERROR_DUP_KEY : 1022,
ERROR_ERROR_ON_CLOSE : 1023,
ERROR_ERROR_ON_READ : 1024,
ERROR_ERROR_ON_RENAME : 1025,
ERROR_ERROR_ON_WRITE : 1026,
ERROR_FILE_USED : 1027,
ERROR_FILSORT_ABORT : 1028,
ERROR_FORM_NOT_FOUND : 1029,
ERROR_GET_ERRNO : 1030,
ERROR_ILLEGAL_HA : 1031,
ERROR_KEY_NOT_FOUND : 1032,
ERROR_NOT_FORM_FILE : 1033,
ERROR_NOT_KEYFILE : 1034,
ERROR_OLD_KEYFILE : 1035,
ERROR_OPEN_AS_READONLY : 1036,
ERROR_OUTOFMEMORY : 1037,
ERROR_OUT_OF_SORTMEMORY : 1038,
ERROR_UNEXPECTED_EOF : 1039,
ERROR_CON_COUNT_ERROR : 1040,
ERROR_OUT_OF_RESOURCES : 1041,
ERROR_BAD_HOST_ERROR : 1042,
ERROR_HANDSHAKE_ERROR : 1043,
ERROR_DBACCESS_DENIED_ERROR : 1044,
ERROR_ACCESS_DENIED_ERROR : 1045,
ERROR_NO_DB_ERROR : 1046,
ERROR_UNKNOWN_COM_ERROR : 1047,
ERROR_BAD_NULL_ERROR : 1048,
ERROR_BAD_DB_ERROR : 1049,
ERROR_TABLE_EXISTS_ERROR : 1050,
ERROR_BAD_TABLE_ERROR : 1051,
ERROR_NON_UNIQ_ERROR : 1052,
ERROR_SERVERROR_SHUTDOWN : 1053,
ERROR_BAD_FIELD_ERROR : 1054,
ERROR_WRONG_FIELD_WITH_GROUP : 1055,
ERROR_WRONG_GROUP_FIELD : 1056,
ERROR_WRONG_SUM_SELECT : 1057,
ERROR_WRONG_VALUE_COUNT : 1058,
ERROR_TOO_LONG_IDENT : 1059,
ERROR_DUP_FIELDNAME : 1060,
ERROR_DUP_KEYNAME : 1061,
ERROR_DUP_ENTRY : 1062,
ERROR_WRONG_FIELD_SPEC : 1063,
ERROR_PARSE_ERROR : 1064,
ERROR_EMPTY_QUERY : 1065,
ERROR_NONUNIQ_TABLE : 1066,
ERROR_INVALID_DEFAULT : 1067,
ERROR_MULTIPLE_PRI_KEY : 1068,
ERROR_TOO_MANY_KEYS : 1069,
ERROR_TOO_MANY_KEY_PARTS : 1070,
ERROR_TOO_LONG_KEY : 1071,
ERROR_KEY_COLUMN_DOES_NOT_EXITS : 1072,
ERROR_BLOB_USED_AS_KEY : 1073,
ERROR_TOO_BIG_FIELDLENGTH : 1074,
ERROR_WRONG_AUTO_KEY : 1075,
ERROR_READY : 1076,
ERROR_NORMAL_SHUTDOWN : 1077,
ERROR_GOT_SIGNAL : 1078,
ERROR_SHUTDOWN_COMPLETE : 1079,
ERROR_FORCING_CLOSE : 1080,
ERROR_IPSOCK_ERROR : 1081,
ERROR_NO_SUCH_INDEX : 1082,
ERROR_WRONG_FIELD_TERMINATORS : 1083,
ERROR_BLOBS_AND_NO_TERMINATED : 1084,
ERROR_TEXTFILE_NOT_READABLE : 1085,
ERROR_FILE_EXISTS_ERROR : 1086,
ERROR_LOAD_INFO : 1087,
ERROR_ALTERROR_INFO : 1088,
ERROR_WRONG_SUB_KEY : 1089,
ERROR_CANT_REMOVE_ALL_FIELDS : 1090,
ERROR_CANT_DROP_FIELD_OR_KEY : 1091,
ERROR_INSERT_INFO : 1092,
ERROR_UPDATE_TABLE_USED : 1093,
ERROR_NO_SUCH_THREAD : 1094,
ERROR_KILL_DENIED_ERROR : 1095,
ERROR_NO_TABLES_USED : 1096,
ERROR_TOO_BIG_SET : 1097,
ERROR_NO_UNIQUE_LOGFILE : 1098,
ERROR_TABLE_NOT_LOCKED_FOR_WRITE : 1099,
ERROR_TABLE_NOT_LOCKED : 1100,
ERROR_BLOB_CANT_HAVE_DEFAULT : 1101,
ERROR_WRONG_DB_NAME : 1102,
ERROR_WRONG_TABLE_NAME : 1103,
ERROR_TOO_BIG_SELECT : 1104,
ERROR_UNKNOWN_ERROR : 1105,
ERROR_UNKNOWN_PROCEDURE : 1106,
ERROR_WRONG_PARAMCOUNT_TO_PROCEDURE : 1107,
ERROR_WRONG_PARAMETERS_TO_PROCEDURE : 1108,
ERROR_UNKNOWN_TABLE : 1109,
ERROR_FIELD_SPECIFIED_TWICE : 1110,
ERROR_INVALID_GROUP_FUNC_USE : 1111,
ERROR_UNSUPPORTED_EXTENSION : 1112,
ERROR_TABLE_MUST_HAVE_COLUMNS : 1113,
ERROR_RECORD_FILE_FULL : 1114,
ERROR_UNKNOWN_CHARACTERROR_SET : 1115,
ERROR_TOO_MANY_TABLES : 1116,
ERROR_TOO_MANY_FIELDS : 1117,
ERROR_TOO_BIG_ROWSIZE : 1118,
ERROR_STACK_OVERRUN : 1119,
ERROR_WRONG_OUTERROR_JOIN : 1120,
ERROR_NULL_COLUMN_IN_INDEX : 1121,
ERROR_CANT_FIND_UDF : 1122,
ERROR_CANT_INITIALIZE_UDF : 1123,
ERROR_UDF_NO_PATHS : 1124,
ERROR_UDF_EXISTS : 1125,
ERROR_CANT_OPEN_LIBRARY : 1126,
ERROR_CANT_FIND_DL_ENTRY : 1127,
ERROR_FUNCTION_NOT_DEFINED : 1128,
ERROR_HOST_IS_BLOCKED : 1129,
ERROR_HOST_NOT_PRIVILEGED : 1130,
ERROR_PASSWORD_ANONYMOUS_USER : 1131,
ERROR_PASSWORD_NOT_ALLOWED : 1132,
ERROR_PASSWORD_NO_MATCH : 1133,
ERROR_UPDATE_INFO : 1134,
ERROR_CANT_CREATE_THREAD : 1135,
ERROR_WRONG_VALUE_COUNT_ON_ROW : 1136,
ERROR_CANT_REOPEN_TABLE : 1137,
ERROR_INVALID_USE_OF_NULL : 1138,
ERROR_REGEXP_ERROR : 1139,
ERROR_MIX_OF_GROUP_FUNC_AND_FIELDS : 1140,
ERROR_NONEXISTING_GRANT : 1141,
ERROR_TABLEACCESS_DENIED_ERROR : 1142,
ERROR_COLUMNACCESS_DENIED_ERROR : 1143,
ERROR_ILLEGAL_GRANT_FOR_TABLE : 1144,
ERROR_GRANT_WRONG_HOST_OR_USER : 1145,
ERROR_NO_SUCH_TABLE : 1146,
ERROR_NONEXISTING_TABLE_GRANT : 1147,
ERROR_NOT_ALLOWED_COMMAND : 1148,
ERROR_SYNTAX_ERROR : 1149,
ERROR_DELAYED_CANT_CHANGE_LOCK : 1150,
ERROR_TOO_MANY_DELAYED_THREADS : 1151,
ERROR_ABORTING_CONNECTION : 1152,
ERROR_NET_PACKET_TOO_LARGE : 1153,
ERROR_NET_READ_ERROR_FROM_PIPE : 1154,
ERROR_NET_FCNTL_ERROR : 1155,
ERROR_NET_PACKETS_OUT_OF_ORDER : 1156,
ERROR_NET_UNCOMPRESS_ERROR : 1157,
ERROR_NET_READ_ERROR : 1158,
ERROR_NET_READ_INTERRUPTED : 1159,
ERROR_NET_ERROR_ON_WRITE : 1160,
ERROR_NET_WRITE_INTERRUPTED : 1161,
ERROR_TOO_LONG_STRING : 1162,
ERROR_TABLE_CANT_HANDLE_BLOB : 1163,
ERROR_TABLE_CANT_HANDLE_AUTO_INCREMENT : 1164,
ERROR_DELAYED_INSERT_TABLE_LOCKED : 1165,
ERROR_WRONG_COLUMN_NAME : 1166,
ERROR_WRONG_KEY_COLUMN : 1167,
ERROR_WRONG_MRG_TABLE : 1168,
ERROR_DUP_UNIQUE : 1169,
ERROR_BLOB_KEY_WITHOUT_LENGTH : 1170,
ERROR_PRIMARY_CANT_HAVE_NULL : 1171,
ERROR_TOO_MANY_ROWS : 1172,
ERROR_REQUIRES_PRIMARY_KEY : 1173,
ERROR_NO_RAID_COMPILED : 1174,
ERROR_UPDATE_WITHOUT_KEY_IN_SAFE_MODE : 1175,
ERROR_KEY_DOES_NOT_EXITS : 1176,
ERROR_CHECK_NO_SUCH_TABLE : 1177,
ERROR_CHECK_NOT_IMPLEMENTED : 1178,
ERROR_CANT_DO_THIS_DURING_AN_TRANSACTION : 1179,
ERROR_ERROR_DURING_COMMIT : 1180,
ERROR_ERROR_DURING_ROLLBACK : 1181,
ERROR_ERROR_DURING_FLUSH_LOGS : 1182,
ERROR_ERROR_DURING_CHECKPOINT : 1183,
ERROR_NEW_ABORTING_CONNECTION : 1184,
ERROR_DUMP_NOT_IMPLEMENTED : 1185,
ERROR_FLUSH_MASTERROR_BINLOG_CLOSED : 1186,
ERROR_INDEX_REBUILD : 1187,
ERROR_MASTER : 1188,
ERROR_MASTERROR_NET_READ : 1189,
ERROR_MASTERROR_NET_WRITE : 1190,
ERROR_FT_MATCHING_KEY_NOT_FOUND : 1191,
ERROR_LOCK_OR_ACTIVE_TRANSACTION : 1192,
ERROR_UNKNOWN_SYSTEM_VARIABLE : 1193,
ERROR_CRASHED_ON_USAGE : 1194,
ERROR_CRASHED_ON_REPAIR : 1195,
ERROR_WARNING_NOT_COMPLETE_ROLLBACK : 1196,
ERROR_TRANS_CACHE_FULL : 1197,
ERROR_SLAVE_MUST_STOP : 1198,
ERROR_SLAVE_NOT_RUNNING : 1199,
ERROR_BAD_SLAVE : 1200,
ERROR_MASTERROR_INFO : 1201,
ERROR_SLAVE_THREAD : 1202,
ERROR_TOO_MANY_USERROR_CONNECTIONS : 1203,
ERROR_SET_CONSTANTS_ONLY : 1204,
ERROR_LOCK_WAIT_TIMEOUT : 1205,
ERROR_LOCK_TABLE_FULL : 1206,
ERROR_READ_ONLY_TRANSACTION : 1207,
ERROR_DROP_DB_WITH_READ_LOCK : 1208,
ERROR_CREATE_DB_WITH_READ_LOCK : 1209,
ERROR_WRONG_ARGUMENTS : 1210,
ERROR_NO_PERMISSION_TO_CREATE_USER : 1211,
ERROR_UNION_TABLES_IN_DIFFERENT_DIR : 1212,
ERROR_LOCK_DEADLOCK : 1213,
ERROR_TABLE_CANT_HANDLE_FT : 1214,
ERROR_CANNOT_ADD_FOREIGN : 1215,
ERROR_NO_REFERENCED_ROW : 1216,
ERROR_ROW_IS_REFERENCED : 1217,
ERROR_CONNECT_TO_MASTER : 1218,
ERROR_QUERY_ON_MASTER : 1219,
ERROR_ERROR_WHEN_EXECUTING_COMMAND : 1220,
ERROR_WRONG_USAGE : 1221,
ERROR_WRONG_NUMBERROR_OF_COLUMNS_IN_SELECT : 1222,
ERROR_CANT_UPDATE_WITH_READLOCK : 1223,
ERROR_MIXING_NOT_ALLOWED : 1224,
ERROR_DUP_ARGUMENT : 1225,
ERROR_USERROR_LIMIT_REACHED : 1226,
ERROR_SPECIFIC_ACCESS_DENIED_ERROR : 1227,
ERROR_LOCAL_VARIABLE : 1228,
ERROR_GLOBAL_VARIABLE : 1229,
ERROR_NO_DEFAULT : 1230,
ERROR_WRONG_VALUE_FOR_VAR : 1231,
ERROR_WRONG_TYPE_FOR_VAR : 1232,
ERROR_VAR_CANT_BE_READ : 1233,
ERROR_CANT_USE_OPTION_HERE : 1234,
ERROR_NOT_SUPPORTED_YET : 1235,
ERROR_MASTERROR_FATAL_ERROR_READING_BINLOG : 1236,
ERROR_SLAVE_IGNORED_TABLE : 1237,
ERROR_INCORRECT_GLOBAL_LOCAL_VAR : 1238,
ERROR_WRONG_FK_DEF : 1239,
ERROR_KEY_REF_DO_NOT_MATCH_TABLE_REF : 1240,
ERROR_OPERAND_COLUMNS : 1241,
ERROR_SUBQUERY_NO_1_ROW : 1242,
ERROR_UNKNOWN_STMT_HANDLER : 1243,
ERROR_CORRUPT_HELP_DB : 1244,
ERROR_CYCLIC_REFERENCE : 1245,
ERROR_AUTO_CONVERT : 1246,
ERROR_ILLEGAL_REFERENCE : 1247,
ERROR_DERIVED_MUST_HAVE_ALIAS : 1248,
ERROR_SELECT_REDUCED : 1249,
ERROR_TABLENAME_NOT_ALLOWED_HERE : 1250,
ERROR_NOT_SUPPORTED_AUTH_MODE : 1251,
ERROR_SPATIAL_CANT_HAVE_NULL : 1252,
ERROR_COLLATION_CHARSET_MISMATCH : 1253,
ERROR_SLAVE_WAS_RUNNING : 1254,
ERROR_SLAVE_WAS_NOT_RUNNING : 1255,
ERROR_TOO_BIG_FOR_UNCOMPRESS : 1256,
ERROR_ZLIB_Z_MEM_ERROR : 1257,
ERROR_ZLIB_Z_BUF_ERROR : 1258,
ERROR_ZLIB_Z_DATA_ERROR : 1259,
ERROR_CUT_VALUE_GROUP_CONCAT : 1260,
ERROR_WARN_TOO_FEW_RECORDS : 1261,
ERROR_WARN_TOO_MANY_RECORDS : 1262,
ERROR_WARN_NULL_TO_NOTNULL : 1263,
ERROR_WARN_DATA_OUT_OF_RANGE : 1264,
WARN_DATA_TRUNCATED : 1265,
ERROR_WARN_USING_OTHERROR_HANDLER : 1266,
ERROR_CANT_AGGREGATE_2COLLATIONS : 1267,
ERROR_DROP_USER : 1268,
ERROR_REVOKE_GRANTS : 1269,
ERROR_CANT_AGGREGATE_3COLLATIONS : 1270,
ERROR_CANT_AGGREGATE_NCOLLATIONS : 1271,
ERROR_VARIABLE_IS_NOT_STRUCT : 1272,
ERROR_UNKNOWN_COLLATION : 1273,
ERROR_SLAVE_IGNORED_SSL_PARAMS : 1274,
ERROR_SERVERROR_IS_IN_SECURE_AUTH_MODE : 1275,
ERROR_WARN_FIELD_RESOLVED : 1276,
ERROR_BAD_SLAVE_UNTIL_COND : 1277,
ERROR_MISSING_SKIP_SLAVE : 1278,
ERROR_UNTIL_COND_IGNORED : 1279,
ERROR_WRONG_NAME_FOR_INDEX : 1280,
ERROR_WRONG_NAME_FOR_CATALOG : 1281,
ERROR_WARN_QC_RESIZE : 1282,
ERROR_BAD_FT_COLUMN : 1283,
ERROR_UNKNOWN_KEY_CACHE : 1284,
ERROR_WARN_HOSTNAME_WONT_WORK : 1285,
ERROR_UNKNOWN_STORAGE_ENGINE : 1286,
ERROR_WARN_DEPRECATED_SYNTAX : 1287,
ERROR_NON_UPDATABLE_TABLE : 1288,
ERROR_FEATURE_DISABLED : 1289,
ERROR_OPTION_PREVENTS_STATEMENT : 1290,
ERROR_DUPLICATED_VALUE_IN_TYPE : 1291,
ERROR_TRUNCATED_WRONG_VALUE : 1292,
ERROR_TOO_MUCH_AUTO_TIMESTAMP_COLS : 1293,
ERROR_INVALID_ON_UPDATE : 1294,
ERROR_UNSUPPORTED_PS : 1295,
ERROR_GET_ERRMSG : 1296,
ERROR_GET_TEMPORARY_ERRMSG : 1297,
ERROR_UNKNOWN_TIME_ZONE : 1298,
ERROR_WARN_INVALID_TIMESTAMP : 1299,
ERROR_INVALID_CHARACTERROR_STRING : 1300,
ERROR_WARN_ALLOWED_PACKET_OVERFLOWED : 1301,
ERROR_CONFLICTING_DECLARATIONS : 1302,
ERROR_SP_NO_RECURSIVE_CREATE : 1303,
ERROR_SP_ALREADY_EXISTS : 1304,
ERROR_SP_DOES_NOT_EXIST : 1305,
ERROR_SP_DROP_FAILED : 1306,
ERROR_SP_STORE_FAILED : 1307,
ERROR_SP_LILABEL_MISMATCH : 1308,
ERROR_SP_LABEL_REDEFINE : 1309,
ERROR_SP_LABEL_MISMATCH : 1310,
ERROR_SP_UNINIT_VAR : 1311,
ERROR_SP_BADSELECT : 1312,
ERROR_SP_BADRETURN : 1313,
ERROR_SP_BADSTATEMENT : 1314,
ERROR_UPDATE_LOG_DEPRECATED_IGNORED : 1315,
ERROR_UPDATE_LOG_DEPRECATED_TRANSLATED : 1316,
ERROR_QUERY_INTERRUPTED : 1317,
ERROR_SP_WRONG_NO_OF_ARGS : 1318,
ERROR_SP_COND_MISMATCH : 1319,
ERROR_SP_NORETURN : 1320,
ERROR_SP_NORETURNEND : 1321,
ERROR_SP_BAD_CURSOR_QUERY : 1322,
ERROR_SP_BAD_CURSOR_SELECT : 1323,
ERROR_SP_CURSOR_MISMATCH : 1324,
ERROR_SP_CURSOR_ALREADY_OPEN : 1325,
ERROR_SP_CURSOR_NOT_OPEN : 1326,
ERROR_SP_UNDECLARED_VAR : 1327,
ERROR_SP_WRONG_NO_OF_FETCH_ARGS : 1328,
ERROR_SP_FETCH_NO_DATA : 1329,
ERROR_SP_DUP_PARAM : 1330,
ERROR_SP_DUP_VAR : 1331,
ERROR_SP_DUP_COND : 1332,
ERROR_SP_DUP_CURS : 1333,
ERROR_SP_CANT_ALTER : 1334,
ERROR_SP_SUBSELECT_NYI : 1335,
ERROR_STMT_NOT_ALLOWED_IN_SF_OR_TRG : 1336,
ERROR_SP_VARCOND_AFTERROR_CURSHNDLR : 1337,
ERROR_SP_CURSOR_AFTERROR_HANDLER : 1338,
ERROR_SP_CASE_NOT_FOUND : 1339,
ERROR_FPARSERROR_TOO_BIG_FILE : 1340,
ERROR_FPARSERROR_BAD_HEADER : 1341,
ERROR_FPARSERROR_EOF_IN_COMMENT : 1342,
ERROR_FPARSERROR_ERROR_IN_PARAMETER : 1343,
ERROR_FPARSERROR_EOF_IN_UNKNOWN_PARAMETER : 1344,
ERROR_VIEW_NO_EXPLAIN : 1345,
ERROR_FRM_UNKNOWN_TYPE : 1346,
ERROR_WRONG_OBJECT : 1347,
ERROR_NONUPDATEABLE_COLUMN : 1348,
ERROR_VIEW_SELECT_DERIVED : 1349,
ERROR_VIEW_SELECT_CLAUSE : 1350,
ERROR_VIEW_SELECT_VARIABLE : 1351,
ERROR_VIEW_SELECT_TMPTABLE : 1352,
ERROR_VIEW_WRONG_LIST : 1353,
ERROR_WARN_VIEW_MERGE : 1354,
ERROR_WARN_VIEW_WITHOUT_KEY : 1355,
ERROR_VIEW_INVALID : 1356,
ERROR_SP_NO_DROP_SP : 1357,
ERROR_SP_GOTO_IN_HNDLR : 1358,
ERROR_TRG_ALREADY_EXISTS : 1359,
ERROR_TRG_DOES_NOT_EXIST : 1360,
ERROR_TRG_ON_VIEW_OR_TEMP_TABLE : 1361,
ERROR_TRG_CANT_CHANGE_ROW : 1362,
ERROR_TRG_NO_SUCH_ROW_IN_TRG : 1363,
ERROR_NO_DEFAULT_FOR_FIELD : 1364,
ERROR_DIVISION_BY_ZERO : 1365,
ERROR_TRUNCATED_WRONG_VALUE_FOR_FIELD : 1366,
ERROR_ILLEGAL_VALUE_FOR_TYPE : 1367,
ERROR_VIEW_NONUPD_CHECK : 1368,
ERROR_VIEW_CHECK_FAILED : 1369,
ERROR_PROCACCESS_DENIED_ERROR : 1370,
ERROR_RELAY_LOG_FAIL : 1371,
ERROR_PASSWD_LENGTH : 1372,
ERROR_UNKNOWN_TARGET_BINLOG : 1373,
ERROR_IO_ERR_LOG_INDEX_READ : 1374,
ERROR_BINLOG_PURGE_PROHIBITED : 1375,
ERROR_FSEEK_FAIL : 1376,
ERROR_BINLOG_PURGE_FATAL_ERR : 1377,
ERROR_LOG_IN_USE : 1378,
ERROR_LOG_PURGE_UNKNOWN_ERR : 1379,
ERROR_RELAY_LOG_INIT : 1380,
ERROR_NO_BINARY_LOGGING : 1381,
ERROR_RESERVED_SYNTAX : 1382,
ERROR_WSAS_FAILED : 1383,
ERROR_DIFF_GROUPS_PROC : 1384,
ERROR_NO_GROUP_FOR_PROC : 1385,
ERROR_ORDERROR_WITH_PROC : 1386,
ERROR_LOGGING_PROHIBIT_CHANGING_OF : 1387,
ERROR_NO_FILE_MAPPING : 1388,
ERROR_WRONG_MAGIC : 1389,
ERROR_PS_MANY_PARAM : 1390,
ERROR_KEY_PART_0 : 1391,
ERROR_VIEW_CHECKSUM : 1392,
ERROR_VIEW_MULTIUPDATE : 1393,
ERROR_VIEW_NO_INSERT_FIELD_LIST : 1394,
ERROR_VIEW_DELETE_MERGE_VIEW : 1395,
ERROR_CANNOT_USER : 1396,
ERROR_XAERROR_NOTA : 1397,
ERROR_XAERROR_INVAL : 1398,
ERROR_XAERROR_RMFAIL : 1399,
ERROR_XAERROR_OUTSIDE : 1400,
ERROR_XAERROR_RMERR : 1401,
ERROR_XA_RBROLLBACK : 1402,
ERROR_NONEXISTING_PROC_GRANT : 1403,
ERROR_PROC_AUTO_GRANT_FAIL : 1404,
ERROR_PROC_AUTO_REVOKE_FAIL : 1405,
ERROR_DATA_TOO_LONG : 1406,
ERROR_SP_BAD_SQLSTATE : 1407,
ERROR_STARTUP : 1408,
ERROR_LOAD_FROM_FIXED_SIZE_ROWS_TO_VAR : 1409,
ERROR_CANT_CREATE_USERROR_WITH_GRANT : 1410,
ERROR_WRONG_VALUE_FOR_TYPE : 1411,
ERROR_TABLE_DEF_CHANGED : 1412,
ERROR_SP_DUP_HANDLER : 1413,
ERROR_SP_NOT_VAR_ARG : 1414,
ERROR_SP_NO_RETSET : 1415,
ERROR_CANT_CREATE_GEOMETRY_OBJECT : 1416,
ERROR_FAILED_ROUTINE_BREAK_BINLOG : 1417,
ERROR_BINLOG_UNSAFE_ROUTINE : 1418,
ERROR_BINLOG_CREATE_ROUTINE_NEED_SUPER : 1419,
ERROR_EXEC_STMT_WITH_OPEN_CURSOR : 1420,
ERROR_STMT_HAS_NO_OPEN_CURSOR : 1421,
ERROR_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG : 1422,
ERROR_NO_DEFAULT_FOR_VIEW_FIELD : 1423,
ERROR_SP_NO_RECURSION : 1424,
ERROR_TOO_BIG_SCALE : 1425,
ERROR_TOO_BIG_PRECISION : 1426,
ERROR_M_BIGGERROR_THAN_D : 1427,
ERROR_WRONG_LOCK_OF_SYSTEM_TABLE : 1428,
ERROR_CONNECT_TO_FOREIGN_DATA_SOURCE : 1429,
ERROR_QUERY_ON_FOREIGN_DATA_SOURCE : 1430,
ERROR_FOREIGN_DATA_SOURCE_DOESNT_EXIST : 1431,
ERROR_FOREIGN_DATA_STRING_INVALID_CANT_CREATE : 1432,
ERROR_FOREIGN_DATA_STRING_INVALID : 1433,
ERROR_CANT_CREATE_FEDERATED_TABLE : 1434,
ERROR_TRG_IN_WRONG_SCHEMA : 1435,
ERROR_STACK_OVERRUN_NEED_MORE : 1436,
ERROR_TOO_LONG_BODY : 1437,
ERROR_WARN_CANT_DROP_DEFAULT_KEYCACHE : 1438,
ERROR_TOO_BIG_DISPLAYWIDTH : 1439,
ERROR_XAERROR_DUPID : 1440,
ERROR_DATETIME_FUNCTION_OVERFLOW : 1441,
ERROR_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG : 1442,
ERROR_VIEW_PREVENT_UPDATE : 1443,
ERROR_PS_NO_RECURSION : 1444,
ERROR_SP_CANT_SET_AUTOCOMMIT : 1445,
ERROR_MALFORMED_DEFINER : 1446,
ERROR_VIEW_FRM_NO_USER : 1447,
ERROR_VIEW_OTHERROR_USER : 1448,
ERROR_NO_SUCH_USER : 1449,
ERROR_FORBID_SCHEMA_CHANGE : 1450,
ERROR_ROW_IS_REFERENCED_2 : 1451,
ERROR_NO_REFERENCED_ROW_2 : 1452,
ERROR_SP_BAD_VAR_SHADOW : 1453,
ERROR_TRG_NO_DEFINER : 1454,
ERROR_OLD_FILE_FORMAT : 1455,
ERROR_SP_RECURSION_LIMIT : 1456,
ERROR_SP_PROC_TABLE_CORRUPT : 1457,
ERROR_SP_WRONG_NAME : 1458,
ERROR_TABLE_NEEDS_UPGRADE : 1459,
ERROR_SP_NO_AGGREGATE : 1460,
ERROR_MAX_PREPARED_STMT_COUNT_REACHED : 1461,
ERROR_VIEW_RECURSIVE : 1462,
ERROR_NON_GROUPING_FIELD_USED : 1463,
ERROR_TABLE_CANT_HANDLE_SPKEYS : 1464,
ERROR_NO_TRIGGERS_ON_SYSTEM_SCHEMA : 1465,
ERROR_REMOVED_SPACES : 1466,
ERROR_AUTOINC_READ_FAILED : 1467,
ERROR_USERNAME : 1468,
ERROR_HOSTNAME : 1469,
ERROR_WRONG_STRING_LENGTH : 1470,
ERROR_NON_INSERTABLE_TABLE : 1471,
ERROR_ADMIN_WRONG_MRG_TABLE : 1472,
ERROR_TOO_HIGH_LEVEL_OF_NESTING_FOR_SELECT : 1473,
ERROR_NAME_BECOMES_EMPTY : 1474,
ERROR_AMBIGUOUS_FIELD_TERM : 1475,
ERROR_LOAD_DATA_INVALID_COLUMN : 1476,
ERROR_LOG_PURGE_NO_FILE : 1477,
ERROR_XA_RBTIMEOUT : 1478,
ERROR_XA_RBDEADLOCK : 1479,
ERROR_TOO_MANY_CONCURRENT_TRXS : 1480,
});

View File

@@ -0,0 +1,18 @@
var mysql = exports;
var hashish = require('hashish');
var Client = exports.Client = require('./client');
var constants = require('./constants');
var fs = require('fs');
mysql.PACKAGE = (function() {
var json = fs.readFileSync(__dirname + '/../package.json', 'utf8');
return JSON.parse(json);
})();
mysql.createClient = function(config) {
var client = new Client();
hashish.update(client, config || {});
return client;
};
hashish.update(exports, constants);

View File

@@ -0,0 +1,79 @@
var Buffer = require('buffer').Buffer;
function OutgoingPacket(size, num) {
this.buffer = new Buffer(size + 3 + 1);
this.index = 0;
this.writeNumber(3, size);
this.writeNumber(1, num || 0);
};
module.exports = OutgoingPacket;
OutgoingPacket.prototype.writeNumber = function(bytes, number) {
for (var i = 0; i < bytes; i++) {
this.buffer[this.index++] = (number >> (i * 8)) & 0xff;
}
};
OutgoingPacket.prototype.writeFiller = function(bytes) {
for (var i = 0; i < bytes; i++) {
this.buffer[this.index++] = 0;
}
};
OutgoingPacket.prototype.write = function(bufferOrString, encoding) {
if (typeof bufferOrString == 'string') {
this.index += this.buffer.write(bufferOrString, this.index, encoding);
return;
}
bufferOrString.copy(this.buffer, this.index, 0);
this.index += bufferOrString.length;
};
OutgoingPacket.prototype.writeNullTerminated = function(bufferOrString, encoding) {
this.write(bufferOrString, encoding);
this.buffer[this.index++] = 0;
};
OutgoingPacket.prototype.writeLengthCoded = function(bufferOrStringOrNumber, encoding) {
if (bufferOrStringOrNumber === null) {
this.buffer[this.index++] = 251;
return;
}
if (typeof bufferOrStringOrNumber == 'number') {
if (bufferOrStringOrNumber <= 250) {
this.buffer[this.index++] = bufferOrStringOrNumber;
return;
}
// @todo support 8-byte numbers and simplify this
if (bufferOrStringOrNumber < 0xffff) {
this.buffer[this.index++] = 252;
this.buffer[this.index++] = (bufferOrStringOrNumber >> 0) & 0xff;
this.buffer[this.index++] = (bufferOrStringOrNumber >> 8) & 0xff;
} else if (bufferOrStringOrNumber < 0xffffff) {
this.buffer[this.index++] = 253;
this.buffer[this.index++] = (bufferOrStringOrNumber >> 0) & 0xff;
this.buffer[this.index++] = (bufferOrStringOrNumber >> 8) & 0xff;
this.buffer[this.index++] = (bufferOrStringOrNumber >> 16) & 0xff;
} else {
throw new Error('8 byte length coded numbers not supported yet');
}
return;
}
if (bufferOrStringOrNumber instanceof Buffer) {
this.writeLengthCoded(bufferOrStringOrNumber.length);
this.write(bufferOrStringOrNumber);
return;
}
if (typeof bufferOrStringOrNumber == 'string') {
this.writeLengthCoded(Buffer.byteLength(bufferOrStringOrNumber, encoding));
this.write(bufferOrStringOrNumber, encoding);
return;
}
throw new Error('passed argument not a buffer, string or number: '+bufferOrStringOrNumber);
};

View File

@@ -0,0 +1,650 @@
var util = require('util');
var Buffer = require('buffer').Buffer;
var EventEmitter = require('events').EventEmitter;
var POWS = [1, 256, 65536, 16777216];
function Parser() {
EventEmitter.call(this);
this.state = Parser.PACKET_LENGTH;
this.packet = null;
this.greeted = false;
this.authenticated = false;
this.receivingFieldPackets = false;
this.receivingRowPackets = false;
this._lengthCodedLength = null;
this._lengthCodedStringLength = null;
};
util.inherits(Parser, EventEmitter);
module.exports = Parser;
Parser.prototype.write = function(buffer) {
var i = 0,
c = null,
self = this,
state = this.state,
length = buffer.length,
packet = this.packet,
advance = function(newState) {
self.state = state = (newState === undefined)
? self.state + 1
: newState;
packet.index = -1;
},
lengthCoded = function(val, nextState) {
if (self._lengthCodedLength === null) {
if (c === Parser.LENGTH_CODED_16BIT_WORD) {
self._lengthCodedLength = 2;
} else if (c === Parser.LENGTH_CODED_24BIT_WORD) {
self._lengthCodedLength = 3;
} else if (c === Parser.LENGTH_CODED_64BIT_WORD) {
self._lengthCodedLength = 8;
} else if (c === Parser.LENGTH_CODED_NULL) {
advance(nextState);
return null;
} else if (c < Parser.LENGTH_CODED_NULL) {
advance(nextState);
return c;
}
return 0;
}
if (c) {
val += POWS[packet.index - 1] * c;
}
if (packet.index === self._lengthCodedLength) {
self._lengthCodedLength = null;
advance(nextState);
}
return val;
},
emitPacket = function() {
self.packet = null;
self.state = state = Parser.PACKET_LENGTH;
self.greeted = true;
delete packet.index;
self.emit('packet', packet);
packet = null;
};
for (; i < length; i++) {
c = buffer[i];
if (state > Parser.PACKET_NUMBER) {
packet.received++;
}
switch (state) {
// PACKET HEADER
case 0: // PACKET_LENGTH:
if (!packet) {
packet = this.packet = new EventEmitter();
packet.index = 0;
packet.length = 0;
packet.received = 0;
packet.number = 0;
}
// 3 bytes - Little endian
packet.length += POWS[packet.index] * c;
if (packet.index == 2) {
advance();
}
break;
case 1: // PACKET_NUMBER:
// 1 byte
packet.number = c;
if (!this.greeted) {
advance(Parser.GREETING_PROTOCOL_VERSION);
break;
}
if (this.receivingFieldPackets) {
advance(Parser.FIELD_CATALOG_LENGTH);
} else if (this.receivingRowPackets) {
advance(Parser.COLUMN_VALUE_LENGTH);
} else {
advance(Parser.FIELD_COUNT);
}
break;
// GREETING_PACKET
case 2: // GREETING_PROTOCOL_VERSION:
// Nice undocumented MySql gem, the initial greeting can be an error
// packet. Happens for too many connections errors.
if (c === 0xff) {
packet.type = Parser.ERROR_PACKET;
advance(Parser.ERROR_NUMBER);
break;
}
// 1 byte
packet.type = Parser.GREETING_PACKET;
packet.protocolVersion = c;
advance();
break;
case 3: // GREETING_SERVER_VERSION:
if (packet.index == 0) {
packet.serverVersion = '';
}
// Null-Terminated String
if (c != 0) {
packet.serverVersion += String.fromCharCode(c);
} else {
advance();
}
break;
case 4: // GREETING_THREAD_ID:
if (packet.index == 0) {
packet.threadId = 0;
}
// 4 bytes = probably Little endian, protocol docs are not clear
packet.threadId += POWS[packet.index] * c;
if (packet.index == 3) {
advance();
}
break;
case 5: // GREETING_SCRAMBLE_BUFF_1:
if (packet.index == 0) {
packet.scrambleBuffer = new Buffer(8 + 12);
}
// 8 bytes
packet.scrambleBuffer[packet.index] = c;
if (packet.index == 7) {
advance();
}
break;
case 6: // GREETING_FILLER_1:
// 1 byte - 0x00
advance();
break;
case 7: // GREETING_SERVER_CAPABILITIES:
if (packet.index == 0) {
packet.serverCapabilities = 0;
}
// 2 bytes = probably Little endian, protocol docs are not clear
packet.serverCapabilities += POWS[packet.index] * c;
if (packet.index == 1) {
advance();
}
break;
case 8: // GREETING_SERVER_LANGUAGE:
packet.serverLanguage = c;
advance();
break;
case 9: // GREETING_SERVER_STATUS:
if (packet.index == 0) {
packet.serverStatus = 0;
}
// 2 bytes = probably Little endian, protocol docs are not clear
packet.serverStatus += POWS[packet.index] * c;
if (packet.index == 1) {
advance();
}
break;
case 10: // GREETING_FILLER_2:
// 13 bytes - 0x00
if (packet.index == 12) {
advance();
}
break;
case 11: // GREETING_SCRAMBLE_BUFF_2:
// 12 bytes - not 13 bytes like the protocol spec says ...
if (packet.index < 12) {
packet.scrambleBuffer[packet.index + 8] = c;
}
break;
// OK_PACKET, ERROR_PACKET, or RESULT_SET_HEADER_PACKET
case 12: // FIELD_COUNT:
if (packet.index == 0) {
if (c === 0xff) {
packet.type = Parser.ERROR_PACKET;
advance(Parser.ERROR_NUMBER);
break;
}
if (c == 0xfe && !this.authenticated) {
packet.type = Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET;
break;
}
if (c === 0x00) {
// after the first OK PACKET, we are authenticated
this.authenticated = true;
packet.type = Parser.OK_PACKET;
advance(Parser.AFFECTED_ROWS);
break;
}
}
this.receivingFieldPackets = true;
packet.type = Parser.RESULT_SET_HEADER_PACKET;
packet.fieldCount = lengthCoded(packet.fieldCount, Parser.EXTRA_LENGTH);
break;
// ERROR_PACKET
case 13: // ERROR_NUMBER:
if (packet.index == 0) {
packet.errorNumber = 0;
}
// 2 bytes = Little endian
packet.errorNumber += POWS[packet.index] * c;
if (packet.index == 1) {
if (!this.greeted) {
// Turns out error packets are confirming to the 4.0 protocol when
// not greeted yet. Oh MySql, you are such a thing of beauty ...
advance(Parser.ERROR_MESSAGE);
break;
}
advance();
}
break;
case 14: // ERROR_SQL_STATE_MARKER:
// 1 character - always #
packet.sqlStateMarker = String.fromCharCode(c);
packet.sqlState = '';
advance();
break;
case 15: // ERROR_SQL_STATE:
// 5 characters
if (packet.index < 5) {
packet.sqlState += String.fromCharCode(c);
}
if (packet.index == 4) {
advance(Parser.ERROR_MESSAGE);
}
break;
case 16: // ERROR_MESSAGE:
if (packet.received <= packet.length) {
packet.errorMessage = (packet.errorMessage || '') + String.fromCharCode(c);
}
break;
// OK_PACKET
case 17: // AFFECTED_ROWS:
packet.affectedRows = lengthCoded(packet.affectedRows);
break;
case 18: // INSERT_ID:
packet.insertId = lengthCoded(packet.insertId);
break;
case 19: // SERVER_STATUS:
if (packet.index == 0) {
packet.serverStatus = 0;
}
// 2 bytes - Little endian
packet.serverStatus += POWS[packet.index] * c;
if (packet.index == 1) {
advance();
}
break;
case 20: // WARNING_COUNT:
if (packet.index == 0) {
packet.warningCount = 0;
}
// 2 bytes - Little endian
packet.warningCount += POWS[packet.index] * c;
if (packet.index == 1) {
packet.message = '';
advance();
}
break;
case 21: // MESSAGE:
if (packet.received <= packet.length) {
packet.message += String.fromCharCode(c);
}
break;
// RESULT_SET_HEADER_PACKET
case 22: // EXTRA_LENGTH:
packet.extra = '';
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
break;
case 23: // EXTRA_STRING:
packet.extra += String.fromCharCode(c);
break;
// FIELD_PACKET or EOF_PACKET
case 24: // FIELD_CATALOG_LENGTH:
if (packet.index == 0) {
if (c === 0xfe) {
packet.type = Parser.EOF_PACKET;
advance(Parser.EOF_WARNING_COUNT);
break;
}
packet.type = Parser.FIELD_PACKET;
}
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
break;
case 25: // FIELD_CATALOG_STRING:
if (packet.index == 0) {
packet.catalog = '';
}
packet.catalog += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 26: // FIELD_DB_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 27: // FIELD_DB_STRING:
if (packet.index == 0) {
packet.db = '';
}
packet.db += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 28: // FIELD_TABLE_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 29: // FIELD_TABLE_STRING:
if (packet.index == 0) {
packet.table = '';
}
packet.table += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 30: // FIELD_ORIGINAL_TABLE_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 31: // FIELD_ORIGINAL_TABLE_STRING:
if (packet.index == 0) {
packet.originalTable = '';
}
packet.originalTable += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 32: // FIELD_NAME_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
break;
case 33: // FIELD_NAME_STRING:
if (packet.index == 0) {
packet.name = '';
}
packet.name += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 34: // FIELD_ORIGINAL_NAME_LENGTH:
self._lengthCodedStringLength = lengthCoded(self._lengthCodedStringLength);
if (self._lengthCodedStringLength == 0) {
advance();
}
break;
case 35: // FIELD_ORIGINAL_NAME_STRING:
if (packet.index == 0) {
packet.originalName = '';
}
packet.originalName += String.fromCharCode(c);
if (packet.index + 1 === self._lengthCodedStringLength) {
advance();
}
break;
case 36: // FIELD_FILLER_1:
// 1 bytes - 0x00
advance();
break;
case 37: // FIELD_CHARSET_NR:
if (packet.index == 0) {
packet.charsetNumber = 0;
}
// 2 bytes - Little endian
packet.charsetNumber += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
advance();
}
break;
case 38: // FIELD_LENGTH:
if (packet.index == 0) {
packet.fieldLength = 0;
}
// 4 bytes - Little endian
packet.fieldLength += Math.pow(256, packet.index) * c;
if (packet.index == 3) {
advance();
}
break;
case 39: // FIELD_TYPE:
// 1 byte
packet.fieldType = c;
advance();
case 40: // FIELD_FLAGS:
if (packet.index == 0) {
packet.flags = 0;
}
// 2 bytes - Little endian
packet.flags += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
advance();
}
break;
case 41: // FIELD_DECIMALS:
// 1 byte
packet.decimals = c;
advance();
break;
case 42: // FIELD_FILLER_2:
// 2 bytes - 0x00
if (packet.index == 1) {
advance();
}
break;
case 43: // FIELD_DEFAULT:
// TODO: Only occurs for mysql_list_fields()
break;
// EOF_PACKET
case 44: // EOF_WARNING_COUNT:
if (packet.index == 0) {
packet.warningCount = 0;
}
// 2 bytes - Little endian
packet.warningCount += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
advance();
}
break;
case 45: // EOF_SERVER_STATUS:
if (packet.index == 0) {
packet.serverStatus = 0;
}
// 2 bytes - Little endian
packet.serverStatus += Math.pow(256, packet.index) * c;
if (packet.index == 1) {
if (this.receivingFieldPackets) {
this.receivingFieldPackets = false;
this.receivingRowPackets = true;
} else {
}
}
break;
case 46: // COLUMN_VALUE_LENGTH:
if (packet.index == 0) {
packet.columnLength = 0;
packet.type = Parser.ROW_DATA_PACKET;
}
if (packet.received == 1) {
if (c === 0xfe) {
packet.type = Parser.EOF_PACKET;
this.receivingRowPackets = false;
advance(Parser.EOF_WARNING_COUNT);
break;
}
this.emit('packet', packet);
}
packet.columnLength = lengthCoded(packet.columnLength);
if (!packet.columnLength && !this._lengthCodedLength) {
packet.emit('data', (packet.columnLength === null ? null : new Buffer(0)), 0);
if (packet.received < packet.length) {
advance(Parser.COLUMN_VALUE_LENGTH);
} else {
self.packet = packet = null;
self.state = state = Parser.PACKET_LENGTH;
continue;
}
}
break;
case 47: // COLUMN_VALUE_STRING:
var remaining = packet.columnLength - packet.index, read;
if (i + remaining > buffer.length) {
read = buffer.length - i;
packet.index += read;
packet.emit('data', buffer.slice(i, buffer.length), remaining - read);
// the -1 offsets are because these values are also manipulated by the loop itself
packet.received += read - 1;
i = buffer.length;
} else {
packet.emit('data', buffer.slice(i, i + remaining), 0);
i += remaining - 1;
packet.received += remaining - 1;
advance(Parser.COLUMN_VALUE_LENGTH);
// advance() sets this to -1, but packet.index++ is skipped, so we need to manually fix
packet.index = 0;
}
if (packet.received == packet.length) {
self.packet = packet = null;
self.state = state = Parser.PACKET_LENGTH;
}
continue;
}
packet.index++;
if (state > Parser.PACKET_NUMBER && packet.received === packet.length) {
emitPacket();
}
}
};
Parser.LENGTH_CODED_NULL = 251;
Parser.LENGTH_CODED_16BIT_WORD= 252;
Parser.LENGTH_CODED_24BIT_WORD= 253;
Parser.LENGTH_CODED_64BIT_WORD= 254;
// Parser states
var s = 0;
Parser.PACKET_LENGTH = s++;
Parser.PACKET_NUMBER = s++;
Parser.GREETING_PROTOCOL_VERSION = s++;
Parser.GREETING_SERVER_VERSION = s++;
Parser.GREETING_THREAD_ID = s++;
Parser.GREETING_SCRAMBLE_BUFF_1 = s++;
Parser.GREETING_FILLER_1 = s++;
Parser.GREETING_SERVER_CAPABILITIES = s++;
Parser.GREETING_SERVER_LANGUAGE = s++;
Parser.GREETING_SERVER_STATUS = s++;
Parser.GREETING_FILLER_2 = s++;
Parser.GREETING_SCRAMBLE_BUFF_2 = s++;
Parser.FIELD_COUNT = s++;
Parser.ERROR_NUMBER = s++;
Parser.ERROR_SQL_STATE_MARKER = s++;
Parser.ERROR_SQL_STATE = s++;
Parser.ERROR_MESSAGE = s++;
Parser.AFFECTED_ROWS = s++;
Parser.INSERT_ID = s++;
Parser.SERVER_STATUS = s++;
Parser.WARNING_COUNT = s++;
Parser.MESSAGE = s++;
Parser.EXTRA_LENGTH = s++;
Parser.EXTRA_STRING = s++;
Parser.FIELD_CATALOG_LENGTH = s++;
Parser.FIELD_CATALOG_STRING = s++;
Parser.FIELD_DB_LENGTH = s++;
Parser.FIELD_DB_STRING = s++;
Parser.FIELD_TABLE_LENGTH = s++;
Parser.FIELD_TABLE_STRING = s++;
Parser.FIELD_ORIGINAL_TABLE_LENGTH = s++;
Parser.FIELD_ORIGINAL_TABLE_STRING = s++;
Parser.FIELD_NAME_LENGTH = s++;
Parser.FIELD_NAME_STRING = s++;
Parser.FIELD_ORIGINAL_NAME_LENGTH = s++;
Parser.FIELD_ORIGINAL_NAME_STRING = s++;
Parser.FIELD_FILLER_1 = s++;
Parser.FIELD_CHARSET_NR = s++;
Parser.FIELD_LENGTH = s++;
Parser.FIELD_TYPE = s++;
Parser.FIELD_FLAGS = s++;
Parser.FIELD_DECIMALS = s++;
Parser.FIELD_FILLER_2 = s++;
Parser.FIELD_DEFAULT = s++;
Parser.EOF_WARNING_COUNT = s++;
Parser.EOF_SERVER_STATUS = s++;
Parser.COLUMN_VALUE_LENGTH = s++;
Parser.COLUMN_VALUE_STRING = s++;
// Packet types
var p = 0;
Parser.GREETING_PACKET = p++;
Parser.OK_PACKET = p++;
Parser.ERROR_PACKET = p++;
Parser.RESULT_SET_HEADER_PACKET = p++;
Parser.FIELD_PACKET = p++;
Parser.EOF_PACKET = p++;
Parser.ROW_DATA_PACKET = p++;
Parser.ROW_DATA_BINARY_PACKET = p++;
Parser.OK_FOR_PREPARED_STATEMENT_PACKET = p++;
Parser.PARAMETER_PACKET = p++;
Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET = p++;

View File

@@ -0,0 +1,140 @@
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var Parser = require('./parser');
var Client;
function Query(properties) {
EventEmitter.call(this);
this.sql = null;
this.typeCast = true;
for (var key in properties) {
this[key] = properties[key];
}
};
util.inherits(Query, EventEmitter);
module.exports = Query;
Query.prototype._handlePacket = function(packet) {
var self = this;
// We can't do this require() on top of the file.
// That's because there is circular dependency and we're overwriting
// module.exports
Client = Client || require('./client');
switch (packet.type) {
case Parser.OK_PACKET:
this.emit('end', Client._packetToUserObject(packet));
break;
case Parser.ERROR_PACKET:
packet.sql = this.sql;
this.emit('error', Client._packetToUserObject(packet));
break;
case Parser.FIELD_PACKET:
if (!this._fields) {
this._fields = [];
}
this._fields.push(packet);
this.emit('field', packet);
break;
case Parser.EOF_PACKET:
if (!this._eofs) {
this._eofs = 1;
} else {
this._eofs++;
}
if (this._eofs == 2) {
this.emit('end');
}
break;
case Parser.ROW_DATA_PACKET:
var row = this._row = {}, field;
this._rowIndex = 0;
packet.on('data', function(buffer, remaining) {
if (!field) {
field = self._fields[self._rowIndex];
row[field.name] = '';
}
if (buffer) {
row[field.name] += buffer.toString('utf-8');
} else {
row[field.name] = null;
}
if (remaining !== 0) {
return;
}
self._rowIndex++;
if (self.typeCast && buffer !== null) {
switch (field.fieldType) {
case Query.FIELD_TYPE_TIMESTAMP:
case Query.FIELD_TYPE_DATE:
case Query.FIELD_TYPE_DATETIME:
case Query.FIELD_TYPE_NEWDATE:
row[field.name] = new Date(row[field.name]);
break;
case Query.FIELD_TYPE_TINY:
case Query.FIELD_TYPE_SHORT:
case Query.FIELD_TYPE_LONG:
case Query.FIELD_TYPE_LONGLONG:
case Query.FIELD_TYPE_INT24:
case Query.FIELD_TYPE_YEAR:
row[field.name] = parseInt(row[field.name], 10);
break;
case Query.FIELD_TYPE_FLOAT:
case Query.FIELD_TYPE_DOUBLE:
// decimal types cannot be parsed as floats because
// V8 Numbers have less precision than some MySQL Decimals
row[field.name] = parseFloat(row[field.name]);
break;
}
}
if (self._rowIndex == self._fields.length) {
delete self._row;
delete self._rowIndex;
self.emit('row', row);
return;
}
field = null;
});
break;
}
};
Query.FIELD_TYPE_DECIMAL = 0x00;
Query.FIELD_TYPE_TINY = 0x01;
Query.FIELD_TYPE_SHORT = 0x02;
Query.FIELD_TYPE_LONG = 0x03;
Query.FIELD_TYPE_FLOAT = 0x04;
Query.FIELD_TYPE_DOUBLE = 0x05;
Query.FIELD_TYPE_NULL = 0x06;
Query.FIELD_TYPE_TIMESTAMP = 0x07;
Query.FIELD_TYPE_LONGLONG = 0x08;
Query.FIELD_TYPE_INT24 = 0x09;
Query.FIELD_TYPE_DATE = 0x0a;
Query.FIELD_TYPE_TIME = 0x0b;
Query.FIELD_TYPE_DATETIME = 0x0c;
Query.FIELD_TYPE_YEAR = 0x0d;
Query.FIELD_TYPE_NEWDATE = 0x0e;
Query.FIELD_TYPE_VARCHAR = 0x0f;
Query.FIELD_TYPE_BIT = 0x10;
Query.FIELD_TYPE_NEWDECIMAL = 0xf6;
Query.FIELD_TYPE_ENUM = 0xf7;
Query.FIELD_TYPE_SET = 0xf8;
Query.FIELD_TYPE_TINY_BLOB = 0xf9;
Query.FIELD_TYPE_MEDIUM_BLOB = 0xfa;
Query.FIELD_TYPE_LONG_BLOB = 0xfb;
Query.FIELD_TYPE_BLOB = 0xfc;
Query.FIELD_TYPE_VAR_STRING = 0xfd;
Query.FIELD_TYPE_STRING = 0xfe;
Query.FIELD_TYPE_GEOMETRY = 0xff;

View File

View File

@@ -0,0 +1,191 @@
Hashish
=======
Hashish is a node.js library for manipulating hash data structures.
It is distilled from the finest that ruby, perl, and haskell have to offer by
way of hash/map interfaces.
Hashish provides a chaining interface, where you can do:
var Hash = require('hashish');
Hash({ a : 1, b : 2, c : 3, d : 4 })
.map(function (x) { return x * 10 })
.filter(function (x) { return x < 30 })
.forEach(function (x, key) {
console.log(key + ' => ' + x);
})
;
Output:
a => 10
b => 20
Some functions and attributes in the chaining interface are terminal, like
`.items` or `.detect()`. They return values of their own instead of the chain
context.
Each function in the chainable interface is also attached to `Hash` in chainless
form:
var Hash = require('hashish');
var obj = { a : 1, b : 2, c : 3, d : 4 };
var mapped = Hash.map(obj, function (x) {
return x * 10
});
console.dir(mapped);
Output:
{ a: 10, b: 20, c: 30, d: 40 }
In either case, the 'this' context of the function calls is the same object that
the chained functions return, so you can make nested chains.
Methods
=======
forEach(cb)
-----------
For each key/value in the hash, calls `cb(value, key)`.
map(cb)
-------
For each key/value in the hash, calls `cb(value, key)`.
The return value of `cb` is the new value at `key` in the resulting hash.
filter(cb)
----------
For each key/value in the hash, calls `cb(value, key)`.
The resulting hash omits key/value pairs where `cb` returned a falsy value.
detect(cb)
----------
Returns the first value in the hash for which `cb(value, key)` is non-falsy.
Order of hashes is not well-defined so watch out for that.
reduce(cb)
----------
Returns the accumulated value of a left-fold over the key/value pairs.
some(cb)
--------
Returns a boolean: whether or not `cb(value, key)` ever returned a non-falsy
value.
update(obj1, [obj2, obj3, ...])
-----------
Mutate the context hash, merging the key/value pairs from the passed objects
and overwriting keys from the context hash if the current `obj` has keys of
the same name. Falsy arguments are silently ignored.
updateAll([ obj1, obj2, ... ])
------------------------------
Like multi-argument `update()` but operate on an array directly.
merge(obj1, [obj2, obj3, ...])
----------
Merge the key/value pairs from the passed objects into the resultant hash
without modifying the context hash. Falsy arguments are silently ignored.
mergeAll([ obj1, obj2, ... ])
------------------------------
Like multi-argument `merge()` but operate on an array directly.
has(key)
--------
Return whether the hash has a key, `key`.
valuesAt(keys)
--------------
Return an Array with the values at the keys from `keys`.
tap(cb)
-------
Call `cb` with the present raw hash.
This function is chainable.
extract(keys)
-------------
Filter by including only those keys in `keys` in the resulting hash.
exclude(keys)
-------------
Filter by excluding those keys in `keys` in the resulting hash.
Attributes
==========
These are attributes in the chaining interface and functions in the `Hash.xxx`
interface.
keys
----
Return all the enumerable attribute keys in the hash.
values
------
Return all the enumerable attribute values in the hash.
compact
-------
Filter out values which are `=== undefined`.
clone
-----
Make a deep copy of the hash.
copy
----
Make a shallow copy of the hash.
length
------
Return the number of key/value pairs in the hash.
Note: use `Hash.size()` for non-chain mode.
size
----
Alias for `length` since `Hash.length` is masked by `Function.prototype`.
See Also
========
See also [creationix's pattern/hash](http://github.com/creationix/pattern),
which does a similar thing except with hash inputs and array outputs.
Installation
============
To install with [npm](http://github.com/isaacs/npm):
npm install hashish
To run the tests with [expresso](http://github.com/visionmedia/expresso):
expresso

View File

@@ -0,0 +1,9 @@
var Hash = require('hashish');
Hash({ a : 1, b : 2, c : 3, d : 4 })
.map(function (x) { return x * 10 })
.filter(function (x) { return x < 30 })
.forEach(function (x, key) {
console.log(key + ' => ' + x);
})
;

View File

@@ -0,0 +1,7 @@
var Hash = require('hashish');
var obj = { a : 1, b : 2, c : 3, d : 4 };
var mapped = Hash.map(obj, function (x) {
return x * 10
});
console.dir(mapped);

View File

@@ -0,0 +1,253 @@
module.exports = Hash;
var Traverse = require('traverse');
function Hash (hash, xs) {
if (Array.isArray(hash) && Array.isArray(xs)) {
var to = Math.min(hash.length, xs.length);
var acc = {};
for (var i = 0; i < to; i++) {
acc[hash[i]] = xs[i];
}
return Hash(acc);
}
if (hash === undefined) return Hash({});
var self = {
map : function (f) {
var acc = { __proto__ : hash.__proto__ };
Object.keys(hash).forEach(function (key) {
acc[key] = f.call(self, hash[key], key);
});
return Hash(acc);
},
forEach : function (f) {
Object.keys(hash).forEach(function (key) {
f.call(self, hash[key], key);
});
return self;
},
filter : function (f) {
var acc = { __proto__ : hash.__proto__ };
Object.keys(hash).forEach(function (key) {
if (f.call(self, hash[key], key)) {
acc[key] = hash[key];
}
});
return Hash(acc);
},
detect : function (f) {
for (var key in hash) {
if (f.call(self, hash[key], key)) {
return hash[key];
}
}
return undefined;
},
reduce : function (f, acc) {
var keys = Object.keys(hash);
if (acc === undefined) acc = keys.shift();
keys.forEach(function (key) {
acc = f.call(self, acc, hash[key], key);
});
return acc;
},
some : function (f) {
for (var key in hash) {
if (f.call(self, hash[key], key)) return true;
}
return false;
},
update : function (obj) {
if (arguments.length > 1) {
self.updateAll([].slice.call(arguments));
}
else {
Object.keys(obj).forEach(function (key) {
hash[key] = obj[key];
});
}
return self;
},
updateAll : function (xs) {
xs.filter(Boolean).forEach(function (x) {
self.update(x);
});
return self;
},
merge : function (obj) {
if (arguments.length > 1) {
return self.copy.updateAll([].slice.call(arguments));
}
else {
return self.copy.update(obj);
}
},
mergeAll : function (xs) {
return self.copy.updateAll(xs);
},
has : function (key) { // only operates on enumerables
return Array.isArray(key)
? key.every(function (k) { return self.has(k) })
: self.keys.indexOf(key.toString()) >= 0;
},
valuesAt : function (keys) {
return Array.isArray(keys)
? keys.map(function (key) { return hash[key] })
: hash[keys]
;
},
tap : function (f) {
f.call(self, hash);
return self;
},
extract : function (keys) {
var acc = {};
keys.forEach(function (key) {
acc[key] = hash[key];
});
return Hash(acc);
},
exclude : function (keys) {
return self.filter(function (_, key) {
return keys.indexOf(key) < 0
});
},
end : hash,
items : hash
};
var props = {
keys : function () { return Object.keys(hash) },
values : function () {
return Object.keys(hash).map(function (key) { return hash[key] });
},
compact : function () {
return self.filter(function (x) { return x !== undefined });
},
clone : function () { return Hash(Hash.clone(hash)) },
copy : function () { return Hash(Hash.copy(hash)) },
length : function () { return Object.keys(hash).length },
size : function () { return self.length }
};
if (Object.defineProperty) {
// es5-shim has an Object.defineProperty but it throws for getters
try {
for (var key in props) {
Object.defineProperty(self, key, { get : props[key] });
}
}
catch (err) {
for (var key in props) {
if (key !== 'clone' && key !== 'copy' && key !== 'compact') {
// ^ those keys use Hash() so can't call them without
// a stack overflow
self[key] = props[key]();
}
}
}
}
else if (self.__defineGetter__) {
for (var key in props) {
self.__defineGetter__(key, props[key]);
}
}
else {
// non-lazy version for browsers that suck >_<
for (var key in props) {
self[key] = props[key]();
}
}
return self;
};
// deep copy
Hash.clone = function (ref) {
return Traverse.clone(ref);
};
// shallow copy
Hash.copy = function (ref) {
var hash = { __proto__ : ref.__proto__ };
Object.keys(ref).forEach(function (key) {
hash[key] = ref[key];
});
return hash;
};
Hash.map = function (ref, f) {
return Hash(ref).map(f).items;
};
Hash.forEach = function (ref, f) {
Hash(ref).forEach(f);
};
Hash.filter = function (ref, f) {
return Hash(ref).filter(f).items;
};
Hash.detect = function (ref, f) {
return Hash(ref).detect(f);
};
Hash.reduce = function (ref, f, acc) {
return Hash(ref).reduce(f, acc);
};
Hash.some = function (ref, f) {
return Hash(ref).some(f);
};
Hash.update = function (a /*, b, c, ... */) {
var args = Array.prototype.slice.call(arguments, 1);
var hash = Hash(a);
return hash.update.apply(hash, args).items;
};
Hash.merge = function (a /*, b, c, ... */) {
var args = Array.prototype.slice.call(arguments, 1);
var hash = Hash(a);
return hash.merge.apply(hash, args).items;
};
Hash.has = function (ref, key) {
return Hash(ref).has(key);
};
Hash.valuesAt = function (ref, keys) {
return Hash(ref).valuesAt(keys);
};
Hash.tap = function (ref, f) {
return Hash(ref).tap(f).items;
};
Hash.extract = function (ref, keys) {
return Hash(ref).extract(keys).items;
};
Hash.exclude = function (ref, keys) {
return Hash(ref).exclude(keys).items;
};
Hash.concat = function (xs) {
var hash = Hash({});
xs.forEach(function (x) { hash.update(x) });
return hash.items;
};
Hash.zip = function (xs, ys) {
return Hash(xs, ys).items;
};
// .length is already defined for function prototypes
Hash.size = function (ref) {
return Hash(ref).size;
};
Hash.compact = function (ref) {
return Hash(ref).compact.items;
};

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,24 @@
Copyright 2010 James Halliday (mail@substack.net)
This project is free software released under the MIT/X11 license:
http://www.opensource.org/licenses/mit-license.php
Copyright 2010 James Halliday (mail@substack.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.

View File

@@ -0,0 +1,256 @@
traverse
========
Traverse and transform objects by visiting every node on a recursive walk.
examples
========
transform negative numbers in-place
-----------------------------------
negative.js
````javascript
var traverse = require('traverse');
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
traverse(obj).forEach(function (x) {
if (x < 0) this.update(x + 128);
});
console.dir(obj);
````
Output:
[ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ]
collect leaf nodes
------------------
leaves.js
````javascript
var traverse = require('traverse');
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 },
};
var leaves = traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
return acc;
}, []);
console.dir(leaves);
````
Output:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
scrub circular references
-------------------------
scrub.js:
````javascript
var traverse = require('traverse');
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
obj.c.push(obj);
var scrubbed = traverse(obj).map(function (x) {
if (this.circular) this.remove()
});
console.dir(scrubbed);
````
output:
{ a: 1, b: 2, c: [ 3, 4 ] }
methods
=======
Each method that takes an `fn` uses the context documented below in the context
section.
.map(fn)
--------
Execute `fn` for each node in the object and return a new object with the
results of the walk. To update nodes in the result use `this.update(value)`.
.forEach(fn)
------------
Execute `fn` for each node in the object but unlike `.map()`, when
`this.update()` is called it updates the object in-place.
.reduce(fn, acc)
----------------
For each node in the object, perform a
[left-fold](http://en.wikipedia.org/wiki/Fold_(higher-order_function))
with the return value of `fn(acc, node)`.
If `acc` isn't specified, `acc` is set to the root object for the first step
and the root element is skipped.
.paths()
--------
Return an `Array` of every possible non-cyclic path in the object.
Paths are `Array`s of string keys.
.nodes()
--------
Return an `Array` of every node in the object.
.clone()
--------
Create a deep clone of the object.
.get(path)
----------
Get the element at the array `path`.
.set(path, value)
-----------------
Set the element at the array `path` to `value`.
.has(path)
----------
Return whether the element at the array `path` exists.
context
=======
Each method that takes a callback has a context (its `this` object) with these
attributes:
this.node
---------
The present node on the recursive walk
this.path
---------
An array of string keys from the root to the present node
this.parent
-----------
The context of the node's parent.
This is `undefined` for the root node.
this.key
--------
The name of the key of the present node in its parent.
This is `undefined` for the root node.
this.isRoot, this.notRoot
-------------------------
Whether the present node is the root node
this.isLeaf, this.notLeaf
-------------------------
Whether or not the present node is a leaf node (has no children)
this.level
----------
Depth of the node within the traversal
this.circular
-------------
If the node equals one of its parents, the `circular` attribute is set to the
context of that parent and the traversal progresses no deeper.
this.update(value, stopHere=false)
----------------------------------
Set a new value for the present node.
All the elements in `value` will be recursively traversed unless `stopHere` is
true.
this.remove(stopHere=false)
-------------
Remove the current element from the output. If the node is in an Array it will
be spliced off. Otherwise it will be deleted from its parent.
this.delete(stopHere=false)
-------------
Delete the current element from its parent in the output. Calls `delete` even on
Arrays.
this.before(fn)
---------------
Call this function before any of the children are traversed.
You can assign into `this.keys` here to traverse in a custom order.
this.after(fn)
--------------
Call this function after any of the children are traversed.
this.pre(fn)
------------
Call this function before each of the children are traversed.
this.post(fn)
-------------
Call this function after each of the children are traversed.
install
=======
Using [npm](http://npmjs.org) do:
$ npm install traverse
test
====
Using [expresso](http://github.com/visionmedia/expresso) do:
$ expresso
100% wahoo, your stuff is not broken!
in the browser
==============
Use [browserify](https://github.com/substack/node-browserify) to run traverse in
the browser.
traverse has been tested and works with:
* Internet Explorer 5.5, 6.0, 7.0, 8.0, 9.0
* Firefox 3.5
* Chrome 6.0
* Opera 10.6
* Safari 5.0

View File

@@ -0,0 +1,16 @@
var traverse = require('traverse');
var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
var scrubbed = traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});
console.dir(scrubbed);
console.dir(callbacks);

View File

@@ -0,0 +1,15 @@
var traverse = require('traverse');
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 },
};
var leaves = traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
return acc;
}, []);
console.dir(leaves);

View File

@@ -0,0 +1,8 @@
var traverse = require('traverse');
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
traverse(obj).forEach(function (x) {
if (x < 0) this.update(x + 128);
});
console.dir(obj);

View File

@@ -0,0 +1,10 @@
// scrub out circular references
var traverse = require('traverse');
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
obj.c.push(obj);
var scrubbed = traverse(obj).map(function (x) {
if (this.circular) this.remove()
});
console.dir(scrubbed);

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env node
var traverse = require('traverse');
var obj = [ 'five', 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var s = '';
traverse(obj).forEach(function to_s (node) {
if (Array.isArray(node)) {
this.before(function () { s += '[' });
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += ']' });
}
else if (typeof node == 'object') {
this.before(function () { s += '{' });
this.pre(function (x, key) {
to_s(key);
s += ':';
});
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += '}' });
}
else if (typeof node == 'string') {
s += '"' + node.toString().replace(/"/g, '\\"') + '"';
}
else if (typeof node == 'function') {
s += 'null';
}
else {
s += node.toString();
}
});
console.log('JSON.stringify: ' + JSON.stringify(obj));
console.log('this stringify: ' + s);

View File

@@ -0,0 +1,295 @@
var traverse = module.exports = function (obj) {
return new Traverse(obj);
};
function Traverse (obj) {
this.value = obj;
}
Traverse.prototype.get = function (ps) {
var node = this.value;
for (var i = 0; i < ps.length; i ++) {
var key = ps[i];
if (!Object.hasOwnProperty.call(node, key)) {
node = undefined;
break;
}
node = node[key];
}
return node;
};
Traverse.prototype.has = function (ps) {
var node = this.value;
for (var i = 0; i < ps.length; i ++) {
var key = ps[i];
if (!Object.hasOwnProperty.call(node, key)) {
return false;
}
node = node[key];
}
return true;
};
Traverse.prototype.set = function (ps, value) {
var node = this.value;
for (var i = 0; i < ps.length - 1; i ++) {
var key = ps[i];
if (!Object.hasOwnProperty.call(node, key)) node[key] = {};
node = node[key];
}
node[ps[i]] = value;
return value;
};
Traverse.prototype.map = function (cb) {
return walk(this.value, cb, true);
};
Traverse.prototype.forEach = function (cb) {
this.value = walk(this.value, cb, false);
return this.value;
};
Traverse.prototype.reduce = function (cb, init) {
var skip = arguments.length === 1;
var acc = skip ? this.value : init;
this.forEach(function (x) {
if (!this.isRoot || !skip) {
acc = cb.call(this, acc, x);
}
});
return acc;
};
Traverse.prototype.paths = function () {
var acc = [];
this.forEach(function (x) {
acc.push(this.path);
});
return acc;
};
Traverse.prototype.nodes = function () {
var acc = [];
this.forEach(function (x) {
acc.push(this.node);
});
return acc;
};
Traverse.prototype.clone = function () {
var parents = [], nodes = [];
return (function clone (src) {
for (var i = 0; i < parents.length; i++) {
if (parents[i] === src) {
return nodes[i];
}
}
if (typeof src === 'object' && src !== null) {
var dst = copy(src);
parents.push(src);
nodes.push(dst);
forEach(Object_keys(src), function (key) {
dst[key] = clone(src[key]);
});
parents.pop();
nodes.pop();
return dst;
}
else {
return src;
}
})(this.value);
};
function walk (root, cb, immutable) {
var path = [];
var parents = [];
var alive = true;
return (function walker (node_) {
var node = immutable ? copy(node_) : node_;
var modifiers = {};
var keepGoing = true;
var state = {
node : node,
node_ : node_,
path : [].concat(path),
parent : parents[parents.length - 1],
parents : parents,
key : path.slice(-1)[0],
isRoot : path.length === 0,
level : path.length,
circular : null,
update : function (x, stopHere) {
if (!state.isRoot) {
state.parent.node[state.key] = x;
}
state.node = x;
if (stopHere) keepGoing = false;
},
'delete' : function (stopHere) {
delete state.parent.node[state.key];
if (stopHere) keepGoing = false;
},
remove : function (stopHere) {
if (Array_isArray(state.parent.node)) {
state.parent.node.splice(state.key, 1);
}
else {
delete state.parent.node[state.key];
}
if (stopHere) keepGoing = false;
},
keys : null,
before : function (f) { modifiers.before = f },
after : function (f) { modifiers.after = f },
pre : function (f) { modifiers.pre = f },
post : function (f) { modifiers.post = f },
stop : function () { alive = false },
block : function () { keepGoing = false }
};
if (!alive) return state;
if (typeof node === 'object' && node !== null) {
state.keys = Object_keys(node);
state.isLeaf = state.keys.length == 0;
for (var i = 0; i < parents.length; i++) {
if (parents[i].node_ === node_) {
state.circular = parents[i];
break;
}
}
}
else {
state.isLeaf = true;
}
state.notLeaf = !state.isLeaf;
state.notRoot = !state.isRoot;
// use return values to update if defined
var ret = cb.call(state, state.node);
if (ret !== undefined && state.update) state.update(ret);
if (modifiers.before) modifiers.before.call(state, state.node);
if (!keepGoing) return state;
if (typeof state.node == 'object'
&& state.node !== null && !state.circular) {
parents.push(state);
forEach(state.keys, function (key, i) {
path.push(key);
if (modifiers.pre) modifiers.pre.call(state, state.node[key], key);
var child = walker(state.node[key]);
if (immutable && Object.hasOwnProperty.call(state.node, key)) {
state.node[key] = child.node;
}
child.isLast = i == state.keys.length - 1;
child.isFirst = i == 0;
if (modifiers.post) modifiers.post.call(state, child);
path.pop();
});
parents.pop();
}
if (modifiers.after) modifiers.after.call(state, state.node);
return state;
})(root).node;
}
function copy (src) {
if (typeof src === 'object' && src !== null) {
var dst;
if (Array_isArray(src)) {
dst = [];
}
else if (isDate(src)) {
dst = new Date(src);
}
else if (isRegExp(src)) {
dst = new RegExp(src);
}
else if (isError(src)) {
dst = { message: src.message };
}
else if (isBoolean(src)) {
dst = new Boolean(src);
}
else if (isNumber(src)) {
dst = new Number(src);
}
else if (isString(src)) {
dst = new String(src);
}
else if (Object.create && Object.getPrototypeOf) {
dst = Object.create(Object.getPrototypeOf(src));
}
else if (src.__proto__ || src.constructor.prototype) {
var proto = src.__proto__ || src.constructor.prototype || {};
var T = function () {};
T.prototype = proto;
dst = new T;
if (!dst.__proto__) dst.__proto__ = proto;
}
forEach(Object_keys(src), function (key) {
dst[key] = src[key];
});
return dst;
}
else return src;
}
var Object_keys = Object.keys || function keys (obj) {
var res = [];
for (var key in obj) res.push(key)
return res;
};
function toS (obj) { return Object.prototype.toString.call(obj) }
function isDate (obj) { return toS(obj) === '[object Date]' }
function isRegExp (obj) { return toS(obj) === '[object RegExp]' }
function isError (obj) { return toS(obj) === '[object Error]' }
function isBoolean (obj) { return toS(obj) === '[object Boolean]' }
function isNumber (obj) { return toS(obj) === '[object Number]' }
function isString (obj) { return toS(obj) === '[object String]' }
var Array_isArray = Array.isArray || function isArray (xs) {
return Object.prototype.toString.call(xs) === '[object Array]';
};
var forEach = function (xs, fn) {
if (xs.forEach) return xs.forEach(fn)
else for (var i = 0; i < xs.length; i++) {
fn(xs[i], i, xs);
}
};
forEach(Object_keys(Traverse.prototype), function (key) {
traverse[key] = function (obj) {
var args = [].slice.call(arguments, 1);
var t = new Traverse(obj);
return t[key].apply(t, args);
};
});

View File

@@ -0,0 +1,34 @@
{
"name": "traverse",
"version": "0.6.1",
"description": "Traverse and transform objects by visiting every node on a recursive walk",
"author": {
"name": "James Halliday"
},
"license": "MIT/X11",
"main": "./index",
"repository": {
"type": "git",
"url": "git://github.com/substack/js-traverse.git"
},
"devDependencies": {
"expresso": "0.7.x"
},
"scripts": {
"test": "expresso"
},
"_id": "traverse@0.6.1",
"dependencies": {},
"optionalDependencies": {},
"engines": {
"node": "*"
},
"_engineSupported": true,
"_npmVersion": "1.1.21",
"_nodeVersion": "v0.6.18",
"_defaultsLoaded": true,
"dist": {
"shasum": "e9298f7ff0039a10c6e72e840ae2f9689c91ce89"
},
"_from": "traverse@>=0.2.4"
}

View File

@@ -0,0 +1,115 @@
var assert = require('assert');
var Traverse = require('../');
var deepEqual = require('./lib/deep_equal');
var util = require('util');
exports.circular = function () {
var obj = { x : 3 };
obj.y = obj;
var foundY = false;
Traverse(obj).forEach(function (x) {
if (this.path.join('') == 'y') {
assert.equal(
util.inspect(this.circular.node),
util.inspect(obj)
);
foundY = true;
}
});
assert.ok(foundY);
};
exports.deepCirc = function () {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
var times = 0;
Traverse(obj).forEach(function (x) {
if (this.circular) {
assert.deepEqual(this.circular.path, []);
assert.deepEqual(this.path, [ 'y', 2 ]);
times ++;
}
});
assert.deepEqual(times, 1);
};
exports.doubleCirc = function () {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
var circs = [];
Traverse(obj).forEach(function (x) {
if (this.circular) {
circs.push({ circ : this.circular, self : this, node : x });
}
});
assert.deepEqual(circs[0].self.path, [ 'x', 3, 2 ]);
assert.deepEqual(circs[0].circ.path, []);
assert.deepEqual(circs[1].self.path, [ 'y', 2 ]);
assert.deepEqual(circs[1].circ.path, []);
assert.deepEqual(circs.length, 2);
};
exports.circDubForEach = function () {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
Traverse(obj).forEach(function (x) {
if (this.circular) this.update('...');
});
assert.deepEqual(obj, { x : [ 1, 2, 3, [ 4, 5, '...' ] ], y : [ 4, 5, '...' ] });
};
exports.circDubMap = function () {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
var c = Traverse(obj).map(function (x) {
if (this.circular) {
this.update('...');
}
});
assert.deepEqual(c, { x : [ 1, 2, 3, [ 4, 5, '...' ] ], y : [ 4, 5, '...' ] });
};
exports.circClone = function () {
var obj = { x : [ 1, 2, 3 ], y : [ 4, 5 ] };
obj.y[2] = obj;
obj.x.push(obj.y);
var clone = Traverse.clone(obj);
assert.ok(obj !== clone);
assert.ok(clone.y[2] === clone);
assert.ok(clone.y[2] !== obj);
assert.ok(clone.x[3][2] === clone);
assert.ok(clone.x[3][2] !== obj);
assert.deepEqual(clone.x.slice(0,3), [1,2,3]);
assert.deepEqual(clone.y.slice(0,2), [4,5]);
};
exports.circMapScrub = function () {
var obj = { a : 1, b : 2 };
obj.c = obj;
var scrubbed = Traverse(obj).map(function (node) {
if (this.circular) this.remove();
});
assert.deepEqual(
Object.keys(scrubbed).sort(),
[ 'a', 'b' ]
);
assert.ok(deepEqual(scrubbed, { a : 1, b : 2 }));
assert.equal(obj.c, obj);
};

View File

@@ -0,0 +1,35 @@
var assert = require('assert');
var Traverse = require('../');
exports.dateEach = function () {
var obj = { x : new Date, y : 10, z : 5 };
var counts = {};
Traverse(obj).forEach(function (node) {
var t = (node instanceof Date && 'Date') || typeof node;
counts[t] = (counts[t] || 0) + 1;
});
assert.deepEqual(counts, {
object : 1,
Date : 1,
number : 2,
});
};
exports.dateMap = function () {
var obj = { x : new Date, y : 10, z : 5 };
var res = Traverse(obj).map(function (node) {
if (typeof node === 'number') this.update(node + 100);
});
assert.ok(obj.x !== res.x);
assert.deepEqual(res, {
x : obj.x,
y : 110,
z : 105,
});
};

View File

@@ -0,0 +1,220 @@
var assert = require('assert');
var traverse = require('../');
var deepEqual = require('./lib/deep_equal');
exports.deepDates = function () {
assert.ok(
deepEqual(
{ d : new Date, x : [ 1, 2, 3 ] },
{ d : new Date, x : [ 1, 2, 3 ] }
),
'dates should be equal'
);
var d0 = new Date;
setTimeout(function () {
assert.ok(
!deepEqual(
{ d : d0, x : [ 1, 2, 3 ], },
{ d : new Date, x : [ 1, 2, 3 ] }
),
'microseconds should count in date equality'
);
}, 5);
};
exports.deepCircular = function () {
var a = [1];
a.push(a); // a = [ 1, *a ]
var b = [1];
b.push(a); // b = [ 1, [ 1, *a ] ]
assert.ok(
!deepEqual(a, b),
'circular ref mount points count towards equality'
);
var c = [1];
c.push(c); // c = [ 1, *c ]
assert.ok(
deepEqual(a, c),
'circular refs are structurally the same here'
);
var d = [1];
d.push(a); // c = [ 1, [ 1, *d ] ]
assert.ok(
deepEqual(b, d),
'non-root circular ref structural comparison'
);
};
exports.deepInstances = function () {
assert.ok(
!deepEqual([ new Boolean(false) ], [ false ]),
'boolean instances are not real booleans'
);
assert.ok(
!deepEqual([ new String('x') ], [ 'x' ]),
'string instances are not real strings'
);
assert.ok(
!deepEqual([ new Number(4) ], [ 4 ]),
'number instances are not real numbers'
);
assert.ok(
deepEqual([ new RegExp('x') ], [ /x/ ]),
'regexp instances are real regexps'
);
assert.ok(
!deepEqual([ new RegExp(/./) ], [ /../ ]),
'these regexps aren\'t the same'
);
assert.ok(
!deepEqual(
[ function (x) { return x * 2 } ],
[ function (x) { return x * 2 } ]
),
'functions with the same .toString() aren\'t necessarily the same'
);
var f = function (x) { return x * 2 };
assert.ok(
deepEqual([ f ], [ f ]),
'these functions are actually equal'
);
};
exports.deepEqual = function () {
assert.ok(
!deepEqual([ 1, 2, 3 ], { 0 : 1, 1 : 2, 2 : 3 }),
'arrays are not objects'
);
};
exports.falsy = function () {
assert.ok(
!deepEqual([ undefined ], [ null ]),
'null is not undefined!'
);
assert.ok(
!deepEqual([ null ], [ undefined ]),
'undefined is not null!'
);
assert.ok(
!deepEqual(
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
{ a : 1, b : 2, c : [ 3, null, 5 ] }
),
'undefined is not null, however deeply!'
);
assert.ok(
!deepEqual(
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
{ a : 1, b : 2, c : [ 3, null, 5 ] }
),
'null is not undefined, however deeply!'
);
assert.ok(
!deepEqual(
{ a : 1, b : 2, c : [ 3, undefined, 5 ] },
{ a : 1, b : 2, c : [ 3, null, 5 ] }
),
'null is not undefined, however deeply!'
);
};
exports.deletedArrayEqual = function () {
var xs = [ 1, 2, 3, 4 ];
delete xs[2];
var ys = Object.create(Array.prototype);
ys[0] = 1;
ys[1] = 2;
ys[3] = 4;
assert.ok(
deepEqual(xs, ys),
'arrays with deleted elements are only equal to'
+ ' arrays with similarly deleted elements'
);
assert.ok(
!deepEqual(xs, [ 1, 2, undefined, 4 ]),
'deleted array elements cannot be undefined'
);
assert.ok(
!deepEqual(xs, [ 1, 2, null, 4 ]),
'deleted array elements cannot be null'
);
};
exports.deletedObjectEqual = function () {
var obj = { a : 1, b : 2, c : 3 };
delete obj.c;
assert.ok(
deepEqual(obj, { a : 1, b : 2 }),
'deleted object elements should not show up'
);
assert.ok(
!deepEqual(obj, { a : 1, b : 2, c : undefined }),
'deleted object elements are not undefined'
);
assert.ok(
!deepEqual(obj, { a : 1, b : 2, c : null }),
'deleted object elements are not null'
);
};
exports.emptyKeyEqual = function () {
assert.ok(!deepEqual(
{ a : 1 }, { a : 1, '' : 55 }
));
};
exports.deepArguments = function () {
assert.ok(
!deepEqual(
[ 4, 5, 6 ],
(function () { return arguments })(4, 5, 6)
),
'arguments are not arrays'
);
assert.ok(
deepEqual(
(function () { return arguments })(4, 5, 6),
(function () { return arguments })(4, 5, 6)
),
'arguments should equal'
);
};
exports.deepUn = function () {
assert.ok(!deepEqual({ a : 1, b : 2 }, undefined));
assert.ok(!deepEqual({ a : 1, b : 2 }, {}));
assert.ok(!deepEqual(undefined, { a : 1, b : 2 }));
assert.ok(!deepEqual({}, { a : 1, b : 2 }));
assert.ok(deepEqual(undefined, undefined));
assert.ok(deepEqual(null, null));
assert.ok(!deepEqual(undefined, null));
};
exports.deepLevels = function () {
var xs = [ 1, 2, [ 3, 4, [ 5, 6 ] ] ];
assert.ok(!deepEqual(xs, []));
};

View File

@@ -0,0 +1,13 @@
var assert = require('assert');
var Traverse = require('../');
exports['traverse an Error'] = function () {
var obj = new Error("test");
var results = Traverse(obj).map(function (node) { });
assert.deepEqual(results, {
message: 'test'
});
};

View File

@@ -0,0 +1,13 @@
var assert = require('assert');
var traverse = require('../');
exports.has = function () {
var obj = { a : 2, b : [ 4, 5, { c : 6 } ] };
assert.equal(traverse(obj).has([ 'b', 2, 'c' ]), true)
assert.equal(traverse(obj).has([ 'b', 2, 'c', 0 ]), false)
assert.equal(traverse(obj).has([ 'b', 2, 'd' ]), false)
assert.equal(traverse(obj).has([]), true)
assert.equal(traverse(obj).has([ 'a' ]), true)
assert.equal(traverse(obj).has([ 'a', 2 ]), false)
};

View File

@@ -0,0 +1,17 @@
var assert = require('assert');
var Traverse = require('../');
var EventEmitter = require('events').EventEmitter;
exports['check instanceof on node elems'] = function () {
var counts = { emitter : 0 };
Traverse([ new EventEmitter, 3, 4, { ev : new EventEmitter }])
.forEach(function (node) {
if (node instanceof EventEmitter) counts.emitter ++;
})
;
assert.equal(counts.emitter, 2);
};

View File

@@ -0,0 +1,42 @@
var assert = require('assert');
var Traverse = require('../');
exports['interface map'] = function () {
var obj = { a : [ 5,6,7 ], b : { c : [8] } };
assert.deepEqual(
Traverse.paths(obj)
.sort()
.map(function (path) { return path.join('/') })
.slice(1)
.join(' ')
,
'a a/0 a/1 a/2 b b/c b/c/0'
);
assert.deepEqual(
Traverse.nodes(obj),
[
{ a: [ 5, 6, 7 ], b: { c: [ 8 ] } },
[ 5, 6, 7 ], 5, 6, 7,
{ c: [ 8 ] }, [ 8 ], 8
]
);
assert.deepEqual(
Traverse.map(obj, function (node) {
if (typeof node == 'number') {
return node + 1000;
}
else if (Array.isArray(node)) {
return node.join(' ');
}
}),
{ a: '5 6 7', b: { c: '8' } }
);
var nodes = 0;
Traverse.forEach(obj, function (node) { nodes ++ });
assert.deepEqual(nodes, 8);
};

View File

@@ -0,0 +1,47 @@
var assert = require('assert');
var Traverse = require('../');
exports['json test'] = function () {
var id = 54;
var callbacks = {};
var obj = { moo : function () {}, foo : [2,3,4, function () {}] };
var scrubbed = Traverse(obj).map(function (x) {
if (typeof x === 'function') {
callbacks[id] = { id : id, f : x, path : this.path };
this.update('[Function]');
id++;
}
});
assert.equal(
scrubbed.moo, '[Function]',
'obj.moo replaced with "[Function]"'
);
assert.equal(
scrubbed.foo[3], '[Function]',
'obj.foo[3] replaced with "[Function]"'
);
assert.deepEqual(scrubbed, {
moo : '[Function]',
foo : [ 2, 3, 4, "[Function]" ]
}, 'Full JSON string matches');
assert.deepEqual(
typeof obj.moo, 'function',
'Original obj.moo still a function'
);
assert.deepEqual(
typeof obj.foo[3], 'function',
'Original obj.foo[3] still a function'
);
assert.deepEqual(callbacks, {
54: { id: 54, f : obj.moo, path: [ 'moo' ] },
55: { id: 55, f : obj.foo[3], path: [ 'foo', '3' ] },
}, 'Check the generated callbacks list');
};

View File

@@ -0,0 +1,29 @@
var assert = require('assert');
var Traverse = require('../');
exports['sort test'] = function () {
var acc = [];
Traverse({
a: 30,
b: 22,
id: 9
}).forEach(function (node) {
if ((! Array.isArray(node)) && typeof node === 'object') {
this.before(function(node) {
this.keys = Object.keys(node);
this.keys.sort(function(a, b) {
a = [a === "id" ? 0 : 1, a];
b = [b === "id" ? 0 : 1, b];
return a < b ? -1 : a > b ? 1 : 0;
});
});
}
if (this.isLeaf) acc.push(node);
});
assert.equal(
acc.join(' '),
'9 30 22',
'Traversal in a custom order'
);
};

View File

@@ -0,0 +1,21 @@
var assert = require('assert');
var Traverse = require('../');
exports['leaves test'] = function () {
var acc = [];
Traverse({
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 }
}).forEach(function (x) {
if (this.isLeaf) acc.push(x);
});
assert.equal(
acc.join(' '),
'1 2 3 4 5 6 7 8 9',
'Traversal in the right(?) order'
);
};

View File

@@ -0,0 +1,96 @@
var traverse = require('../../');
module.exports = function (a, b) {
if (arguments.length !== 2) {
throw new Error(
'deepEqual requires exactly two objects to compare against'
);
}
var equal = true;
var node = b;
traverse(a).forEach(function (y) {
var notEqual = (function () {
equal = false;
//this.stop();
return undefined;
}).bind(this);
//if (node === undefined || node === null) return notEqual();
if (!this.isRoot) {
/*
if (!Object.hasOwnProperty.call(node, this.key)) {
return notEqual();
}
*/
if (typeof node !== 'object') return notEqual();
node = node[this.key];
}
var x = node;
this.post(function () {
node = x;
});
var toS = function (o) {
return Object.prototype.toString.call(o);
};
if (this.circular) {
if (traverse(b).get(this.circular.path) !== x) notEqual();
}
else if (typeof x !== typeof y) {
notEqual();
}
else if (x === null || y === null || x === undefined || y === undefined) {
if (x !== y) notEqual();
}
else if (x.__proto__ !== y.__proto__) {
notEqual();
}
else if (x === y) {
// nop
}
else if (typeof x === 'function') {
if (x instanceof RegExp) {
// both regexps on account of the __proto__ check
if (x.toString() != y.toString()) notEqual();
}
else if (x !== y) notEqual();
}
else if (typeof x === 'object') {
if (toS(y) === '[object Arguments]'
|| toS(x) === '[object Arguments]') {
if (toS(x) !== toS(y)) {
notEqual();
}
}
else if (toS(y) === '[object RegExp]'
|| toS(x) === '[object RegExp]') {
if (!x || !y || x.toString() !== y.toString()) notEqual();
}
else if (x instanceof Date || y instanceof Date) {
if (!(x instanceof Date) || !(y instanceof Date)
|| x.getTime() !== y.getTime()) {
notEqual();
}
}
else {
var kx = Object.keys(x);
var ky = Object.keys(y);
if (kx.length !== ky.length) return notEqual();
for (var i = 0; i < kx.length; i++) {
var k = kx[i];
if (!Object.hasOwnProperty.call(y, k)) {
notEqual();
}
}
}
}
});
return equal;
};

View File

@@ -0,0 +1,252 @@
var assert = require('assert');
var Traverse = require('../');
var deepEqual = require('./lib/deep_equal');
exports.mutate = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse(obj).forEach(function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
assert.deepEqual(obj, res);
assert.deepEqual(obj, { a : 1, b : 20, c : [ 3, 40 ] });
};
exports.mutateT = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse.forEach(obj, function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
assert.deepEqual(obj, res);
assert.deepEqual(obj, { a : 1, b : 20, c : [ 3, 40 ] });
};
exports.map = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse(obj).map(function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
assert.deepEqual(res, { a : 1, b : 20, c : [ 3, 40 ] });
};
exports.mapT = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse.map(obj, function (x) {
if (typeof x === 'number' && x % 2 === 0) {
this.update(x * 10);
}
});
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
assert.deepEqual(res, { a : 1, b : 20, c : [ 3, 40 ] });
};
exports.clone = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse(obj).clone();
assert.deepEqual(obj, res);
assert.ok(obj !== res);
obj.a ++;
assert.deepEqual(res.a, 1);
obj.c.push(5);
assert.deepEqual(res.c, [ 3, 4 ]);
};
exports.cloneT = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse.clone(obj);
assert.deepEqual(obj, res);
assert.ok(obj !== res);
obj.a ++;
assert.deepEqual(res.a, 1);
obj.c.push(5);
assert.deepEqual(res.c, [ 3, 4 ]);
};
exports.reduce = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse(obj).reduce(function (acc, x) {
if (this.isLeaf) acc.push(x);
return acc;
}, []);
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
assert.deepEqual(res, [ 1, 2, 3, 4 ]);
};
exports.reduceInit = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse(obj).reduce(function (acc, x) {
if (this.isRoot) assert.fail('got root');
return acc;
});
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
assert.deepEqual(res, obj);
};
exports.remove = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
Traverse(obj).forEach(function (x) {
if (this.isLeaf && x % 2 == 0) this.remove();
});
assert.deepEqual(obj, { a : 1, c : [ 3 ] });
};
exports.removeNoStop = function() {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 }, f: 5 };
var keys = [];
Traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.remove();
});
assert.deepEqual(keys, [undefined, 'a', 'b', 'c', 'd', 'e', 'f'])
}
exports.removeStop = function() {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 }, f: 5 };
var keys = [];
Traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.remove(true);
});
assert.deepEqual(keys, [undefined, 'a', 'b', 'c', 'f'])
}
exports.removeMap = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse(obj).map(function (x) {
if (this.isLeaf && x % 2 == 0) this.remove();
});
assert.deepEqual(obj, { a : 1, b : 2, c : [ 3, 4 ] });
assert.deepEqual(res, { a : 1, c : [ 3 ] });
};
exports.delete = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
Traverse(obj).forEach(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
assert.ok(!deepEqual(
obj, { a : 1, c : [ 3, undefined ] }
));
assert.ok(deepEqual(
obj, { a : 1, c : [ 3 ] }
));
assert.ok(!deepEqual(
obj, { a : 1, c : [ 3, null ] }
));
};
exports.deleteNoStop = function() {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 } };
var keys = [];
Traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.delete();
});
assert.deepEqual(keys, [undefined, 'a', 'b', 'c', 'd', 'e'])
}
exports.deleteStop = function() {
var obj = { a : 1, b : 2, c : { d: 3, e: 4 } };
var keys = [];
Traverse(obj).forEach(function (x) {
keys.push(this.key)
if (this.key == 'c') this.delete(true);
});
assert.deepEqual(keys, [undefined, 'a', 'b', 'c'])
}
exports.deleteRedux = function () {
var obj = { a : 1, b : 2, c : [ 3, 4, 5 ] };
Traverse(obj).forEach(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
assert.ok(!deepEqual(
obj, { a : 1, c : [ 3, undefined, 5 ] }
));
assert.ok(deepEqual(
obj, { a : 1, c : [ 3 ,, 5 ] }
));
assert.ok(!deepEqual(
obj, { a : 1, c : [ 3, null, 5 ] }
));
assert.ok(!deepEqual(
obj, { a : 1, c : [ 3, 5 ] }
));
};
exports.deleteMap = function () {
var obj = { a : 1, b : 2, c : [ 3, 4 ] };
var res = Traverse(obj).map(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
assert.ok(deepEqual(
obj,
{ a : 1, b : 2, c : [ 3, 4 ] }
));
var xs = [ 3, 4 ];
delete xs[1];
assert.ok(deepEqual(
res, { a : 1, c : xs }
));
assert.ok(deepEqual(
res, { a : 1, c : [ 3, ] }
));
assert.ok(deepEqual(
res, { a : 1, c : [ 3 ] }
));
};
exports.deleteMapRedux = function () {
var obj = { a : 1, b : 2, c : [ 3, 4, 5 ] };
var res = Traverse(obj).map(function (x) {
if (this.isLeaf && x % 2 == 0) this.delete();
});
assert.ok(deepEqual(
obj,
{ a : 1, b : 2, c : [ 3, 4, 5 ] }
));
var xs = [ 3, 4, 5 ];
delete xs[1];
assert.ok(deepEqual(
res, { a : 1, c : xs }
));
assert.ok(!deepEqual(
res, { a : 1, c : [ 3, 5 ] }
));
assert.ok(deepEqual(
res, { a : 1, c : [ 3 ,, 5 ] }
));
};

View File

@@ -0,0 +1,20 @@
var Traverse = require('../');
var assert = require('assert');
exports['negative update test'] = function () {
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var fixed = Traverse.map(obj, function (x) {
if (x < 0) this.update(x + 128);
});
assert.deepEqual(fixed,
[ 5, 6, 125, [ 7, 8, 126, 1 ], { f: 10, g: 115 } ],
'Negative values += 128'
);
assert.deepEqual(obj,
[ 5, 6, -3, [ 7, 8, -2, 1 ], { f: 10, g: -13 } ],
'Original references not modified'
);
}

View File

@@ -0,0 +1,15 @@
var assert = require('assert');
var Traverse = require('../');
exports['traverse an object with nested functions'] = function () {
var to = setTimeout(function () {
assert.fail('never ran');
}, 1000);
function Cons (x) {
clearTimeout(to);
assert.equal(x, 10);
};
Traverse(new Cons(10));
};

View File

@@ -0,0 +1,35 @@
var assert = require('assert');
var traverse = require('../');
exports.siblings = function () {
var obj = { a : 1, b : 2, c : [ 4, 5, 6 ] };
var res = traverse(obj).reduce(function (acc, x) {
var p = '/' + this.path.join('/');
if (this.parent) {
acc[p] = {
siblings : this.parent.keys,
key : this.key,
index : this.parent.keys.indexOf(this.key)
};
}
else {
acc[p] = {
siblings : [],
key : this.key,
index : -1
}
}
return acc;
}, {});
assert.deepEqual(res, {
'/' : { siblings : [], key : undefined, index : -1 },
'/a' : { siblings : [ 'a', 'b', 'c' ], key : 'a', index : 0 },
'/b' : { siblings : [ 'a', 'b', 'c' ], key : 'b', index : 1 },
'/c' : { siblings : [ 'a', 'b', 'c' ], key : 'c', index : 2 },
'/c/0' : { siblings : [ '0', '1', '2' ], key : '0', index : 0 },
'/c/1' : { siblings : [ '0', '1', '2' ], key : '1', index : 1 },
'/c/2' : { siblings : [ '0', '1', '2' ], key : '2', index : 2 }
});
};

View File

@@ -0,0 +1,41 @@
var assert = require('assert');
var traverse = require('../');
exports.stop = function () {
var visits = 0;
traverse('abcdefghij'.split('')).forEach(function (node) {
if (typeof node === 'string') {
visits ++;
if (node === 'e') this.stop()
}
});
assert.equal(visits, 5);
};
exports.stopMap = function () {
var s = traverse('abcdefghij'.split('')).map(function (node) {
if (typeof node === 'string') {
if (node === 'e') this.stop()
return node.toUpperCase();
}
}).join('');
assert.equal(s, 'ABCDEfghij');
};
exports.stopReduce = function () {
var obj = {
a : [ 4, 5 ],
b : [ 6, [ 7, 8, 9 ] ]
};
var xs = traverse(obj).reduce(function (acc, node) {
if (this.isLeaf) {
if (node === 7) this.stop();
else acc.push(node)
}
return acc;
}, []);
assert.deepEqual(xs, [ 4, 5, 6 ]);
};

View File

@@ -0,0 +1,36 @@
var assert = require('assert');
var Traverse = require('../');
exports.stringify = function () {
var obj = [ 5, 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];
var s = '';
Traverse(obj).forEach(function (node) {
if (Array.isArray(node)) {
this.before(function () { s += '[' });
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += ']' });
}
else if (typeof node == 'object') {
this.before(function () { s += '{' });
this.pre(function (x, key) {
s += '"' + key + '"' + ':';
});
this.post(function (child) {
if (!child.isLast) s += ',';
});
this.after(function () { s += '}' });
}
else if (typeof node == 'function') {
s += 'null';
}
else {
s += node.toString();
}
});
assert.equal(s, JSON.stringify(obj));
}

View File

@@ -0,0 +1,34 @@
var traverse = require('../');
var assert = require('assert');
exports.subexpr = function () {
var obj = [ 'a', 4, 'b', 5, 'c', 6 ];
var r = traverse(obj).map(function (x) {
if (typeof x === 'number') {
this.update([ x - 0.1, x, x + 0.1 ], true);
}
});
assert.deepEqual(obj, [ 'a', 4, 'b', 5, 'c', 6 ]);
assert.deepEqual(r, [
'a', [ 3.9, 4, 4.1 ],
'b', [ 4.9, 5, 5.1 ],
'c', [ 5.9, 6, 6.1 ],
]);
};
exports.block = function () {
var obj = [ [ 1 ], [ 2 ], [ 3 ] ];
var r = traverse(obj).map(function (x) {
if (Array.isArray(x) && !this.isRoot) {
if (x[0] === 5) this.block()
else this.update([ [ x[0] + 1 ] ])
}
});
assert.deepEqual(r, [
[ [ [ [ [ 5 ] ] ] ] ],
[ [ [ [ 5 ] ] ] ],
[ [ [ 5 ] ] ],
]);
};

View File

@@ -0,0 +1,55 @@
var assert = require('assert');
var traverse = require('../');
var deepEqual = require('./lib/deep_equal');
exports.super_deep = function () {
var util = require('util');
var a0 = make();
var a1 = make();
assert.ok(deepEqual(a0, a1));
a0.c.d.moo = true;
assert.ok(!deepEqual(a0, a1));
a1.c.d.moo = true;
assert.ok(deepEqual(a0, a1));
// TODO: this one
//a0.c.a = a1;
//assert.ok(!deepEqual(a0, a1));
};
function make () {
var a = { self : 'a' };
var b = { self : 'b' };
var c = { self : 'c' };
var d = { self : 'd' };
var e = { self : 'e' };
a.a = a;
a.b = b;
a.c = c;
b.a = a;
b.b = b;
b.c = c;
c.a = a;
c.b = b;
c.c = c;
c.d = d;
d.a = a;
d.b = b;
d.c = c;
d.d = d;
d.e = e;
e.a = a;
e.b = b;
e.c = c;
e.d = d;
e.e = e;
return a;
}

View File

@@ -0,0 +1,22 @@
var traverse = require('traverse');
var test = require('testling');
test('leaves', function (t) {
var obj = {
a : [1,2,3],
b : 4,
c : [5,6],
d : { e : [7,8], f : 9 }
};
var acc = [];
traverse(obj).forEach(function (x) {
if (this.isLeaf) acc.push(x);
});
t.deepEqual(
acc, [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
'traversal in the proper order'
);
t.end();
});

View File

@@ -0,0 +1,48 @@
{
"name": "hashish",
"version": "0.0.4",
"description": "Hash data structure manipulation functions",
"main": "./index.js",
"repository": {
"type": "git",
"url": "git://github.com/substack/node-hashish.git"
},
"keywords": [
"hash",
"object",
"convenience",
"manipulation",
"data structure"
],
"author": {
"name": "James Halliday",
"email": "mail@substack.net",
"url": "http://substack.net"
},
"dependencies": {
"traverse": ">=0.2.4"
},
"devDependencies": {
"expresso": ">=0.6.0"
},
"scripts": {
"test": "expresso"
},
"license": "MIT/X11",
"engine": [
"node >=0.2.0"
],
"_id": "hashish@0.0.4",
"optionalDependencies": {},
"engines": {
"node": "*"
},
"_engineSupported": true,
"_npmVersion": "1.1.21",
"_nodeVersion": "v0.6.18",
"_defaultsLoaded": true,
"dist": {
"shasum": "514474b6dbc8dc3556dee99471304f8c15f41cc9"
},
"_from": "hashish@0.0.4"
}

View File

@@ -0,0 +1,250 @@
var Hash = require('hashish');
var assert = require('assert');
exports.map = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).map(function (v) { return v + 1 }).items;
var hash = Hash.map(ref, function (v) { return v + 1 });
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 2, b : 3 });
assert.deepEqual(hash, { a : 2, b : 3 });
};
exports['cloned map'] = function () {
var ref = { foo : [1,2], bar : [4,5] };
var hash = Hash(ref).clone.map(
function (v) { v.unshift(v[0] - 1); return v }
).items;
assert.deepEqual(ref.foo, [1,2]);
assert.deepEqual(ref.bar, [4,5]);
assert.deepEqual(hash.foo, [0,1,2]);
assert.deepEqual(hash.bar, [3,4,5]);
};
exports.forEach = function () {
var ref = { a : 5, b : 2, c : 7, 1337 : 'leet' };
var xs = [];
Hash(ref).forEach(function (x, i) {
xs.push([ i, x ]);
});
assert.eql(
xs.map(function (x) { return x[0] }).sort(),
[ '1337', 'a', 'b', 'c' ]
);
assert.eql(
xs.map(function (x) { return x[1] }).sort(),
[ 2, 5, 7, 'leet' ]
);
var ys = [];
Hash.forEach(ref, function (x, i) {
ys.push([ i, x ]);
});
assert.eql(xs.sort(), ys.sort());
};
exports.filter_items = function () {
var ref = { a : 5, b : 2, c : 7, 1337 : 'leet' };
var items = Hash(ref).filter(function (v, k) {
return v > 5 || k > 5
}).items;
var hash = Hash.filter(ref, function (v, k) { return v > 5 || k > 5 });
assert.deepEqual(items, { 1337 : 'leet', c : 7 });
assert.deepEqual(hash, { 1337 : 'leet', c : 7 });
assert.deepEqual(ref, { a : 5, b : 2, c : 7, 1337 : 'leet' });
assert.equal(Hash(ref).length, 4);
};
exports.detect = function () {
var h = { a : 5, b : 6, c : 7, d : 8 };
var hh = Hash(h);
var gt6hh = hh.detect(function (x) { return x > 6 });
assert.ok(gt6hh == 7 || gt6hh == 8);
var gt6h = Hash.detect(h, function (x) { return x > 6 });
assert.ok(gt6h == 7 || gt6h == 8);
assert.equal(hh.detect(function (x) { return x > 100 }), undefined);
};
exports.reduce = function () {
var ref = { foo : [1,2], bar : [4,5] };
var sum1 = Hash(ref).reduce(function (acc, v) {
return acc + v.length
}, 0);
assert.equal(sum1, 4);
var sum2 = Hash.reduce(ref, function (acc, v) {
return acc + v.length
}, 0);
assert.equal(sum2, 4);
};
exports.some = function () {
var h = { a : 5, b : 6, c : 7, d : 8 };
var hh = Hash(h);
assert.ok(Hash.some(h, function (x) { return x > 7 }));
assert.ok(Hash.some(h, function (x) { return x < 6 }));
assert.ok(!Hash.some(h, function (x) { return x > 10 }));
assert.ok(!Hash.some(h, function (x) { return x < 0 }));
assert.ok(hh.some(function (x) { return x > 7 }));
assert.ok(hh.some(function (x) { return x < 6 }));
assert.ok(!hh.some(function (x) { return x > 10 }));
assert.ok(!hh.some(function (x) { return x < 0 }));
};
exports.update = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).clone.update({ c : 3, a : 0 }).items;
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 0, b : 2, c : 3 });
var hash = Hash.update(ref, { c : 3, a : 0 });
assert.deepEqual(ref, hash);
assert.deepEqual(hash, { a : 0, b : 2, c : 3 });
var ref2 = {a: 1};
var hash2 = Hash.update(ref2, { b: 2, c: 3 }, undefined, { d: 4 });
assert.deepEqual(ref2, { a: 1, b: 2, c: 3, d: 4 });
};
exports.merge = function () {
var ref = { a : 1, b : 2 };
var items = Hash(ref).merge({ b : 3, c : 3.14 }).items;
var hash = Hash.merge(ref, { b : 3, c : 3.14 });
assert.deepEqual(ref, { a : 1, b : 2 });
assert.deepEqual(items, { a : 1, b : 3, c : 3.14 });
assert.deepEqual(hash, { a : 1, b : 3, c : 3.14 });
var ref2 = { a : 1 };
var hash2 = Hash.merge(ref, { b: 2, c: 3 }, undefined, { d: 4 });
assert.deepEqual(hash2, { a: 1, b: 2, c: 3, d: 4 });
};
exports.has = function () {
var h = { a : 4, b : 5 };
var hh = Hash(h);
assert.ok(hh.has('a'));
assert.equal(hh.has('c'), false);
assert.ok(hh.has(['a','b']));
assert.equal(hh.has(['a','b','c']), false);
assert.ok(Hash.has(h, 'a'));
assert.equal(Hash.has(h, 'c'), false);
assert.ok(Hash.has(h, ['a','b']));
assert.equal(Hash.has(h, ['a','b','c']), false);
};
exports.valuesAt = function () {
var h = { a : 4, b : 5, c : 6 };
assert.equal(Hash(h).valuesAt('a'), 4);
assert.equal(Hash(h).valuesAt(['a'])[0], 4);
assert.deepEqual(Hash(h).valuesAt(['a','b']), [4,5]);
assert.equal(Hash.valuesAt(h, 'a'), 4);
assert.deepEqual(Hash.valuesAt(h, ['a']), [4]);
assert.deepEqual(Hash.valuesAt(h, ['a','b']), [4,5]);
};
exports.tap = function () {
var h = { a : 4, b : 5, c : 6 };
var hh = Hash(h);
hh.tap(function (x) {
assert.ok(this === hh)
assert.eql(x, h);
});
Hash.tap(h, function (x) {
assert.eql(
Object.keys(this).sort(),
Object.keys(hh).sort()
);
assert.eql(x, h);
});
};
exports.extract = function () {
var hash = Hash({ a : 1, b : 2, c : 3 }).clone;
var extracted = hash.extract(['a','b']);
assert.equal(extracted.length, 2);
assert.deepEqual(extracted.items, { a : 1, b : 2 });
};
exports.exclude = function () {
var hash = Hash({ a : 1, b : 2, c : 3 }).clone;
var extracted = hash.exclude(['a','b']);
assert.equal(extracted.length, 1);
assert.deepEqual(extracted.items, { c : 3 });
};
exports.concat = function () {
var ref1 = { a : 1, b : 2 };
var ref2 = { foo : 100, bar : 200 };
var ref3 = { b : 3, c : 4, bar : 300 };
assert.deepEqual(
Hash.concat([ ref1, ref2 ]),
{ a : 1, b : 2, foo : 100, bar : 200 }
);
assert.deepEqual(
Hash.concat([ ref1, ref2, ref3 ]),
{ a : 1, b : 3, c : 4, foo : 100, bar : 300 }
);
};
exports.zip = function () {
var xs = ['a','b','c'];
var ys = [1,2,3,4];
var h = Hash(xs,ys);
assert.equal(h.length, 3);
assert.deepEqual(h.items, { a : 1, b : 2, c : 3 });
var zipped = Hash.zip(xs,ys);
assert.deepEqual(zipped, { a : 1, b : 2, c : 3 });
};
exports.length = function () {
assert.equal(Hash({ a : 1, b : [2,3], c : 4 }).length, 3);
assert.equal(Hash({ a : 1, b : [2,3], c : 4 }).size, 3);
assert.equal(Hash.size({ a : 1, b : [2,3], c : 4 }), 3);
};
exports.compact = function () {
var hash = {
a : 1,
b : undefined,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
};
var compacted = Hash(hash).compact;
assert.deepEqual(
{
a : 1,
b : undefined,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
},
hash, 'compact modified the hash'
);
assert.deepEqual(
compacted.items,
{
a : 1,
c : false,
d : 4,
e : [ undefined, 4 ],
f : null
}
);
var h = Hash.compact(hash);
assert.deepEqual(h, compacted.items);
};

View File

@@ -0,0 +1,69 @@
var Hash = require('hashish');
var assert = require('assert');
var vm = require('vm');
var fs = require('fs');
var src = fs.readFileSync(__dirname + '/../index.js', 'utf8');
exports.defineGetter = function () {
var context = {
module : { exports : {} },
Object : {
keys : Object.keys,
defineProperty : undefined,
},
require : require,
};
context.exports = context.module.exports;
vm.runInNewContext('(function () {' + src + '})()', context);
var Hash_ = context.module.exports;
var times = 0;
Hash_.__proto__.__proto__.__defineGetter__ = function () {
times ++;
return Object.__defineGetter__.apply(this, arguments);
};
assert.equal(vm.runInNewContext('Object.defineProperty', context), null);
assert.deepEqual(
Hash_({ a : 1, b : 2, c : 3 }).values,
[ 1, 2, 3 ]
);
assert.ok(times > 5);
};
exports.defineProperty = function () {
var times = 0;
var context = {
module : { exports : {} },
Object : {
keys : Object.keys,
defineProperty : function (prop) {
times ++;
if (prop.get) throw new TypeError('engine does not support')
assert.fail('should have asserted by now');
},
},
require : require
};
context.exports = context.module.exports;
vm.runInNewContext('(function () {' + src + '})()', context);
var Hash_ = context.module.exports;
Hash_.__proto__.__proto__.__defineGetter__ = function () {
assert.fail('getter called when a perfectly good'
+ ' defineProperty was available'
);
};
assert.deepEqual(
Hash_({ a : 1, b : 2, c : 3 }).values,
[ 1, 2, 3 ]
);
assert.equal(times, 1);
};

View File

@@ -0,0 +1,40 @@
{
"name": "mysql",
"version": "0.9.6",
"author": {
"name": "Felix Geisendörfer",
"email": "felix@debuggable.com",
"url": "http://debuggable.com/"
},
"description": "A pure node.js JavaScript Client implementing the MySQL protocol.",
"homepage": "https://github.com/felixge/node-mysql",
"repository": {
"type": "git",
"url": "git://github.com/felixge/node-mysql.git"
},
"main": "./index",
"scripts": {
"test": "make test"
},
"dependencies": {
"hashish": "0.0.4"
},
"devDependencies": {
"gently": "0.8.0",
"urun": "0.0.4",
"utest": "0.0.3"
},
"engines": {
"node": "*"
},
"optionalDependencies": {},
"_id": "mysql@0.9.6",
"_engineSupported": true,
"_npmVersion": "1.1.21",
"_nodeVersion": "v0.6.18",
"_defaultsLoaded": true,
"dist": {
"shasum": "6b249df71a5d3764d87cb502e65dcdd020a40eb5"
},
"_from": "mysql"
}

View File

@@ -0,0 +1,23 @@
var mysql = require('..');
var path = require('path');
var root = path.join(__dirname, '../');
exports.dir = {
root: root,
lib: root + '/lib',
fixture: root + '/test/fixture',
};
exports.TEST_DB = 'node_mysql_test';
exports.TEST_TABLE = 'posts';
exports.createClient = function() {
try {
var config = require('./config');
} catch (e) {
console.log('Skipping. See test/config.template.js for more information.');
process.exit(0);
}
return mysql.createClient(config);
};

View File

@@ -0,0 +1,8 @@
// Copy this file to test/config.js and fill in your own credentials in order
// to run the system test suite.
module.exports = {
host: 'localhost',
port: 3306,
user: 'root',
password: 'root'
};

View File

@@ -0,0 +1,54 @@
CREATE TABLE `columnia` (
`f0` bigint(20) unsigned NOT NULL DEFAULT '0',
`field0001` bigint(20) unsigned NOT NULL DEFAULT '0',
`field002` bigint(20) unsigned NOT NULL DEFAULT '0',
`field0000000003` bigint(20) unsigned DEFAULT NULL,
`field00004` bigint(20) unsigned NOT NULL DEFAULT '0',
`field000000000005` bigint(20) unsigned DEFAULT NULL,
`field0000000000006` bigint(20) unsigned NOT NULL,
`field000007` text NOT NULL,
`field0000000008` text NOT NULL,
`field009` int(10) NOT NULL DEFAULT '0',
`field00010` text NOT NULL,
`field000000000011` text NOT NULL,
`field00000012` text NOT NULL,
`field0000013` text NOT NULL,
`field00000000000014` text NOT NULL,
`field0000000015` text NOT NULL,
`field000016` text NOT NULL,
`field000000000017` text NOT NULL,
`field0000000000000018` text NOT NULL,
`field0000000000019` text NOT NULL,
`field0000000000020` text NOT NULL,
`field00000000000021` text NOT NULL,
`field00000000022` text NOT NULL,
`field000000000000000023` text NOT NULL,
`field000000000000024` text NOT NULL,
`field000000000000025` text NOT NULL,
`field0000000000000026` text NOT NULL,
`field0000000000027` text NOT NULL,
`field00000028` bigint(20) unsigned NOT NULL,
`field000000029` tinyint(1) NOT NULL,
`field000030` tinyint(1) NOT NULL DEFAULT '0',
`field31` text NOT NULL,
`field00000032` bigint(20) unsigned NOT NULL,
`fi33` int(11) NOT NULL DEFAULT '0',
`field00000034` bigint(20) unsigned DEFAULT NULL,
`field000035` text NOT NULL,
`field0036` int(11) NOT NULL DEFAULT '0',
`field0000000000000000000037` text NOT NULL,
`field0000000000000000000038` text NOT NULL,
`field0000000000000000000000000039` text NOT NULL,
`field0000000000000000040` text NOT NULL,
`field00000000000000000041` text NOT NULL,
`field00000000000000000000042` text NOT NULL,
`field00000000000000000043` text NOT NULL,
`field00000000000000000044` text NOT NULL,
`field00000000000000000000000045` text NOT NULL,
`field00000000000000046` text NOT NULL,
`field000000000000000047` text NOT NULL,
`field000000000000000000048` text NOT NULL,
`field49` text NOT NULL,
`field50` int(11) NOT NULL,
PRIMARY KEY (`f0`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

View File

@@ -0,0 +1,127 @@
#include <stdio.h>
#include <string.h>
#include <math.h>
#define SCRAMBLE_LENGTH_323 8
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned char uchar;
struct rand_struct {
unsigned long seed1,seed2,max_value;
double max_value_dbl;
};
void hash_password(ulong *result, const char *password, uint password_len)
{
register ulong nr=1345345333L, add=7, nr2=0x12345671L;
ulong tmp;
const char *password_end= password + password_len;
for (; password < password_end; password++)
{
if (*password == ' ' || *password == '\t')
continue; /* skip space in password */
tmp= (ulong) (uchar) *password;
nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
nr2+=(nr2 << 8) ^ nr;
add+=tmp;
}
result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
result[1]=nr2 & (((ulong) 1L << 31) -1L);
}
void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2)
{ /* For mysql 3.21.# */
#ifdef HAVE_purify
bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */
#endif
rand_st->max_value= 0x3FFFFFFFL;
rand_st->max_value_dbl=(double) rand_st->max_value;
rand_st->seed1=seed1%rand_st->max_value ;
rand_st->seed2=seed2%rand_st->max_value;
}
double my_rnd(struct rand_struct *rand_st)
{
rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
return (((double) rand_st->seed1)/rand_st->max_value_dbl);
}
void scramble_323(char *to, const char *message, const char *password)
{
struct rand_struct rand_st;
ulong hash_pass[2], hash_message[2];
if (password && password[0])
{
char extra, *to_start=to;
const char *message_end= message + SCRAMBLE_LENGTH_323;
hash_password(hash_pass,password, (uint) strlen(password));
hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
hash_pass[1] ^ hash_message[1]);
for (; message < message_end; message++)
*to++= (char) (floor(my_rnd(&rand_st)*31)+64);
extra=(char) (floor(my_rnd(&rand_st)*31));
while (to_start != to)
*(to_start++)^=extra;
}
*to= 0;
}
int main() {
const char password1[] = "root";
const char password2[] = "long password test";
const char password3[] = "saf789yasfbsd89f";
ulong result[2];
char scrm[9]; // SCRAMBLE_LENGTH_323+1
struct rand_struct rand_st;
int i;
// test hash_password
hash_password((ulong*)result, password1, strlen(password1));
printf("hash_password(\"%s\") = %08x%08x\n", password1, result[0], result[1]);
hash_password((ulong*)result, password2, strlen(password2));
printf("hash_password(\"%s\") = %08x%08x\n", password2, result[0], result[1]);
hash_password((ulong*)result, password3, strlen(password3));
printf("hash_password(\"%s\") = %08x%08x\n", password3, result[0], result[1]);
// test randominit
randominit(&rand_st, 0, 0);
printf("randominit(0x00000000,0x00000000) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
randominit(&rand_st, 0xFFFF, 0xFFFF);
printf("randominit(0x0000FFFF,0x0000FFFF) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
randominit(&rand_st, 0x50000000, 0x50000000);
printf("randominit(0x50000000,0x50000000) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
randominit(&rand_st, 0xFFFFFFFF, 0xFFFFFFFF);
printf("randominit(0xFFFFFFFF,0xFFFFFFFF) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
// test my_rnd
randominit(&rand_st, 3252345, 7149734);
printf("randominit(3252345, 7149734) = %08x, %08x\n", rand_st.seed1, rand_st.seed2);
for (i=0; i<10; i++){
printf("my_rnd() : %.16f\n", my_rnd(&rand_st));
}
// test scramble_323
scramble_323(scrm, "8bytesofstuff", "root");
printf("scramble323(8bytesofstuff, root): %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
scrm[0], scrm[1], scrm[2], scrm[3], scrm[4], scrm[5], scrm[6], scrm[7], scrm[8]);
scramble_323(scrm, "e8cf00cec9ec825af22", "saf789yasfbsd");
printf("scramble323(e8cf00cec9ec825af22, saf789yasfbsd): %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
scrm[0], scrm[1], scrm[2], scrm[3], scrm[4], scrm[5], scrm[6], scrm[7], scrm[8]);
return 23;
}

View File

@@ -0,0 +1,11 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
client.ping(function(err, result) {
if (err) throw err;
assert.strictEqual(result.affectedRows, 0);
client.end();
});

View File

@@ -0,0 +1,10 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
client.statistics(function(err, result) {
if (err) throw err;
assert.ok(result.extra.match(/time/i));
client.end();
});

View File

@@ -0,0 +1,13 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
client.useDatabase(common.TEST_DB, function(err, result) {
// The TEST_DB may not exist right now, so ignore errors related to that
if (err && err.number === mysql.ERROR_BAD_DB_ERROR) err = null;
if (err) throw err;
assert.strictEqual(result.affectedRows, 0);
client.end();
});

View File

@@ -0,0 +1,11 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
client.query('SELECT 1', function(err, results) {
if (err) throw err;
assert.deepEqual(results, [{1: 1}]);
client.end();
});

View File

@@ -0,0 +1,24 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
// Not sure if we need all 3 of these, but they do the trick
client.query('SET interactive_timeout = 1');
client.query('SET wait_timeout = 1');
client.query('SET net_read_timeout = 1');
var result;
client._socket.on('end', function() {
assert.equal(client.connected, false);
client.query('SELECT 1', function(err, _result) {
if (err) throw err;
result = _result;
client.destroy();
});
});
process.on('exit', function() {
assert.deepEqual(result, [{'1': 1}]);
});

View File

@@ -0,0 +1,24 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
client.password = 'thispassworddoesnotreallywork';
var callbacks = [];
client.query('SELECT 1', function(err) {
assert.ok(/access denied/i.test(err.message));
callbacks.push(1);
});
client.query('SELECT 2', function(err) {
assert.ok(/access denied/i.test(err.message));
callbacks.push(2);
client.destroy();
});
process.on('exit', function() {
assert.deepEqual(callbacks, [1, 2]);
});

View File

@@ -0,0 +1,25 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
// Port number outside of range -> triggers connection error
client.port = 999999999;
var callbacks = [];
client.query('SELECT 1', function(err) {
assert.ok(err);
callbacks.push(1);
});
client.query('SELECT 2', function(err) {
assert.ok(err);
callbacks.push(2);
client.destroy();
});
process.on('exit', function() {
assert.deepEqual(callbacks, [1, 2]);
});

View File

@@ -0,0 +1,27 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
var callbacks = [];
client.query('SELECT 1', function(err, results) {
if (err) throw err;
callbacks.push(1);
});
client.end(function(err) {
if (err) throw err;
callbacks.push(2);
});
client.query('SELECT 1', function(err) {
if (err) throw err;
callbacks.push(3);
client.destroy();
});
process.on('exit', function() {
assert.deepEqual(callbacks, [1, 2, 3]);
});

View File

@@ -0,0 +1,17 @@
var common = require('../../../common');
var assert = require('assert');
var INVALID_QUERY = 'first invalid #*&% query';
var client = common.createClient();
var err;
client.query(INVALID_QUERY);
client .on('error', function(_err) {
err = _err;
client.destroy();
});
process.on('exit', function() {
assert.ok(err);
assert.strictEqual(err.sql, INVALID_QUERY);
});

View File

@@ -0,0 +1,18 @@
var common = require('../../../common');
var assert = require('assert');
var INVALID_QUERY = 'first invalid #*&% query';
var client = common.createClient();
var err;
client
.query(INVALID_QUERY)
.on('error', function(_err) {
err = _err;
client.destroy();
});
process.on('exit', function() {
assert.ok(err);
assert.strictEqual(err.sql, INVALID_QUERY);
});

View File

@@ -0,0 +1,37 @@
var common = require('../../../common');
var assert = require('assert');
var INVALID_QUERY = 'first invalid #*&% query';
var client = common.createClient();
var callbacks = [];
client.query(INVALID_QUERY, function(err) {
assert.strictEqual(err.sql, INVALID_QUERY);
callbacks.push(1);
});
client.query('SHOW STATUS', function(err, rows, fields) {
if (err) throw err;
assert.equal(rows.length >= 50, true);
assert.equal(Object.keys(fields).length, 2);
callbacks.push(2);
});
client.query(INVALID_QUERY, function(err) {
assert.strictEqual(err.sql, INVALID_QUERY);
callbacks.push(3);
});
client.query(INVALID_QUERY, function(err) {
assert.strictEqual(err.sql, INVALID_QUERY);
client.destroy();
callbacks.push(4);
});
process.on('exit', function() {
assert.deepEqual(callbacks, [1, 2, 3, 4]);
});

View File

@@ -0,0 +1,16 @@
var common = require('../../../common');
var assert = require('assert');
var INVALID_QUERY = 'first invalid #*&% query';
var client = common.createClient();
var err;
client.query(INVALID_QUERY, function(_err) {
err = _err;
client.destroy();
});
process.on('exit', function() {
assert.ok(err);
assert.strictEqual(err.sql, INVALID_QUERY);
});

View File

@@ -0,0 +1,36 @@
var common = require('../../../common');
var assert = require('assert');
var fs = require('fs');
var mysql = require(common.dir.root);
var REPEATS = 500;
var client = common.createClient();
client.query('CREATE DATABASE ' + common.TEST_DB, function(err) {
if (err && err.number != mysql.ERROR_DB_CREATE_EXISTS) throw err;
});
client.query('USE ' + common.TEST_DB);
client.query('DROP TABLE IF EXISTS columnia');
var fixture = fs.readFileSync(common.dir.fixture + '/columnia.sql', 'utf8');
client.query(fixture);
var finished = 0;
var self = this;
for (var i = 0; i < REPEATS; i++) {
(function(i) {
var query = client.query("SHOW COLUMNS FROM columnia");
query.on('row', function(row) {
if (!row.Type) throw new Error('Column order mixed up after '+i+' queries.');
});
query.on('end', function() {
finished++;
if (finished === REPEATS) client.destroy();
});
})(i);
}
process.on('exit', function() {
assert.equal(finished, REPEATS);
});

View File

@@ -0,0 +1,14 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
var results;
client.query('SELECT "" as field_a', function(err, _results) {
if (err) throw err;
results = _results;
client.destroy();
});
process.on('exit', function() {
assert.strictEqual(results[0].field_a, "");
});

View File

@@ -0,0 +1,43 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
var field_a = makeString(250);
var field_b = makeString(251);
var field_c = makeString(512);
var field_d = makeString(65537);
var callbacks = 0;
// We execute this test twice to be sure the parser is in a good state after
// each run.
test();
test(true);
function test(last) {
var sql = 'SELECT ? as field_a, ? as field_b, ? as field_c, ? as field_d';
var params = [field_a, field_b, field_c, field_d];
var query = client.query(sql, params, function(err, results) {
if (err) throw err;
assert.equal(results[0].field_a, field_a);
assert.equal(results[0].field_b, field_b);
assert.equal(results[0].field_c, field_c);
assert.equal(results[0].field_d, field_d);
callbacks++;
if (last) client.destroy();
});
}
function makeString(length) {
var str = '';
for (var i = 0; i < length; i++) {
str += 'x';
}
return str;
}
process.on('exit', function() {
assert.equal(callbacks, 2);
});

View File

@@ -0,0 +1,15 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
var results;
client.query('SELECT NULL as field_a, NULL as field_b', function(err, _results) {
if (err) throw err;
results = _results;
client.destroy();
});
process.on('exit', function() {
assert.strictEqual(results[0].field_a, null);
assert.strictEqual(results[0].field_b, null);
});

View File

@@ -0,0 +1,51 @@
var common = require('../../../common');
var assert = require('assert');
var mysql = require(common.dir.root);
var client = common.createClient();
client.query('CREATE DATABASE '+common.TEST_DB, function createDbCb(err) {
if (err && err.number != mysql.ERROR_DB_CREATE_EXISTS) done(err);
});
client.query('USE '+common.TEST_DB);
client.query(
'CREATE TEMPORARY TABLE ' + common.TEST_TABLE+
'(id INT(11) AUTO_INCREMENT, title VARCHAR(255), text TEXT, created DATETIME, PRIMARY KEY (id));'
);
client.query(
'INSERT INTO ' + common.TEST_TABLE + ' '+
'SET title = ?, text = ?, created = ?',
['super cool', 'this is a nice long text', '2010-08-16 10:00:23']
);
var query = client.query(
'INSERT INTO '+common.TEST_TABLE+' '+
'SET title = ?, text = ?, created = ?',
['another entry', 'because 2 entries make a better test', null]
);
var endCalled = false;
query.on('end', function insertOkCb(packet) {
endCalled = true;
});
var lastQueryReached = false;
var query = client.query('SELECT * FROM '+common.TEST_TABLE, function selectCb(err, results, fields) {
assert.ok(endCalled);
lastQueryReached = true;
assert.equal(results.length, 2);
assert.equal(results[1].title, 'another entry');
assert.ok(typeof results[1].id == 'number');
assert.ok(results[0].created instanceof Date);
assert.strictEqual(results[1].created, null);
client.destroy();
});
process.on('exit', function() {
assert.ok(lastQueryReached);
});

View File

@@ -0,0 +1,16 @@
var common = require('../../../common');
var assert = require('assert');
var client = common.createClient();
var results;
client.query('SELECT 1 as field_a, 2 as field_b', function(err, _results) {
if (err) throw err;
results = _results;
client.destroy();
});
process.on('exit', function() {
assert.equal(results[0].field_a, 1);
assert.equal(results[0].field_b, 2);
});

View File

@@ -0,0 +1 @@
require('urun')(__dirname)

View File

@@ -0,0 +1,25 @@
var newCommon = require('../../common');
exports.dir = newCommon.dir;
var path = require('path');
var util = require('util');
global.TEST_DB = 'node_mysql_test';
global.TEST_TABLE = 'posts';
global.TEST_FIXTURES = path.join(__dirname, '../fixture');
global.Gently = require('gently');
global.assert = require('assert');
global.p = function(val) {
util.error(util.inspect(val));
};
global.GENTLY = new Gently();
global.HIJACKED = GENTLY.hijacked;
// Stupid new feature in node that complains about gently attaching too many
// listeners to process 'exit'. This is a workaround until I can think of a
// better way to deal with this.
if (process.setMaxListeners) {
process.setMaxListeners(Infinity);
}

View File

@@ -0,0 +1,118 @@
var common = require('./common');
var auth = require(common.dir.lib + '/auth');
function test(test) {
gently = new Gently();
test();
gently.verify(test.name);
}
test(function sha1() {
assert.deepEqual(
auth.sha1('root'),
new Buffer([
220, 118, 233, 240, 192,
0, 110, 143, 145, 158,
12, 81, 92, 102, 219,
186, 57, 130, 247, 133
]).toString('binary')
);
});
test(function xor() {
var a = new Buffer([170, 220]), // 10101010 11011100
b = new Buffer([220, 170]), // 11011100 10101010
expected = new Buffer([118, 118]); // 01110110 01110110
assert.deepEqual(auth.xor(a.toString('binary'), b.toString('binary')), expected);
});
test(function token() {
var SCRAMBLE = new Buffer([0, 1, 2, 3, 4, 5]);
(function testRegular() {
var PASS = 'root',
STAGE_1 = auth.sha1(PASS),
TOKEN = auth.xor(
auth.sha1(new Buffer(SCRAMBLE + auth.sha1(STAGE_1), 'binary')),
STAGE_1
);
assert.deepEqual(auth.token('root', SCRAMBLE), TOKEN);
})();
(function testNoPassword() {
assert.deepEqual(auth.token(null, SCRAMBLE), new Buffer(0));
})();
});
test(function hashPassword() {
function verify(password, bytes){
var expected = new Buffer(bytes);
var actual = auth.hashPassword(password);
assert.deepEqual(actual, expected);
}
verify('root', [0x67, 0x45, 0x7E, 0x22, 0x6a, 0x1a, 0x15, 0xbd]);
verify('long password test', [0x6c, 0x24, 0x68, 0x41, 0x2c, 0xa6, 0x86, 0x56]);
verify('saf789yasfbsd89f', [0x6c, 0x9b, 0x2f, 0x07, 0x17, 0xeb, 0x95, 0xc6]);
});
test(function randomInit() {
function verify(in1, in2, out1, out2){
var r = auth.randomInit(in1, in2);
assert.equal(out1, r.seed1);
assert.equal(out2, r.seed2);
}
verify(0x00000000, 0x00000000, 0x00000000, 0x00000000);
verify(0x0000FFFF, 0x0000FFFF, 0x0000ffff, 0x0000ffff);
verify(0x50000000, 0x50000000, 0x10000001, 0x10000001);
verify(0xFFFFFFFF, 0xFFFFFFFF, 0x00000003, 0x00000003);
verify(3252345, 7149734, 0x0031a079, 0x006d18a6);
});
test(function myRnd() {
function verifySequence(seed1, seed2, expected){
var r = auth.randomInit(seed1, seed2);
for (var i = 0; i < expected.length; i++){
var n = auth.myRnd(r);
// we will test to 14 digits, since
// we only ever use this function mutliplied
// by small numbers anyway
var a = ':'+n;
var b = ':'+expected[i];
assert.equal(a.substr(1, 16), b.substr(1, 16));
}
}
verifySequence(3252345, 7149734, [
0.0157456556481734,
0.0696413620092360,
0.3009698738353047,
0.2959253138824602,
0.5767169786400320,
0.9958089822864243,
0.2488940062456708,
0.2570431151027261,
0.5385335875102631,
0.9215386229767824,
]);
});
test(function scramble323() {
function verify(message, password, bytes){
var expected = new Buffer(bytes);
var actual = auth.scramble323(new Buffer(message), password);
assert.deepEqual(actual, expected);
}
verify('8bytesofstuff', 'root', [0x5a, 0x4d, 0x46, 0x47, 0x43, 0x53, 0x58, 0x5f]);
verify('e8cf00cec9ec825af22', 'saf789yasfbsd', [0x4d, 0x54, 0x5b, 0x47, 0x5f, 0x52, 0x4d, 0x45]);
});

View File

@@ -0,0 +1,95 @@
var common = require('./common');
var Parser = require(common.dir.lib + '/parser');
var constants = require(common.dir.lib + '/constants');
var Client = require(common.dir.lib + '/client');
function test(test) {
client = new Client();
gently = new Gently();
test();
gently.verify(test.name);
};
test(function write() {
var PACKET = {buffer: []},
CONNECTION = client._socket = {};
gently.expect(CONNECTION, 'write', function(buffer) {
assert.strictEqual(buffer, PACKET.buffer);
});
client.write(PACKET);
});
test(function format() {
var sql = client.format('? + ? = ?', [1, 2, 'great']);
assert.equal(sql, '1 + 2 = \'great\'');
assert.throws(function() {
var sql = client.format('? + ? = ?', [1, 2]);
});
assert.throws(function() {
var sql = client.format('? + ? = ?', [1, 2, 3, 4]);
});
});
test(function escape() {
assert.equal(client.escape(undefined), 'NULL');
assert.equal(client.escape(null), 'NULL');
assert.equal(client.escape(false), 'false');
assert.equal(client.escape(true), 'true');
assert.equal(client.escape(5), '5');
assert.equal(client.escape({foo:'bar'}), "'[object Object]'");
assert.equal(client.escape([1,2,3]), "'1','2','3'");
assert.equal(client.escape(new Date(Date.UTC(2011,6,6,6,6,6,6))), "'2011-07-06T06:06:06.006Z'");
assert.equal(client.escape('Super'), "'Super'");
assert.equal(client.escape('Sup\0er'), "'Sup\\0er'");
assert.equal(client.escape('Sup\ber'), "'Sup\\ber'");
assert.equal(client.escape('Sup\ner'), "'Sup\\ner'");
assert.equal(client.escape('Sup\rer'), "'Sup\\rer'");
assert.equal(client.escape('Sup\ter'), "'Sup\\ter'");
assert.equal(client.escape('Sup\\er'), "'Sup\\\\er'");
assert.equal(client.escape('Sup\u001aer'), "'Sup\\Zer'");
assert.equal(client.escape('Sup\'er'), "'Sup\\'er'");
assert.equal(client.escape('Sup"er'), "'Sup\\\"er'");
});
test(function _packetToUserObject() {
(function testOkPacket() {
var PACKET = {
type: Parser.OK_PACKET,
length: 65,
received: 65,
number: 92,
foo: 'bar'
};
var ok = Client._packetToUserObject(PACKET);
assert.notStrictEqual(PACKET, ok);
assert.ok(!(ok instanceof Error));
assert.equal(ok.foo, PACKET.foo);
assert.equal(ok.type, undefined);
assert.equal(ok.length, undefined);
assert.equal(ok.received, undefined);
})();
(function testErrorPacket() {
var PACKET = {
type: Parser.ERROR_PACKET,
foo: 'bar',
errorMessage: 'oh no',
errorNumber: 1007
};
var err = Client._packetToUserObject(PACKET);
assert.ok(err instanceof Error);
assert.equal(err.message, 'oh no');
assert.equal(err.errorMessage, undefined);
assert.equal(err.number, 1007);
assert.equal(err.errorNumber, undefined);
})();
});

View File

@@ -0,0 +1,134 @@
var common = require('./common');
var OutgoingPacket = require(common.dir.lib + '/outgoing_packet'),
Buffer = require('buffer').Buffer;
function test(test) {
gently = new Gently();
test();
gently.verify(test.name);
}
test(function constructor() {
var packet = new OutgoingPacket(10, 5);
assert.equal(packet.buffer.length, 14);
assert.deepEqual(
packet.buffer.slice(0, 3),
new Buffer([10, 0, 0])
);
assert.equal(packet.buffer[3], 5);
assert.equal(packet.index, 4);
});
test(function writeNumber() {
var packet = new OutgoingPacket(4);
packet.writeNumber(4, 257);
assert.deepEqual(
packet.buffer.slice(4, 8),
new Buffer([1, 1, 0, 0])
);
});
test(function writeFiller() {
var packet = new OutgoingPacket(5);
packet.writeFiller(5);
assert.equal(packet.index, 9);
assert.deepEqual(
packet.buffer.slice(4, 9),
new Buffer([0, 0, 0, 0, 0])
);
});
test(function write() {
(function testBuffer() {
var packet = new OutgoingPacket(3),
BUFFER = new Buffer([1, 2, 3]);
packet.write(BUFFER);
assert.equal(packet.index, 7);
assert.deepEqual(packet.buffer.slice(4, 7), BUFFER);
})();
(function testString() {
var packet = new OutgoingPacket(3),
STRING = 'abc';
packet.write(STRING);
assert.equal(packet.index, 7);
assert.equal(packet.buffer.slice(4, 7).toString(), STRING);
})();
});
test(function writeNullTerminated() {
var packet = new OutgoingPacket(4),
BUFFER = new Buffer([17, 23, 42]);
packet.buffer[7] = 100; // set last byte to non-0
gently.expect(packet, 'write', function(buffer) {
assert.strictEqual(buffer, BUFFER);
this.index += buffer.length;
});
packet.writeNullTerminated(BUFFER);
assert.equal(packet.buffer[7], 0);
assert.equal(packet.index, 8);
});
test(function writeLengthCoded() {
(function test1ByteNumber() {
var packet = new OutgoingPacket(1);
packet.writeLengthCoded(250);
assert.equal(packet.buffer[4], 250);
assert.equal(packet.index, 5);
})();
(function test2ByteNumber() {
var packet = new OutgoingPacket(6);
packet.writeLengthCoded(251);
assert.equal(packet.buffer[4], 252);
assert.equal(packet.buffer[5], 251);
assert.equal(packet.buffer[6], 0);
assert.equal(packet.index, 7);
packet.writeLengthCoded(257);
assert.equal(packet.buffer[7], 252);
assert.equal(packet.buffer[8], 1);
assert.equal(packet.buffer[9], 1);
assert.equal(packet.index, 10);
})();
(function test3ByteNumber() {
var packet = new OutgoingPacket(4);
packet.writeLengthCoded(Math.pow(256, 0) * 5 + Math.pow(256, 1) * 6 + Math.pow(256, 2) * 7);
assert.equal(packet.buffer[4], 253);
assert.equal(packet.buffer[5], 5);
assert.equal(packet.buffer[6], 6);
assert.equal(packet.buffer[7], 7);
assert.equal(packet.index, 8);
})();
(function testNull() {
var packet = new OutgoingPacket(1);
packet.writeLengthCoded(null);
assert.equal(packet.buffer[4], 251);
assert.equal(packet.index, 5);
})();
(function testBuffer() {
var packet = new OutgoingPacket(4),
BUFFER = new Buffer([17, 23, 42]);
packet.writeLengthCoded(BUFFER);
assert.equal(packet.buffer[4], 3);
assert.deepEqual(packet.buffer.slice(5, 8), BUFFER);
})();
(function testString() {
var packet = new OutgoingPacket(6),
STRING = 'über';
packet.writeLengthCoded(STRING);
assert.equal(packet.buffer[4], 5);
assert.equal(packet.buffer.slice(5, 10).toString(), STRING);
})();
});

View File

@@ -0,0 +1,387 @@
var common = require('./common');
var EventEmitter = require('events').EventEmitter,
Parser = require(common.dir.lib + '/parser'),
parser,
gently;
function test(test) {
parser = new Parser();
gently = new Gently();
test();
gently.verify(test.name);
}
test(function constructor() {
assert.strictEqual(parser.state, Parser.PACKET_LENGTH);
assert.strictEqual(parser.packet, null);
assert.strictEqual(parser.greeted, false);
assert.strictEqual(parser.authenticated, false);
assert.strictEqual(parser.receivingFieldPackets, false);
assert.strictEqual(parser.receivingRowPackets, false);
assert.strictEqual(parser._lengthCodedLength, null);
assert.strictEqual(parser._lengthCodedStringLength, null);
assert.ok(parser instanceof EventEmitter);
});
test(function write() {
var packet;
(function testPacketLength() {
var LENGTH = 56;
parser.write(new Buffer([LENGTH]));
assert.equal(parser.state, Parser.PACKET_LENGTH);
packet = parser.packet;
assert.ok(packet instanceof EventEmitter);
assert.strictEqual(packet.number, 0);
assert.strictEqual(packet.length, LENGTH);
parser.write(new Buffer([0]));
parser.write(new Buffer([0]));
assert.strictEqual(
packet.length,
Math.pow(256, 0) * LENGTH + Math.pow(256, 1) * 0 + Math.pow(256, 2) * 0
);
})();
(function testPacketNumber() {
parser.write(new Buffer([42]));
assert.strictEqual(packet.number, 42);
assert.equal(parser.state, Parser.GREETING_PROTOCOL_VERSION);
})();
(function testGreetingErrorPacket() {
parser.write(new Buffer([0xff]));
assert.equal(packet.type, Parser.ERROR_PACKET);
assert.equal(parser.state, Parser.ERROR_NUMBER);
parser.write(new Buffer([5, 2]));
assert.equal(packet.errorNumber, Math.pow(256, 0) * 5 + Math.pow(256, 1) * 2);
parser.write(new Buffer('Hello World'));
assert.equal(packet.errorMessage, 'Hello World');
// Reset back to previous state
packet.type = Parser.GREETING_PACKET;
packet.received = 0;
parser.state = Parser.GREETING_PROTOCOL_VERSION;
})();
(function testGreetingPacket() {
parser.write(new Buffer([15]));
assert.equal(packet.type, Parser.GREETING_PACKET);
assert.equal(packet.protocolVersion, 15);
assert.equal(parser.state, Parser.GREETING_SERVER_VERSION);
var VERSION = 'MySql 5.1';
parser.write(new Buffer(VERSION+'\0', 'binary'));
assert.equal(packet.serverVersion, VERSION);
assert.equal(parser.state, Parser.GREETING_THREAD_ID);
parser.write(new Buffer([0, 0, 0, 1]));
assert.equal(packet.threadId, Math.pow(256, 3));
parser.write(new Buffer([1]));
assert.equal(packet.scrambleBuffer[0], 1);
assert.equal(packet.scrambleBuffer.length, 8 + 12);
assert.equal(parser.state, Parser.GREETING_SCRAMBLE_BUFF_1);
parser.write(new Buffer([2, 3, 4, 5, 6, 7, 8]));
assert.deepEqual(
packet.scrambleBuffer.slice(0, 8),
new Buffer([1, 2, 3, 4, 5, 6, 7, 8])
);
assert.equal(parser.state, Parser.GREETING_FILLER_1);
parser.write(new Buffer([0]));
assert.equal(parser.state, Parser.GREETING_SERVER_CAPABILITIES);
parser.write(new Buffer([0, 1]));
assert.equal(packet.serverCapabilities, Math.pow(256, 1));
parser.write(new Buffer([17]));
assert.equal(packet.serverLanguage, 17);
assert.equal(parser.state, Parser.GREETING_SERVER_STATUS);
parser.write(new Buffer([0, 1]));
assert.equal(packet.serverStatus, Math.pow(256, 1));
parser.write(new Buffer([0]));
assert.equal(parser.state, Parser.GREETING_FILLER_2);
parser.write(new Buffer(12));
assert.equal(parser.state, Parser.GREETING_SCRAMBLE_BUFF_2);
parser.write(new Buffer([9]));
assert.equal(packet.scrambleBuffer[8], 9);
assert.equal(parser.state, Parser.GREETING_SCRAMBLE_BUFF_2);
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
assert.ok(!('index' in val));
assert.strictEqual(val, packet);
assert.equal(parser.state, Parser.PACKET_LENGTH);
assert.equal(parser.greeted, true);
});
parser.write(new Buffer([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0]));
assert.deepEqual(
packet.scrambleBuffer.slice(9, 20),
new Buffer([10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
);
assert.strictEqual(parser.packet, null);
})();
(function testUseOldPasswordProtocolPacket() {
parser.write(new Buffer([1, 0, 0, 1]));
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
assert.equal(val.type, Parser.USE_OLD_PASSWORD_PROTOCOL_PACKET);
});
parser.write(new Buffer([254]));
})();
(function testErrorPacket() {
parser.write(new Buffer([12, 0, 0, 1]));
assert.equal(parser.state, Parser.FIELD_COUNT);
var packet = parser.packet;
parser.write(new Buffer([0xff]));
assert.equal(packet.type, Parser.ERROR_PACKET);
assert.equal(parser.state, Parser.ERROR_NUMBER);
parser.write(new Buffer([5, 2]));
assert.equal(packet.errorNumber, Math.pow(256, 0) * 5 + Math.pow(256, 1) * 2);
parser.write(new Buffer('#'));
assert.equal(packet.sqlStateMarker, '#');
assert.equal(parser.state, Parser.ERROR_SQL_STATE);
parser.write(new Buffer('abcde'));
assert.equal(packet.sqlState, 'abcde');
parser.write(new Buffer('er'));
assert.equal(parser.state, Parser.ERROR_MESSAGE);
assert.equal(packet.errorMessage, 'er');
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
assert.equal(packet.errorMessage, 'err');
});
parser.write(new Buffer('r'));
})();
(function testOkPacket() {
parser.write(new Buffer([15, 0, 0, 1]));
var packet = parser.packet;
parser.write(new Buffer([0x00]));
assert.equal(packet.type, Parser.OK_PACKET);
assert.equal(parser.authenticated, true);
assert.equal(parser.state, Parser.AFFECTED_ROWS);
parser.write(new Buffer([252, 17, 23]));
assert.equal(packet.affectedRows, Math.pow(256, 0) * 17 + Math.pow(256, 1) * 23);
parser.write(new Buffer([240]));
assert.equal(packet.insertId, 240);
parser.write(new Buffer([42, 113]));
assert.equal(packet.serverStatus, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
parser.write(new Buffer([32, 153]));
assert.equal(packet.warningCount, Math.pow(256, 0) * 32 + Math.pow(256, 1) * 153);
assert.strictEqual(packet.message, '');
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
assert.equal(packet.message, 'abcdef');
});
parser.write(new Buffer('abcdef'));
})();
(function testResultHeaderPacket() {
parser.write(new Buffer([1, 0, 0, 1]));
var packet = parser.packet;
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
assert.equal(val.type, Parser.RESULT_SET_HEADER_PACKET);
assert.equal(val.fieldCount, 5);
});
parser.write(new Buffer([5]));
})();
(function testResultHeaderPacketWithExtra() {
parser.receivingFieldPackets = false;
parser.write(new Buffer([5, 0, 0, 1]));
var packet = parser.packet;
parser.write(new Buffer([23]));
assert.equal(parser.state, Parser.EXTRA_LENGTH);
assert.equal(packet.fieldCount, 23);
parser.write(new Buffer([3]));
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
assert.equal(val.type, Parser.RESULT_SET_HEADER_PACKET);
assert.equal(val.extra, 'abc');
});
parser.write(new Buffer('abc'));
})();
(function testFieldPacket() {
parser.write(new Buffer([43, 0, 0, 1]));
var packet = parser.packet;
assert.equal(parser.state, Parser.FIELD_CATALOG_LENGTH);
parser.write(new Buffer([3]));
assert.equal(packet.type, Parser.FIELD_PACKET);
parser.write(new Buffer('abc'));
assert.equal(packet.catalog, 'abc');
assert.equal(parser.state, Parser.FIELD_DB_LENGTH);
parser.write(new Buffer([5]));
parser.write(new Buffer('hello'));
assert.equal(packet.db, 'hello');
assert.equal(parser.state, Parser.FIELD_TABLE_LENGTH);
parser.write(new Buffer([2]));
parser.write(new Buffer('ab'));
assert.equal(packet.table, 'ab');
assert.equal(parser.state, Parser.FIELD_ORIGINAL_TABLE_LENGTH);
parser.write(new Buffer([4]));
parser.write(new Buffer('1234'));
assert.equal(packet.originalTable, '1234');
assert.equal(parser.state, Parser.FIELD_NAME_LENGTH);
parser.write(new Buffer([1]));
parser.write(new Buffer('o'));
assert.equal(packet.name, 'o');
assert.equal(parser.state, Parser.FIELD_ORIGINAL_NAME_LENGTH);
parser.write(new Buffer([9]));
parser.write(new Buffer('wonderful'));
assert.equal(packet.originalName, 'wonderful');
assert.equal(parser.state, Parser.FIELD_FILLER_1);
parser.write(new Buffer([0]));
assert.equal(parser.state, Parser.FIELD_CHARSET_NR);
parser.write(new Buffer([42, 113]));
assert.equal(packet.charsetNumber, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
assert.equal(parser.state, Parser.FIELD_LENGTH);
parser.write(new Buffer([42, 113, 50, 30]));
assert.equal(packet.fieldLength, 42 + (256 * 113) + (256 * 256) * 50 + (256 * 256 * 256 * 30));
assert.equal(parser.state, Parser.FIELD_TYPE);
parser.write(new Buffer([58]));
assert.equal(packet.fieldType, 58);
assert.equal(parser.state, Parser.FIELD_FLAGS);
parser.write(new Buffer([42, 113]));
assert.equal(packet.flags, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
assert.equal(parser.state, Parser.FIELD_DECIMALS);
parser.write(new Buffer([58]));
assert.equal(packet.decimals, 58);
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
});
assert.equal(parser.state, Parser.FIELD_FILLER_2);
parser.write(new Buffer([0, 0]));
})();
(function testEofPacket() {
parser.write(new Buffer([5, 0, 0, 1]));
var packet = parser.packet;
parser.write(new Buffer([0xfe]));
assert.equal(packet.type, Parser.EOF_PACKET);
assert.equal(parser.state, Parser.EOF_WARNING_COUNT);
parser.write(new Buffer([42, 113]));
assert.equal(packet.warningCount, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
assert.equal(parser.receivingFieldPackets, false);
assert.equal(parser.receivingRowPackets, true);
});
assert.equal(parser.state, Parser.EOF_SERVER_STATUS);
parser.write(new Buffer([42, 113]));
assert.equal(packet.serverStatus, Math.pow(256, 0) * 42 + Math.pow(256, 1) * 113);
})();
(function testRowPacket() {
parser.write(new Buffer([23, 0, 0, 1]));
var packet = parser.packet;
gently.expect(parser, 'emit', function(event, val) {
assert.equal(event, 'packet');
});
parser.write(new Buffer([16]));
assert.equal(parser.state, Parser.COLUMN_VALUE_STRING);
assert.equal(packet.type, Parser.ROW_DATA_PACKET);
assert.equal(packet.columnLength, 16);
gently.expect(packet, 'emit', function(event, val, remaining) {
assert.equal(event, 'data');
assert.equal(val.toString(), 'hi, ');
assert.equal(remaining, 12);
});
parser.write(new Buffer('hi, '));
gently.expect(packet, 'emit', function(event, val, remaining) {
assert.equal(event, 'data');
assert.equal(val.toString(), 'how');
assert.equal(remaining, 9);
});
parser.write(new Buffer('how'));
gently.expect(packet, 'emit', function(event, val, remaining) {
assert.equal(event, 'data');
assert.equal(val.toString(), ' are you?');
assert.equal(remaining, 0);
});
gently.expect(packet, 'emit', function(event, val, remaining) {
assert.equal(event, 'data');
assert.equal(val.toString(), 'Fine!');
assert.equal(remaining, 0);
assert.equal(packet.index, 0);
});
parser.write(new Buffer(' are you?\u0005Fine!'));
assert.equal(parser.packet, null);
assert.equal(parser.state, Parser.PACKET_LENGTH);
})();
(function testEofPacketAfterRowPacket() {
parser.write(new Buffer([5, 0, 0, 1]));
var packet = parser.packet;
parser.write(new Buffer([0xfe]));
assert.equal(packet.type, Parser.EOF_PACKET);
assert.equal(parser.receivingRowPackets, false);
})();
});

View File

@@ -0,0 +1,68 @@
var common = require('./common');
var Query = require(common.dir.lib + '/query');
var EventEmitter = require('events').EventEmitter;
var Parser = require(common.dir.lib + '/parser');
var query;
var gently;
function test(test) {
query = new Query();
gently = new Gently();
test();
gently.verify(test.name);
}
test(function constructor() {
assert.ok(query instanceof EventEmitter);
assert.strictEqual(query.typeCast, true);
assert.strictEqual(query.sql, null);
assert.equal(new Query({foo: 'bar'}).foo, 'bar');
});
test(function _handlePacket() {
function typeCast(type, strValue) {
query._fields = [{name: 'my_field', fieldType: type}];
var PACKET = new EventEmitter(), r;
PACKET.type = Parser.ROW_DATA_PACKET;
gently.expect(PACKET, 'on', function (event, fn) {
assert.equal(event, 'data');
gently.expect(query, 'emit', function (event, row) {
assert.equal(event, 'row');
r = row.my_field;
});
var val = (strValue === null)
? null
: new Buffer(strValue);
fn(val, 0);
});
query._handlePacket(PACKET);
return r;
}
assert.deepEqual(typeCast(Query.FIELD_TYPE_TIMESTAMP, '2010-10-05 06:23:42 UTC'), new Date('2010-10-05 06:23:42Z'));
assert.deepEqual(typeCast(Query.FIELD_TYPE_TIMESTAMP, '2010-10-05 UTC'), new Date('2010-10-05Z'));
assert.deepEqual(typeCast(Query.FIELD_TYPE_DATE, '2010-10-05 UTC'), new Date('2010-10-05Z'));
assert.deepEqual(typeCast(Query.FIELD_TYPE_DATETIME, '2010-10-05 UTC'), new Date('2010-10-05Z'));
assert.deepEqual(typeCast(Query.FIELD_TYPE_NEWDATE, '2010-10-05 UTC'), new Date('2010-10-05Z'));
assert.strictEqual(typeCast(Query.FIELD_TYPE_TINY, '08'), 8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_SHORT, '08'), 8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_LONG, '08'), 8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_LONGLONG, '08'), 8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_INT24, '08'), 8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_YEAR, '08'), 8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_DECIMAL, '2.8'), '2.8');
assert.strictEqual(typeCast(Query.FIELD_TYPE_FLOAT, '2.8'), 2.8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_DOUBLE, '2.8'), 2.8);
assert.strictEqual(typeCast(Query.FIELD_TYPE_NEWDECIMAL, '2.8'), '2.8');
assert.strictEqual(typeCast(Query.FIELD_TYPE_DATE, null), null);
});

View File

@@ -0,0 +1,33 @@
var common = require('../common');
var assert = require('assert');
var test = require('utest');
var Client = require(common.dir.lib + '/client');
var client;
test('Client', {
before: function() {
client = new Client();
},
'#format() does not manipulate params parameter': function() {
var sql = '?';
var params = [1];
client.format(sql, params);
assert.equal(params.length, 1);
},
'#format() does not quote floats': function() {
var params = [1.23];
var sql = client.format('?', params);
assert.strictEqual(sql, '1.23');
},
'Timeout reconnect works with empty queue': function() {
// A non-error packet
var packet = {};
// This must not throw an error
client._handlePacket(packet);
},
});

View File

@@ -0,0 +1,10 @@
var common = require('../common');
var assert = require('assert');
var test = require('utest');
var mysql = require(common.dir.root);
test('mysql module', {
'Package JSON is exported': function() {
assert.strictEqual(mysql.PACKAGE.name, 'mysql');
},
});

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env node
var sys = require("sys"),
pcap = require("pcap"),
mysqlPort = parseInt(process.argv[3]) || 3306,
pcap_session = pcap.createSession(process.argv[2] || '', 'tcp port '+mysqlPort);
sys.puts('This tool allows to reverse engineer the mysql procotocol using node-pcap.');
sys.puts('');
sys.puts('Available devices (active one is denoted by *):');
// Print all devices, currently listening device prefixed with an asterisk
pcap_session.findalldevs().forEach(function (dev) {
sys.print(' ');
if (pcap_session.device_name === dev.name) {
sys.print("* ");
}
sys.print(dev.name + " ");
if (dev.addresses.length > 0) {
dev.addresses.forEach(function (address) {
sys.print(address.addr + "/" + address.netmask);
});
sys.print("\n");
} else {
sys.print("no address\n");
}
});
sys.puts('');
sys.puts('Execute `./pcap-mysql.js <device> <mysql-port>` to listen on another device.');
sys.puts('');
// Listen for packets, decode them, and feed the simple printer. No tricks.
pcap_session.on('packet', function (raw_packet) {
var packet = pcap.decode.packet(raw_packet);
//sys.puts(pcap.print.packet(packet));
var tcp = packet.link.ip.tcp;
if (!tcp.data) {
return;
}
if (tcp.sport == mysqlPort) {
sys.puts('<- '+tcp.data.inspect());
} else {
sys.puts('-> '+tcp.data.inspect());
}
});

34674
Nodejs-Socketio-Mysql-Demo/node_modules/mysql/v8.log generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
support
test
examples

View File

@@ -0,0 +1,6 @@
language: node_js
node_js:
- 0.6
notifications:
irc: "irc.freenode.org#socket.io"

View File

@@ -0,0 +1,260 @@
0.9.6 / 2012-04-17
==================
* Fixed XSS in jsonp-polling.
0.9.5 / 2012-04-05
==================
* Added test for polling and socket close.
* Ensure close upon request close.
* Fix disconnection reason being lost for polling transports.
* Ensure that polling transports work with Connection: close.
* Log disconnection reason.
0.9.4 / 2012-04-01
==================
* Disconnecting from namespace improvement (#795) [DanielBaulig]
* Bumped client with polling reconnection loop (#438)
0.9.3 / 2012-03-28
==================
* Fix "Syntax error" on FF Web Console with XHR Polling [mikito]
0.9.2 / 2012-03-13
==================
* More sensible close `timeout default` (fixes disconnect issue)
0.9.1-1 / 2012-03-02
====================
* Bumped client with NPM dependency fix.
0.9.1 / 2012-03-02
==================
* Changed heartbeat timeout and interval defaults (60 and 25 seconds)
* Make tests work both on 0.4 and 0.6
* Updated client (improvements + bug fixes).
0.9.0 / 2012-02-26
==================
* Make it possible to use a regexp to match the socket.io resource URL.
We need this because we have to prefix the socket.io URL with a variable ID.
* Supplemental fix to gavinuhma/authfix, it looks like the same Access-Control-Origin logic is needed in the http and xhr-polling transports
* Updated express dep for windows compatibility.
* Combine two substr calls into one in decodePayload to improve performance
* Minor documentation fix
* Minor. Conform to style of other files.
* Switching setting to 'match origin protocol'
* Revert "Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect()."
* Revert "Handle leaked dispatch:[id] subscription."
* Merge pull request #667 from dshaw/patch/redis-disconnect
* Handle leaked dispatch:[id] subscription.
* Fixes leaking Redis subscriptions for #663. The local flag was not getting passed through onClientDisconnect().
* Prevent memory leaking on uncompleted requests & add max post size limitation
* Fix for testcase
* Set Access-Control-Allow-Credentials true, regardless of cookie
* Remove assertvarnish from package as it breaks on 0.6
* Correct irc channel
* Added proper return after reserved field error
* Fixes manager.js failure to close connection after transport error has happened
* Added implicit port 80 for origin checks. fixes #638
* Fixed bug #432 in 0.8.7
* Set Access-Control-Allow-Origin header to origin to enable withCredentials
* Adding configuration variable matchOriginProtocol
* Fixes location mismatch error in Safari.
* Use tty to detect if we should add colors or not by default.
* Updated the package location.
0.8.7 / 2011-11-05
==================
* Fixed memory leaks in closed clients.
* Fixed memory leaks in namespaces.
* Fixed websocket handling for malformed requests from proxies. [einaros]
* Node 0.6 compatibility. [einaros] [3rd-Eden]
* Adapted tests and examples.
0.8.6 / 2011-10-27
==================
* Added JSON decoding on jsonp-polling transport.
* Fixed README example.
* Major speed optimizations [3rd-Eden] [einaros] [visionmedia]
* Added decode/encode benchmarks [visionmedia]
* Added support for black-listing client sent events.
* Fixed logging options, closes #540 [3rd-Eden]
* Added vary header for gzip [3rd-Eden]
* Properly cleaned up async websocket / flashsocket tests, after patching node-websocket-client
* Patched to properly shut down when a finishClose call is made during connection establishment
* Added support for socket.io version on url and far-future Expires [3rd-Eden] [getify]
* Began IE10 compatibility [einaros] [tbranyen]
* Misc WebSocket fixes [einaros]
* Added UTF8 to respone headers for htmlfile [3rd-Eden]
0.8.5 / 2011-10-07
==================
* Added websocket draft HyBi-16 support. [einaros]
* Fixed websocket continuation bugs. [einaros]
* Fixed flashsocket transport name.
* Fixed websocket tests.
* Ensured `parser#decodePayload` doesn't choke.
* Added http referrer verification to manager verifyOrigin.
* Added access control for cross domain xhr handshakes [3rd-Eden]
* Added support for automatic generation of socket.io files [3rd-Eden]
* Added websocket binary support [einaros]
* Added gzip support for socket.io.js [3rd-Eden]
* Expose socket.transport [3rd-Eden]
* Updated client.
0.8.4 / 2011-09-06
==================
* Client build
0.8.3 / 2011-09-03
==================
* Fixed `\n` parsing for non-JSON packets (fixes #479).
* Fixed parsing of certain unicode characters (fixes #451).
* Fixed transport message packet logging.
* Fixed emission of `error` event resulting in an uncaught exception if unhandled (fixes #476).
* Fixed; allow for falsy values as the configuration value of `log level` (fixes #491).
* Fixed repository URI in `package.json`. Fixes #504.
* Added text/plain content-type to handshake responses [einaros]
* Improved single byte writes [einaros]
* Updated socket.io-flashsocket default port from 843 to 10843 [3rd-Eden]
* Updated client.
0.8.2 / 2011-08-29
==================
* Updated client.
0.8.1 / 2011-08-29
==================
* Fixed utf8 bug in send framing in websocket [einaros]
* Fixed typo in docs [Znarkus]
* Fixed bug in send framing for over 64kB of data in websocket [einaros]
* Corrected ping handling in websocket transport [einaros]
0.8.0 / 2011-08-28
==================
* Updated to work with two-level websocket versioning. [einaros]
* Added hybi07 support. [einaros]
* Added hybi10 support. [einaros]
* Added http referrer verification to manager.js verifyOrigin. [einaors]
0.7.11 / 2011-08-27
===================
* Updated socket.io-client.
0.7.10 / 2011-08-27
===================
* Updated socket.io-client.
0.7.9 / 2011-08-12
==================
* Updated socket.io-client.
* Make sure we only do garbage collection when the server we receive is actually run.
0.7.8 / 2011-08-08
==================
* Changed; make sure sio#listen passes options to both HTTP server and socket.io manager.
* Added docs for sio#listen.
* Added options parameter support for Manager constructor.
* Added memory leaks tests and test-leaks Makefile task.
* Removed auto npm-linking from make test.
* Make sure that you can disable heartbeats. [3rd-Eden]
* Fixed rooms memory leak [3rd-Eden]
* Send response once we got all POST data, not immediately [Pita]
* Fixed onLeave behavior with missing clientsk [3rd-Eden]
* Prevent duplicate references in rooms.
* Added alias for `to` to `in` and `in` to `to`.
* Fixed roomClients definition.
* Removed dependency on redis for installation without npm [3rd-Eden]
* Expose path and querystring in handshakeData [3rd-Eden]
0.7.7 / 2011-07-12
==================
* Fixed double dispatch handling with emit to closed clients.
* Added test for emitting to closed clients to prevent regression.
* Fixed race condition in redis test.
* Changed Transport#end instrumentation.
* Leveraged $emit instead of emit internally.
* Made tests faster.
* Fixed double disconnect events.
* Fixed disconnect logic
* Simplified remote events handling in Socket.
* Increased testcase timeout.
* Fixed unknown room emitting (GH-291). [3rd-Eden]
* Fixed `address` in handshakeData. [3rd-Eden]
* Removed transports definition in chat example.
* Fixed room cleanup
* Fixed; make sure the client is cleaned up after booting.
* Make sure to mark the client as non-open if the connection is closed.
* Removed unneeded `buffer` declarations.
* Fixed; make sure to clear socket handlers and subscriptions upon transport close.
0.7.6 / 2011-06-30
==================
* Fixed general dispatching when a client has closed.
0.7.5 / 2011-06-30
==================
* Fixed dispatching to clients that are disconnected.
0.7.4 / 2011-06-30
==================
* Fixed; only clear handlers if they were set. [level09]
0.7.3 / 2011-06-30
==================
* Exposed handshake data to clients.
* Refactored dispatcher interface.
* Changed; Moved id generation method into the manager.
* Added sub-namespace authorization. [3rd-Eden]
* Changed; normalized SocketNamespace local eventing [dvv]
* Changed; Use packet.reason or default to 'packet' [3rd-Eden]
* Changed console.error to console.log.
* Fixed; bind both servers at the same time do that the test never times out.
* Added 304 support.
* Removed `Transport#name` for abstract interface.
* Changed; lazily require http and https module only when needed. [3rd-Eden]
0.7.2 / 2011-06-22
==================
* Make sure to write a packet (of type `noop`) when closing a poll.
This solves a problem with cross-domain requests being flagged as aborted and
reconnection being triggered.
* Added `noop` message type.
0.7.1 / 2011-06-21
==================
* Fixed cross-domain XHR.
* Added CORS test to xhr-polling suite.
0.7.0 / 2010-06-21
==================
* http://socket.io/announcement.html

View File

@@ -0,0 +1,31 @@
ALL_TESTS = $(shell find test/ -name '*.test.js')
ALL_BENCH = $(shell find benchmarks -name '*.bench.js')
run-tests:
@./node_modules/.bin/expresso \
-t 3000 \
-I support \
--serial \
$(TESTFLAGS) \
$(TESTS)
test:
@$(MAKE) NODE_PATH=lib TESTS="$(ALL_TESTS)" run-tests
test-cov:
@TESTFLAGS=--cov $(MAKE) test
test-leaks:
@ls test/leaks/* | xargs node --expose_debug_as=debug --expose_gc
run-bench:
@node $(PROFILEFLAGS) benchmarks/runner.js
bench:
@$(MAKE) BENCHMARKS="$(ALL_BENCH)" run-bench
profile:
@PROFILEFLAGS='--prof --trace-opt --trace-bailout --trace-deopt' $(MAKE) bench
.PHONY: test bench profile

View File

@@ -0,0 +1,345 @@
# Socket.IO
Socket.IO is a Node.JS project that makes WebSockets and realtime possible in
all browsers. It also enhances WebSockets by providing built-in multiplexing,
horizontal scalability, automatic JSON encoding/decoding, and more.
## How to Install
```bash
npm install socket.io
```
## How to use
First, require `socket.io`:
```js
var io = require('socket.io');
```
Next, attach it to a HTTP/HTTPS server. If you're using the fantastic `express`
web framework:
```js
var app = express.createServer()
, io = io.listen(app);
app.listen(80);
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
```
Finally, load it from the client side code:
```html
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
```
For more thorough examples, look at the `examples/` directory.
## Short recipes
### Sending and receiving events.
Socket.IO allows you to emit and receive custom events.
Besides `connect`, `message` and `disconnect`, you can emit custom events:
```js
// note, io.listen(<port>) will create a http server for you
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
io.sockets.emit('this', { will: 'be received by everyone' });
socket.on('private message', function (from, msg) {
console.log('I received a private message by ', from, ' saying ', msg);
});
socket.on('disconnect', function () {
io.sockets.emit('user disconnected');
});
});
```
### Storing data associated to a client
Sometimes it's necessary to store data associated with a client that's
necessary for the duration of the session.
#### Server side
```js
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.on('set nickname', function (name) {
socket.set('nickname', name, function () { socket.emit('ready'); });
});
socket.on('msg', function () {
socket.get('nickname', function (err, name) {
console.log('Chat message by ', name);
});
});
});
```
#### Client side
```html
<script>
var socket = io.connect('http://localhost');
socket.on('connect', function () {
socket.emit('set nickname', confirm('What is your nickname?'));
socket.on('ready', function () {
console.log('Connected !');
socket.emit('msg', confirm('What is your message?'));
});
});
</script>
```
### Restricting yourself to a namespace
If you have control over all the messages and events emitted for a particular
application, using the default `/` namespace works.
If you want to leverage 3rd-party code, or produce code to share with others,
socket.io provides a way of namespacing a `socket`.
This has the benefit of `multiplexing` a single connection. Instead of
socket.io using two `WebSocket` connections, it'll use one.
The following example defines a socket that listens on '/chat' and one for
'/news':
#### Server side
```js
var io = require('socket.io').listen(80);
var chat = io
.of('/chat')
.on('connection', function (socket) {
socket.emit('a message', { that: 'only', '/chat': 'will get' });
chat.emit('a message', { everyone: 'in', '/chat': 'will get' });
});
var news = io
.of('/news');
.on('connection', function (socket) {
socket.emit('item', { news: 'item' });
});
```
#### Client side:
```html
<script>
var chat = io.connect('http://localhost/chat')
, news = io.connect('http://localhost/news');
chat.on('connect', function () {
chat.emit('hi!');
});
news.on('news', function () {
news.emit('woot');
});
</script>
```
### Sending volatile messages.
Sometimes certain messages can be dropped. Let's say you have an app that
shows realtime tweets for the keyword `bieber`.
If a certain client is not ready to receive messages (because of network slowness
or other issues, or because he's connected through long polling and is in the
middle of a request-response cycle), if he doesn't receive ALL the tweets related
to bieber your application won't suffer.
In that case, you might want to send those messages as volatile messages.
#### Server side
```js
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
var tweets = setInterval(function () {
getBieberTweet(function (tweet) {
socket.volatile.emit('bieber tweet', tweet);
});
}, 100);
socket.on('disconnect', function () {
clearInterval(tweets);
});
});
```
#### Client side
In the client side, messages are received the same way whether they're volatile
or not.
### Getting acknowledgements
Sometimes, you might want to get a callback when the client confirmed the message
reception.
To do this, simply pass a function as the last parameter of `.send` or `.emit`.
What's more, when you use `.emit`, the acknowledgement is done by you, which
means you can also pass data along:
#### Server side
```js
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.on('ferret', function (name, fn) {
fn('woot');
});
});
```
#### Client side
```html
<script>
var socket = io.connect(); // TIP: .connect with no args does auto-discovery
socket.on('connect', function () { // TIP: you can avoid listening on `connect` and listen on events directly too!
socket.emit('ferret', 'tobi', function (data) {
console.log(data); // data will be 'woot'
});
});
</script>
```
### Broadcasting messages
To broadcast, simply add a `broadcast` flag to `emit` and `send` method calls.
Broadcasting means sending a message to everyone else except for the socket
that starts it.
#### Server side
```js
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.broadcast.emit('user connected');
socket.broadcast.json.send({ a: 'message' });
});
```
### Rooms
Sometimes you want to put certain sockets in the same room, so that it's easy
to broadcast to all of them together.
Think of this as built-in channels for sockets. Sockets `join` and `leave`
rooms in each socket.
#### Server side
```js
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.join('justin bieber fans');
socket.broadcast.to('justin bieber fans').emit('new fan');
io.sockets.in('rammstein fans').emit('new non-fan');
});
```
### Using it just as a cross-browser WebSocket
If you just want the WebSocket semantics, you can do that too.
Simply leverage `send` and listen on the `message` event:
#### Server side
```js
var io = require('socket.io').listen(80);
io.sockets.on('connection', function (socket) {
socket.on('message', function () { });
socket.on('disconnect', function () { });
});
```
#### Client side
```html
<script>
var socket = io.connect('http://localhost/');
socket.on('connect', function () {
socket.send('hi');
socket.on('message', function (msg) {
// my msg
});
});
</script>
```
### Changing configuration
Configuration in socket.io is TJ-style:
#### Server side
```js
var io = require('socket.io').listen(80);
io.configure(function () {
io.set('transports', ['websocket', 'flashsocket', 'xhr-polling']);
});
io.configure('development', function () {
io.set('transports', ['websocket', 'xhr-polling']);
io.enable('log');
});
```
## License
(The MIT License)
Copyright (c) 2011 Guillermo Rauch &lt;guillermo@learnboost.com&gt;
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.

View File

@@ -0,0 +1,64 @@
/**
* Module dependencies.
*/
var benchmark = require('benchmark')
, colors = require('colors')
, io = require('../')
, parser = io.parser
, suite = new benchmark.Suite('Decode packet');
suite.add('string', function () {
parser.decodePacket('4:::"2"');
});
suite.add('event', function () {
parser.decodePacket('5:::{"name":"woot"}');
});
suite.add('event+ack', function () {
parser.decodePacket('5:1+::{"name":"tobi"}');
});
suite.add('event+data', function () {
parser.decodePacket('5:::{"name":"edwald","args":[{"a": "b"},2,"3"]}');
});
suite.add('heartbeat', function () {
parser.decodePacket('2:::');
});
suite.add('error', function () {
parser.decodePacket('7:::2+0');
});
var payload = parser.encodePayload([
parser.encodePacket({ type: 'message', data: '5', endpoint: '' })
, parser.encodePacket({ type: 'message', data: '53d', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
]);
suite.add('payload', function () {
parser.decodePayload(payload);
});
suite.on('cycle', function (bench, details) {
console.log('\n' + suite.name.grey, details.name.white.bold);
console.log([
details.hz.toFixed(2).cyan + ' ops/sec'.grey
, details.count.toString().white + ' times executed'.grey
, 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey
,
].join(', '.grey));
});
if (!module.parent) {
suite.run();
} else {
module.exports = suite;
}

View File

@@ -0,0 +1,90 @@
/**
* Module dependencies.
*/
var benchmark = require('benchmark')
, colors = require('colors')
, io = require('../')
, parser = io.parser
, suite = new benchmark.Suite('Encode packet');
suite.add('string', function () {
parser.encodePacket({
type: 'json'
, endpoint: ''
, data: '2'
});
});
suite.add('event', function () {
parser.encodePacket({
type: 'event'
, name: 'woot'
, endpoint: ''
, args: []
});
});
suite.add('event+ack', function () {
parser.encodePacket({
type: 'json'
, id: 1
, ack: 'data'
, endpoint: ''
, data: { a: 'b' }
});
});
suite.add('event+data', function () {
parser.encodePacket({
type: 'event'
, name: 'edwald'
, endpoint: ''
, args: [{a: 'b'}, 2, '3']
});
});
suite.add('heartbeat', function () {
parser.encodePacket({
type: 'heartbeat'
, endpoint: ''
})
});
suite.add('error', function () {
parser.encodePacket({
type: 'error'
, reason: 'unauthorized'
, advice: 'reconnect'
, endpoint: ''
})
})
suite.add('payload', function () {
parser.encodePayload([
parser.encodePacket({ type: 'message', data: '5', endpoint: '' })
, parser.encodePacket({ type: 'message', data: '53d', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobarbazfoobarbaz', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobarbaz', endpoint: '' })
, parser.encodePacket({ type: 'message', data: 'foobar', endpoint: '' })
]);
});
suite.on('cycle', function (bench, details) {
console.log('\n' + suite.name.grey, details.name.white.bold);
console.log([
details.hz.toFixed(2).cyan + ' ops/sec'.grey
, details.count.toString().white + ' times executed'.grey
, 'benchmark took '.grey + details.times.elapsed.toString().white + ' sec.'.grey
,
].join(', '.grey));
});
if (!module.parent) {
suite.run();
} else {
module.exports = suite;
}

View File

@@ -0,0 +1,55 @@
/**
* Benchmark runner dependencies
*/
var colors = require('colors')
, path = require('path');
/**
* Find all the benchmarks
*/
var benchmarks_files = process.env.BENCHMARKS.split(' ')
, all = [].concat(benchmarks_files)
, first = all.shift()
, benchmarks = {};
// find the benchmarks and load them all in our obj
benchmarks_files.forEach(function (file) {
benchmarks[file] = require(path.join(__dirname, '..', file));
});
// setup the complete listeners
benchmarks_files.forEach(function (file) {
var benchmark = benchmarks[file]
, next_file = all.shift()
, next = benchmarks[next_file];
/**
* Generate a oncomplete function for the tests, either we are done or we
* have more benchmarks to process.
*/
function complete () {
if (!next) {
console.log(
'\n\nBenchmark completed in'.grey
, (Date.now() - start).toString().green + ' ms'.grey
);
} else {
console.log('\nStarting benchmark '.grey + next_file.yellow);
next.run();
}
}
// attach the listener
benchmark.on('complete', complete);
});
/**
* Start the benchmark
*/
var start = Date.now();
console.log('Starting benchmark '.grey + first.yellow);
benchmarks[first].run();

View File

@@ -0,0 +1,8 @@
/*!
* socket.io-node
* Copyright(c) 2011 LearnBoost <dev@learnboost.com>
* MIT Licensed
*/
module.exports = require('./lib/socket.io');

View File

@@ -0,0 +1,167 @@
/**
* Module dependencies.
*/
var parser = require('socket.io-client').parser
, EventEmitter = require('events').EventEmitter
/**
* Client constructor.
*
* @api public
*/
function Client (id, server) {
this.id = id;
this.acks = {};
this.store = server.store;
var self = this;
store.subscribe(id, function (packet) {
});
store.subscribe(id + '.disconect', function () {
self.onDisconnect();
});
}
/**
* Inherits from EventEmitter.
*/
Client.prototype.__proto__ = EventEmitter.prototype;
/**
* Save reference to original `emit`.
*
* @api private
*/
Client.prototype._emit = Client.prototype.emit;
/**
* Broadcast flag.
*
* @api public
*/
Client.prototype.__defineGetter__('broadcast', function () {
this.flags.broadcast = true;
});
/**
* JSON flag (deprecated)
*
* @api public
*/
Client.prototype.__defineGetter__('json', function () {
this.flags.broadcast = true;
});
/**
* Joins a group.
*
* @param {String} group
* @return {Client} for chaining
* @api public
*/
Client.prototype.join = function (group, fn) {
if (!~this.subscriptions.indexOf(group)) {
var self = this;
this.subscriptions.push(group);
this.store.addToGroup(group, this.sid, function (ev, args) {
self.onGroupEvent(ev, args);
}, fn);
} else {
fn && fn();
}
return this;
};
/**
* Leaves a group.
*
* @return {Client} for chaining
* @api public
*/
Client.prototype.leave = function (group) {
var index = this.subscriptions.indexOf(group);
if (~index) {
this.subscriptions.splice(index, 1);
}
return this;
};
Client.prototype.disconnect = function () {
if (this.socket) {
this.socket.disconnect();
} else {
this.publish('disconnect');
}
}
/**
* Called upon disconnect.
*
* @api private
*/
Client.prototype.onDisconnect = function () {
for (var i = 0, l = this.subscriptions; i < l; i++) {
this.store.removeFromGroup(id, group, fn);
}
};
/**
* Registers ACK.
*/
Client.prototype.ack = function (fn, callback) {
this.subscribe('ack');
};
/**
* Emits an event.
*/
Client.prototype.emit = function () {
var args = toArray(arguments), fn;
if ('function' == typeof args[args.length - 1]) {
fn = args.pop();
}
var data = args.shift();
if (args.length) {
data += '\n' + JSON.stringify(args);
}
if (fn) {
this.ack(fn, function (id) {
self.sendPacket('event', data, id);
});
} else {
this.sendPacket('event', data);
}
return this;
};
/**
* Sends a packet.
*/
Client.prototype.sendPacket = function (type, data, id) {
var data = parser.encode({ type: type, data: data, id: id });
if (this.server.sockets[id]) {
this.server.sockets[id].write(data);
}
};

Some files were not shown because too many files have changed in this diff Show More