first commit

This commit is contained in:
armansansd
2023-07-12 16:31:19 +01:00
commit 3424b1927b
23306 changed files with 2217776 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
node_modules
+3
View File
@@ -0,0 +1,3 @@
language: node_js
node_js:
- 0.10
+296
View File
@@ -0,0 +1,296 @@
Promised-IO is a cross-platform package for asynchronous promise-based IO. Promises
provide a simple robust mechanism for asynchronicity with separation of concerns by encapsulating
eventual completion of an operation with side effect free callback registration
separate from call invocation. Promised-IO provides cross-platform
file, HTTP, and system interaction with promises for asynchronous operations.
Promised-IO also utilizes "lazy arrays" for progressively completed
actions or for streaming of data. Lazy arrays provide all the standard iterative Array methods for
receiving callbacks as actions are completed. Lazy arrays are utilized
for progressive loading of files and HTTP responses.
# Installation
Promised-IO can be installed via npm:
npm install promised-io
# promise
The promise module provides the primary tools for creating new promises and interacting
with promises. The promise API used by promised-io is the [Promises/A](http://wiki.commonjs.org/wiki/Promises/A)
proposal used by Dojo, jQuery, and other toolkits. Within promised-io, a promise is
defined as any object that implements the Promises/A API, that is they provide a
then() method that can take a callback. The then() methods definition is:
promise.then(fulfilledHandler, errorHandler);
Promises can originate from a variety of sources, and promised-io provides a constructor, Deferred,
to create promises.
## when
when = require("promised-io/promise").when;
when(promiseOrValue, fulfilledHandler, errorHandler);
You can pass a promise to the when() function and the fulfillment and error handlers will be registered for it's
completion *or* you can pass a regular value, and the fulfillment handler will be
immediately be called. The when function is a staple of working with promises because
it allows you to write code that normalizes interaction with synchronous values and asynchronous promises.
If you pass in a promise, a new promise for the result of execution of the callback handler
will be returned. If you pass a normal value, the return value will be the value returned
from the fulfilledHandler.
## Deferred
deferred = require("promised-io/promise").Deferred(canceler);
The Deferred constructor is the primary mechanism for creating new promises. The Deferred
object is a form of a promise with an interface for fulfilling or rejecting the promise.
A Deferred object is a means for a producer to resolve a promise and it also provides
a promise for consumers that are listening for the resolution of the promise. The basic
usage pattern looks like:
var Deferred = require("promised-io/promise").Deferred;
function delay(ms, value){
// create a new Deferred
var deferred = new Deferred();
setTimeout(function(){
// fulfill the deferred/promise, all listeners to the promise will be notified, and
// provided the value as the value of the promise
deferred.resolve(value);
}, ms);
// return the promise that is associated with the Deferred object
return deferred.promise;
}
The Deferred can optionally take a canceler function. This function will cause resulting
promises to have a cancel() method, and if the cancel() method is called, the
Deferred will be canceled and the canceler function will be called.
The Deferred object has the following methods and properties:
### resolve
deferred.resolve(value);
This will fulfill the Deferred's promise with the provided value. The fulfillment listeners to the promise
will be notified.
### reject
deferred.reject(error);
This will reject the Deferred's promise with the provided error. The error listeners to the promise
will be notified.
### promise
This is the promise object associated with the Deferred instance. The promise object
will not have any of the Deferred's fulfill or reject methods, and only provides an interface
for listening. This can be safely provided to consumers without any chance of being modified.
### cancel
deferred.cancel();
This will cancel the Deferred.
## currentContext
One of the challenges with working asynchronous code is that there can be times when
you wish for some contextual state information to be preserved across multiple
asynchronous actions, without having to actually pass the state to each function in
the asynchronous chain. Common examples of such contextual state would be tracking
the current transaction or the currently logged in user. Such state information could be
stored in a singleton (a module property or a global variable), but with asynchronous
actions being interleaved, this is unsuitable for tracking state across asynchronous continuations
of an action.
The promised-io package's promise module provides a facility for tracking state across
asynchronous operations. The promise module tracks the "currentContext" global variable,
and whatever value that was in the variable at the time a promise was created
will be restored when that promise is fulfilled (or rejected).
## all
group = require("promised-io/promise").all(arrayOfPromises);
The all() function can be passed an array of promises, or multiple promises as individual
arguments, and all() will return a new promise that represents the completed values when all the promises
have been fulfilled. This allows you to easily run multiple asynchronous actions, and wait
for the completion ("join") of all the actions. For example:
group = all(promise1, promise2, promise3);
group.then(function(array){
var value1 = array[0]; // result of promise1
var value2 = array[1]; // result of promise2
var value3 = array[2]; // result of promise3
});
## first
first = require("promised-io/promise").first(arrayOfPromises);
The first() function can be passed an array of promises, or multiple promises as individual
arguments, and first() will return a new promise that represents the completed value when the first promise
is fulfilled. This allows you to run multiple asynchronous actions and get the first result. For example:
response = first(requestToMainSite, requestToMirrorSite1, requestToMirrorSite2);
response.then(function(response){
// response from the first site to respond
});
## seq
result = require("promised-io/promise").seq(arrayOfActionFunctions, startingValue);
The seq() function can be passed an array of functions, and seq() will execute each function
in sequence, waiting for the promise returned from each one to complete before executing
the next function. Each function will be called with the result of the last function (or the
startingValue for the first function).
## whenPromise
resultPromise = require("promised-io/promise").whenPromise(valueOrPromise, fulfillmentHandler, errorHandler);
The whenPromise() function behaves exactly like when() except that whenPromise
will always return a promise, even if a non-promise value is passed in.
## allKeys
group = require("promised-io/promise").allKeys(hashOfPromises);
Takes a hash of promises and returns a promise that is fulfilled once all the promises in the hash keys are fulfilled.
# fs
This module provides promise-based access to the filesystem. The API of the fs module
basically follows the [Node File System module API](http://nodejs.org/api/fs.html).
Each of the asynchronous functions in the Node's FS API is reflected with a corresponding
function in the fs module that returns a promise (instead of requiring a callback argument in the initial call).
For example, where Node has fs.rename(path1, path2, [callback]), with promised-io
you would call it:
var fs = require("promised-io/fs");
fs.rename(path1, path2).then(function(){
// finished renaming
});
Any callback arguments will be the same minus the error argument:
var fs = require("promised-io/fs");
fs.readdir(path).then(function(files){
// use the result
}, function(error) {
// Handle errors here instead
});
One function that does differ from NodeJS's fs module is the open() function.
## open
var file = require("promised-io/fs").open(path, mode);
The open() function differs from simply being a promise-based version of the Node's
open() function in that it immediately returns (even though the opening of the
file is asynchronous) a file object that be used to read from and write to the file.
To write to the file object, we can write:
promiseForCompletion = file.write(contents, options, encoding);
To close the file object, we can write:
promiseForCompletion = file.close();
We can also use file.writeSync and file.closeSync for the synchronous versions of these
functions.
The file object is also a lazy array, which means you can read from the file using
standard array methods. To asynchronously read the contents of a file, you can do:
file.forEach(function(chunk){
// called for each chunk of the file until the end of the file is reached.
});
# lazy-array
The lazy-array module provides the functionality for creating and using lazy arrays,
which are objects that implement the interface of the standard iterative array methods for accessing
streams of data. Array methods can be called and they will be
asynchronously executed as data is available. Lazy arrays are powerful way to model
asynchronous streams since they can used like other JavaScript arrays.
Typically you don't need to directly use this module, rather other IO modules like the
file system (fs) and HTTP (http-client) modules provide lazy arrays that you can interact
with. For example, we could search through a file for the string "lazy" and stop reading
once we find it using the standard some() method:
if(file.some(function(chunk){
return chunk.toString().indexOf("lazy") > -1;
}));
Lazy arrays include the follow standard array methods, providing access to the data
as the stream data becomes available:
* forEach
* concat
Additional iterative methods can also access data as it available *and* as it is requested from
the returned lazy array. This means that in order for this function to be excuted, the
resulting array should have a forEach (or any of the other standard methods) called on
it to trigger the request for data. These methods follow this behavior:
* filter
* every
* some
* map
And also these standard methods, although these must fully fetch the stream:
* join
* sort
* reverse
Also the following additional methods are available on lazy arrays:
* toRealArray() - This will fetch all the data and return it as a real JavaScript array.
* get(index) - This retrieves an element by index.
## LazyArray
lazyArray = require("promised-io/lazy-array").LazyArray({
some: someImplementation,
length: arrayLength
});
This function is a constructor for creating your own lazy arrays. With this function,
you don't need to implement the entire set of array methods, you can just implement
the some() method and provide an array length, if it is known.
## first
first = require("promised-io/lazy-array").first(lazyArray);
This function returns the first element in a lazy array.
## last
last = require("promised-io/lazy-array").last(lazyArray);
This function returns the last element in a lazy array.
## get
item = require("promised-io/lazy-array").get(index);
This function returns an element by index from a lazy array.
# http-client
This module provides convenient promise-based access to making HTTP requests.
Promised-IO is part of the Persevere project, and therefore is licensed under the
AFL or BSD license. The Persevere project is administered under the Dojo foundation,
and all contributions require a Dojo CLA.
+35
View File
@@ -0,0 +1,35 @@
/*
* Provides time-based promise-returning delay and schedule functions
*/
({define:typeof define!="undefined"?define:function(factory){module.exports=factory(require)}}).
define(function(require){
if (typeof system === "object" && system.engine === "rhino"){
// for rhino
return require("./engines/rhino/delay");
}
var defer = require("./promise").defer,
LazyArray = require("./lazy-array").LazyArray;
// returns a promise that is fulfilled after the given number of milliseconds
function delay(ms){
var deferred = defer();
setTimeout(deferred.resolve, ms);
return deferred.promise;
};
// returns a lazy array that iterates one every given number of milliseconds
delay.schedule = function(ms){
var callbacks = [];
setInterval(function(){
callbacks.forEach(function(callback){
if(callback()){
callbacks.splice(callbacks.indexOf(callback), 1);
}
});
}, ms);
return LazyArray({
some: function(callback){
callbacks.push(callback);
}
});
};
return delay.delay = delay;
});
+113
View File
@@ -0,0 +1,113 @@
/**
* HTTP Client using the JSGI standard objects
*/
var defer = require("../../promise").defer,
when = require("../../promise").when,
LazyArray = require("../../lazy-array").LazyArray,
http = require("http"),
https = require("https"),
parse = require("url").parse;
// configurable proxy server setting, defaults to http_proxy env var
exports.proxyServer = require("../../process").env.http_proxy;
exports.request = function(originalRequest){
// make a shallow copy of original request object
var request = {};
for(var key in originalRequest){
if(originalRequest.hasOwnProperty(key)){
request[key] = originalRequest[key];
}
}
if(request.timeout === undefined)request.timeout= 20000; // default timeout.
if(request.url){
var parsed = parse(request.url);
if (parsed.pathname) {
parsed.pathInfo = parsed.pathname;
} else {
parsed.pathInfo = "/";
}
request.queryString = parsed.query || "";
for(var i in parsed){
request[i] = parsed[i];
}
}
var deferred = defer();
if(exports.proxyServer){
request.pathname = request.url;
var proxySettings = parse(exports.proxyServer);
request.port = proxySettings.port;
request.protocol = proxySettings.protocol;
request.hostname = proxySettings.hostname;
}
if(!request.protocol){
throw new Error("No valid protocol/URL provided");
}
var timedOut, bodyDeferred;
// Limits the time of sending the request + receiving the response header to 20 seconds.
// No timeout is used on the client stream, but we do destroy the stream if a timeout is reached.
var timeout = setTimeout(function(){
timedOut = true;
req.destroy();
deferred.reject(new Error("Timeout"));
}, request.timeout);
var secure = request.protocol.indexOf("s") > -1;
request.port = request.port || (secure ? 443 : 80);
request.headers = request.headers || {host: request.host || request.hostname + (request.port ? ":" + request.port : "")};
request.host = request.hostname;
request.method = request.method || "GET";
request.path = request.pathname || request.pathInfo || "";
if (request.queryString) {
request.path += "?"+request.queryString;
}
var timedOut;
var req = (secure ? https : http).request(request);
req.on("response", function (response){
if(timedOut){
return;
}
response.status = response.statusCode;
var sendData = function(block){
buffer.push(block);
};
var buffer = [];
bodyDeferred = defer();
var body = response.body = LazyArray({
some: function(callback){
buffer.forEach(callback);
sendData = callback;
return bodyDeferred.promise;
}
});
response.setEncoding(request.encoding || "utf8");
response.on("data", function (chunk) {
sendData(chunk);
});
response.on("end", function(){
bodyDeferred.resolve();
});
deferred.resolve(response);
clearTimeout(timeout);
});
req.on('error', function(e) {
if (!timedOut) {
deferred.reject(e);
clearTimeout(timeout);
}
});
if(request.body){
return when(request.body.forEach(function(block){
req.write(block);
}), function(){
req.end();
return deferred.promise;
});
}else{
req.end();
return deferred.promise;
}
};
+79
View File
@@ -0,0 +1,79 @@
/*
* Provides time-based promise-returning delay and schedule functions for Rhino
*/
var defer = require("../../promise").defer,
LazyArray = require("../../lazy-array").LazyArray;
// returns a promise that is fulfilled after the given number of milliseconds
exports.delay = function(ms){
var deferred = defer();
_scheduleTimeout(deferred.resolve, ms, false);
return deferred.promise;
};
// returns a lazy array that iterates one every given number of milliseconds
exports.schedule = function(ms){
var callbacks = [];
_scheduleTimeout(function(){
callbacks.forEach(function(callback){
if(callback()){
callbacks.splice(callbacks.indexOf(callback), 1);
}
});
}, ms, true);
return LazyArray({
some: function(callback){
callbacks.push(callback);
}
});
};
var nextId = 1,
timeouts = {},
timer,
queue;
var _scheduleTimeout = function(callback, delay, repeat)
{
if (typeof callback == "function")
var func = callback;
else if (typeof callback == "string")
var func = new Function(callback);
else
return;
var timeout = {
};
var id = nextId++;
timeouts[id] = timeout;
timer = timer || new java.util.Timer("JavaScript timer thread", true);
queue = queue || require("event-loop");
var lastFinished = true;
var task = timeout.task = new java.util.TimerTask({
run: function(){
if(lastFinished){
lastFinished = false;
queue.enqueue(function(){
if(!timeout.cancelled){ // check to make sure it wasn't enqueued and then later cancelled
try{
func();
}finally{
lastFinished = true;
}
}
});
}
}
});
delay = Math.floor(delay);
if(repeat){
timer.schedule(task, delay, delay);
}
else{
timer.schedule(task, delay);
}
return id;
}
+74
View File
@@ -0,0 +1,74 @@
var File = require("fs"),
LazyArray = require("../../lazy-array").LazyArray,
defer = require("../../promise").defer;
for(var i in File){
exports[i] = File[i];
}
exports.readFileSync = File.read;
exports.writeFileSync = File.write;
exports.mkdirSync = File.mkdir;
exports.readdir = exports.list;
exports.stat = exports.statSync = function(path) {
try{
return {
isFile: function(){
return File.isFile(path);
},
size: File.size(path)
};
}catch(e){
var deferred = defer();
deferred.reject(e);
return deferred.promise;
}
};
exports.makeTree = File.mkdirs;
exports.makeDirectory = File.mkdir;
exports.open = function(){
var file = File.open.apply(this, arguments);
var array = LazyArray({
some: function(callback){
while(true){
var buffer = file.read(4096);
if(buffer.length <= 0){
return;
}
if(callback(buffer)){
return;
}
}
}
});
for(var i in array){
file[i] = array[i];
}
return file;
};
exports.openSync = exports.open;
exports.createWriteStream = function(path, options) {
options = options || {};
options.flags = options.flags || "w";
var flags = options.flags || "w",
f = File.open(path, flags);
return {
writable: true,
write: function() {
var deferred = defer();
try {
f.write.apply(this, arguments);
f.flush();
}
catch (e) {
return stream.writable = false;
}
deferred.resolve();
return deferred.promise;
},
end: f.close,
destroy: f.close
}
}
+84
View File
@@ -0,0 +1,84 @@
/**
* HTTP Client using the JSGI standard objects
*/
var LazyArray = require("../../lazy-array").LazyArray;
// configurable proxy server setting, defaults to http_proxy env var
exports.proxyServer = require("../../process").env.http_proxy;
exports.request = function(request){
var url = new java.net.URL(request.url),
connection = url.openConnection(),
method = request.method || "GET",
is = null,
promised = true;
if (request.jsgi && "async" in request.jsgi) promised = request.jsgi.async;
for (var header in this.headers) {
var value = this.headers[header];
connection.addRequestProperty(String(header), String(value));
}
connection.setFollowRedirects(false)
connection.setDoInput(true);
connection.setRequestMethod(method);
if (request.body && typeof request.body.forEach === "function") {
connection.setDoOutput(true);
var writer = new java.io.OutputStreamWriter(connection.getOutputStream());
request.body.forEach(function(chunk) {
writer.write(chunk);
writer.flush();
});
}
if (typeof writer !== "undefined") writer.close();
try {
connection.connect();
is = connection.getInputStream();
}
catch (e) {
is = connection.getErrorStream();
}
var status = Number(connection.getResponseCode()),
headers = {};
for (var i = 0;; i++) {
var key = connection.getHeaderFieldKey(i),
value = connection.getHeaderField(i);
if (!key && !value)
break;
// returns the HTTP status code with no key, ignore it.
if (key) {
key = String(key).toLowerCase();
value = String(value);
if (headers[key]) {
if (!Array.isArray(headers[key])) headers[key] = [headers[key]];
headers[key].push(value);
}
else {
headers[key] = value;
}
}
}
var reader = new java.io.BufferedReader(new java.io.InputStreamReader(is)),
builder = new java.lang.StringBuilder(),
line;
// FIXME create deferred and LazyArray
while((line = reader.readLine()) != null){
builder.append(line + '\n');
}
if (typeof writer !== "undefined") writer.close();
reader.close();
return {
status: status,
headers: headers,
body: LazyArray({
some: function(write) {
write(builder.toString());
}
})
}
};
Generated Vendored Executable
+187
View File
@@ -0,0 +1,187 @@
/**
* Node fs module that returns promises
*/
if (typeof java === "object"){
var fs = require("./engines/rhino/fs");
// for rhino
for(var i in fs){
exports[i] = fs[i];
}
}
else{
var fs = require("fs"),
LazyArray = require("./lazy-array").LazyArray,
Buffer = require("buffer").Buffer,
defer = require("./promise").defer,
when = require("./promise").when,
convertNodeAsyncFunction = require("./promise").convertNodeAsyncFunction;
// convert all the non-sync functions that have a sync counterpart
for (var i in fs) {
if ((i + 'Sync') in fs) {
// async
exports[i] = convertNodeAsyncFunction(fs[i], i === "readFile");
}
else{
// sync, or something that we can't auto-convert
exports[i] = fs[i];
}
}
function File(fd){
var file = new LazyArray({
some: function(callback){
var deferred = defer();
function readAndSend(){
var buffer = new Buffer(4096);
if(fd.then){
fd.then(function(resolvedFd){
fd = resolvedFd;
fs.read(fd, buffer, 0, 4096, null, readResponse);
});
}else{
fs.read(fd, buffer, 0, 4096, null, readResponse);
}
function readResponse(err, bytesRead){
if(err){
deferred.reject(err);
return;
}
if (bytesRead === 0){
fs.close(fd);
deferred.resolve();
}
else {
var result;
if(bytesRead < 4096){
result = callback(buffer.slice(0, bytesRead));
}else{
result = callback(buffer);
}
if(result){
// if a promise is returned, we wait for it be fulfilled, allows for back-pressure indication
if(result.then){
result.then(function(result){
if(result){
deferred.resolve();
}
else{
readAndSend(fd);
}
}, deferred.reject);
}
else{
deferred.resolve();
}
}else{
readAndSend(fd);
}
}
}
}
readAndSend();
return deferred.promise;
},
length: 0
});
file.fd = fd;
file.then = function(callback, errback){
fd.then(function(){
callback(file);
}, errback);
};
file.write = function(contents, options, encoding){
return exports.write(file, contents, options, encoding);
}
file.close = function(){
return exports.close(file);
}
file.writeSync = function(contents, options, encoding){
return exports.writeSync(file.fd, contents, options, encoding);
}
file.closeSync = function(){
return exports.closeSync(file.fd);
}
return file;
}
File.prototype = LazyArray.prototype;
var nodeRead = exports.read;
exports.read = function(path, options){
if(path instanceof File){
var args = arguments;
return when(path.fd, function(fd){
args[0] = fd;
return nodeRead.apply(this, args);
});
}else{
return exports.readFileSync(path, options).toString((options && options.charset) || "utf8");
}
};
var nodeWrite = exports.write;
exports.write = function(path, contents, options, encoding){
if(path instanceof File){
var id = Math.random();
var args = arguments;
return when(path.fd, function(fd){
args[0] = fd;
if(typeof contents == "string"){
return nodeWrite(fd, contents, options, encoding);
}
return nodeWrite(fd, contents, 0, contents.length, null);
});
}else{
return exports.writeFileSync(path, contents, options);
}
};
var nodeClose = exports.close;
exports.close = function(file){
if(file instanceof File){
var args = arguments;
return when(file.fd, function(fd){
args[0] = fd;
return nodeClose.apply(this, args);
});
}
throw new Error("Must be given a file descriptor");
};
var nodeOpenSync = exports.openSync;
exports.openSync = function(){
if(typeof mode == "string"){
arguments[1] = mode.replace(/b/,'');
}
return File(nodeOpenSync.apply(this, arguments));
};
nodeOpen = exports.open;
exports.open = function(path, mode){
if(typeof mode == "string"){
arguments[1] = mode.replace(/b/,'');
}
return File(nodeOpen.apply(this, arguments));
};
exports.makeDirectory = exports.mkdirSync;
exports.makeTree = function(path){
if(path.charAt(path.length-1) == '/') {
path = path.substring(0, path.length - 1);
}
try{
fs.statSync(path);
}catch(e){
var index = path.lastIndexOf('/');
if(index > -1){
exports.makeTree(path.substring(0, index));
}
fs.mkdirSync(path, 0777);
}
};
exports.absolute = exports.realpathSync;
exports.list = exports.readdirSync;
exports.move = exports.rename;
}
+189
View File
@@ -0,0 +1,189 @@
/**
* HTTP Client using the JSGI standard objects
*/
({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory.apply(this, deps.slice(0,2).map(require).concat(require))}}).
define(["./promise", "./process", "require"],
function(promise, process, require){
var defer = promise.defer,
when = promise.when,
print = process.print,
request;
if(typeof XMLHttpRequest === "undefined"){
request = require("./engines/node/http-client").request;
}
else{
request = function(request){
var
scheme = request.scheme || "http",
serverName = request.serverName || request.hostname || "localhost",
serverPort = request.serverPort || request.port || 80,
xhr = new XMLHttpRequest();
xhr.open(request.method || "GET",
request.url || // allow request.url to shortcut creating a URL from all the various parts
(scheme + "://" + serverName + ":" + serverPort + request.pathInfo + (request.queryString ? '?' + request.queryString : '')), true);
for(var i in request.headers){
xhr.setRequestHeader(i, request.headers[i]);
}
var deferred = defer(),
response,
lastUpdate;
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 || xhr.readyState == 3){
if(!response){
response = {
body: [xhr.responseText],
status: xhr.status,
headers: {}
};
lastUpdate = xhr.responseText.length;
var headers = xhr.getAllResponseHeaders();
headers = headers.split(/\n/);
for(var i = 0; i < headers.length; i++){
var nameValue = headers[i].split(": ", 2);
if(nameValue){
var name = nameValue[0];
response.headers[name.toLowerCase()] = xhr.getResponseHeader(name);
}
}
}
else{
response.body = [xhr.responseText];
lastUpdate = xhr.responseText.length;
}
if(xhr.readyState == 4){
deferred.resolve(response);
}
else{
deferred.progress(response);
}
}
}
xhr.send(request.body && request.body.toString());
return deferred.promise;
}
}
// for back-compat
request.request = request;
// FIXME this way too naive
var isRedirect = request.isRedirect = function(response){
return [301,302,303,307].indexOf(response.status) >= 0;
}
request.Redirect = function(nextApp, maxRedirects){
maxRedirects = maxRedirects || 10;
return function(request){
var remaining = maxRedirects,
deferred = defer();
function next(){
when(nextApp(request), function(response) {
if(remaining--){
// TODO cache safe redirects when cache is added
if(isRedirect(response)){
request.url = response.headers.location;
next();
}else{
deferred.resolve(response);
}
}else{
if(isRedirect(response)) print("Maximum redirects reached")
deferred.resolve(response);
}
}, deferred.reject);
}
next();
return deferred.promise;
}
}
request.CookieJar = function(nextApp) {
var domainToCookies = {};
return function(req) {
var
querystring = require("./querystring"),
parseUri = require("./util/uri").parseUri;
if (req.url) {
var url = parseUri(req.url);
req.hostname = url.host;
req.port = url.port;
req.pathInfo = url.path;
req.authority = url.authority;
}
if (req.hostname && domainToCookies[req.hostname]) {
var cookieString = "";
req.headers["Cookie"] = domainToCookies[req.hostname].map(function(cookie) {
return querystring.toQueryString(cookie);
}).join(',');
}
return when(nextApp(req), function(response) {
var cookies;
if (response.headers["set-cookie"]) {
var path, domain = req.hostname + (req.port ? ":"+req.port : "");
if (Array.isArray(response.headers["set-cookie"])) {
cookies = [];
response.headers["set-cookie"].forEach(function(cookie) {
cookie = querystring.parseQuery(cookie, /[;,]/g);
if (cookie.Version !== undefined) { delete cookie.Version; }
if (cookie.Path !== undefined) { path = cookie.Path; delete cookie.Path; }
if (cookie.HttpOnly !== undefined) { delete cookie.HttpOnly; }
if (cookie.Domain !== undefined) { domain = cookie.Domain; delete cookie.Domain; }
cookies.push(cookie);
});
} else {
cookies = querystring.parseQuery(response.headers["set-cookie"], /[;,]/g);
if (cookies.Version !== undefined) { delete cookies.Version; }
if (cookies.Path !== undefined) { path = cookies.Path; delete cookies.Path; }
if (cookies.HttpOnly !== undefined) { delete cookies.HttpOnly; }
if (cookies.Domain !== undefined) { domain = cookies.Domain; delete cookies.Domain; }
cookies = [ cookies ];
}
for (var k in cookies) {
if (Array.isArray(cookies[k])) {
cookies[k] = cookies[k][0];
}
}
if (cookies) {
domainToCookies[req.hostname] = cookies;
}
}
return response;
});
};
};
// TODO request.Cache
// TODO request.CookieJar
request.Client = function(options) {
if (!(this instanceof request.Client)) return new request.Client(options);
options = options || {};
for (var key in options) {
this[key] = options[key];
}
this.request = request;
// turn on redirects by default
var redirects = "redirects" in this ? this.redirects : 20;
if (redirects) {
this.request = request.CookieJar(request.Redirect(this.request, typeof redirects === "number" && redirects));
}
var finalRequest = this.request;
this.request = function(options) {
if (typeof options === "string") options = {url: options};
return finalRequest(options);
}
}
return request;
});
+158
View File
@@ -0,0 +1,158 @@
({define:typeof define!="undefined"?define:function(deps, factory){module.exports = factory.apply(this, deps.map(function(id){return require(id)}))}}).
define(["./promise"], function(promise){
try{
var when = promise.when;
}catch(e){
console.log("couldn't load promise library", e.stack);
when = function(value, callback){
return callback(value);
}
}
function LazyArray(hasSomeAndLength){
return new SomeWrapper(hasSomeAndLength);
};
var exports = LazyArray;
exports.LazyArray = LazyArray;
exports.first = function(array){
return exports.get(array, 0);
};
exports.last = function(array){
return exports.get(array, array.length-1);
};
exports.get = function(array, index){
var result, i = 0;
return when(array.some(function(item){
if(i == index){
result = item;
return true;
}
i++;
}),
function(){
return result;
});
};
var testProto = {};
var testProto2 = {a:"b"};
testProto.__proto__ = testProto2;
var mutableProto = testProto.a == "b";
function SomeWrapper(hasSomeAndLength){
if(mutableProto){
hasSomeAndLength.source = hasSomeAndLength;
hasSomeAndLength.__proto__ = SomeWrapper.prototype;
return hasSomeAndLength;
}
this.source = hasSomeAndLength;
if(hasSomeAndLength.length){
this.length = hasSomeAndLength.length;
}
this.totalCount = hasSomeAndLength.totalCount;
}
exports.LazyArray.prototype = SomeWrapper.prototype = [];
SomeWrapper.prototype.some = function(callback){
this.source.some(callback);
}
SomeWrapper.prototype.filter = function(fn, thisObj){
var results = [];
return when(this.source.some(function(item){
if(fn.call(thisObj, item)){
results.push(item);
}
}), function(){
return results;
});
};
SomeWrapper.prototype.every = function(fn, thisObj){
return when(this.source.some(function(item){
if(!fn.call(thisObj, item)){
return true;
}
}), function(result){return !result;});
};
SomeWrapper.prototype.forEach= function(fn, thisObj){
return this.source.some(function(item){
fn.call(thisObj, item);
});
};
SomeWrapper.prototype.concat = function(someOther){
var source = this.source;
return new SomeWrapper({
length : source.length + someOther.length,
some : function(fn,thisObj){
return when(source.some(fn,thisObj), function(result){
return result || someOther.some(fn,thisObj);
});
}
});
};
SomeWrapper.prototype.map = function(mapFn, mapThisObj){
var source = this.source;
return new SomeWrapper({
length : source.length,
some : function(fn,thisObj){
return source.some(function(item){
return fn.call(thisObj, mapFn.call(mapThisObj, item));
});
}
});
};
SomeWrapper.prototype.toRealArray= function(mapFn, mapThisObj){
var array = [];
return when(this.source.some(function(item){
array.push(item);
}), function(){
return array;
});
};
SomeWrapper.prototype.join = function(){
var args = arguments;
return when(this.toRealArray(), function(realArray){
return Array.prototype.join.apply(realArray, args);
});
};
SomeWrapper.prototype.sort = function(){
var args = arguments;
return when(this.toRealArray(), function(realArray){
return Array.prototype.sort.apply(realArray, args);
});
};
SomeWrapper.prototype.reverse = function(){
var args = arguments;
return when(this.toRealArray(), function(realArray){
return Array.prototype.reverse.apply(realArray, args);
});
};
SomeWrapper.prototype.get = SomeWrapper.prototype.item = function(index){
var result, i = 0;
return when(this.source.some(function(item){
if(i == index){
result = item;
return true;
}
i++;
}), function(){
return result;
});
};
SomeWrapper.prototype.toSource = function(){
var serializedParts = [];
return when(this.source.some(function(item){
serializedParts.push(item && item.toSource());
}), function(){
return '[' + serializedParts.join(",") + ']';
});
};
SomeWrapper.prototype.toJSON = function(){
var loadedParts = [];
return when(this.source.some(function(item){
loadedParts.push(item);
}), function(){
return loadedParts;
});
};
return exports;
});
+13
View File
@@ -0,0 +1,13 @@
var oauth = require("./oauth");
for(var p in oauth){
exports[p] = oauth[p];
}
exports.xauth = function(client, username, password, mode){
return client.obtainTokenCredentials("", "", "", {
x_auth_username: username,
x_auth_password: password,
x_auth_mode: mode
});
};
+315
View File
@@ -0,0 +1,315 @@
var parseUrl = require("url").parse;
var querystring = require("./util/querystring");
var whenPromise = require("./promise").whenPromise;
var makeRequest = require("./http-client").request;
var crypto = require("crypto");
function encodeRfc3986(str){
return !str ? "" : encodeURIComponent(str)
.replace(/\!/g, "%21")
.replace(/\'/g, "%27")
.replace(/\(/g, "%28")
.replace(/\)/g, "%29")
.replace(/\*/g, "%2A");
}
function parseResponse(response){
return response.body.join("").then(function(body){
if(response.status == 200){
return querystring.parse(body);
}else{
var err = new Error(response.status + ": " + body);
err.status = response.status;
err.headers = response.headers;
err.body = body;
throw err;
}
});
}
exports.Client = Client;
function Client(identifier, secret, tempRequestUrl, tokenRequestUrl, callback, version, signatureMethod, nonceGenerator, headers){
this.identifier = identifier;
this.tempRequestUrl = tempRequestUrl;
this.tokenRequestUrl = tokenRequestUrl;
this.callback = callback;
this.version = version || false;
// _createSignature actually uses the variable, not the instance property
this.signatureMethod = signatureMethod = signatureMethod || "HMAC-SHA1";
this.generateNonce = nonceGenerator || Client.makeNonceGenerator(32);
this.headers = headers || Client.Headers;
if(this.signatureMethod != "PLAINTEXT" && this.signatureMethod != "HMAC-SHA1"){
throw new Error("Unsupported signature method: " + this.signatureMethod);
}
// We don't store the secrets on the instance itself, that way it can
// be passed to other actors without leaking
secret = encodeRfc3986(secret);
this._createSignature = function(tokenSecret, baseString){
if(baseString === undefined){
baseString = tokenSecret;
tokenSecret = "";
}
var key = secret + "&" + tokenSecret;
if(signatureMethod == "PLAINTEXT"){
return key;
}else{
return crypto.createHmac("SHA1", key).update(baseString).digest("base64");
}
};
}
Client.Headers = {
Accept: "*/*",
Connection: "close",
"User-Agent": "promised-io/oauth"
};
// The default headers shouldn't change after clients have been created,
// but you're free to replace the object or pass headers to the Client
// constructor.
Object.freeze(Client.Headers);
Client.NonceChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Client.makeNonceGenerator = function(nonceSize){
var nonce = Array(nonceSize + 1).join("-").split("");
var chars = Client.NonceChars.split("");
return function nonceGenerator(){
return nonce.map(getRandomChar).join("");
};
function getRandomChar(){
return chars[Math.floor(Math.random() * chars.length)];
}
};
Client.getTimestamp = function(){
return Math.floor(Date.now() / 1000).toString();
};
// Binds the client against a set of token credentials.
// The resulting object can be used to make signed requests.
// The secret won't be exposed on the object itself.
Client.prototype.bind = function(tokenIdentifier, tokenSecret){
var bound = {
identifier: this.identifier,
tokenIdentifier: tokenIdentifier,
signatureMethod: this.signatureMethod,
version: this.version,
headers: this.headers,
generateNonce: this.generateNonce
};
bound._createSignature = this._createSignature.bind(this, encodeRfc3986(tokenSecret));
bound._createSignatureBase = this._createSignatureBase;
bound._normalizeUrl = this._normalizeUrl;
bound._collectOAuthParams = this._collectOAuthParams;
bound._normalizeParams = this._normalizeParams;
bound._signRequest = this._signRequest;
bound.request = this.request;
return bound;
};
// Wrapper for `http-client.request` which signs the request
// with the client credentials, and optionally token credentials if bound.
Client.prototype.request = function(originalRequest){
var request = {};
for(var key in originalRequest){
if(originalRequest.hasOwnProperty(key)){
request[key] = originalRequest[key];
}
}
// Normalize the request. `engines/node/http-client.request` is
// quite flexible, but this should do it.
if(request.url){
var parsed = parseUrl(request.url);
parsed.pathInfo = parsed.pathname;
parsed.queryString = parsed.query;
for(var i in parsed){
request[i] = parsed[i];
}
}
request.pathname = request.pathname || request.pathInfo || "/";
request.queryString = request.queryString || request.query || "";
request.method = (request.method || "GET").toUpperCase();
request.protocol = request.protocol.toLowerCase();
request.hostname = (request.host || request.hostname).toLowerCase();
request.headers = {};
for(var h in this.headers){
request.headers[h] = this.headers[h];
}
for(var h in originalRequest.headers){
request.headers[h] = originalRequest.headers[h];
}
// We'll be setting the Authorization header; due to how `engines/node/http-client.request`
// is implemented we need to set the Host header as well.
request.headers.host = request.headers.host || request.hostname + (request.port ? ":" + request.port : "");
// Parse all request parameters into a flattened array of parameter pairs.
// Note that this array contains munged parameter names.
var requestParams = [], uncombinedRequestParams = [];
// Start with parameters that were defined in the query string
if(request.queryString){
querystring.parseToArray(requestParams, request.queryString);
}
// Allow parameters to be defined in object notation, this is *not* part of `http-client.request`!
// It saves an extra stringify+parse though.
if(request.requestParams){
for(var i in request.requestParams){
if(request.requestParams.hasOwnProperty(i)){
querystring.addToArray(uncombinedRequestParams, i, request.requestParams[i]);
}
}
}
// Send the parameters from `request.requestParams` in the query string
// for GET and DELETE requests. We immediately concat to the `requestParams` array,
// which is then built into the query string.
if(request.method == "GET" || request.method == "DELETE"){
requestParams = requestParams.concat(uncombinedRequestParams);
}
// Rebuild the query string
request.queryString = requestParams.reduce(function(qs, v, i){
return qs + (i % 2 ? "=" + querystring.escape(v) : (qs.length ? "&" : "") + querystring.escape(v));
}, "");
// Depending on the request content type, look for request parameters in the body
var waitForBody = false;
if(request.headers && request.headers["Content-Type"] == "application/x-www-form-urlencoded"){
waitForBody = whenPromise(request.body.join(""), function(body){
querystring.parseToArray(requestParams, body);
return body;
});
}
// If we're a POST or PUT and are not sending any content, or are sending urlencoded content,
// add the `request.request` to the request body. If we are sending non-urlencoded content through
// a POST or PUT, the `request.requestParams` are ignored.
if(request.requestParams && (request.method == "POST" || request.method == "PUT") && (!request.headers || !request.headers["Content-Type"] || request.headers["Content-Type"] == "application/x-www-form-urlencoded")){
waitForBody = whenPromise(waitForBody, function(body){
requestParams = requestParams.concat(uncombinedRequestParams);
body = (body ? body + "&" : "") + querystring.stringify(request.requestParams);
request.body = [body];
request.headers["Content-Type"] = "application/x-www-form-urlencoded";
});
}
// Sign the request and then actually make it.
return whenPromise(waitForBody, function(){
this._signRequest(request, requestParams);
return makeRequest(request);
}.bind(this));
};
Client.prototype._normalizeUrl = function(request){
var normalized = request.protocol + "//" + request.hostname;
if(request.protocol == "http:" && request.port && (request.port + "") != "80"){
normalized += ":" + request.port;
}
if(request.protocol == "https:" && request.port && (request.port + "") != "443"){
normalized += ":" + request.port;
}
return normalized + request.pathname;
};
Client.prototype._collectOAuthParams = function(request, requestParams){
var oauthParams = {};
if(request.oauthParams){
for(var p in request.oauthParams){
// Don't allow `request.oauthParams` to override standard values.
// `oauth_token` and `oauth_version` are conditionally added,
// the other parameters are always set. Hence we just test for
// the first two.
if(p != "oauth_token" && p != "oauth_version"){
oauthParams[p] = request.oauthParams[p];
}
}
}
oauthParams.oauth_consumer_key = this.identifier;
oauthParams.oauth_signature_method = this.signatureMethod;
oauthParams.oauth_timestamp = Client.getTimestamp();
oauthParams.oauth_nonce = this.generateNonce();
if(this.tokenIdentifier){
oauthParams.oauth_token = this.tokenIdentifier;
}
if(this.version){
oauthParams.oauth_version = this.version;
}
for(var i in oauthParams){
requestParams.push(i, oauthParams[i]);
}
return oauthParams;
};
Client.prototype._normalizeParams = function(requestParams){
// Encode requestParams
requestParams = requestParams.map(encodeRfc3986);
// Unflatten the requestParams for sorting
requestParams = requestParams.reduce(function(result, _, i, arr){
if(i % 2 == 0){
result.push(arr.slice(i, i + 2));
}
return result;
}, []);
// Sort the unflattened requestParams
requestParams.sort(function(a, b){
if(a[0] == b[0]){
return a[1] < b[1] ? -1 : 1;
}else{
return a[0] < b[0] ? -1 : 1;
}
});
return requestParams.map(function(pair){ return pair.join("="); }).join("&");
};
Client.prototype._createSignatureBase = function(requestMethod, baseUri, params){
return [requestMethod, baseUri, params].map(encodeRfc3986).join("&");
};
Client.prototype._signRequest = function(request, requestParams){
// Calculate base URI string
var baseUri = this._normalizeUrl(request);
// Register OAuth parameters and add to the request parameters
// Additional parameters can be specified via the `request.oauthParams` object
var oauthParams = this._collectOAuthParams(request, requestParams);
// Generate parameter string
var params = this._normalizeParams(requestParams);
// Sign the base string
var baseString = this._createSignatureBase(request.method, baseUri, params);
oauthParams.oauth_signature = this._createSignature(baseString);
// Add Authorization header
request.headers.authorization = "OAuth " + Object.keys(oauthParams).map(function(name){
return encodeRfc3986(name) + "=\"" + encodeRfc3986(oauthParams[name]) + "\"";
}).join(",");
// Now the request object can be used to make a signed request
return request;
};
Client.prototype.obtainTempCredentials = function(oauthParams, extraParams){
oauthParams = oauthParams || {};
if(this.callback && !oauthParams.oauth_callback){
oauthParams.oauth_callback = this.callback;
}
return this.request({
method: "POST",
url: this.tempRequestUrl,
oauthParams: oauthParams,
requestParams: extraParams || {}
}).then(parseResponse);
};
Client.prototype.obtainTokenCredentials = function(tokenIdentifier, tokenSecret, verifierToken, extraParams){
return this.bind(tokenIdentifier, tokenSecret).request({
method: "POST",
url: this.tokenRequestUrl,
oauthParams: { oauth_verifier: verifierToken },
requestParams: extraParams
}).then(parseResponse);
};
+94
View File
@@ -0,0 +1,94 @@
/**
* AOP style event handling, for listening for method calls. Very similar to dojo.connect
/* Add a listener for the execution of the given function slot on the given object.
*
* When object[eventName]() is executed the handler is called.
* The optional before parameter can be used to indicate if the listener
* should be fired before or after the default action (default is after)
*/
exports.observe = function(object, eventName, listener, before) {
if(!listener){
throw new Error("No listener provided");
}
if(typeof object.observe === "function"){
// CommonJS observable object
return object.observe(eventName, listener);
}
var listenerProxy = function(that, args){//make sure we have unique function so we can remove it
try{
listener.apply(that, args);
}catch(e){
require("./process").print(e);
}
};
if(typeof object.addListener === "function"){
// NodeJS EventEmitter
object.addListener(eventName, listener);
return {
observe: function(listener){
return exports.observe(object, eventName, listener);
},
emit: function(event){
object.emit(eventName, event);
},
dismiss: function(){
object.removeListener(eventName, listener);
}
};
}
var afters, befores,
main = object[eventName];
if(typeof main != "function"){
main = function(){};
}
if(main._afters){
afters = main._afters;
befores = main._befores;
}
else{
befores = [];
afters = [];
var newFunc = object[eventName] = function(){
for(var i = 0; i < befores.length; i++){
befores[i](this, arguments);
}
try{
return main.apply(this, arguments);
}
finally{
for(var i = 0; i < afters.length; i++){
afters[i](this, arguments);
}
}
};
newFunc._befores = befores;
newFunc._afters = afters;
}
if(before){
befores.push(listenerProxy);
}
else{
afters.push(listenerProxy);
}
return createSignal();
function createSignal(){
var observers;
return {
observe: function(listener){
afters.push(listener);
},
emit: function(){
main.apply(object, arguments);
},
dismiss: function(){
if(before){
befores.splice(befores.indexOf(listenerProxy), 1);
}
else{
afters.splice(afters.indexOf(listenerProxy), 1);
}
}
};
};
};
+86
View File
@@ -0,0 +1,86 @@
{
"_from": "promised-io@^0.3.5",
"_id": "promised-io@0.3.5",
"_inBundle": false,
"_integrity": "sha1-StIXuzZYvKrplGsXqGaOzYUeE1Y=",
"_location": "/promised-io",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "promised-io@^0.3.5",
"name": "promised-io",
"escapedName": "promised-io",
"rawSpec": "^0.3.5",
"saveSpec": null,
"fetchSpec": "^0.3.5"
},
"_requiredBy": [
"/customizr"
],
"_resolved": "https://registry.npmjs.org/promised-io/-/promised-io-0.3.5.tgz",
"_shasum": "4ad217bb3658bcaae9946b17a8668ecd851e1356",
"_spec": "promised-io@^0.3.5",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/customizr",
"author": {
"name": "Kris Zyp"
},
"bugs": {
"url": "https://github.com/kriszyp/promised-io/issues"
},
"bundleDependencies": false,
"contributors": [
{
"name": "Dean Landolt"
},
{
"name": "Nathan Stott",
"email": "nathan.stott@whiteboard-it.com"
},
{
"name": "Mark Wubben",
"email": "mark@novemberborn.net"
},
{
"name": "Vladimir Dronnikov",
"email": "dronnikov@gmail.com"
}
],
"deprecated": false,
"description": "Promise-based IO",
"devDependencies": {
"patr": ">=0.2.6",
"promises-aplus-tests": "*"
},
"directories": {
"lib": "."
},
"homepage": "https://github.com/kriszyp/promised-io#readme",
"keywords": [
"promise",
"io"
],
"licenses": [
{
"type": "AFLv2.1",
"url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L43"
},
{
"type": "BSD",
"url": "http://trac.dojotoolkit.org/browser/dojo/trunk/LICENSE#L13"
}
],
"main": "./promise",
"mappings": {
"patr": "http://github.com/kriszyp/patr/zipball/v0.2.5"
},
"name": "promised-io",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/kriszyp/promised-io.git"
},
"scripts": {
"test": "node tests/"
},
"version": "0.3.5"
}
+23
View File
@@ -0,0 +1,23 @@
({define:typeof define!="undefined"?define:function(factory){factory(require,exports)}}).
define(function(req,exports){
if(typeof console !== "undefined"){
exports.print = function(){
console.log.apply(console, arguments);
}
}
if(typeof process !== "undefined"){
exports.args = process.argv;
exports.env = process.env;
exports.print = console.log;
exports.dir = console.dir;
}
else if(typeof navigator === "undefined"){
try{
exports.args = req("" + "system").args;
exports.env = req("" + "system").env;
}catch(e){
// in raw rhino, we don't even have system
}
exports.print = print;
}
});
+730
View File
@@ -0,0 +1,730 @@
(function(define){
define(function(require,exports){
// Kris Zyp
// this is based on the CommonJS spec for promises:
// http://wiki.commonjs.org/wiki/Promises
// Includes convenience functions for promises, much of this is taken from Tyler Close's ref_send
// and Kris Kowal's work on promises.
// // MIT License
// A typical usage:
// A default Promise constructor can be used to create a self-resolving deferred/promise:
// var Promise = require("promise").Promise;
// var promise = new Promise();
// asyncOperation(function(){
// Promise.resolve("succesful result");
// });
// promise -> given to the consumer
//
// A consumer can use the promise
// promise.then(function(result){
// ... when the action is complete this is executed ...
// },
// function(error){
// ... executed when the promise fails
// });
//
// Alternately, a provider can create a deferred and resolve it when it completes an action.
// The deferred object a promise object that provides a separation of consumer and producer to protect
// promises from being fulfilled by untrusted code.
// var defer = require("promise").defer;
// var deferred = defer();
// asyncOperation(function(){
// deferred.resolve("succesful result");
// });
// deferred.promise -> given to the consumer
//
// Another way that a consumer can use the promise (using promise.then is also allowed)
// var when = require("promise").when;
// when(promise,function(result){
// ... when the action is complete this is executed ...
// },
// function(error){
// ... executed when the promise fails
// });
exports.errorTimeout = 100;
var freeze = Object.freeze || function(){};
/**
* Default constructor that creates a self-resolving Promise. Not all promise implementations
* need to use this constructor.
*/
var Promise = function(canceller){
};
/**
* Promise implementations must provide a "then" function.
*/
Promise.prototype.then = function(resolvedCallback, errorCallback, progressCallback){
throw new TypeError("The Promise base class is abstract, this function must be implemented by the Promise implementation");
};
/**
* If an implementation of a promise supports a concurrency model that allows
* execution to block until the promise is resolved, the wait function may be
* added.
*/
/**
* If an implementation of a promise can be cancelled, it may add this function
*/
// Promise.prototype.cancel = function(){
// };
Promise.prototype.get = function(propertyName){
return this.then(function(value){
return value[propertyName];
});
};
Promise.prototype.put = function(propertyName, value){
return this.then(function(object){
return object[propertyName] = value;
});
};
Promise.prototype.call = function(functionName /*, args */){
var fnArgs = Array.prototype.slice.call(arguments, 1);
return this.then(function(value){
return value[functionName].apply(value, fnArgs);
});
};
/**
* This can be used to conviently resolve a promise with auto-handling of errors:
* setTimeout(deferred.resolverCallback(function(){
* return doSomething();
* }), 100);
*/
Promise.prototype.resolverCallback = function(callback){
var self = this;
return function(){
try{
self.resolve(callback());
}catch(e){
self.reject(e);
}
}
};
/** Dojo/NodeJS methods*/
Promise.prototype.addCallback = function(callback){
return this.then(callback);
};
Promise.prototype.addErrback = function(errback){
return this.then(function(){}, errback);
};
/*Dojo methods*/
Promise.prototype.addBoth = function(callback){
return this.then(callback, callback);
};
Promise.prototype.addCallbacks = function(callback, errback){
return this.then(callback, errback);
};
/*NodeJS method*/
Promise.prototype.wait = function(){
return exports.wait(this);
};
Deferred.prototype = Promise.prototype;
// A deferred provides an API for creating and resolving a promise.
exports.Promise = exports.Deferred = exports.defer = defer;
function defer(canceller){
return new Deferred(canceller);
}
// currentContext can be set to other values
// and mirrors the global. We need to go off the global in case of multiple instances
// of this module, which isn't rare with NPM's package policy.
Object.defineProperty && Object.defineProperty(exports, "currentContext", {
set: function(value){
currentContext = value;
},
get: function(){
return currentContext;
}
});
exports.currentContext = null;
function Deferred(canceller){
var result, finished, isError, waiting = [], handled;
var promise = this.promise = new Promise();
var context = exports.currentContext;
function notifyAll(value){
var previousContext = exports.currentContext;
if(finished){
throw new Error("This deferred has already been resolved");
}
try{
if(previousContext !== context){
if(previousContext && previousContext.suspend){
previousContext.suspend();
}
exports.currentContext = context;
if(context && context.resume){
context.resume();
}
}
result = value;
finished = true;
for(var i = 0; i < waiting.length; i++){
notify(waiting[i]);
}
}
finally{
if(previousContext !== context){
if(context && context.suspend){
context.suspend();
}
if(previousContext && previousContext.resume){
previousContext.resume();
}
exports.currentContext = previousContext;
}
}
}
function notify(listener){
var func = (isError ? listener.error : listener.resolved);
if(func){
handled ?
(handled.handled = true) : (handled = true);
try{
var newResult = func(result);
if(newResult && typeof newResult.then === "function"){
newResult.then(listener.deferred.resolve, listener.deferred.reject);
return;
}
listener.deferred.resolve(newResult);
}
catch(e){
listener.deferred.reject(e);
}
}
else{
if(isError){
listener.deferred.reject(result, typeof handled === "object" ? handled : (handled = {}));
}
else{
listener.deferred.resolve.call(listener.deferred, result);
}
}
}
// calling resolve will resolve the promise
this.resolve = this.callback = this.emitSuccess = function(value){
notifyAll(value);
};
// calling error will indicate that the promise failed
var reject = this.reject = this.errback = this.emitError = function(error, handledObject){
if (typeof handledObject == "object") {
if (handled) {
handledObject.handled = true;
} else {
handled = handledObject;
}
}
isError = true;
notifyAll(error);
if (!handledObject && typeof setTimeout !== "undefined") {
if (!(typeof handled == "object" ? handled.handled : handled)) {
// set the time out if it has not already been handled
setTimeout(function () {
if (!(typeof handled == "object" ? handled.handled : handled)) {
throw error;
}
}, exports.errorTimeout);
}
}
return handled;
};
// call progress to provide updates on the progress on the completion of the promise
this.progress = function(update){
for(var i = 0; i < waiting.length; i++){
var progress = waiting[i].progress;
progress && progress(update);
}
}
// provide the implementation of the promise
this.then = promise.then = function(resolvedCallback, errorCallback, progressCallback){
var returnDeferred = new Deferred(promise.cancel);
var listener = {resolved: resolvedCallback, error: errorCallback, progress: progressCallback, deferred: returnDeferred};
if(finished){
notify(listener);
}
else{
waiting.push(listener);
}
return returnDeferred.promise;
};
var timeout;
if(typeof setTimeout !== "undefined") {
this.timeout = function (ms) {
if (ms === undefined) {
return timeout;
}
timeout = ms;
setTimeout(function () {
if (!finished) {
if (promise.cancel) {
promise.cancel(new Error("timeout"));
}
else {
reject(new Error("timeout"));
}
}
}, ms);
return promise;
};
}
if(canceller){
this.cancel = promise.cancel = function(){
var error = canceller();
if(!(error instanceof Error)){
error = new Error(error);
}
reject(error);
}
}
freeze(promise);
};
function perform(value, async, sync){
try{
if(value && typeof value.then === "function"){
value = async(value);
}
else{
value = sync(value);
}
if(value && typeof value.then === "function"){
return value;
}
var deferred = new Deferred();
deferred.resolve(value);
return deferred.promise;
}catch(e){
var deferred = new Deferred();
deferred.reject(e);
return deferred.promise;
}
}
/**
* Promise manager to make it easier to consume promises
*/
function rethrow(err){ throw err; }
/**
* Registers an observer on a promise, always returning a promise
* @param value promise or value to observe
* @param resolvedCallback function to be called with the resolved value
* @param rejectCallback function to be called with the rejection reason
* @param progressCallback function to be called when progress is made
* @return promise for the return value from the invoked callback
*/
exports.whenPromise = function(value, resolvedCallback, rejectCallback, progressCallback){
var deferred = defer();
if(value && typeof value.then === "function"){
value.then(function(next){
deferred.resolve(next);
},function(error){
deferred.reject(error);
});
rejectCallback = rejectCallback || rethrow;
}else{
deferred.resolve(value);
}
return deferred.promise.then(resolvedCallback, rejectCallback, progressCallback);
};
/**
* Registers an observer on a promise.
* @param value promise or value to observe
* @param resolvedCallback function to be called with the resolved value
* @param rejectCallback function to be called with the rejection reason
* @param progressCallback function to be called when progress is made
* @return promise for the return value from the invoked callback or the value if it
* is a non-promise value
*/
exports.when = function(value, resolvedCallback, rejectCallback, progressCallback){
if(value && typeof value.then === "function"){
if(value instanceof Promise){
return value.then(resolvedCallback, rejectCallback, progressCallback);
}
else{
return exports.whenPromise(value, resolvedCallback, rejectCallback, progressCallback);
}
}
return resolvedCallback ? resolvedCallback(value) : value;
};
/**
* This is convenience function for catching synchronously and asynchronously thrown
* errors. This is used like when() except you execute the initial action in a callback:
* whenCall(function(){
* return doSomethingThatMayReturnAPromise();
* }, successHandler, errorHandler);
*/
exports.whenCall = function(initialCallback, resolvedCallback, rejectCallback, progressCallback){
try{
return exports.when(initialCallback(), resolvedCallback, rejectCallback, progressCallback);
}catch(e){
return rejectCallback(e);
}
}
/**
* Gets the value of a property in a future turn.
* @param target promise or value for target object
* @param property name of property to get
* @return promise for the property value
*/
exports.get = function(target, property){
return perform(target, function(target){
return target.get(property);
},
function(target){
return target[property]
});
};
/**
* Invokes a method in a future turn.
* @param target promise or value for target object
* @param methodName name of method to invoke
* @param args array of invocation arguments
* @return promise for the return value
*/
exports.call = function(target, methodName, args){
return perform(target, function(target){
return target.call(methodName, args);
},
function(target){
return target[methodName].apply(target, args);
});
};
/**
* Sets the value of a property in a future turn.
* @param target promise or value for target object
* @param property name of property to set
* @param value new value of property
* @return promise for the return value
*/
exports.put = function(target, property, value){
return perform(target, function(target){
return target.put(property, value);
},
function(target){
return target[property] = value;
});
};
/**
* Waits for the given promise to finish, blocking (and executing other events)
* if necessary to wait for the promise to finish. If target is not a promise
* it will return the target immediately. If the promise results in an reject,
* that reject will be thrown.
* @param target promise or value to wait for.
* @return the value of the promise;
*/
var queue;
//try {
// queue = require("event-loop");
//}
//catch (e) {}
exports.wait = function(target){
if(!queue){
throw new Error("Can not wait, the event-queue module is not available");
}
if(target && typeof target.then === "function"){
var isFinished, isError, result;
target.then(function(value){
isFinished = true;
result = value;
},
function(error){
isFinished = true;
isError = true;
result = error;
});
while(!isFinished){
queue.processNextEvent(true);
}
if(isError){
throw result;
}
return result;
}
else{
return target;
}
};
/**
* Takes an array of promises and returns a promise that is fulfilled once all
* the promises in the array are fulfilled
* @param array The array of promises
* @return the promise that is fulfilled when all the array is fulfilled, resolved to the array of results
*/
exports.all = function(array){
var deferred = new Deferred();
if(Object.prototype.toString.call(array) !== '[object Array]'){
array = Array.prototype.slice.call(arguments);
}
var fulfilled = 0, length = array.length, rejected = false;
var results = [];
if (length === 0) deferred.resolve(results);
else {
array.forEach(function(promise, index){
exports.when(promise,
function(value){
results[index] = value;
fulfilled++;
if(fulfilled === length){
deferred.resolve(results);
}
},
function(error){
if(!rejected){
deferred.reject(error);
}
rejected = true;
});
});
}
return deferred.promise;
};
/**
* Takes a hash of promises and returns a promise that is fulfilled once all
* the promises in the hash keys are fulfilled
* @param hash The hash of promises
* @return the promise that is fulfilled when all the hash keys is fulfilled, resolved to the hash of results
*/
exports.allKeys = function(hash){
var deferred = new Deferred();
var array = Object.keys(hash);
var fulfilled = 0, length = array.length;
var results = {};
if (length === 0) deferred.resolve(results);
else {
array.forEach(function(key){
exports.when(hash[key],
function(value){
results[key] = value;
fulfilled++;
if(fulfilled === length){
deferred.resolve(results);
}
},
deferred.reject);
});
}
return deferred.promise;
};
/**
* Takes an array of promises and returns a promise that is fulfilled when the first
* promise in the array of promises is fulfilled
* @param array The array of promises
* @return a promise that is fulfilled with the value of the value of first promise to be fulfilled
*/
exports.first = function(array){
var deferred = new Deferred();
if(Object.prototype.toString.call(array) !== '[object Array]'){
array = Array.prototype.slice.call(arguments);
}
var fulfilled;
array.forEach(function(promise, index){
exports.when(promise, function(value){
if (!fulfilled) {
fulfilled = true;
deferred.resolve(value);
}
},
function(error){
if (!fulfilled) {
fulfilled = true;
deferred.resolve(error);
}
});
});
return deferred.promise;
};
/**
* Takes an array of asynchronous functions (that return promises) and
* executes them sequentially. Each funtion is called with the return value of the last function
* @param array The array of function
* @param startingValue The value to pass to the first function
* @return the value returned from the last function
*/
exports.seq = function(array, startingValue){
array = array.concat(); // make a copy
var deferred = new Deferred();
function next(value){
var nextAction = array.shift();
if(nextAction){
exports.when(nextAction(value), next, function(error){
deferred.reject(error, true);
});
}
else {
deferred.resolve(value);
}
}
next(startingValue);
return deferred.promise;
};
/**
* Delays for a given amount of time and then fulfills the returned promise.
* @param milliseconds The number of milliseconds to delay
* @return A promise that will be fulfilled after the delay
*/
if(typeof setTimeout !== "undefined") {
exports.delay = function(milliseconds) {
var deferred = new Deferred();
setTimeout(function(){
deferred.resolve();
}, milliseconds);
return deferred.promise;
};
}
/**
* Runs a function that takes a callback, but returns a Promise instead.
* @param func node compatible async function which takes a callback as its last argument
* @return promise for the return value from the callback from the function
*/
exports.execute = function(asyncFunction){
var args = Array.prototype.slice.call(arguments, 1);
var deferred = new Deferred();
args.push(function(error, result){
if(error) {
deferred.emitError(error);
}
else {
if(arguments.length > 2){
// if there are multiple success values, we return an array
Array.prototype.shift.call(arguments, 1);
deferred.emitSuccess(arguments);
}
else{
deferred.emitSuccess(result);
}
}
});
asyncFunction.apply(this, args);
return deferred.promise;
};
function isGeneratorFunction(obj){
return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
}
/**
* Promise-based coroutine trampoline
* Adapted from https://github.com/deanlandolt/copromise/blob/master/copromise.js
*/
function run(coroutine){
var deferred = defer();
(function next(value, exception) {
var result;
try {
result = exception ? coroutine.throw(value) : coroutine.next(value);
}
catch (error) {
return deferred.reject(error);
}
if (result.done) return deferred.resolve(result.value);
exports.when(result.value, next, function(error) {
next(error, true);
});
})();
return deferred.promise;
};
/**
* Creates a task from a coroutine, provided as generator. The `yield` function can be provided
* a promise (or any value) to wait on, and the value will be provided when the promise resolves.
* @param coroutine generator or generator function to treat as a coroutine
* @return promise for the return value from the coroutine
*/
exports.spawn = function(coroutine){
if (isGeneratorFunction(coroutine)) {
coroutine = coroutine();
}
return run(coroutine);
}
/**
* Converts a Node async function to a promise returning function
* @param func node compatible async function which takes a callback as its last argument
* @return A function that returns a promise
*/
exports.convertNodeAsyncFunction = function(asyncFunction, callbackNotDeclared){
var arity = asyncFunction.length;
return function(){
var deferred = new Deferred();
if(callbackNotDeclared){
arity = arguments.length + 1;
}
arguments.length = arity;
arguments[arity - 1] = function(error, result){
if(error) {
deferred.emitError(error);
}
else {
if(arguments.length > 2){
// if there are multiple success values, we return an array
Array.prototype.shift.call(arguments, 1);
deferred.emitSuccess(arguments);
}
else{
deferred.emitSuccess(result);
}
}
};
asyncFunction.apply(this, arguments);
return deferred.promise;
};
};
/**
* Returns a promise. If the object is already a Promise it is returned; otherwise
* the object is wrapped in a Promise.
* @param value The value to be treated as a Promise
* @return A promise wrapping the original value
*/
exports.as = function(value){
if (value instanceof Promise) {
return value;
} else {
var ret = defer();
ret.resolve(value);
return ret;
}
};
});
})(typeof define!="undefined"?define:function(factory){factory(require,exports)});
+128
View File
@@ -0,0 +1,128 @@
// Query String Utilities
// Taken from Jack
var DEFAULT_SEP = "&";
var DEFAULT_EQ = "=";
exports.unescape = function (str, decodeSpaces) {
return decodeURIComponent(decodeSpaces ? str.replace(/\+/g, " ") : str);
};
exports.escape = function (str) {
return encodeURIComponent(str);
};
exports.toQueryString = function (obj, sep, eq, name) {
sep = sep || DEFAULT_SEP;
eq = eq || DEFAULT_EQ;
if (isA(obj, null) || isA(obj, undefined)) {
return name ? encodeURIComponent(name) + eq : '';
}
if (isNumber(obj) || isString(obj)) {
return encodeURIComponent(name) + eq + encodeURIComponent(obj);
}
if (isA(obj, [])) {
var s = [];
name = name+'[]';
for (var i = 0, l = obj.length; i < l; i ++) {
s.push( exports.toQueryString(obj[i], sep, eq, name) );
}
return s.join(sep);
}
// now we know it's an object.
var s = [];
var begin = name ? name + '[' : '';
var end = name ? ']' : '';
for (var i in obj) if (obj.hasOwnProperty(i)) {
var n = begin + i + end;
s.push(exports.toQueryString(obj[i], sep, eq, n));
}
return s.join(sep);
};
exports.parseQuery = function(qs, sep, eq) {
return qs
.split(sep||DEFAULT_SEP)
.map(pieceParser(eq||DEFAULT_EQ))
.reduce(mergeParams);
};
// Parse a key=val string.
// These can get pretty hairy
// example flow:
// parse(foo[bar][][bla]=baz)
// return parse(foo[bar][][bla],"baz")
// return parse(foo[bar][], {bla : "baz"})
// return parse(foo[bar], [{bla:"baz"}])
// return parse(foo, {bar:[{bla:"baz"}]})
// return {foo:{bar:[{bla:"baz"}]}}
var pieceParser = function (eq) {
return function parsePiece (key, val) {
if (arguments.length !== 2) {
// key=val, called from the map/reduce
key = key.split(eq);
return parsePiece(
exports.unescape(key.shift(), true), exports.unescape(key.join(eq), true)
);
}
key = key.replace(/^\s+|\s+$/g, '');
if (isString(val)) val = val.replace(/^\s+|\s+$/g, '');
var sliced = /(.*)\[([^\]]*)\]$/.exec(key);
if (!sliced) {
var ret = {};
if (key) ret[key] = val;
return ret;
}
// ["foo[][bar][][baz]", "foo[][bar][]", "baz"]
var tail = sliced[2], head = sliced[1];
// array: key[]=val
if (!tail) return parsePiece(head, [val]);
// obj: key[subkey]=val
var ret = {};
ret[tail] = val;
return parsePiece(head, ret);
};
};
// the reducer function that merges each query piece together into one set of params
function mergeParams (params, addition) {
return (
// if it's uncontested, then just return the addition.
(!params) ? addition
// if the existing value is an array, then concat it.
: (isA(params, [])) ? params.concat(addition)
// if the existing value is not an array, arrayify it.
: (!isA(params, {}) || !isA(addition, {})) ? [params].concat(addition)
// else merge them as objects, which is a little more complex
: mergeObjects(params, addition)
)
};
// Merge two *objects* together. If this is called, we've already ruled
// out the simple cases, and need to do the for-in business.
function mergeObjects (params, addition) {
for (var i in addition) if (i && addition.hasOwnProperty(i)) {
params[i] = mergeParams(params[i], addition[i]);
}
return params;
};
// duck typing
function isA (thing, canon) {
return (
// truthiness. you can feel it in your gut.
(!thing === !canon)
// typeof is usually "object"
&& typeof(thing) === typeof(canon)
// check the constructor
&& Object.prototype.toString.call(thing) === Object.prototype.toString.call(canon)
);
};
function isNumber (thing) {
return typeof(thing) === "number" && isFinite(thing);
};
function isString (thing) {
return typeof(thing) === "string";
};
+42
View File
@@ -0,0 +1,42 @@
'use strict';
(function(define){
define(function(require,exports){
// Vladimir Dronnikov
// inspired by creationix's Step
var when = require('./promise').when;
// execute sequentially functions taken from steps[]
// each successive is fed with the result of prior
// each function can return an immediate value, a promise, or just throw
// in the latter case the next function will receive Error object
// return "undefined" to full stop.
//
// "context" is available to each steps as "this"
//
exports.Step = function(context, steps) {
var next;
next = function() {
var fn, result;
if (!steps.length) {
return arguments[0];
}
fn = steps.shift();
try {
result = fn.apply(context, arguments);
if (result !== void 0) {
result = when(result, next, next);
}
} catch (err) {
next(err);
}
return result;
};
return next();
};
});
})(typeof define!="undefined"?define:function(factory){factory(require,exports)});
+13
View File
@@ -0,0 +1,13 @@
exports.testOAuth = require('./oauth');
exports.testPromise = require('./promise');
exports.testQuerystring = require('./querystring');
// test spawning coroutines if generators are supported on platform
try {
eval("(function* (){})()");
exports.testPromiseSpawn = require('./promise-spawn');
}
catch (err) {}
if (require.main === module)
require("patr/runner").run(exports);
+114
View File
@@ -0,0 +1,114 @@
var assert = require("assert");
var oauth = require("../oauth");
var querystring = require("../util/querystring");
exports.testGeneratingSignatureBaseString = function(){
// base string described in <http://oauth.net/core/1.0/#sig_base_example>
var client = new oauth.Client;
var result = client._createSignatureBase("GET", "http://photos.example.net/photos",
"file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original");
assert.equal(result, "GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal");
};
exports.testNormalizingUrl = function(){
var client = new oauth.Client;
// default ports should be stripped
assert.equal(client._normalizeUrl({ protocol: "https:", hostname: "somehost.com", port: "443", pathname: "/foo/bar" }), "https://somehost.com/foo/bar");
assert.equal(client._normalizeUrl({ protocol: "http:", hostname: "somehost.com", port: "80", pathname: "/foo/bar" }), "http://somehost.com/foo/bar");
// should leave non-default ports from URLs for use in signature generation
assert.equal(client._normalizeUrl({ protocol: "https:", hostname: "somehost.com", port: "446", pathname: "/foo/bar" }), "https://somehost.com:446/foo/bar");
assert.equal(client._normalizeUrl({ protocol: "http:", hostname: "somehost.com", port: "81", pathname: "/foo/bar" }), "http://somehost.com:81/foo/bar");
};
exports.testNormalizingRequestParams = function(){
var client = new oauth.Client;
// ordered by name
assert.equal(client._normalizeParams(["z", "a", "a", "b", "1", "c"]), "1=c&a=b&z=a");
// if two parameter names are the same then order by the value
assert.equal(client._normalizeParams(["z", "b", "z", "a", "1", "c"]), "1=c&z=a&z=b");
// resulting parameters should be encoded and ordered as per <http://tools.ietf.org/html/rfc5849#section-3.1> (3.4.1.3.2)
var requestParams = [];
querystring.parseToArray(requestParams, querystring.stringify({
"b5" : "=%3D",
"c@": "",
"a2": "r b",
"oauth_consumer_key": "9djdj82h48djs9d2",
"oauth_token":"kkk9d7dh3k39sjv7",
"oauth_signature_method": "HMAC-SHA1",
"oauth_timestamp": "137131201",
"oauth_nonce": "7d8f3e4a",
"c2" : ""
}));
querystring.addToArray(requestParams, "a3", "a");
querystring.addToArray(requestParams, "a3", "2 q");
assert.equal(client._normalizeParams(requestParams), "a2=r%20b&a3=2%20q&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9djdj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7");
};
function generateClient() {
var client = new oauth.Client("consumerkey", "consumersecret", null, null, null, "1.0", null, function(){ return "ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp"; });
oauth.Client.getTimestamp = function(){ return "1272399856"; };
return client;
}
exports.testSigningUrl = {
"test without token": function(){
var client = generateClient();
// provide a valid signature when no token is present
var requestParams = ["bar", "foo"];
var oauthParams = client._collectOAuthParams({}, requestParams);
var params = client._normalizeParams(requestParams);
var baseString = client._createSignatureBase("GET", "http://somehost.com:3323/foo/poop", params);
var signature = client._createSignature(baseString);
assert.equal(signature, "7ytO8vPSLut2GzHjU9pn1SV9xjc=");
},
"test with token": function(){
var client = generateClient();
// provide a valid signature when a token is present
var bound = client.bind("token", "");
var requestParams = ["bar", "foo"];
var oauthParams = bound._collectOAuthParams({}, requestParams);
var params = bound._normalizeParams(requestParams);
var baseString = bound._createSignatureBase("GET", "http://somehost.com:3323/foo/poop", params);
var signature = bound._createSignature(baseString);
assert.equal(oauthParams.oauth_token, "token");
assert.equal(signature, "9LwCuCWw5sURtpMroIolU3YwsdI=");
},
"test with token and secret": function(){
var client = generateClient();
// provide a valid signature when a token and a token secret are present
var bound = client.bind("token", "tokensecret");
var requestParams = ["bar", "foo"];
var oauthParams = bound._collectOAuthParams({}, requestParams);
var params = bound._normalizeParams(requestParams);
var baseString = bound._createSignatureBase("GET", "http://somehost.com:3323/foo/poop", params);
var signature = bound._createSignature(baseString);
assert.equal(signature, "zeOR0Wsm6EG6XSg0Vw/sbpoSib8=");
}
};
exports.testBuildingAuthHeader = function(){
var client = generateClient();
// all provided OAuth arguments should be concatenated correctly
var request = client._signRequest({
method: "GET",
protocol: "http:",
hostname: "somehost.com",
port: "3323",
pathname: "/foo/poop",
headers: {}
}, ["bar", "foo"]);
assert.equal(request.headers.authorization, 'OAuth oauth_consumer_key="consumerkey",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1272399856",oauth_nonce="ybHPeOEkAUJ3k2wJT9Xb43MjtSgTvKqp",oauth_version="1.0",oauth_signature="7ytO8vPSLut2GzHjU9pn1SV9xjc%3D"');
};
if (require.main === module)
require("patr/runner").run(exports);
+56
View File
@@ -0,0 +1,56 @@
var assert = require("assert");
var promise = require("../promise");
exports.testResolved = function (){
return promise.spawn(function* (){
yield promise.delay(100);
// can yield non-promise values
var foo = yield 'foo';
assert.ok(foo === 'foo');
var result = yield promise.delay(50).then(function () {
return foo;
});
var error = new Error('Boom!');
var start = new Date();
try {
yield promise.delay(100).then(function () {
throw error;
});
// Should never come here
assert.ok(true === false);
}
catch (e) {
assert.ok(e === error);
assert.ok(new Date() - start >= 100);
}
return result;
}).then(function (value) {
assert.ok(value === 'foo');
return true;
}, function (e) {
// Should never come here
assert.ok(true === false);
});
};
exports.testRejected = function (){
var error = new Error('Boom!');
return promise.spawn(function* (){
var start = new Date();
yield promise.delay(100).then(function () {
assert.ok(new Date() - start >= 100);
throw error;
});
// Should never come here
assert.ok(true === false);
}).then(function () {
// Should never come here
assert.ok(true === false);
}, function (e) {
assert.ok(e === error);
return true;
});
};
if (require.main === module)
require("patr/runner").run(exports);
+138
View File
@@ -0,0 +1,138 @@
var assert = require("assert"),
all = require("../promise").all,
when = require("../promise").when,
whenPromise = require("../promise").whenPromise,
defer = require("../promise").defer,
Promise = require("../promise"),
Step = require("../step").Step;
exports.testSpeedPlainValue = function(){
for(var i = 0; i < 1000; i++){
when(3, function(){
});
}
};
exports.testSpeedPromise = function(){
var deferred = defer();
for(var i = 0; i < 1000; i++){
when(deferred.promise, function(){
});
}
deferred.resolve(3);
};
exports.testWhenPromiseRejectHandled = function(){
// The inner whenPromise doesn't have a rejectCallback, but the outer one does.
// This means the error then *is* handled, but the inner whenPromise doesn't know about that.
// This shouldn't result in an uncaught exception thrown by the promise library.
whenPromise(true, function(){
return whenPromise((function(){
var deferred = defer();
deferred.reject({});
return deferred.promise;
})());
}).then(null, function(){});
};
exports.testMultipleReject = function(){
all(delayedFail(25), delayedFail(50), delayedSuccess(75)).then(function () {
throw new Error('There should be no success here.');
}, function () {
// This is where we want to end up, once only.
});
};
function delayedSuccess(delay) {
var deferred = defer();
setTimeout(function () {
deferred.resolve();
}, delay);
return deferred.promise;
}
function delayedFail(delay) {
var deferred = defer();
setTimeout(function () {
deferred.reject();
}, delay);
return deferred.promise;
}
function veryDeferred(){
var deferred = defer();
setTimeout(function(){
deferred.resolve(true);
}, 100);
return deferred.promise;
}
function veryDeferred(){
var deferred = defer();
setTimeout(function(){
deferred.resolve(true);
}, 100);
return deferred.promise;
}
exports.testStep = function(){
var deferred = defer();
Step({foo: 'bar'}, [
function(){
console.log('S1');
assert.ok(this.foo === 'bar');
return false;
},
function(result){
console.log('S2');
assert.ok(result === false);
this.foo = 'baz';
return veryDeferred();
},
function(result){
console.log('S3');
assert.ok(this.foo === 'baz');
assert.ok(result === true);
throw Error('Catchme!');
},
function(result){
console.log('S4');
assert.ok(result instanceof Error);
assert.ok(result.message === 'Catchme!');
deferred.resolve(true);
// return undefined;
},
function(result){
console.log('S5', result);
// Should never come here
deferred.reject(false);
assert.ok(true === false);
},
]);
return deferred.promise;
};
exports.testAsValue = function (){
var deferred = Promise.as("supercala");
deferred.then(function (res) {
assert.ok(res == "supercala");
}, function () {
throw new Error("Unexpected error");
});
};
exports.testAsPromise = function (){
var temp = defer();
temp.resolve("supercala");
var deferred = Promise.as(temp);
assert.ok(temp === deferred);
deferred.then(function (res) {
assert.ok(res == "supercala");
}, function () {
throw new Error("Unexpected error");
});
};
if (require.main === module)
require("patr/runner").run(exports);
+44
View File
@@ -0,0 +1,44 @@
var lib = require("../promise");
var promisesAplusTests = require("promises-aplus-tests");
exports.baseAdapter = {
resolved: Promise.resolve,
rejected: Promise.reject,
deferred: function () {
var resolver, rejecter;
var promise = new Promise(function (resolve, reject) {
resolver = resolve;
rejecter = reject;
});
return {
promise: promise,
resolve: resolver,
reject: rejecter
};
}
};
exports.libAdapter = {
resolved: function (value) {
var deferred = lib.defer();
deferred.resolve(value);
return deferred.promise;
},
rejected: function (reason) {
var deferred = lib.defer();
deferred.reject(reason);
return deferred.promise;
},
deferred: function () {
return lib.defer();
}
}
function run(adapter, callback) {
promisesAplusTests(adapter, callback);
}
if (require.main === module) {
// run(exports.baseAdapter, function () {});
run(exports.libAdapter, function () {});
}
+26
View File
@@ -0,0 +1,26 @@
var assert = require("assert");
var querystring = require("../util/querystring");
exports.testMungingArrayParams = function(){
var arr = ["bar", "baz"];
var requestParams = [];
querystring.addToArray(requestParams, "foo", arr);
assert.deepEqual(requestParams, ["foo[]", "bar", "foo[]", "baz"]);
};
exports.testMungingObjectParams = function(){
var obj = { "key": "value" };
var requestParams = [];
querystring.addToArray(requestParams, "obj", obj);
assert.deepEqual(requestParams, ["obj[key]", "value"]);
};
exports.testParsingMungedQuerystring = function(){
var qs = "foo=bar&foo=baz&obj[key]=value";
var requestParams = [];
querystring.parseToArray(requestParams, qs);
assert.deepEqual(requestParams, ["foo[]", "bar", "foo[]", "baz", "obj[key]", "value"]);
};
if (require.main === module)
require("patr/runner").run(exports);
+38
View File
@@ -0,0 +1,38 @@
var querystring = require("querystring");
for(var i in querystring){
exports[i] = querystring[i];
}
var type = Function.prototype.call.bind(Object.prototype.toString);
// Parse the name/value pairs of the query string into a flattened array
// Automatically munge the parameters
exports.parseToArray = function(arr, qs){
var parsed = exports.parse(qs);
for(var i in parsed){
exports.addToArray(arr, i, parsed[i]);
}
};
// Add munged values with name/value pairs to the flattened array
exports.addToArray = function(arr, name, value){
if(value === undefined || value === null){
value = "";
}
switch(type(value)){
case "[object String]":
case "[object Number]":
case "[object Boolean]":
arr.push(name, value + "");
break;
case "[object Array]":
value.forEach(function(value){
exports.addToArray(arr, name + "[]", value);
});
break;
case "[object Object]":
for(var k in value){
exports.addToArray(arr, name + "[" + k + "]", value[k]);
}
};
};
+38
View File
@@ -0,0 +1,38 @@
// Adapted from the following:
//
// Original License
// ----------------
// parseUri 1.2.2
// (c) Steven Levithan <stevenlevithan.com>
// MIT License
function parseUri (str) {
var o = parseUri.options,
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
uri = {},
i = 14;
while (i--) uri[o.key[i]] = m[i] || "";
uri[o.q.name] = {};
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
if ($1) uri[o.q.name][$1] = $2;
});
return uri;
};
parseUri.options = {
strictMode: false,
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
q: {
name: "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
},
parser: {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
}
};
exports.parseUri = parseUri;