mirror of
https://github.com/amark/gun.git
synced 2025-03-30 15:08:33 +00:00
fixed major bug, added tests.
This commit is contained in:
parent
13f6e287fa
commit
fa5096d075
1
examples/.gitignore
vendored
1
examples/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
node_modules/*
|
||||
npm-debug.log
|
||||
*data.json
|
||||
|
@ -1 +0,0 @@
|
||||
web: node all.js
|
@ -55,7 +55,7 @@
|
||||
$scope.data = {};
|
||||
$scope.$data = gun.load('example/angular/todo').blank(function(){
|
||||
console.log("Initializing Data!")
|
||||
gun.set({chicken: 'feet'}).key('example/angular/todo');
|
||||
gun.set({}).key('example/angular/todo');
|
||||
}).on(function(data){
|
||||
Gun.obj.map(data, function(val, field){
|
||||
if(val === $scope.data[field]){ return }
|
||||
|
@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# curl -fsSL http://gunDB.io/example.sh | bash -c
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
|
||||
echo "updating apt-get..."
|
||||
sudo bash -c "apt-get update -qq -y < /dev/null" > /dev/null
|
||||
|
||||
# git, wget, curl, build-essential
|
||||
if [ -z "$(which git | grep git)" ] || [ -z "$(which wget | grep wget)" ] || [ -z "$(which curl | grep curl)" ] || [ -z "$(which gcc | grep gcc)" ] || [ -z "$(which rsync | grep rsync)" ]
|
||||
then
|
||||
echo "installing git, wget, curl, build-essential, rsync..."
|
||||
sudo bash -c "apt-get install -qq -y git wget curl build-essential rsync < /dev/null" > /dev/null 2>/dev/null
|
||||
fi
|
||||
|
||||
# node
|
||||
NODE_VER=v0.11.14
|
||||
CUR_NODE_VER=$(node -v 2>/dev/null)
|
||||
if [ -n "$(which node | grep node)" ] && [ "${NODE_VER}" == "$(node -v 2>/dev/null)" ]; then
|
||||
#if [ "${NODE_VER}" == "${CUR_NODE_VER}" ]; then
|
||||
echo node ${NODE_VER} already installed
|
||||
else
|
||||
echo "installing node ${NODE_VER}..."
|
||||
curl -fsSL "http://nodejs.org/dist/${NODE_VER}/node-${NODE_VER}-linux-x64.tar.gz" \
|
||||
-o "/tmp/node-${NODE_VER}-linux-x64.tar.gz"
|
||||
pushd /tmp
|
||||
tar xf /tmp/node-${NODE_VER}-linux-x64.tar.gz
|
||||
rm node-${NODE_VER}-linux-x64/{LICENSE,ChangeLog,README.md}
|
||||
sudo rsync -a /tmp/node-${NODE_VER}-linux-x64/ /usr/local/
|
||||
sudo chown -R $(whoami) /usr/local
|
||||
fi
|
||||
|
||||
mkdir -p ~/gun
|
||||
cd ~/gun
|
||||
npm install gun
|
||||
cd ./node_modules/gun/examples
|
||||
npm install .
|
||||
node all.js
|
||||
|
||||
echo ""
|
@ -1,18 +1,18 @@
|
||||
var Gun = require('gun');
|
||||
var gun = Gun({
|
||||
s3: {
|
||||
key: '', // AWS Access Key
|
||||
secret: '', // AWS Secret Token
|
||||
bucket: '' // The bucket you want to save into
|
||||
}
|
||||
s3: {
|
||||
key: '', // AWS Access Key
|
||||
secret: '', // AWS Secret Token
|
||||
bucket: '' // The bucket you want to save into
|
||||
}
|
||||
});
|
||||
gun.set({ hello: 'world' }).key('my/first/data');
|
||||
|
||||
var http = require('http');
|
||||
http.createServer(function (req, res) {
|
||||
gun.load('my/first/data', function(err, data){
|
||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
||||
res.end(JSON.stringify(data));
|
||||
});
|
||||
http.createServer(function(req, res){
|
||||
gun.load('my/first/data', function(err, data){
|
||||
res.writeHead(200, {'Content-Type': 'application/json'});
|
||||
res.end(JSON.stringify(data));
|
||||
});
|
||||
}).listen(1337, '127.0.0.1');
|
||||
console.log('Server running at http://127.0.0.1:1337/');
|
||||
console.log('Server running at http://127.0.0.1:1337/');
|
||||
|
@ -4,9 +4,18 @@ var http = require('http');
|
||||
|
||||
var Gun = require('gun');
|
||||
var gun = Gun({
|
||||
s3: (process.env.NODE_ENV === 'production')? null : require('../test/shotgun') // replace this with your own keys!
|
||||
file: 'data.json',
|
||||
s3: {
|
||||
key: '', // AWS Access Key
|
||||
secret: '', // AWS Secret Token
|
||||
bucket: '' // The bucket you want to save into
|
||||
}
|
||||
});
|
||||
|
||||
var server = gun.attach(http.createServer(gun.server)).listen(port);
|
||||
var server = http.createServer(function(req, res){
|
||||
gun.server(req, res);
|
||||
});
|
||||
gun.attach(server);
|
||||
server.listen(port);
|
||||
|
||||
console.log('Server started on port ' + port + ' with /gun');
|
||||
|
@ -25,4 +25,4 @@ cd ./node_modules/gun/examples
|
||||
|
||||
npm install .
|
||||
|
||||
sudo /usr/local/bin/node ./all.js
|
||||
sudo /usr/local/bin/node ./express.js
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "examples",
|
||||
"main": "all.js",
|
||||
"main": "express.js",
|
||||
"description": "Example gun apps"
|
||||
, "version": "0.0.1"
|
||||
, "engines": {
|
||||
@ -11,7 +11,7 @@
|
||||
"gun": "~>0.0.9a"
|
||||
}
|
||||
, "scripts": {
|
||||
"start": "node all.js",
|
||||
"start": "node express.js",
|
||||
"test": "mocha"
|
||||
}
|
||||
}
|
||||
|
34
gun.js
34
gun.js
@ -322,7 +322,6 @@
|
||||
var gun = this.chain();
|
||||
path = (path || '').split('.');
|
||||
gun.back.shot.then(function trace(node){ // should handle blank and err! Err already handled?
|
||||
console.log("path happening"); // MARK COME BACK HERE!!!
|
||||
gun.field = null;
|
||||
gun._.node = node;
|
||||
if(!path.length){ // if the path resolves to another node, we finish here.
|
||||
@ -331,13 +330,14 @@
|
||||
var field = Gun.text.ify(path.shift())
|
||||
, val = node[field];
|
||||
gun.field = field;
|
||||
console.log('path');
|
||||
if(Gun.is.soul(val)){ // we might end on a link, so we must resolve
|
||||
return gun.load(val).shot.then(trace);
|
||||
} else
|
||||
if(path.length){ // we cannot go any further, despite the fact there is more path, which means the thing we wanted does not exist.
|
||||
gun.shot('then').fire();
|
||||
} else { // we are done, and this should be the value we wanted.
|
||||
gun.shot('then').fire(val); // internals use this thus not frozen yet, but primitive values are passed as copies anyways in js.
|
||||
gun.shot('then').fire(node, field); // js copies primitive values, thus we must pass by reference.
|
||||
}
|
||||
});
|
||||
if(gun.back && gun.back._ && gun.back._.loaded){ // is this causing a double trigger?
|
||||
@ -349,11 +349,10 @@
|
||||
}
|
||||
Chain.get = function(cb){
|
||||
var gun = this;
|
||||
gun.shot.then(function(val){
|
||||
console.log("get happening", val);
|
||||
if(!gun._.node){ return } // I think this is safe?
|
||||
gun.shot.then(function(node, field){
|
||||
cb = cb || function(){};
|
||||
cb.call(gun, Gun.obj.is(val)? Gun.obj.copy(val) : val); // frozen copy
|
||||
cb.call(gun, field? node[field] : Gun.obj.copy(node)); // frozen copy
|
||||
// TODO! BUG! Maybe? Should a field that is null trigger a blank instead?
|
||||
});
|
||||
return gun;
|
||||
}
|
||||
@ -372,6 +371,7 @@
|
||||
if(Gun.obj.has(delta, get.field)){
|
||||
delta = delta[get.field];
|
||||
cb.call(get, Gun.obj.is(delta)? Gun.obj.copy(delta) : delta); // frozen copy
|
||||
// TODO: BUG! Maybe? Interesting, if delta is an object, I might have to .load!
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -404,13 +404,13 @@
|
||||
// TODO: should be able to handle val being a relation or a gun context or a gun promise.
|
||||
// TODO: BUG: IF we are setting an object, doing a partial merge, and they are reusing a frozen copy, we need to do a DIFF to update the HAM! Or else we'll get "old" HAM.
|
||||
val._ = Gun.ify.soul.call(gun, {}, gun._.node || val); // set their souls to be the same that way they will merge correctly for us during the union!
|
||||
console.log("what do you want to set?", val);
|
||||
set = Gun.ify.call(gun, val, set);
|
||||
cb.root = set.root;
|
||||
if(set.err || !cb.root){ return cb(set.err || {err: "No root object!"}), gun }
|
||||
set = Gun.ify.state(set.nodes, Gun.time.is()); // set time state on nodes?
|
||||
if(set.err){ return cb(set.err), gun }
|
||||
gun.union(set.nodes); // while this maybe should return a list of the nodes that were changed, we want to send the actual delta
|
||||
console.log(cb.root);
|
||||
gun._.node = gun.__.graph[cb.root._[Gun._.soul]] || cb.root;
|
||||
// TODO? ^ Maybe BUG! if val is a new node on a field, _.node should now be that! Or will that happen automatically?
|
||||
if(Gun.fns.is(gun.__.opt.hooks.set)){
|
||||
@ -418,7 +418,9 @@
|
||||
if(err){ return cb(err) }
|
||||
return cb(null);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Gun.log.call(gun, "Warning! You have no persistence layer to save to!");
|
||||
}
|
||||
});
|
||||
if(!gun.back){
|
||||
gun.shot('then').fire(set);
|
||||
@ -447,16 +449,20 @@
|
||||
gun.shot.then(function(val){
|
||||
cb = cb || function(){};
|
||||
opt = opt || {};
|
||||
Gun.obj.map(val, function(obj, soul){ // by default it only maps over nodes
|
||||
if(!Gun.is.soul(obj) && !opt.all){ return } // {all: true} maps over everything
|
||||
gun.load(obj).get(function(val){ // should map have support for blank?
|
||||
cb.call(this, obj, soul);
|
||||
});
|
||||
Gun.obj.map(val, function(val, field){ // by default it only maps over nodes
|
||||
if(Gun._.meta == field){ return }
|
||||
if(Gun.is.soul(val)){
|
||||
gun.load(val).get(function(val){ // should map have support for blank?
|
||||
cb.call(this, val, field);
|
||||
});
|
||||
} else {
|
||||
if(!opt.all){ return } // {all: true} maps over everything
|
||||
cb.call(this, val, field);
|
||||
}
|
||||
});
|
||||
});
|
||||
return gun;
|
||||
}
|
||||
*/
|
||||
// Union is different than set. Set casts non-gun style of data into a gun compatible data.
|
||||
// Union takes already gun compatible data and validates it for a merge.
|
||||
// Meaning it is more low level, such that even set uses union internally.
|
||||
|
15
lib/file.js
15
lib/file.js
@ -6,13 +6,12 @@
|
||||
var Gun = require('../gun'), file = {};
|
||||
|
||||
Gun.on('opt').event(function(gun, opts){
|
||||
if(!opts || (opts.s3 && opts.s3.key)){ return } // don't use this plugin if S3 is being used.
|
||||
if(opts.s3 && opts.s3.key){ return } // don't use this plugin if S3 is being used.
|
||||
|
||||
var fs = file.fs = file.fs || require('fs');
|
||||
var raw = file.raw = file.raw || fs.existsSync(opts.file || data.txt)?
|
||||
fs.readFileSync(opts.file || 'data.json').toString()
|
||||
: null;
|
||||
var all = file.all = file.all || Gun.obj.ify(raw || {nodes: {}, keys: {}});
|
||||
opts.file = opts.file || 'data.json';
|
||||
var fs = require('fs');
|
||||
file.raw = file.raw || fs.existsSync(opts.file)? fs.readFileSync(opts.file).toString() : null;
|
||||
var all = file.all = file.all || Gun.obj.ify(file.raw || {nodes: {}, keys: {}});
|
||||
|
||||
gun.opt({hooks: {
|
||||
load: function(key, cb, options){
|
||||
@ -30,11 +29,11 @@ Gun.on('opt').event(function(gun, opts){
|
||||
}
|
||||
}
|
||||
}
|
||||
fs.writeFile(opts.file || 'data.json', Gun.text.ify(all), cb);
|
||||
fs.writeFile(opts.file, Gun.text.ify(all), cb);
|
||||
}
|
||||
,key: function(key, soul, cb){
|
||||
all.keys[key] = soul;
|
||||
fs.writeFile(opts.file || 'data.json', Gun.text.ify(all), cb);
|
||||
fs.writeFile(opts.file, Gun.text.ify(all), cb);
|
||||
}
|
||||
}}, true);
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
var S3 = require('./aws');
|
||||
|
||||
Gun.on('opt').event(function(gun, opt){
|
||||
if(!opt || !opt.s3){ return } // don't use S3 if it isn't specified.
|
||||
if(!opt.s3){ return } // don't use S3 if it isn't specified.
|
||||
opt.s3 = opt.s3 || {};
|
||||
var s3 = gun.__.opt.s3 = gun.__.opt.s3 || S3(opt && opt.s3);
|
||||
s3.prefix = s3.prefix || opt.s3.prefix || '';
|
||||
|
@ -107,7 +107,7 @@
|
||||
key = {};
|
||||
key[Gun._.soul] = req.url.query[Gun._.soul];
|
||||
}
|
||||
console.log("transport.loading key ->", key, gun.__.graph, gun.__.keys);
|
||||
//console.log("transport.loading key ->", key, gun.__.graph, gun.__.keys);
|
||||
gun.load(key, function(err, node){
|
||||
//tran.sub.scribe(req.tab, node._[Gun._.soul]);
|
||||
cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)});
|
||||
|
354
lib/xhr.js
354
lib/xhr.js
@ -1,354 +0,0 @@
|
||||
;(function(){
|
||||
var Gun = require('../gun')
|
||||
, formidable = require('formidable')
|
||||
, url = require('url')
|
||||
, meta = {};
|
||||
Gun.on('opt').event(function(gun, opt){
|
||||
gun.server = gun.server || function(req, res, next){ // this whole function needs refactoring and modularization
|
||||
//console.log("\n\n GUN SERVER!");
|
||||
next = next || function(){};
|
||||
if(!req || !res){ return next() }
|
||||
if(!req.url){ return next() }
|
||||
if(!req.method){ return next() }
|
||||
var msg = {};
|
||||
msg.url = url.parse(req.url, true);
|
||||
if(!gun.server.regex.test(msg.url.pathname)){ return next() }
|
||||
msg.url.key = msg.url.pathname.replace(gun.server.regex,'') || '';
|
||||
if(msg.url.key.toLowerCase() === '.js'){
|
||||
res.writeHead(200, {'Content-Type': 'text/javascript'});
|
||||
res.end(gun.server.js = gun.server.js || require('fs').readFileSync(__dirname + '/gun.js')); // gun server is caching the gun library for the client
|
||||
return;
|
||||
}
|
||||
msg.url.key = msg.url.key.replace(/^\//i,'') || ''; // strip the base slash
|
||||
msg.method = (req.method||'').toLowerCase();
|
||||
msg.headers = req.headers;
|
||||
var body
|
||||
, form = new formidable.IncomingForm()
|
||||
, post = function(err, body){
|
||||
msg.body = body;
|
||||
gun.__.opt.hooks.transport(msg, function(reply){
|
||||
if(!res){ return }
|
||||
if(!reply){ return res.end() }
|
||||
if(reply.headers){
|
||||
if(!res._headerSent){
|
||||
Gun.obj.map(reply.headers, function(val, field){
|
||||
res.setHeader(field, val);
|
||||
});
|
||||
}
|
||||
}
|
||||
meta.CORS(req, res); // add option to disable this
|
||||
if(Gun.obj.has(reply,'chunk')){
|
||||
res.write(Gun.text.ify(reply.chunk) || '');
|
||||
}
|
||||
if(Gun.obj.has(reply,'body')){
|
||||
res.end(Gun.text.ify(reply.body) || '');
|
||||
}
|
||||
});
|
||||
}
|
||||
form.on('field',function(k,v){
|
||||
(body = body || {})[k] = v;
|
||||
}).on('file',function(k,v){
|
||||
return; // files not supported in gun yet
|
||||
}).on('error',function(e){
|
||||
if(form.done){ return }
|
||||
post(e);
|
||||
}).on('end', function(){
|
||||
if(form.done){ return }
|
||||
post(null, body);
|
||||
});
|
||||
form.parse(req);
|
||||
}
|
||||
gun.server.regex = /^\/gun/i;
|
||||
if(!gun.__.opt.keepMaxSockets){ require('https').globalAgent.maxSockets = require('http').globalAgent.maxSockets = Infinity } // WARNING: Document this!
|
||||
|
||||
gun.server.transport = (function(){
|
||||
function tran(req, cb){
|
||||
//console.log(req);
|
||||
req.sub = req.headers['gun-sub']; // grab the sub
|
||||
req.tab = tran.sub.s[req.sub] || {}; // check to see if there already is a tab associated with it, or create one
|
||||
req.tab.sub = req.sub = req.sub || Gun.text.random(); // Generate a session id if we don't already have one
|
||||
req.tran = tran.xhr(req, cb) || tran.jsonp(req, cb); // polyfill transport layer
|
||||
clearTimeout(req.tab.timeout);
|
||||
// raw test for now, no auth:
|
||||
if(!req.tran){ return cb({headers: {"Content-Type": tran.json}, body: {err: "No transport layer!"}}) }
|
||||
if('post' === req.method || 'patch' === req.method){ return tran.post(req, req.tran) } // TODO: Handle JSONP emulated POST via GET
|
||||
if('get' !== req.method){ return req.tran({body: {err: "Invalid method"}}) }
|
||||
if(!req.url.key){ return tran.sub(req, req.tran) } // get acts as sub, too.
|
||||
return tran.load(req, req.tran); // else load the state for the tab!
|
||||
}
|
||||
tran.load = function(req, cb){
|
||||
var reply = {}, key;
|
||||
reply.headers = {'Content-Type': tran.json};
|
||||
reply.headers['Gun-Sub'] = req.tab.sub = req.sub;
|
||||
key = (Gun._.meta == req.url.key)? req.url.query : req.url.key;
|
||||
console.log("Loading", req.url.key, 'for', req.tab);
|
||||
gun.load(key, function(node){
|
||||
tran.sub.scribe(req.tab, node._[Gun._.soul]);
|
||||
cb({
|
||||
headers: reply.headers
|
||||
,body: node
|
||||
});
|
||||
}).blank(function(){
|
||||
cb({
|
||||
headers: reply.headers
|
||||
,body: null
|
||||
});
|
||||
}).dud(function(err){
|
||||
cb({
|
||||
headers: reply.headers
|
||||
,body: {err: err || "Unknown error."}
|
||||
});
|
||||
});
|
||||
}
|
||||
tran.post = function(req, cb){ // post is used as patch, sad that patch has such poor support
|
||||
if(!req.body){ return cb({body: {err: "No body"}}) }
|
||||
if(req.url.key && Gun.obj.has(req.body, Gun._.soul)){ // key hook!
|
||||
req.tab = tran.sub.s[req.sub] || {};
|
||||
req.tab.sub = req.sub;
|
||||
console.log("key.hook", req.tab);
|
||||
tran.sub.scribe(req.tab, req.body[Gun._.soul]);
|
||||
return gun.load(req.body).get(function(frozen){
|
||||
// NEED to allow a security callback so server can tamper with this!
|
||||
this.key(req.url.key, function(err, reply){
|
||||
reply = reply || {};
|
||||
if(err){ reply.err = err }
|
||||
reply = {body: reply};
|
||||
reply.headers = {'Content-Type': tran.json};
|
||||
reply.headers['Gun-Sub'] = req.tab.sub;
|
||||
cb(reply);
|
||||
});
|
||||
}); // do I need to handle the blank case? :/ Not sure.
|
||||
}
|
||||
// raw test for now, no auth:
|
||||
// should probably load all the nodes first? YES.
|
||||
var context = gun.union(req.body, function(err, context){ // data safely transformed
|
||||
cb = cb || function(){};
|
||||
if(err || context.err){ return cb({body: {err: context.err}}) }
|
||||
if(Gun.fns.is(gun.__.opt.hooks.set)){
|
||||
gun.__.opt.hooks.set(context.nodes, function saved(err, data){ // now iterate through those nodes to S3 and get a callback once all are saved
|
||||
var body = {};
|
||||
if(err){
|
||||
body.err = err ;
|
||||
}
|
||||
if(!req.sub){
|
||||
if(!err){
|
||||
body = defer.map({}, context.nodes, 1);
|
||||
}
|
||||
return cb({body: body});
|
||||
}
|
||||
var now = tran.post.s[req.sub]; // begin our stupid Chrome fix, we should abstract this out into defer (where it belogns) to keep things clean.
|
||||
if(!now){ return } // utoh we've lost our reply to the tab!
|
||||
clearTimeout(now.timeout);
|
||||
now.body = now.body || {}; // make sure we have a body for our multi-response in a single response.
|
||||
if(req.wait){ // did this request get deferred?
|
||||
(now.body.refed = now.body.refed || {})[req.wait] = err? {err: err} : defer.map({}, context.nodes, 1); // then reply to it "here".
|
||||
} else {
|
||||
now.body.reply = err? {err: err} : defer.map({}, context.nodes, 1); // else this is the original POST that had to be upgraded.
|
||||
}
|
||||
if(0 < (now.count = ((now.count || 0) - 1))){
|
||||
// Don't reply till all deferred POSTs have successfully heard back from S3. (Sarcasm: Like counting guarantees that)
|
||||
return now.timeout = setTimeout(saved, gun.__.opt.throttle * 2 * 1000); // reply not guaranteed, so time it out, in seconds.
|
||||
}
|
||||
if(Gun.fns.is(now)){
|
||||
now({body: now.body}); // FINALLY reply for ALL the POSTs for that session that accumulated.
|
||||
} else {
|
||||
// console.log("Error! We deleted our response!");
|
||||
}
|
||||
Gun.obj.del(tran.post.s, req.sub); // clean up our memory.
|
||||
// need to rewrite that if Stream is enabled that both Stream + State save are guaranteed before replying.
|
||||
});
|
||||
// stuff past this point is just stupid implementation optimizations.
|
||||
function defer(nodes, req){ // because Chrome can only handle 4 requests at a time, sad face.
|
||||
if(!req.sub){
|
||||
return;
|
||||
}
|
||||
var next = tran.post.s[req.sub];
|
||||
if(!next){ // was there a previous POST? If not, we become the previous POST.
|
||||
//cb({chunk: ''}); // because on some services (heroku) you need to reply starting a stream to keep the connection open.
|
||||
return tran.post.s[req.sub] = cb;
|
||||
}
|
||||
next.count = (next.count || 1) + 1; // start counting how many we accumulate
|
||||
next.body = next.body || {}; // this becomes the polyfill for all the posts
|
||||
next.body.refed = next.body.refed || {}; // where we refeed the responses for the deferred POSTs.
|
||||
req.wait = Gun.text.random(); // generate an random id for this deferred POST.
|
||||
next.body.refed[req.wait] = false; // establish that we are incomplete.
|
||||
cb({body: {defer: req.wait}}); // end this POST immediately so Chrome only ever uses a couple connections.
|
||||
cb = null; // null it out so we don't accidentally reply to it once we hear back from S3.
|
||||
}
|
||||
defer.map = function(now, nodes, val){ // shortcut for maping which nodes were saved successfully
|
||||
if(!now){ return }
|
||||
Gun.obj.map(nodes, function(node, soul, map){
|
||||
now[soul] = val;
|
||||
});
|
||||
return now;
|
||||
}
|
||||
defer(context.nodes, req); // actually do the weird stuff to make Chrome not be slow
|
||||
} else {
|
||||
context.err = "Warning! You have no persistence layer to save to!";
|
||||
Gun.log(context.err);
|
||||
cb({body: {err: "Server has no persistence layer!"}});
|
||||
}
|
||||
});
|
||||
if(context.err){
|
||||
cb({body: {err: context.err}});
|
||||
return cb = null;
|
||||
}
|
||||
Gun.obj.map(context.nodes, function(node, soul){ // live push the stream out in realtime to every tab subscribed
|
||||
var msg = {};
|
||||
msg.headers = req.headers; // polyfill the delta as its own message.
|
||||
msg.body = node;
|
||||
console.log("emit delta", soul);
|
||||
tran.push(soul).emit(msg);
|
||||
});
|
||||
}
|
||||
tran.post.s = {};
|
||||
tran.sub = function(req, cb){
|
||||
//console.log("<-- ", req.sub, req.tran ," -->");
|
||||
req.tab = tran.sub.s[req.sub];
|
||||
if(!req.tab){
|
||||
cb({
|
||||
headers: {'Gun-Sub': ''}
|
||||
,body: {err: "Please re-initialize sub."}
|
||||
});
|
||||
return;
|
||||
}
|
||||
//console.log("\n\n\n THE CURRENT STATUS IS");console.log(req.tab);
|
||||
if(req.tab.queue && req.tab.queue.length){
|
||||
tran.clean(req.tab); // We flush their data now, if they don't come back for more within timeout, we remove their session
|
||||
console.log("_____ NOW PUSHING YOUR DATA ______", req.sub);
|
||||
cb({ headers: {'Gun-Sub': req.sub} });
|
||||
while(1 < req.tab.queue.length){
|
||||
cb({ chunk: req.tab.queue.shift() });
|
||||
}
|
||||
cb({ body: req.tab.queue.shift() });
|
||||
} else {
|
||||
cb({chunk: ''}); // same thing as the defer code, initialize a stream to support some services (heroku).
|
||||
req.tab.reply = cb;
|
||||
console.log("_____ STANDING BY, WAITING FOR DATA ______", req.sub);
|
||||
}
|
||||
}
|
||||
tran.sub.s = {};
|
||||
tran.clean = function(tab, mult){
|
||||
if(!tab){ return }
|
||||
mult = mult || 1;
|
||||
clearTimeout(tab.timeout);
|
||||
tab.timeout = setTimeout(function(){
|
||||
if(!tab){ return }
|
||||
if(tab.reply){ tab.reply({body: {err: "Connection timed out"}}) }
|
||||
console.log("!!!! DISCONNECTING CLIENT !!!!!", tab.sub);
|
||||
Gun.obj.del(tran.sub.s, tab.sub)
|
||||
}, gun.__.opt.disconnect * mult * 1000); // in seconds
|
||||
}
|
||||
tran.sub.scribe = function(tab, soul){
|
||||
tran.sub.s[tab.sub] = tab;
|
||||
tab.subs = tab.subs || {};
|
||||
console.log("meow subscribes", soul);
|
||||
tab.subs[soul] = tab.subs[soul] || tran.push(soul).event(function(req){
|
||||
if(!req){ return }
|
||||
if(!tab){ return this.off() } // resolve any dangling callbacks
|
||||
req.sub = req.sub || req.headers['gun-sub'];
|
||||
if(req.sub === tab.sub){ return } // do not send back to the tab that sent it
|
||||
console.log('FROM:', req.sub, "TO:", tab.sub);
|
||||
tran.clean(tab);
|
||||
if(tab.reply){
|
||||
tab.reply({
|
||||
headers: {'Gun-Sub': tab.sub}
|
||||
,body: req.body
|
||||
});
|
||||
tab.reply = null;
|
||||
return;
|
||||
}
|
||||
(tab.queue = tab.queue || []).push(req.body);
|
||||
});
|
||||
tran.clean(tab, 2);
|
||||
}
|
||||
tran.xhr = function(req, cb){ // Streaming Long Polling
|
||||
return req.tran || (req.headers['x-requested-with'] === 'XMLHttpRequest'? transport : null);
|
||||
function transport(res){
|
||||
if(!res){ return }
|
||||
var reply = {headers: {}};
|
||||
if(res.headers){
|
||||
Gun.obj.map(res.headers, function(val, field){
|
||||
reply.headers[field] = val;
|
||||
});
|
||||
}
|
||||
reply.headers["Content-Type"] = tran.json;
|
||||
if(Gun.obj.has(res,'chunk')){
|
||||
cb({
|
||||
headers: reply.headers
|
||||
,chunk: Gun.text.ify(res.chunk) + '\n'
|
||||
})
|
||||
}
|
||||
if(Gun.obj.has(res,'body')){
|
||||
cb({
|
||||
headers: reply.headers
|
||||
,body: Gun.text.ify(res.body)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
tran.jsonp = function(req, cb){
|
||||
var reply = {headers: {}};
|
||||
if(req.tran || req.headers['x-requested-with']){ return }
|
||||
if((req.url.query||{}).jsonp){
|
||||
cb.jsonp = req.url.query.jsonp;
|
||||
Gun.obj.del(req.url.query, 'jsonp');
|
||||
req.headers['x-requested-with'] = 'jsonp'; // polyfill
|
||||
req.sub = req.headers['gun-sub'] = req.headers['gun-sub'] || req.url.query['Gun-Sub'] || req.url.query['gun-sub'];
|
||||
Gun.obj.del(req.url.query, 'Gun-Sub');
|
||||
Gun.obj.del(req.url.query, 'gun-sub');
|
||||
return transport;
|
||||
}
|
||||
function transport(res){
|
||||
if(!res){ return }
|
||||
if(res.headers){
|
||||
Gun.obj.map(res.headers, function(val, field){
|
||||
reply.headers[field] = val;
|
||||
});
|
||||
}
|
||||
if(Gun.obj.has(res,'chunk') && (!reply.body || Gun.list.is(reply.chunks))){
|
||||
(reply.chunks = reply.chunks || []).push(res.chunk);
|
||||
}
|
||||
if(Gun.obj.has(res,'body')){
|
||||
reply.body = res.body; // self-reference yourself so on the client we can get the headers and body.
|
||||
reply.body = ';'+ cb.jsonp + '(' + Gun.text.ify(reply) + ');'; // javascriptify it! can't believe the client trusts us.
|
||||
cb(reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
tran.json = 'application/json';
|
||||
tran.push = Gun.on.create();
|
||||
return tran;
|
||||
}());
|
||||
|
||||
opt.hooks = opt.hooks || {};
|
||||
gun.opt({hooks: {
|
||||
transport: opt.hooks.transport || gun.server.transport
|
||||
}}, true);
|
||||
});
|
||||
meta.json = 'application/json';
|
||||
meta.JSON = function(res, data, multi){
|
||||
if(res && !res._headerSent){
|
||||
res.setHeader('Content-Type', meta.json);
|
||||
}
|
||||
if(!data && multi){
|
||||
res.write(Gun.text.ify(multi||'') + '\n');
|
||||
return;
|
||||
}
|
||||
return res.end(Gun.text.ify(data||''));
|
||||
};
|
||||
meta.CORS = function(req, res){
|
||||
if(!res || res.CORSHeader || res._headerSent){ return }
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", ["POST", "GET", "PUT", "DELETE", "OPTIONS"]);
|
||||
res.setHeader("Access-Control-Allow-Credentials", false);
|
||||
res.setHeader("Access-Control-Max-Age", 1000 * 60 * 60 * 24);
|
||||
res.setHeader("Access-Control-Allow-Headers", ["X-Requested-With", "X-HTTP-Method-Override", "Content-Type", "Accept", "Gun-Sub"]);
|
||||
res.setHeader("Access-Control-Expose-Headers", ["Content-Type", "Gun-Sub"]);
|
||||
res.CORSHeader = true;
|
||||
if(req && req.method === 'OPTIONS'){
|
||||
res.end();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}());
|
@ -1,7 +1,6 @@
|
||||
describe('Gun', function(){
|
||||
var Gun = require('../gun')
|
||||
, t = {};
|
||||
require(__dirname + '/../lib/file');
|
||||
describe('Utility', function(){
|
||||
describe('Type Check', function(){
|
||||
it('binary', function(){
|
||||
@ -304,8 +303,6 @@ describe('Gun', function(){
|
||||
data = {c: function(){}, d: 'hi'};
|
||||
test = Gun.ify(data);
|
||||
expect(test.err.invalid).to.be.ok();
|
||||
|
||||
console.log(test.nodes);
|
||||
});
|
||||
|
||||
it('union', function(){
|
||||
@ -317,32 +314,61 @@ describe('Gun', function(){
|
||||
Gun.union(graph, prime); // TODO: BUG! Where is the expect???
|
||||
});
|
||||
|
||||
it('path', function(done){
|
||||
console.log("fix path!");
|
||||
return done(); // TODO: FIX! This is broken because of API changes, fix it!
|
||||
describe('API', function(){
|
||||
|
||||
this.timeout(9000);
|
||||
var gun = require('gun')({
|
||||
s3: require('./shotgun') // replace this with your own keys!
|
||||
});
|
||||
gun.load('d1ed426098eae2bba8c60605e1e4552f66281770', null, {id: true}) // get Rodney Morris
|
||||
.path('parent.parent.first') // Rodney's parent is Juan Colon, whose parent is Francis Peters
|
||||
.get(function(val){
|
||||
console.log("val!", val);
|
||||
done();
|
||||
});
|
||||
console.log("________________________");
|
||||
});
|
||||
var gun = Gun();
|
||||
|
||||
it('set key get', function(done){
|
||||
var gun = require('gun/gun')();
|
||||
|
||||
gun.set({hello: "world"}).key('hello/world').get(function(val){
|
||||
console.log("?", val);
|
||||
it('set key get', function(done){
|
||||
gun.set({hello: "world"}).key('hello/world').get(function(val){
|
||||
expect(val.hello).to.be('world');
|
||||
done();
|
||||
}).blank(function(){ console.log("nothing"); })
|
||||
.err(function(){ console.log("err"); })
|
||||
});
|
||||
});
|
||||
});
|
||||
/*
|
||||
it('load', function(done){
|
||||
gun.load('hello/world').get(function(val){
|
||||
expect(val.hello).to.be('world');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('load path', function(done){
|
||||
gun.load('hello/world').path('hello').get(function(val){
|
||||
expect(val).to.be('world');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('load set path', function(done){
|
||||
gun.load('hello/world').set({hello: 'Mark'}).path('hello').get(function(val){
|
||||
expect(val).to.be('Mark');
|
||||
done();
|
||||
});
|
||||
});
|
||||
*/
|
||||
it('load path set', function(done){
|
||||
gun.load('hello/world').path('hello').set('World').get(function(val){
|
||||
expect(val).to.be('World');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('path', function(done){
|
||||
console.log("fix path!");
|
||||
return done(); // TODO: FIX! This is broken because of API changes, fix it!
|
||||
|
||||
this.timeout(9000);
|
||||
var gun = require('gun')({
|
||||
s3: require('./shotgun') // replace this with your own keys!
|
||||
});
|
||||
gun.load('d1ed426098eae2bba8c60605e1e4552f66281770', null, {id: true}) // get Rodney Morris
|
||||
.path('parent.parent.first') // Rodney's parent is Juan Colon, whose parent is Francis Peters
|
||||
.get(function(val){
|
||||
console.log("val!", val);
|
||||
done();
|
||||
});
|
||||
console.log("________________________");
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user