<commit_before>/*
Copyright 2011 Timothy J Fontaine <tjfontaine@gmail.com>

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

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

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

*/

"use strict";

var dgram = require('dgram');

var consts = require('./consts');
var types = require('./types');
var Question = require('./question');
var Response = require('./response');

var random_integer = function () {
  return Math.floor(Math.random() * 50000 + 1);
};

var remote_dns = {
  address: '8.8.8.8',
  port: 53,
};

var PendingRequests = function () {
  this.active_ = {};
  this.active_.count = 0;
  this._socket4 = undefined;
  this._socket6 = undefined;
};

PendingRequests.prototype.add = function (request) {
  var id = random_integer();
  while (this.active_[id] !== undefined) {
    id = random_integer();
  }
  request.id = id;
  this.active_[id] = request;
  this.active_.count++;
};

PendingRequests.prototype.remove = function (request) {
  delete this.active_[request.id];
  this.active_.count--;

  if (this.active_.count === 0) {
    if (this._socket4) {
      this._socket4.close();
      this._socket4 = undefined;
    }
  }
};

PendingRequests.prototype.getSocket = function () {
  if (!this._socket4) {
    this._socket4 = dgram.createSocket('udp4');
    this._socket4.bind();
    var self = this;
    this._socket4.on('message', function (msg, remote) {
      self.handleMessage(msg, remote);
    });
  }

  return this._socket4;
};

PendingRequests.prototype.handleMessage = function (msg, remote) {
  var err, request,
    answer = new Response(this.getSocket(), remote);

  answer.unpack(msg);
  if (this.active_[answer.header.id]) {
    request = this.active_[answer.header.id];
    request.handle(err, answer);
  }
};

var IN_FLIGHT = new PendingRequests();

var Request = function (question, callback) {
  this.question = question;
  this.callback = callback;
  IN_FLIGHT.add(this);
  this.start();
};

Request.prototype.remove = function () {
  IN_FLIGHT.remove(this);
};

Request.prototype.cancel = function () {
  this.remove();
  console.log("Request ID:", this.id, "cancelled");
};

Request.prototype.start = function () {
  this.started = new Date().getTime();
  this.message = new Response(IN_FLIGHT.getSocket(), remote_dns);
  this.message.header.id = this.id;
  this.message.header.rd = 1;
  this.message.question.push(this.question);
  this.message.send();
};

Request.prototype.handle = function (err, answer) {
  this.ended = new Date().getTime();
  console.log("Request ID:", this.id, "round tripped in:", this.ended - this.started);
  this.remove();
  this.callback(err, answer);
};

var inner_resolve = function (domain, rrtype, callback) {
  var question = new Question();
  question.name = domain;
  question.type = rrtype;
  question.class = consts.NAME_TO_QCLASS.IN;

  return new Request(question, function (err, response) {
    callback(err, response);
  });
};

var resolve = function (domain) {
  var rrtype = consts.NAME_TO_QTYPE.A,
    callback = arguments[arguments.length - 1];

  if (arguments.length === 3) {
    rrtype = consts.NAME_TO_QTYPE[arguments[1]];
  }

  return inner_resolve(domain, rrtype, function (err, response) {
    var ret = [], i, a;
    switch (rrtype) {
    case consts.NAME_TO_QTYPE.A:
    case consts.NAME_TO_QTYPE.AAAA:
      for (i = 0; i < response.answer.length; i++) {
        a = response.answer[i];
        if (a.type === rrtype) {
          ret.push(a.promote().address);
        }
      }
      break;
    }
    callback(err, ret);
  });
};
exports.resolve = resolve;

var resolve4 = function (domain, callback) {
  return resolve(domain, 'A', function (err, results) {
    callback(err, results);
  });
};
exports.resolve4 = resolve4;

var resolve6 = function (domain, callback) {
  return resolve(domain, 'AAAA', function (err, results) {
    callback(err, results);
  });
};
exports.resolve6 = resolve6;

var lookup = function (domain) {
  var callback = arguments[arguments.length - 1],
    family = 4,
    rrtype;

  if (arguments.length === 3) {
    family = arguments.length[1];
  }

  rrtype = consts.FAMILY_TO_QTYPE[family];

  return inner_resolve(domain, rrtype, function (err, response) {
    var i, family, address, a;

    for (i = 0; i < response.answer.lenth; i++) {
      if (family && address) {
        break;
      }
      a = response.answer[i];
      switch (a.type) {
      case consts.NAME_TO_QTYPE.A:
      case consts.NAME_TO_QTYPE.AAAA:
        family = consts.QTYPE_TO_FAMILY[a.type];
        address = a.promote().address;
        break;
      }
    }
    callback(err, family, address);
  });
};
exports.lookup = lookup;
<commit_msg>remove getSocket use property<commit_after>/*
Copyright 2011 Timothy J Fontaine <tjfontaine@gmail.com>

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

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

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

*/

"use strict";

var dgram = require('dgram'),
  consts = require('./consts'),
  types = require('./types'),
  Question = require('./question'),
  Response = require('./response');

var random_integer = function () {
  return Math.floor(Math.random() * 50000 + 1);
};

var remote_dns = {
  address: '8.8.8.8',
  port: 53,
};

var PendingRequests = function () {
  this.active_ = {};
  this.active_.count = 0;

  var socket, self = this;

  Object.defineProperty(this, 'socket', {
    get: function () {
      if (!socket) {
        socket = dgram.createSocket('udp4');
        socket.bind();
        socket.on('message', function (msg, remote) {
          self.handleMessage(msg, remote);
        });
      }
      return socket;
    },
    set: function (value) {
      if (value === undefined && socket) {
        socket.close();
      }
      socket = value;
    },
  });
};

PendingRequests.prototype.add = function (request) {
  var id = random_integer();
  while (this.active_[id] !== undefined) {
    id = random_integer();
  }
  request.id = id;
  this.active_[id] = request;
  this.active_.count++;
};

PendingRequests.prototype.remove = function (request) {
  delete this.active_[request.id];
  this.active_.count--;

  if (this.active_.count === 0) {
    this.socket = undefined;
  }
};

PendingRequests.prototype.handleMessage = function (msg, remote) {
  var err, request,
    answer = new Response(this.socket, remote);

  answer.unpack(msg);
  if (this.active_[answer.header.id]) {
    request = this.active_[answer.header.id];
    request.handle(err, answer);
  }
};

var IN_FLIGHT = new PendingRequests();

var Request = function (question, callback) {
  this.question = question;
  this.callback = callback;
  IN_FLIGHT.add(this);
  this.start();
};

Request.prototype.remove = function () {
  IN_FLIGHT.remove(this);
};

Request.prototype.cancel = function () {
  this.remove();
  console.log("Request ID:", this.id, "cancelled");
};

Request.prototype.start = function () {
  this.started = new Date().getTime();
  this.message = new Response(IN_FLIGHT.socket, remote_dns);
  this.message.header.id = this.id;
  this.message.header.rd = 1;
  this.message.question.push(this.question);
  this.message.send();
};

Request.prototype.handle = function (err, answer) {
  this.ended = new Date().getTime();
  console.log("Request ID:", this.id, "round tripped in:", this.ended - this.started);
  this.remove();
  this.callback(err, answer);
};

var inner_resolve = function (domain, rrtype, callback) {
  var question = new Question();
  question.name = domain;
  question.type = rrtype;
  question.class = consts.NAME_TO_QCLASS.IN;

  return new Request(question, function (err, response) {
    callback(err, response);
  });
};

var resolve = function (domain) {
  var rrtype = consts.NAME_TO_QTYPE.A,
    callback = arguments[arguments.length - 1];

  if (arguments.length === 3) {
    rrtype = consts.NAME_TO_QTYPE[arguments[1]];
  }

  return inner_resolve(domain, rrtype, function (err, response) {
    var ret = [], i, a;
    switch (rrtype) {
    case consts.NAME_TO_QTYPE.A:
    case consts.NAME_TO_QTYPE.AAAA:
      for (i = 0; i < response.answer.length; i++) {
        a = response.answer[i];
        if (a.type === rrtype) {
          ret.push(a.promote().address);
        }
      }
      break;
    }
    callback(err, ret);
  });
};
exports.resolve = resolve;

var resolve4 = function (domain, callback) {
  return resolve(domain, 'A', function (err, results) {
    callback(err, results);
  });
};
exports.resolve4 = resolve4;

var resolve6 = function (domain, callback) {
  return resolve(domain, 'AAAA', function (err, results) {
    callback(err, results);
  });
};
exports.resolve6 = resolve6;

var lookup = function (domain) {
  var callback = arguments[arguments.length - 1],
    family = 4,
    rrtype;

  if (arguments.length === 3) {
    family = arguments.length[1];
  }

  rrtype = consts.FAMILY_TO_QTYPE[family];

  return inner_resolve(domain, rrtype, function (err, response) {
    var i, family, address, a;

    for (i = 0; i < response.answer.lenth; i++) {
      if (family && address) {
        break;
      }
      a = response.answer[i];
      switch (a.type) {
      case consts.NAME_TO_QTYPE.A:
      case consts.NAME_TO_QTYPE.AAAA:
        family = consts.QTYPE_TO_FAMILY[a.type];
        address = a.promote().address;
        break;
      }
    }
    callback(err, family, address);
  });
};
exports.lookup = lookup;
<|endoftext|>


<commit_before>var ua = require('../../vigour-js/browser/ua')

module.exports = exports = {}

if(ua.native) {
	(function () {
		var util = require('./util')
			, nativeUtil = require('./nativeUtil')

		exports.readFile = function (path, options, cb) {
			nativeUtil.whenFsReady(function () {
				if (!cb) {
		  		cb = options
		  		options = {}
		  	}
		  	options = util.treatOptions(options)
		  	;(options.url !== false && util.url(path))
					? util.readAjax(path, options, cb)
					: readFile(path, options, cb)
			})
	  }

	  function readFile (path, options, cb) {
		  window.fs.root.getFile(path
		  	, {
		  		create: false
		  	}
		  	, function (fileEntry) {
		    fileEntry.file(function (file) {
		      var reader = new FileReader()
		      reader.onloadend = function (err) {
		      	if (err.error) {
		      		cb(err)
		      	} else {
		        	cb(null, this.result)
		      	}
		      }
		      reader.readAsText(file)
		    }, function (err) {
		    	err.failPoint = 'fileEntry.file'
		    })
		  }, function (err) {
		  	err.failPoint = 'getFile'
		  	cb(err)
		  })
		}

	  exports.writeFile = function (path, data, options, cb) {
	  	nativeUtil.whenFsReady(function () {
	  		if (!cb) {
		  		cb = options
		  		options = {}
		  	}
		  	options = util.treatOptions(options)
		  	;(options.url !== false && util.url(data))
					? exports.writeHttp(path, data, options, cb)
					: writeFile(path, data, options, cb)
	  	})
	  }

	  function writeFile (path, data, options, cb) {
			window.fs.root.getFile(path
				, {
					create: true
				}
				, function (fileEntry) {
			    fileEntry.createWriter(function (fileWriter) {
			      fileWriter.onwriteend = function (err) {
			      	if (err.error) {
			        	cb(err)
			        } else {
			        	cb(null)
			        }
			      }
			      fileWriter.onerror = function(err) {
			      	err.failPoint = 'onerror'
			        cb(err)
			      }
			    	fileWriter.write(data)
			    }, function (err) {
			    	err.failPoint = 'createWriter'
			    	cb(err)
			    })
		  }, function (err) {
	    	err.failPoint = 'getFile'
	    	cb(err)
	    })
		}

	  // exports.writeHttp = function (path, url, options, cb) {
	  // 	alert('writeHttp')
	  // 	util.readHttp(url, options, function (err, data) {
	  // 		if (err) {
	  // 			cb(err)
	  // 		} else {
	  // 			writeFile(path, data, options, cb)
	  // 		}
	  // 	})
	  // }

	  // exports.exists = function (path, cb) {
	  // 	whenFsReady(function () {
	  // 		window.fs.root.getFile(path, { create : false }, function () {
   //          cb(true)
   //      }, function () {
   //          cb(false)
   //      })
	  // 	})
	  // }

	  // exports.mkdirp = function (path, cb) {
	  // 	whenFsReady(function () {
			//     var folderParts = path.split('/')

			//     var createDir = function(rootDir, folders) {
			//       // Throw out './' or '/' and move on. Prevents: '/foo/.//bar'.
			//       if (folders[0] == '.' || folders[0] == '') {
			//         folders = folders.slice(1)
			//       }

			//       rootDir.getDirectory(folders[0]
			//       	, { create: true }
			//       	, function (dirEntry) {
			// 	          if (dirEntry.isDirectory) {
			// 	            if (folders.length && folderParts.length != 1) {
			// 	              createDir(dirEntry, folders.slice(1))
			// 	            } else {
			// 	              cb(null)
			// 	            }
			// 	          } else {
			// 	            var error = new Error(path + ' is not a directory')
			// 	            cb(error)
			// 	          }
			// 	        }
			//         , function (err) {
			// 	          if (err.code === FileError.INVALID_MODIFICATION_ERR) {
			// 	            cb(null)
			// 	          } else {
			// 	          	cb(err)
			// 	          }
			// 	        })
			//     }

			//     createDir(window.fs.root, folderParts)
			//   })
	  // }
	}())
}<commit_msg>Added writeHTTP on client<commit_after>var ua = require('../../vigour-js/browser/ua')
	, fsRoot = "cdvfile://localhost/persistent/"
module.exports = exports = {}

if(ua.native) {
	(function () {
		var util = require('./util')
			, nativeUtil = require('./nativeUtil')

		exports.readFile = function (path, options, cb) {
			nativeUtil.whenFsReady(function () {
				if (!cb) {
		  		cb = options
		  		options = {}
		  	}
		  	options = util.treatOptions(options)
		  	;(options.url !== false && util.url(path))
					? util.readAjax(path, options, cb)
					: readFile(path, options, cb)
			})
	  }

	  function readFile (path, options, cb) {
		  window.fs.root.getFile(path
		  	, {
		  		create: false
		  	}
		  	, function (fileEntry) {
		    fileEntry.file(function (file) {
		      var reader = new FileReader()
		      reader.onloadend = function (err) {
		      	if (err.error) {
		      		cb(err)
		      	} else {
		        	cb(null, this.result)
		      	}
		      }
		      reader.readAsText(file)
		    }, function (err) {
		    	err.failPoint = 'fileEntry.file'
		    })
		  }, function (err) {
		  	err.failPoint = 'getFile'
		  	cb(err)
		  })
		}

	  exports.writeFile = function (path, data, options, cb) {
	  	nativeUtil.whenFsReady(function () {
	  		if (!cb) {
		  		cb = options
		  		options = {}
		  	}
		  	options = util.treatOptions(options)
		  	;(options.url !== false && util.url(data))
					? exports.writeHttp(path, data, options, cb)
					: writeFile(path, data, options, cb)
	  	})
	  }

	  function writeFile (path, data, options, cb) {
			window.fs.root.getFile(path
				, {
					create: true
				}
				, function (fileEntry) {
			    fileEntry.createWriter(function (fileWriter) {
			      fileWriter.onwriteend = function (err) {
			      	if (err.error) {
			        	cb(err)
			        } else {
			        	cb(null)
			        }
			      }
			      fileWriter.onerror = function(err) {
			      	err.failPoint = 'onerror'
			        cb(err)
			      }
			    	fileWriter.write(data)
			    }, function (err) {
			    	err.failPoint = 'createWriter'
			    	cb(err)
			    })
		  }, function (err) {
	    	err.failPoint = 'getFile'
	    	cb(err)
	    })
		}

	  exports.writeHttp = function (path, url, options, cb) {
	  	var fileTransfer = new FileTransfer();
			var uri = encodeURI(url);

			fileTransfer.download(
		    uri
		    , fsRoot + path
		    , function (entry) {
		    	cb(null)
		    }
		    , function (err) {
					cb(err)
		    }
		    , false
			);
	  }

	  // exports.exists = function (path, cb) {
	  // 	whenFsReady(function () {
	  // 		window.fs.root.getFile(path, { create : false }, function () {
   //          cb(true)
   //      }, function () {
   //          cb(false)
   //      })
	  // 	})
	  // }

	  // exports.mkdirp = function (path, cb) {
	  // 	whenFsReady(function () {
			//     var folderParts = path.split('/')

			//     var createDir = function(rootDir, folders) {
			//       // Throw out './' or '/' and move on. Prevents: '/foo/.//bar'.
			//       if (folders[0] == '.' || folders[0] == '') {
			//         folders = folders.slice(1)
			//       }

			//       rootDir.getDirectory(folders[0]
			//       	, { create: true }
			//       	, function (dirEntry) {
			// 	          if (dirEntry.isDirectory) {
			// 	            if (folders.length && folderParts.length != 1) {
			// 	              createDir(dirEntry, folders.slice(1))
			// 	            } else {
			// 	              cb(null)
			// 	            }
			// 	          } else {
			// 	            var error = new Error(path + ' is not a directory')
			// 	            cb(error)
			// 	          }
			// 	        }
			//         , function (err) {
			// 	          if (err.code === FileError.INVALID_MODIFICATION_ERR) {
			// 	            cb(null)
			// 	          } else {
			// 	          	cb(err)
			// 	          }
			// 	        })
			//     }

			//     createDir(window.fs.root, folderParts)
			//   })
	  // }
	}())
}<|endoftext|>


<commit_before>import {version} from "node:process";
import {CheckResult} from "./check_result.js";
import pkg from "../package.json" assert {type: "json"};

/**
 * Submits comments to the [Akismet](https://akismet.com) service.
 */
export class Client {

	/**
	 * The Akismet API key.
	 * @type {string}
	 * @readonly
	 */
	apiKey;

	/**
	 * The base URL of the remote API endpoint.
	 * @type {URL}
	 * @readonly
	 */
	baseUrl;

	/**
	 * The front page or home URL of the instance making requests.
	 * @type {import("./blog.js").Blog}
	 * @readonly
	 */
	blog;

	/**
	 * Value indicating whether the client operates in test mode.
	 * @type {boolean}
	 * @readonly
	 */
	isTest;

	/**
	 * The user agent string to use when making requests.
	 * @type {string}
	 * @readonly
	 */
	userAgent;

	/**
	 * The final URL of the remote API endpoint.
	 * @type {URL}
	 */
	#endpoint;

	/**
	 * The response returned by the `submit-ham` and `submit-spam` endpoints when the outcome is a success.
	 * @type {string}
	 */
	static #successfulResponse = "Thanks for making the web a better place.";

	/**
	 * Creates a new client.
	 * @param {string} apiKey The Akismet API key.
	 * @param {import("./blog.js").Blog} blog The front page or home URL of the instance making requests.
	 * @param {Partial<ClientOptions>} [options] An object providing values to initialize this instance.
	 */
	constructor(apiKey, blog, options = {}) {
		this.apiKey = apiKey;
		this.baseUrl = new URL(options.baseUrl ?? "https://rest.akismet.com/1.1/");
		this.blog = blog;
		this.isTest = options.isTest ?? false;
		this.userAgent = options.userAgent ?? `Node.js/${version.slice(1)} | Akismet/${pkg.version}`;
		this.#endpoint = new URL(`${this.baseUrl.protocol}//${this.apiKey}.${this.baseUrl.host}${this.baseUrl.pathname}`);
	}

	/**
	 * Checks the specified comment against the service database, and returns a value indicating whether it is spam.
	 * @param {import("./comment.js").Comment} comment The comment to be checked.
	 * @returns {Promise<CheckResult>} A value indicating whether the specified comment is spam.
	 */
	async checkComment(comment) {
		const response = await this.#fetch(new URL("comment-check", this.#endpoint), comment.toJSON());
		return await response.text() == "false"
			? CheckResult.ham
			: response.headers.get("x-akismet-pro-tip") == "discard" ? CheckResult.pervasiveSpam : CheckResult.spam;
	}

	/**
	 * Submits the specified comment that was incorrectly marked as spam but should not have been.
	 * @param {import("./comment.js").Comment} comment The comment to be submitted.
	 * @returns {Promise<void>} Resolves once the comment has been submitted.
	 */
	async submitHam(comment) {
		const response = await this.#fetch(new URL("submit-ham", this.#endpoint), comment.toJSON());
		if (await response.text() != Client.#successfulResponse) throw new Error("Invalid server response.");
	}

	/**
	 * Submits the specified comment that was not marked as spam but should have been.
	 * @param {import("./comment.js").Comment} comment The comment to be submitted.
	 * @returns {Promise<void>} Resolves once the comment has been submitted.
	 */
	async submitSpam(comment) {
		const response = await this.#fetch(new URL("submit-spam", this.#endpoint), comment.toJSON());
		if (await response.text() != Client.#successfulResponse) throw new Error("Invalid server response.");
	}

	/**
	 * Checks the API key against the service database, and returns a value indicating whether it is valid.
	 * @returns {Promise<boolean>} `true` if the specified API key is valid, otherwise `false`.
	 */
	async verifyKey() {
		const response = await this.#fetch(new URL("verify-key", this.baseUrl), {key: this.apiKey});
		return await response.text() == "valid";
	}

	/**
	 * Queries the service by posting the specified fields to a given end point, and returns the response.
	 * @param {URL} endpoint The URL of the end point to query.
	 * @param {Record<string, string>} fields The fields describing the query body.
	 * @returns {Promise<Response>} The server response.
	 */
	async #fetch(endpoint, fields) {
		const body = new URLSearchParams({...this.blog.toJSON(), ...fields});
		if (this.isTest) body.set("is_test", "1");

		const response = await fetch(endpoint.href, {
			method: "POST",
			headers: {"Content-Type": "application/x-www-form-urlencoded", "User-Agent": this.userAgent},
			body
		});

		if (!response.ok) throw new Error(response.statusText);

		if (response.headers.has("x-akismet-alert-code")) {
			const code = response.headers.get("x-akismet-alert-code") ?? "";
			const error = new Error(response.headers.get("x-akismet-alert-msg") ?? "");
			error.name = code ? `AkismetError #${code}` : "AkismetError";
			throw error;
		}

		if (response.headers.has("x-akismet-debug-help")) {
			const error = new Error(response.headers.get("x-akismet-debug-help") ?? "");
			error.name = "AkismetError";
			throw error;
		}

		return response;
	}
}

/**
 * Defines the options of a {@link Client} instance.
 * @typedef {object} ClientOptions
 * @property {string} baseUrl The base URL of the remote API endpoint.
 * @property {boolean} isTest Value indicating whether the client operates in test mode.
 * @property {string} userAgent The user agent string to use when making requests.
 */
<commit_msg>Updated the error message [skip ci]<commit_after>import {version} from "node:process";
import {CheckResult} from "./check_result.js";
import pkg from "../package.json" assert {type: "json"};

/**
 * Submits comments to the [Akismet](https://akismet.com) service.
 */
export class Client {

	/**
	 * The Akismet API key.
	 * @type {string}
	 * @readonly
	 */
	apiKey;

	/**
	 * The base URL of the remote API endpoint.
	 * @type {URL}
	 * @readonly
	 */
	baseUrl;

	/**
	 * The front page or home URL of the instance making requests.
	 * @type {import("./blog.js").Blog}
	 * @readonly
	 */
	blog;

	/**
	 * Value indicating whether the client operates in test mode.
	 * @type {boolean}
	 * @readonly
	 */
	isTest;

	/**
	 * The user agent string to use when making requests.
	 * @type {string}
	 * @readonly
	 */
	userAgent;

	/**
	 * The final URL of the remote API endpoint.
	 * @type {URL}
	 */
	#endpoint;

	/**
	 * The response returned by the `submit-ham` and `submit-spam` endpoints when the outcome is a success.
	 * @type {string}
	 */
	static #successfulResponse = "Thanks for making the web a better place.";

	/**
	 * Creates a new client.
	 * @param {string} apiKey The Akismet API key.
	 * @param {import("./blog.js").Blog} blog The front page or home URL of the instance making requests.
	 * @param {Partial<ClientOptions>} [options] An object providing values to initialize this instance.
	 */
	constructor(apiKey, blog, options = {}) {
		this.apiKey = apiKey;
		this.baseUrl = new URL(options.baseUrl ?? "https://rest.akismet.com/1.1/");
		this.blog = blog;
		this.isTest = options.isTest ?? false;
		this.userAgent = options.userAgent ?? `Node.js/${version.slice(1)} | Akismet/${pkg.version}`;
		this.#endpoint = new URL(`${this.baseUrl.protocol}//${this.apiKey}.${this.baseUrl.host}${this.baseUrl.pathname}`);
	}

	/**
	 * Checks the specified comment against the service database, and returns a value indicating whether it is spam.
	 * @param {import("./comment.js").Comment} comment The comment to be checked.
	 * @returns {Promise<CheckResult>} A value indicating whether the specified comment is spam.
	 */
	async checkComment(comment) {
		const response = await this.#fetch(new URL("comment-check", this.#endpoint), comment.toJSON());
		return await response.text() == "false"
			? CheckResult.ham
			: response.headers.get("x-akismet-pro-tip") == "discard" ? CheckResult.pervasiveSpam : CheckResult.spam;
	}

	/**
	 * Submits the specified comment that was incorrectly marked as spam but should not have been.
	 * @param {import("./comment.js").Comment} comment The comment to be submitted.
	 * @returns {Promise<void>} Resolves once the comment has been submitted.
	 */
	async submitHam(comment) {
		const response = await this.#fetch(new URL("submit-ham", this.#endpoint), comment.toJSON());
		if (await response.text() != Client.#successfulResponse) throw new Error("Invalid server response.");
	}

	/**
	 * Submits the specified comment that was not marked as spam but should have been.
	 * @param {import("./comment.js").Comment} comment The comment to be submitted.
	 * @returns {Promise<void>} Resolves once the comment has been submitted.
	 */
	async submitSpam(comment) {
		const response = await this.#fetch(new URL("submit-spam", this.#endpoint), comment.toJSON());
		if (await response.text() != Client.#successfulResponse) throw new Error("Invalid server response.");
	}

	/**
	 * Checks the API key against the service database, and returns a value indicating whether it is valid.
	 * @returns {Promise<boolean>} `true` if the specified API key is valid, otherwise `false`.
	 */
	async verifyKey() {
		const response = await this.#fetch(new URL("verify-key", this.baseUrl), {key: this.apiKey});
		return await response.text() == "valid";
	}

	/**
	 * Queries the service by posting the specified fields to a given end point, and returns the response.
	 * @param {URL} endpoint The URL of the end point to query.
	 * @param {Record<string, string>} fields The fields describing the query body.
	 * @returns {Promise<Response>} The server response.
	 */
	async #fetch(endpoint, fields) {
		const body = new URLSearchParams({...this.blog.toJSON(), ...fields});
		if (this.isTest) body.set("is_test", "1");

		const response = await fetch(endpoint.href, {
			method: "POST",
			headers: {"Content-Type": "application/x-www-form-urlencoded", "User-Agent": this.userAgent},
			body
		});

		if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);

		if (response.headers.has("x-akismet-alert-code")) {
			const code = response.headers.get("x-akismet-alert-code") ?? "";
			const error = new Error(response.headers.get("x-akismet-alert-msg") ?? "");
			error.name = code ? `AkismetError #${code}` : "AkismetError";
			throw error;
		}

		if (response.headers.has("x-akismet-debug-help")) {
			const error = new Error(response.headers.get("x-akismet-debug-help") ?? "");
			error.name = "AkismetError";
			throw error;
		}

		return response;
	}
}

/**
 * Defines the options of a {@link Client} instance.
 * @typedef {object} ClientOptions
 * @property {string} baseUrl The base URL of the remote API endpoint.
 * @property {boolean} isTest Value indicating whether the client operates in test mode.
 * @property {string} userAgent The user agent string to use when making requests.
 */
<|endoftext|>


<commit_before>'use strict'

const mqtt = require('mqtt-packet')
const EventEmitter = require('events')
const util = require('util')
const eos = require('end-of-stream')
const Packet = require('aedes-packet')
const write = require('./write')
const QoSPacket = require('./qos-packet')
const handleSubscribe = require('./handlers/subscribe')
const handleUnsubscribe = require('./handlers/unsubscribe')
const handle = require('./handlers')
const { pipeline } = require('readable-stream')
const { through } = require('./utils')

module.exports = Client

function Client (broker, conn, req) {
  const that = this

  // metadata
  this.closed = false
  this.connecting = false
  this.connected = false
  this.connackSent = false
  this.errored = false

  // mqtt params
  this.id = null
  this.clean = true
  this.version = null

  this.subscriptions = {}
  this.duplicates = {}

  this.broker = broker
  this.conn = conn
  conn.client = this

  this._disconnected = false
  this._authorized = false
  this._parsingBatch = 1
  this._nextId = Math.ceil(Math.random() * 65535)

  this.req = req
  this.connDetails = null

  // we use two variables for the will
  // because we store in _will while
  // we are authenticating
  this.will = null
  this._will = null

  this._parser = mqtt.parser()
  this._parser.client = this
  this._parser._queue = [] // queue packets received before client fires 'connect' event. Prevents memory leaks on 'connect' event
  this._parser.on('packet', enqueue)
  this.once('connected', dequeue)

  function nextBatch (err) {
    if (err) {
      that.emit('error', err)
      return
    }

    const client = that

    if (client._paused) {
      return
    }

    that._parsingBatch--
    if (that._parsingBatch <= 0) {
      that._parsingBatch = 0
      var buf = client.conn.read(null)
      if (!client.connackSent && client.broker.decodeProtocol && client.broker.trustProxy && buf) {
        const { data } = client.broker.decodeProtocol(client, buf)
        if (data) {
          client._parser.parse(data)
        } else {
          client._parser.parse(buf)
        }
      } else if (buf) {
        client._parser.parse(buf)
      }
    }
  }
  this._nextBatch = nextBatch

  conn.on('readable', nextBatch)

  this.on('error', onError)
  conn.on('error', this.emit.bind(this, 'error'))
  this._parser.on('error', this.emit.bind(this, 'error'))

  conn.on('end', this.close.bind(this))
  this._eos = eos(this.conn, this.close.bind(this))

  this.deliver0 = function deliverQoS0 (_packet, cb) {
    const toForward = dedupe(that, _packet) &&
      that.broker.authorizeForward(that, _packet)
    if (toForward) {
      // Give nodejs some time to clear stacks, or we will see
      // "Maximum call stack size exceeded" in a very high load
      setImmediate(() => {
        var packet = new Packet(toForward, broker)
        packet.qos = 0
        write(that, packet, cb)
      })
    } else {
      setImmediate(cb)
    }
  }

  this.deliverQoS = function deliverQoS (_packet, cb) {
    // downgrade to qos0 if requested by publish
    if (_packet.qos === 0) {
      that.deliver0(_packet, cb)
      return
    }
    const toForward = dedupe(that, _packet) &&
      that.broker.authorizeForward(that, _packet)
    if (toForward) {
      setImmediate(() => {
        var packet = new QoSPacket(toForward, that)
        // Downgrading to client subscription qos if needed
        const clientSub = that.subscriptions[packet.topic]
        if (clientSub && (clientSub.qos || 0) < packet.qos) {
          packet.qos = clientSub.qos
        }
        packet.writeCallback = cb
        if (that.clean || packet.retain) {
          writeQoS(null, that, packet)
        } else {
          broker.persistence.outgoingUpdate(that, packet, writeQoS)
        }
      })
    } else if (that.clean === false) {
      that.broker.persistence.outgoingClearMessageId(that, _packet, noop)
      // we consider this to be an error, since the packet is undefined
      // so there's nothing to send
      setImmediate(cb)
    } else {
      setImmediate(cb)
    }
  }

  this._keepaliveTimer = null
  this._keepaliveInterval = -1

  this._connectTimer = setTimeout(function () {
    that.emit('error', new Error('connect did not arrive in time'))
  }, broker.connectTimeout)
}

function dedupe (client, packet) {
  const id = packet.brokerId
  if (!id) {
    return true
  }
  var duplicates = client.duplicates
  const counter = packet.brokerCounter
  const result = (duplicates[id] || 0) < counter
  if (result) {
    duplicates[id] = counter
  }
  return result
}

function writeQoS (err, client, packet) {
  if (err) {
    // is this right, or we should ignore thins?
    client.emit('error', err)
  } else {
    write(client, packet, packet.writeCallback)
  }
}

function onError (err) {
  this.errored = true
  this.conn.removeAllListeners('error')
  this.conn.on('error', noop)
  this.broker.emit(this.id ? 'clientError' : 'connectionError', this, err)
  this.close()
}

util.inherits(Client, EventEmitter)

Client.prototype._onError = onError

Client.prototype.publish = function (message, done) {
  const packet = new Packet(message, this.broker)
  const that = this
  if (packet.qos === 0) {
    // skip offline and send it as it is
    this.deliver0(packet, done)
    return
  }
  if (!this.clean && this.id) {
    this.broker.persistence.outgoingEnqueue({
      clientId: this.id
    }, packet, function deliver (err) {
      if (err) {
        return done(err)
      }
      that.deliverQoS(packet, done)
    })
  } else {
    that.deliverQoS(packet, done)
  }
}

Client.prototype.subscribe = function (packet, done) {
  if (!packet.subscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      subscriptions: packet
    }
  }
  handleSubscribe(this, packet, false, done)
}

Client.prototype.unsubscribe = function (packet, done) {
  if (!packet.unsubscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      unsubscriptions: packet
    }
  }
  handleUnsubscribe(this, packet, done)
}

Client.prototype.close = function (done) {
  if (this.closed) {
    if (typeof done === 'function') {
      done()
    }
    return
  }

  const that = this
  const conn = this.conn

  this.closed = true

  this._parser.removeAllListeners('packet')
  conn.removeAllListeners('readable')

  this._parser._queue = null

  if (this._keepaliveTimer) {
    this._keepaliveTimer.clear()
    this._keepaliveInterval = -1
    this._keepaliveTimer = null
  }

  if (this._connectTimer) {
    clearTimeout(this._connectTimer)
    this._connectTimer = null
  }

  this._eos()
  this._eos = noop

  handleUnsubscribe(
    this,
    {
      unsubscriptions: Object.keys(this.subscriptions)
    },
    finish)

  function finish () {
    if (!that._disconnected && that.will) {
      const will = Object.assign({}, that.will)
      that.broker.authorizePublish(that, will, function (err) {
        if (!err) {
          that.broker.publish(will, that, done)
        } else {
          done()
        }

        function done (err) {
          if (!err) {
            that.broker.persistence.delWill({
              id: that.id,
              brokerId: that.broker.id
            }, noop)
          }
        }
      })
    }
    that.will = null // this function might be called twice
    that._will = null

    that.connected = false
    that.connecting = false

    conn.removeAllListeners('error')
    conn.on('error', noop)

    if (that.broker.clients[that.id] && that._authorized) {
      that.broker.unregisterClient(that)
    }

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    conn.destroy()

    if (typeof done === 'function') {
      done()
    }
  }
}

Client.prototype.pause = function () {
  this._paused = true
}

Client.prototype.resume = function () {
  this._paused = false
  this._nextBatch()
}

function enqueue (packet) {
  const client = this.client
  client._parsingBatch++
  // already connected or it's the first packet
  if (client.connackSent || client._parsingBatch === 1) {
    handle(client, packet, client._nextBatch)
  } else {
    if (this._queue.length < client.broker.queueLimit) {
      this._queue.push(packet)
    } else {
      this.emit('error', new Error('Client queue limit reached'))
    }
  }
}

function dequeue () {
  const q = this._parser._queue
  if (q) {
    for (var i = 0, len = q.length; i < len; i++) {
      handle(this, q[i], this._nextBatch)
    }

    this._parser._queue = null
  }
}

Client.prototype.emptyOutgoingQueue = function (done) {
  const client = this
  const persistence = client.broker.persistence

  function filter (packet, enc, next) {
    persistence.outgoingClearMessageId(client, packet, next)
  }

  pipeline(
    persistence.outgoingStream(client),
    through(filter),
    done
  )
}

function noop () {}
<commit_msg>fix: clean up the write callbacks in case of error (#492)<commit_after>'use strict'

const mqtt = require('mqtt-packet')
const EventEmitter = require('events')
const util = require('util')
const eos = require('end-of-stream')
const Packet = require('aedes-packet')
const write = require('./write')
const QoSPacket = require('./qos-packet')
const handleSubscribe = require('./handlers/subscribe')
const handleUnsubscribe = require('./handlers/unsubscribe')
const handle = require('./handlers')
const { pipeline } = require('readable-stream')
const { through } = require('./utils')

module.exports = Client

function Client (broker, conn, req) {
  const that = this

  // metadata
  this.closed = false
  this.connecting = false
  this.connected = false
  this.connackSent = false
  this.errored = false

  // mqtt params
  this.id = null
  this.clean = true
  this.version = null

  this.subscriptions = {}
  this.duplicates = {}

  this.broker = broker
  this.conn = conn
  conn.client = this

  this._disconnected = false
  this._authorized = false
  this._parsingBatch = 1
  this._nextId = Math.ceil(Math.random() * 65535)

  this.req = req
  this.connDetails = null

  // we use two variables for the will
  // because we store in _will while
  // we are authenticating
  this.will = null
  this._will = null

  this._parser = mqtt.parser()
  this._parser.client = this
  this._parser._queue = [] // queue packets received before client fires 'connect' event. Prevents memory leaks on 'connect' event
  this._parser.on('packet', enqueue)
  this.once('connected', dequeue)

  function nextBatch (err) {
    if (err) {
      that.emit('error', err)
      return
    }

    const client = that

    if (client._paused) {
      return
    }

    that._parsingBatch--
    if (that._parsingBatch <= 0) {
      that._parsingBatch = 0
      var buf = client.conn.read(null)
      if (!client.connackSent && client.broker.decodeProtocol && client.broker.trustProxy && buf) {
        const { data } = client.broker.decodeProtocol(client, buf)
        if (data) {
          client._parser.parse(data)
        } else {
          client._parser.parse(buf)
        }
      } else if (buf) {
        client._parser.parse(buf)
      }
    }
  }
  this._nextBatch = nextBatch

  conn.on('readable', nextBatch)

  this.on('error', onError)
  conn.on('error', this.emit.bind(this, 'error'))
  this._parser.on('error', this.emit.bind(this, 'error'))

  conn.on('end', this.close.bind(this))
  this._eos = eos(this.conn, this.close.bind(this))

  this.deliver0 = function deliverQoS0 (_packet, cb) {
    const toForward = dedupe(that, _packet) &&
      that.broker.authorizeForward(that, _packet)
    if (toForward) {
      // Give nodejs some time to clear stacks, or we will see
      // "Maximum call stack size exceeded" in a very high load
      setImmediate(() => {
        var packet = new Packet(toForward, broker)
        packet.qos = 0
        write(that, packet, cb)
      })
    } else {
      setImmediate(cb)
    }
  }

  this.deliverQoS = function deliverQoS (_packet, cb) {
    // downgrade to qos0 if requested by publish
    if (_packet.qos === 0) {
      that.deliver0(_packet, cb)
      return
    }
    const toForward = dedupe(that, _packet) &&
      that.broker.authorizeForward(that, _packet)
    if (toForward) {
      setImmediate(() => {
        var packet = new QoSPacket(toForward, that)
        // Downgrading to client subscription qos if needed
        const clientSub = that.subscriptions[packet.topic]
        if (clientSub && (clientSub.qos || 0) < packet.qos) {
          packet.qos = clientSub.qos
        }
        packet.writeCallback = cb
        if (that.clean || packet.retain) {
          writeQoS(null, that, packet)
        } else {
          broker.persistence.outgoingUpdate(that, packet, writeQoS)
        }
      })
    } else if (that.clean === false) {
      that.broker.persistence.outgoingClearMessageId(that, _packet, noop)
      // we consider this to be an error, since the packet is undefined
      // so there's nothing to send
      setImmediate(cb)
    } else {
      setImmediate(cb)
    }
  }

  this._keepaliveTimer = null
  this._keepaliveInterval = -1

  this._connectTimer = setTimeout(function () {
    that.emit('error', new Error('connect did not arrive in time'))
  }, broker.connectTimeout)
}

function dedupe (client, packet) {
  const id = packet.brokerId
  if (!id) {
    return true
  }
  var duplicates = client.duplicates
  const counter = packet.brokerCounter
  const result = (duplicates[id] || 0) < counter
  if (result) {
    duplicates[id] = counter
  }
  return result
}

function writeQoS (err, client, packet) {
  if (err) {
    // is this right, or we should ignore thins?
    client.emit('error', err)
  } else {
    write(client, packet, packet.writeCallback)
  }
}

function drainRequest (req) {
  req.callback()
}

function onError (err) {
  this.errored = true
  this.conn.removeAllListeners('error')
  this.conn.on('error', noop)
  // hack to clean up the write callbacks in case of error
  var state = this.conn._writableState
  var list = typeof state.getBuffer === 'function' ? state.getBuffer() : state.buffer
  list.forEach(drainRequest)
  this.broker.emit(this.id ? 'clientError' : 'connectionError', this, err)
  this.close()
}

util.inherits(Client, EventEmitter)

Client.prototype._onError = onError

Client.prototype.publish = function (message, done) {
  const packet = new Packet(message, this.broker)
  const that = this
  if (packet.qos === 0) {
    // skip offline and send it as it is
    this.deliver0(packet, done)
    return
  }
  if (!this.clean && this.id) {
    this.broker.persistence.outgoingEnqueue({
      clientId: this.id
    }, packet, function deliver (err) {
      if (err) {
        return done(err)
      }
      that.deliverQoS(packet, done)
    })
  } else {
    that.deliverQoS(packet, done)
  }
}

Client.prototype.subscribe = function (packet, done) {
  if (!packet.subscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      subscriptions: packet
    }
  }
  handleSubscribe(this, packet, false, done)
}

Client.prototype.unsubscribe = function (packet, done) {
  if (!packet.unsubscriptions) {
    if (!Array.isArray(packet)) {
      packet = [packet]
    }
    packet = {
      unsubscriptions: packet
    }
  }
  handleUnsubscribe(this, packet, done)
}

Client.prototype.close = function (done) {
  if (this.closed) {
    if (typeof done === 'function') {
      done()
    }
    return
  }

  const that = this
  const conn = this.conn

  this.closed = true

  this._parser.removeAllListeners('packet')
  conn.removeAllListeners('readable')

  this._parser._queue = null

  if (this._keepaliveTimer) {
    this._keepaliveTimer.clear()
    this._keepaliveInterval = -1
    this._keepaliveTimer = null
  }

  if (this._connectTimer) {
    clearTimeout(this._connectTimer)
    this._connectTimer = null
  }

  this._eos()
  this._eos = noop

  handleUnsubscribe(
    this,
    {
      unsubscriptions: Object.keys(this.subscriptions)
    },
    finish)

  function finish () {
    if (!that._disconnected && that.will) {
      const will = Object.assign({}, that.will)
      that.broker.authorizePublish(that, will, function (err) {
        if (!err) {
          that.broker.publish(will, that, done)
        } else {
          done()
        }

        function done (err) {
          if (!err) {
            that.broker.persistence.delWill({
              id: that.id,
              brokerId: that.broker.id
            }, noop)
          }
        }
      })
    }
    that.will = null // this function might be called twice
    that._will = null

    that.connected = false
    that.connecting = false

    conn.removeAllListeners('error')
    conn.on('error', noop)

    if (that.broker.clients[that.id] && that._authorized) {
      that.broker.unregisterClient(that)
    }

    // clear up the drain event listeners
    that.conn.emit('drain')
    that.conn.removeAllListeners('drain')

    conn.destroy()

    if (typeof done === 'function') {
      done()
    }
  }
}

Client.prototype.pause = function () {
  this._paused = true
}

Client.prototype.resume = function () {
  this._paused = false
  this._nextBatch()
}

function enqueue (packet) {
  const client = this.client
  client._parsingBatch++
  // already connected or it's the first packet
  if (client.connackSent || client._parsingBatch === 1) {
    handle(client, packet, client._nextBatch)
  } else {
    if (this._queue.length < client.broker.queueLimit) {
      this._queue.push(packet)
    } else {
      this.emit('error', new Error('Client queue limit reached'))
    }
  }
}

function dequeue () {
  const q = this._parser._queue
  if (q) {
    for (var i = 0, len = q.length; i < len; i++) {
      handle(this, q[i], this._nextBatch)
    }

    this._parser._queue = null
  }
}

Client.prototype.emptyOutgoingQueue = function (done) {
  const client = this
  const persistence = client.broker.persistence

  function filter (packet, enc, next) {
    persistence.outgoingClearMessageId(client, packet, next)
  }

  pipeline(
    persistence.outgoingStream(client),
    through(filter),
    done
  )
}

function noop () {}
<|endoftext|>


<commit_before>var language = {};

language.max = 'upp till';
language.min = 'minst';

language.and = 'och';
language.or  = 'eller';
language.loading = 'Hämtar...';

language.next = 'Nästa';
language.prev = 'Tidigare';

language.find = 'Leta efter';

language.get_sentence = 'Hämta kontext';

language.link = 'länk';

language.text  = 'text';
language.saldo  = 'saldo-id';
language.pos  = 'ordklass';
language.base_form = 'grundform';
language.lemgram   = 'lemgram';
language.sentence  = 'mening';
language.paragraph  = 'stycke';
<commit_msg>mh<commit_after>var language = {};

language.max = 'upp till';
language.min = 'minst';

language.and = 'och';
language.or  = 'eller';
language.loading = 'Hämtar...';

language.next = 'Nästa';
language.prev = 'Tidigare';

language.find = 'Leta efter';

language.get_sentence = 'Hämta kontext';

language.link = 'länk';

language.text  = 'text';
language.saldo  = 'saldo-id';
language.pos  = 'ordklass';
language.msd  = 'msd';
language.base_form = 'grundform';
language.lemgram   = 'lemgram';
language.dephead  = 'dephead';
language.deprel  = 'deprel';
language.sentence  = 'mening';
language.paragraph  = 'stycke';
<|endoftext|>


<commit_before>'use strict';

var OFF = 'off';
var ERROR = 'error';
var WARN = 'warn';

module.exports = {
  extends: 'eslint:recommended',
  env: {
    node: true,
  },
  globals: {
    require: false,
    module: false,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'script',
    arrowFunctions: true,
    binaryLiterals: true,
    blockBindings: true,
    classes: false,
    defaultParams: true,
    destructuring: true,
    forOf: true,
    generators: false,
    modules: false,
    objectLiteralComputedProperties: true,
    objectLiteralDuplicateProperties: true,
    objectLiteralShorthandMethods: true,
    objectLiteralShorthandProperties: true,
    octalLiterals: false,
    regexUFlag: true,
    regexYFlag: true,
    restParams: true,
    spread: true,
    superInFunctions: true,
    templateStrings: true,
    unicodeCodePointEscapes: true,
  },
  rules: {
    'accessor-pairs': [
      ERROR,
      { getWithoutSet: false, setWithoutGet: true },
    ],
    'array-bracket-spacing': [
      ERROR,
      'never',
    ],
    'array-callback-return': ERROR,
    'arrow-body-style': [
      ERROR,
      'as-needed',
    ],
    'arrow-parens': ERROR,
    'arrow-spacing': ERROR,
    'block-spacing': [
      ERROR,
      'always',
    ],
    'brace-style': OFF,
    'callback-return': [
      ERROR, [
        'callback',
        'cb',
        'next',
        'done',
      ],
    ],
    camelcase: OFF,
    'comma-dangle': [
      ERROR,
      'always-multiline',
    ],
    'comma-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'comma-style': [
      ERROR,
      'last',
    ],
    complexity: [
      WARN,
      5,
    ],
    'computed-property-spacing': [
      ERROR,
      'never',
    ],
    'consistent-return': ERROR,
    'consistent-this': [
      ERROR,
      '_this',
    ],
    curly: ERROR,
    'default-case': ERROR,
    'dot-location': [
      ERROR,
      'property',
    ],
    'dot-notation': ERROR,
    'eol-last': ERROR,
    eqeqeq: [
      ERROR,
      'smart',
    ],
    'func-names': WARN,
    'func-style': [
      ERROR,
      'declaration',
    ],
    'global-require': ERROR,
    'guard-for-in': ERROR,
    'handle-callback-err': ERROR,
    'id-blacklist': OFF,
    'id-length': OFF,
    'id-match': OFF,
    indent: [
      ERROR,
      2,
      { SwitchCase: 1 },
    ],
    'init-declarations': OFF,
    'jsx-quotes': OFF,
    'key-spacing': [
      ERROR,
      { beforeColon: false, afterColon: true },
    ],
    'linebreak-style': [
      ERROR,
      'unix',
    ],
    'lines-around-comment': OFF,
    'max-len': [
      ERROR,
      100,
      2,
    ],
    'max-nested-callbacks': [
      ERROR,
      4,
    ],
    'max-params': [
      ERROR,
      4,
    ],
    'max-statements-per-line': [
      WARN,
      {
        max: 2,
      },
    ],
    'new-cap': [
      ERROR,
      { newIsCap: true, capIsNew: true },
    ],
    'new-parens': ERROR,
    'newline-after-var': WARN,
    'newline-before-return': OFF,
    'newline-per-chained-call': OFF,
    'no-alert': ERROR,
    'no-array-constructor': ERROR,
    'no-caller': ERROR,
    'no-case-declarations': ERROR,
    'no-catch-shadow': ERROR,
    'no-class-assign': ERROR,
    'no-confusing-arrow': ERROR,
    'no-cond-assign': ERROR,
    'no-console': ERROR,
    'no-const-assign': ERROR,
    'no-constant-condition': ERROR,
    'no-continue': OFF,
    'no-control-regex': ERROR,
    'no-debugger': ERROR,
    'no-delete-var': ERROR,
    'no-div-regex': ERROR,
    'no-dupe-args': ERROR,
    'no-dupe-class-members': OFF,
    'no-dupe-keys': ERROR,
    'no-duplicate-case': ERROR,
    'no-duplicate-imports': ERROR,
    'no-else-return': ERROR,
    'no-empty': ERROR,
    'no-empty-character-class': ERROR,
    'no-empty-pattern': ERROR,
    'no-labels': ERROR,
    'no-eq-null': ERROR,
    'no-eval': ERROR,
    'no-ex-assign': ERROR,
    'no-extend-native': ERROR,
    'no-extra-bind': ERROR,
    'no-extra-boolean-cast': ERROR,
    'no-extra-label': OFF,
    'no-extra-parens': OFF,
    'no-extra-semi': ERROR,
    'no-fallthrough': ERROR,
    'no-floating-decimal': ERROR,
    'no-func-assign': ERROR,
    'no-implicit-coercion': OFF,
    'no-implicit-globals': OFF,
    'no-implied-eval': ERROR,
    'no-inline-comments': OFF,
    'no-inner-declarations': ERROR,
    'no-invalid-regexp': ERROR,
    'no-invalid-this': WARN,
    'no-irregular-whitespace': ERROR,
    'no-iterator': ERROR,
    'no-label-var': ERROR,
    'no-lone-blocks': ERROR,
    'no-lonely-if': ERROR,
    'no-loop-func': ERROR,
    'no-magic-numbers': OFF,
    'no-mixed-requires': OFF,
    'no-mixed-spaces-and-tabs': ERROR,
    'no-multi-spaces': ERROR,
    'no-multi-str': ERROR,
    'no-multiple-empty-lines': [
      ERROR,
      { max: 2 },
    ],
    'no-native-reassign': ERROR,
    'no-negated-condition': OFF,
    'no-negated-in-lhs': ERROR,
    'no-nested-ternary': OFF,
    'no-new': WARN,
    'no-new-func': ERROR,
    'no-new-object': ERROR,
    'no-new-require': ERROR,
    'no-new-symbol': ERROR,
    'no-obj-calls': ERROR,
    'no-octal': ERROR,
    'no-octal-escape': ERROR,
    'no-param-reassign': OFF,
    'no-path-concat': ERROR,
    'no-process-env': OFF,
    'no-process-exit': OFF,
    'no-proto': ERROR,
    'no-redeclare': ERROR,
    'no-regex-spaces': ERROR,
    'no-reserved-keys': OFF,
    'no-restricted-globals': OFF,
    'no-restricted-imports': OFF,
    'no-restricted-modules': OFF,
    'no-restricted-syntax': OFF,
    'no-return-assign': ERROR,
    'no-script-url': ERROR,
    'no-self-compare': ERROR,
    'no-self-assign': ERROR,
    'no-sequences': ERROR,
    'no-shadow': WARN,
    'no-shadow-restricted-names': ERROR,
    'no-spaced-func': ERROR,
    'no-sparse-arrays': ERROR,
    'no-sync': OFF,
    'no-ternary': OFF,
    'no-throw-literal': ERROR,
    'no-trailing-spaces': ERROR,
    'no-undef': ERROR,
    'no-undef-init': ERROR,
    'no-undefined': ERROR,
    'no-underscore-dangle': OFF,
    'no-unexpected-multiline': ERROR,
    'no-unneeded-ternary': ERROR,
    'no-unmodified-loop-condition': ERROR,
    'no-unreachable': ERROR,
    'no-unused-expressions': ERROR,
    'no-unused-labels': OFF,
    'no-unused-vars': ERROR,
    'no-use-before-define': [
      ERROR,
      'nofunc',
    ],
    'no-useless-call': ERROR,
    'no-useless-concat': ERROR,
    'no-useless-constructor': ERROR,
    'no-useless-escape': WARN,
    'no-void': ERROR,
    'no-warning-comments': OFF,
    'no-with': ERROR,
    'no-whitespace-before-property': WARN,
    'object-curly-spacing': [
      ERROR,
      'always',
    ],
    'one-var': [
      ERROR,
      'never',
    ],
    'one-var-declaration-per-line': OFF,
    'operator-assignment': [
      ERROR,
      'always',
    ],
    'operator-linebreak': [
      ERROR,
      'after',
    ],
    'padded-blocks': OFF,
    'prefer-arrow-callback': OFF,
    'prefer-reflect': OFF,
    'prefer-rest-params': WARN,
    'prefer-spread': OFF,
    'prefer-template': OFF,
    'quote-props': [
      ERROR,
      'as-needed',
    ],
    quotes: [
      ERROR,
      'single',
    ],
    radix: ERROR,
    'require-jsdoc': OFF,
    'require-yield': ERROR,
    semi: [
      ERROR,
      'always',
    ],
    'semi-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'sort-vars': OFF,
    'sort-imports': OFF,
    'space-after-keywords': OFF,
    'space-before-blocks': [
      ERROR,
      'always',
    ],
    'space-before-function-paren': [
      ERROR,
      'never',
    ],
    'keyword-spacing': [
      'error',
      { before: true, after: true, overrides: {
        if: { after: false },
        for: { after: false },
        while: { after: false },
      } },
    ],
    'space-in-parens': [
      ERROR,
      'never',
    ],
    'space-infix-ops': ERROR,
    'space-unary-ops': ERROR,
    'spaced-comment': [
      ERROR,
      'always',
    ],
    'template-curly-spacing': [
      ERROR,
      'never',
    ],
    'valid-jsdoc': ERROR,
    'valid-typeof': ERROR,
    'vars-on-top': ERROR,
    'wrap-iife': ERROR,
    'wrap-regex': ERROR,
    yoda: [
      ERROR,
      'always',
    ],
  },
};
<commit_msg>feat(rules): add eslint v2.9.0 rules<commit_after>'use strict';

var OFF = 'off';
var ERROR = 'error';
var WARN = 'warn';

module.exports = {
  extends: 'eslint:recommended',
  env: {
    node: true,
  },
  globals: {
    require: false,
    module: false,
  },
  parserOptions: {
    ecmaVersion: 6,
    sourceType: 'script',
    arrowFunctions: true,
    binaryLiterals: true,
    blockBindings: true,
    classes: false,
    defaultParams: true,
    destructuring: true,
    forOf: true,
    generators: false,
    modules: false,
    objectLiteralComputedProperties: true,
    objectLiteralDuplicateProperties: true,
    objectLiteralShorthandMethods: true,
    objectLiteralShorthandProperties: true,
    octalLiterals: false,
    regexUFlag: true,
    regexYFlag: true,
    restParams: true,
    spread: true,
    superInFunctions: true,
    templateStrings: true,
    unicodeCodePointEscapes: true,
  },
  rules: {
    'accessor-pairs': [
      ERROR,
      { getWithoutSet: false, setWithoutGet: true },
    ],
    'array-bracket-spacing': [
      ERROR,
      'never',
    ],
    'array-callback-return': ERROR,
    'arrow-body-style': [
      ERROR,
      'as-needed',
    ],
    'arrow-parens': ERROR,
    'arrow-spacing': ERROR,
    'block-spacing': [
      ERROR,
      'always',
    ],
    'brace-style': OFF,
    'callback-return': [
      ERROR, [
        'callback',
        'cb',
        'next',
        'done',
      ],
    ],
    camelcase: OFF,
    'comma-dangle': [
      ERROR,
      'always-multiline',
    ],
    'comma-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'comma-style': [
      ERROR,
      'last',
    ],
    complexity: [
      WARN,
      5,
    ],
    'computed-property-spacing': [
      ERROR,
      'never',
    ],
    'consistent-return': ERROR,
    'consistent-this': [
      ERROR,
      '_this',
    ],
    curly: ERROR,
    'default-case': ERROR,
    'dot-location': [
      ERROR,
      'property',
    ],
    'dot-notation': ERROR,
    'eol-last': ERROR,
    eqeqeq: [
      ERROR,
      'smart',
    ],
    'func-names': WARN,
    'func-style': [
      ERROR,
      'declaration',
    ],
    'global-require': ERROR,
    'guard-for-in': ERROR,
    'handle-callback-err': ERROR,
    'id-blacklist': OFF,
    'id-length': OFF,
    'id-match': OFF,
    indent: [
      ERROR,
      2,
      { SwitchCase: 1 },
    ],
    'init-declarations': OFF,
    'jsx-quotes': OFF,
    'key-spacing': [
      ERROR,
      { beforeColon: false, afterColon: true },
    ],
    'linebreak-style': [
      ERROR,
      'unix',
    ],
    'lines-around-comment': OFF,
    'max-len': [
      ERROR,
      100,
      2,
    ],
    'max-nested-callbacks': [
      ERROR,
      4,
    ],
    'max-params': [
      ERROR,
      4,
    ],
    'max-statements-per-line': [
      WARN,
      {
        max: 2,
      },
    ],
    'new-cap': [
      ERROR,
      { newIsCap: true, capIsNew: true },
    ],
    'new-parens': ERROR,
    'newline-after-var': WARN,
    'newline-before-return': OFF,
    'newline-per-chained-call': OFF,
    'no-alert': ERROR,
    'no-array-constructor': ERROR,
    'no-caller': ERROR,
    'no-case-declarations': ERROR,
    'no-catch-shadow': ERROR,
    'no-class-assign': ERROR,
    'no-confusing-arrow': ERROR,
    'no-cond-assign': ERROR,
    'no-console': ERROR,
    'no-const-assign': ERROR,
    'no-constant-condition': ERROR,
    'no-continue': OFF,
    'no-control-regex': ERROR,
    'no-debugger': ERROR,
    'no-delete-var': ERROR,
    'no-div-regex': ERROR,
    'no-dupe-args': ERROR,
    'no-dupe-class-members': OFF,
    'no-dupe-keys': ERROR,
    'no-duplicate-case': ERROR,
    'no-duplicate-imports': ERROR,
    'no-else-return': ERROR,
    'no-empty': ERROR,
    'no-empty-character-class': ERROR,
    'no-empty-pattern': ERROR,
    'no-labels': ERROR,
    'no-eq-null': ERROR,
    'no-eval': ERROR,
    'no-ex-assign': ERROR,
    'no-extend-native': ERROR,
    'no-extra-bind': ERROR,
    'no-extra-boolean-cast': ERROR,
    'no-extra-label': OFF,
    'no-extra-parens': OFF,
    'no-extra-semi': ERROR,
    'no-fallthrough': ERROR,
    'no-floating-decimal': ERROR,
    'no-func-assign': ERROR,
    'no-implicit-coercion': OFF,
    'no-implicit-globals': OFF,
    'no-implied-eval': ERROR,
    'no-inline-comments': OFF,
    'no-inner-declarations': ERROR,
    'no-invalid-regexp': ERROR,
    'no-invalid-this': WARN,
    'no-irregular-whitespace': ERROR,
    'no-iterator': ERROR,
    'no-label-var': ERROR,
    'no-lone-blocks': ERROR,
    'no-lonely-if': ERROR,
    'no-loop-func': ERROR,
    'no-magic-numbers': OFF,
    'no-mixed-requires': OFF,
    'no-mixed-spaces-and-tabs': ERROR,
    'no-multi-spaces': ERROR,
    'no-multi-str': ERROR,
    'no-multiple-empty-lines': [
      ERROR,
      { max: 2 },
    ],
    'no-native-reassign': ERROR,
    'no-negated-condition': OFF,
    'no-negated-in-lhs': ERROR,
    'no-nested-ternary': OFF,
    'no-new': WARN,
    'no-new-func': ERROR,
    'no-new-object': ERROR,
    'no-new-require': ERROR,
    'no-new-symbol': ERROR,
    'no-obj-calls': ERROR,
    'no-octal': ERROR,
    'no-octal-escape': ERROR,
    'no-param-reassign': OFF,
    'no-path-concat': ERROR,
    'no-process-env': OFF,
    'no-process-exit': OFF,
    'no-proto': ERROR,
    'no-redeclare': ERROR,
    'no-regex-spaces': ERROR,
    'no-reserved-keys': OFF,
    'no-restricted-globals': OFF,
    'no-restricted-imports': OFF,
    'no-restricted-modules': OFF,
    'no-restricted-syntax': OFF,
    'no-return-assign': ERROR,
    'no-script-url': ERROR,
    'no-self-assign': ERROR,
    'no-self-compare': ERROR,
    'no-sequences': ERROR,
    'no-shadow': WARN,
    'no-shadow-restricted-names': ERROR,
    'no-spaced-func': ERROR,
    'no-sparse-arrays': ERROR,
    'no-sync': OFF,
    'no-ternary': OFF,
    'no-throw-literal': ERROR,
    'no-trailing-spaces': ERROR,
    'no-undef': ERROR,
    'no-undef-init': ERROR,
    'no-undefined': ERROR,
    'no-underscore-dangle': OFF,
    'no-unexpected-multiline': ERROR,
    'no-unmodified-loop-condition': ERROR,
    'no-unneeded-ternary': ERROR,
    'no-unreachable': ERROR,
    'no-unsafe-finally': ERROR,
    'no-unused-expressions': ERROR,
    'no-unused-labels': OFF,
    'no-unused-vars': ERROR,
    'no-use-before-define': [
      ERROR,
      'nofunc',
    ],
    'no-useless-call': ERROR,
    'no-useless-computed-key': ERROR,
    'no-useless-concat': ERROR,
    'no-useless-constructor': ERROR,
    'no-useless-escape': WARN,
    'no-void': ERROR,
    'no-warning-comments': OFF,
    'no-with': ERROR,
    'no-whitespace-before-property': WARN,
    'object-curly-spacing': [
      ERROR,
      'always',
    ],
    'one-var': [
      ERROR,
      'never',
    ],
    'one-var-declaration-per-line': OFF,
    'operator-assignment': [
      ERROR,
      'always',
    ],
    'operator-linebreak': [
      ERROR,
      'after',
    ],
    'padded-blocks': OFF,
    'prefer-arrow-callback': OFF,
    'prefer-reflect': OFF,
    'prefer-rest-params': WARN,
    'prefer-spread': OFF,
    'prefer-template': OFF,
    'quote-props': [
      ERROR,
      'as-needed',
    ],
    quotes: [
      ERROR,
      'single',
    ],
    radix: ERROR,
    'require-jsdoc': OFF,
    'require-yield': ERROR,
    semi: [
      ERROR,
      'always',
    ],
    'semi-spacing': [
      ERROR,
      { before: false, after: true },
    ],
    'sort-vars': OFF,
    'sort-imports': OFF,
    'space-after-keywords': OFF,
    'space-before-blocks': [
      ERROR,
      'always',
    ],
    'space-before-function-paren': [
      ERROR,
      'never',
    ],
    'keyword-spacing': [
      'error',
      { before: true, after: true, overrides: {
        if: { after: false },
        for: { after: false },
        while: { after: false },
      } },
    ],
    'space-in-parens': [
      ERROR,
      'never',
    ],
    'space-infix-ops': ERROR,
    'space-unary-ops': ERROR,
    'spaced-comment': [
      ERROR,
      'always',
    ],
    'template-curly-spacing': [
      ERROR,
      'never',
    ],
    'valid-jsdoc': ERROR,
    'valid-typeof': ERROR,
    'vars-on-top': ERROR,
    'wrap-iife': ERROR,
    'wrap-regex': ERROR,
    yoda: [
      ERROR,
      'always',
    ],
  },
};
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////
// Sails
// common.js
////////////////////////////////////////////////////////////
// Define app and configuration object
var sails = {
	config: {}
};

// Utility libs
var _ = require('underscore');
_.str = require('underscore.string');
var async = require('async');

// TODO: make this disableable
//////////////////////////////
global['_'] = _;
global['sails'] = sails;
global['async'] = async;
//////////////////////////////

// Node.js dependencies
var fs = require('fs');
var path = require('path');

// Routing and HTTP server
var express = require('express');

// WebSocket server (+polyfill support)
var socketio = require('socket.io');

// Templating
var ejs = require('ejs');

// Sails ecosystem dependencies
var waterline = require('waterline');
var modules = require('sails-moduleloader');
var util = require('sails-util');
sails.util = util;

// Internal dependencies
// var util = require('./util');
var configuration = require("./configuration");
var pubsub = require('./rooms');

/**
 * Load and initialize the app
 */
exports.lift = function liftSails(userConfig, cb) {
	exports.load(userConfig, function() {
		exports.initialize(userConfig, cb);
	});
};
exports.hoist = exports.lift;
exports.start = exports.lift;

/**
 * Load the dependencies and app-specific components
 */
exports.load = function loadSails(userConfig, cb) {
	// If appPath not specified, use process.cwd() to get the app dir
	userConfig = userConfig || {};
	userConfig.appPath = userConfig.appPath || process.cwd();

	// Get config files (must be in /config)
	userConfig = _.extend(userConfig,modules.aggregate({
		dirname		: userConfig.appPath + '/config',
		exclude		: ['locales', 'local.js'],
		filter		: /(.+)\.js$/,
		identity	: false
	}));

	// Extend with local and (TODO) environment-specific config
	userConfig = _.extend(userConfig,modules.aggregate({
		dirname		: userConfig.appPath + '/config',
		filter		: /local\.js$/,
		identity	: false
	}));

	// Merge user config with defaults
	sails.config = _.extend(sails.config,configuration.build(configuration.defaults(userConfig), userConfig));

	// Initialize captains logger
	sails.log = require('./logger')(sails.config.log);

	// Pass logger down to submodules

	// For reference
	// var someOtherModulePath = require('path').dirname(require.resolve('someOtherModule')) + '/../package.json';

	// Validate user config
	sails.config = configuration.validate(sails.config, userConfig);

	// EXPERIMENTAL connect v2 support
	// see https://github.com/senchalabs/connect/issues/588
	//var connect = require('connect');
	//var connectCookie = require('cookie');
	//var cookieSecret = "k3yboard_kat";
	//parseCookie = function(cookie) {
	//	return connect.utils.parseSignedCookies(connectCookie.parse(decodeURIComponent(cookie)),cookieSecret);
	//}
	var connect = require('connect');
	parseCookie = connect.utils.parseCookie;
	ConnectSession = connect.middleware.session.Session;

	// Override the environment variable so express mirrors the sails env:
	process.env['NODE_ENV'] = sails.config.environment;

	// Load app's models
	// Case-insensitive, using filename to determine identity
	sails.models = modules.optional({
		dirname		: sails.config.paths.models,
		filter		: /(.+)\.js$/
	});

	// Augment models with room/socket logic (& bind context)
	for (var identity in sails.models) {
		sails.models[identity] = _.extend(sails.models[identity],pubsub);
		_.bindAll(sails.models[identity], 'subscribe', 'introduce', 'unsubscribe', 'publish', 'room');
	}

	// Load custom adapters
	// Case-insensitive, using filename to determine identity
	sails.adapters = modules.optional({
		dirname		: sails.config.paths.adapters,
		filter		: /(.+Adapter)\.js$/,
		replaceExpr	: /Adapter/
	});

	// Include default adapters automatically
	// (right now, just waterline-dirty)
	_.extend(sails.adapters, {
		'waterline-dirty': require('waterline-dirty')
	});

	// Start up waterline (ORM) and pass in adapters and models
	// as well as the sails logger and a copy of the default adapter configuration
	waterline({

		// Let waterline know about our app path
		appPath: userConfig.appPath,

		adapters: sails.adapters,

		collections: sails.models,

		log: sails.log,

		collection: _.clone(sails.config.modelDefaults) || { adapter: 'waterline-dirty' }

	}, function afterWaterlineInstantiated (err, instantiatedModules){
		if (err) throw new Error(err);

		// Make instantiated adapters and collections globally accessible
		sails.adapters = instantiatedModules.adapters;
		sails.models = sails.collections = instantiatedModules.collections;

		// Load app's service modules (case-insensitive)
		sails.services = modules.optional({
			dirname		: sails.config.paths.services,
			filter		: /(.+)\.js$/,
			caseSensitive: true
		});
		// Provide global access (if allowed in config)
		// TODO: support disablement of _, sails, and async globalization
		// if (sails.config.globals._) global['_'] = _;
		// if (sails.config.globals.async) global['async'] = async;
		// if (sails.config.globals.sails) global['sails'] = sails;
		if (sails.config.globals.services) {
			_.each(sails.services,function (service,identity) {
				var globalName = service.globalId || service.identity;
				global[globalName] = service;
			});
		}

		// Load app controllers
		sails.controllers = modules.optional({
			dirname		: sails.config.paths.controllers,
			filter		: /(.+)Controller\.js$/,
			replaceExpr	: /Controller/
		});

		// Get federated controllers where actions are specified each in their own file
		var federatedControllers = modules.optional({
			dirname			: sails.config.paths.controllers,
			pathFilter		: /(.+)\/(.+)\.js$/
		});
		sails.controllers = _.extend(sails.controllers,federatedControllers);

		// Load policy modules
		sails.policies = modules.optional({
			dirname		: sails.config.paths.policies,
			filter		: /(.+)\.js$/,
			replaceExpr	: null
		});

		// Run boostrap script
		if (sails.config.bootstrap) sails.config.bootstrap(afterBootstrap);
		else afterBootstrap();

		function afterBootstrap (err) {
			require('./express.js').configure();
			cb && cb(err);
		}
	});
};

// Get socket interpreter
var socketInterpreter = require("./interpreter");

// Initialize the app (start the servers)
exports.initialize = function initSails(userConfig, cb) {

	// Extend w/ data from Sails package.json
	var packageConfig = require('./package.js');
	sails.version = packageConfig.version;
	sails.dependencies = packageConfig.dependencies;

	// Listen for websocket connections (and rejects) through socket.io
	var io = sails.io = socketio.listen(sails.express.app);

	// Configure socket.io
	function commonSocketIOConfig() {
		io.set('log level', 0);
	}
	io.configure('development', function() {
		commonSocketIOConfig();
	});
	io.configure('production', function() {
		commonSocketIOConfig();
	});

	// logic modules USED TO BE HERE!!!

	// Load app policy tree
	sails.policies = _.extend({ "*" : true },sails.config.policies);

	// Load route config
	sails.routes = _.extend({},sails.config.routes);

	// Map Routes
	// Link Express HTTP requests to a function which handles them
	// *** NOTE: MUST BE AFTER app.configure in order for bodyparser to work ***
	require('./router').listen(function(url, fn, httpVerb) {
		// Use all,get,post,put,or delete conditionally based on http verb
		// null === *any* of the HTTP verbs
		if(!httpVerb) {
			sails.express.app.all(url, fn);
		} else {
			_.isFunction(sails.express.app[httpVerb]) && sails.express.app[httpVerb](url, fn);
		}
	});

	// Link Socket.io requests to a controller/action
	// When a socket.io client connects, listen for the actions in the routing table
	io.sockets.on('connection', function(socket) {
		sails.log.verbose("New socket.io client connected!", socket.id);

		// Cookie is socket.handshake.headers.cookie
		// Maybe do something w/ it later
		// Prune data from the session to avoid sharing anything inadvertently
		// By default, very restrictive
		var pruneFn = sails.config.sessionPruneFn || function(session) {
			return {};
		};

		// Respond w/ information about session
		socket.emit('sessionUpdated', pruneFn(socket.handshake.session));

		// Map routes
		socket.on('message', function(socketReq, fn) {
			socketInterpreter.route(socketReq, fn, socket);
		});
	});

	// Fire up app servers
    startServers();

	// Add beforeShutdown event
	var exiting;
	process.on('SIGINT', function() {
		beforeShutdown();
	});
	process.on('SIGTERM', function() {
		beforeShutdown();
	});
	process.on('exit', function() {
		if (!exiting) beforeShutdown();
	});
	function beforeShutdown() {
		exiting = true;
		if(_.isFunction(sails.config.beforeShutdown)) {
			sails.config.beforeShutdown();
		}
	}

	// start the ws(s):// and http(s):// servers
	function startServers() {

		// Start http(s):// server
		sails.express.server = sails.express.app.listen(sails.config.port);
		if(!sails.express.app.address()) {
			// '\nBut there was an error detecting sails.express.app.address().' +
			sails.log.error('Trying to start server on port ' + sails.config.port + '...');
			sails.log.error('But something else is already running on that port!');
			sails.log.error('Please disable the other server, or choose a different port, and try again.');
		} else {
			sails.log.ship();
			sails.log('Running server at: ' + sails.config.appPath);
			sails.log('Sails lifted on port ' + sails.config.port + ' in ' + sails.config.environment + ' mode.');
		}

		// Configure auth for ws(s):// server
		io.set('authorization', function(data, accept) {
			// If a cookie was provided in the query string, use it.
			if (data.query.cookie) {
				data.headers.cookie = data.query.cookie;
			}

			// Attach authorization policy to socket event receiver
			if(data.headers.cookie) {
				// TODO: Support for Express 3.x, see:
				// https://gist.github.com/3337459
				// https://groups.google.com/forum/?fromgroups=#!topic/socket_io/pMuHFFZRfpQ

				// Solution found here in connect source code:
				// https://github.com/senchalabs/connect/blob/master/lib/middleware/session.js

				data.cookie = parseCookie(data.headers.cookie);

				data.sessionID = data.cookie[sails.config.session.key];
				data.sessionStore = sails.config.session.store;

				// (literally) get the session handshake from the session store
				sails.config.session.store.get(data.sessionID, function(err, session) {
					// An error occurred, so refuse the connection
					if(err) {
						accept('Error loading session from socket.io! \n' + err, false);
					}
					// Cookie is invalid, so regenerate a new one
					else if(!session) {
						data.session = new ConnectSession(data, {
							cookie: {
								// Prevent access from client-side javascript
								httpOnly: true
							}
						});
						sails.log.verbose("Generated new session....", data);
						accept(null, true);
					}

					// Save the session handshake and accept the connection
					else {
						// Create a session object, passing our just-acquired session handshake
						data.session = new ConnectSession(data, session);
						sails.log.verbose("Connected to existing session....");
						accept(null, true);
					}
				});
			} else {
				return accept('No cookie transmitted with socket.io connection.', false);
			}
		});

		// Trigger sails.initialize() callback if specified
		cb && cb(null, sails);
	}
};

/**
 * Kill the http and socket.io servers
 */
exports.lower = function lowerSails(cb) {
	sails.express.server.close();

	// This is probably unnecessary (seems to be killing it when express server is killed)
	// io.server.close();
};
exports.stop = exports.lower;
exports.close = exports.lower;
exports.kill = exports.lower;

// Export sails object
exports.sails = sails;
<commit_msg>Provides feedback that server is starting up (in case assets are getting compiled.)<commit_after>////////////////////////////////////////////////////////////
// Sails
// common.js
////////////////////////////////////////////////////////////
// Define app and configuration object
var sails = {
	config: {}
};

// Utility libs
var _ = require('underscore');
_.str = require('underscore.string');
var async = require('async');

// TODO: make this disableable
//////////////////////////////
global['_'] = _;
global['sails'] = sails;
global['async'] = async;
//////////////////////////////

// Node.js dependencies
var fs = require('fs');
var path = require('path');

// Routing and HTTP server
var express = require('express');

// WebSocket server (+polyfill support)
var socketio = require('socket.io');

// Templating
var ejs = require('ejs');

// Sails ecosystem dependencies
var waterline = require('waterline');
var modules = require('sails-moduleloader');
var util = require('sails-util');
sails.util = util;

// Internal dependencies
// var util = require('./util');
var configuration = require("./configuration");
var pubsub = require('./rooms');

/**
 * Load and initialize the app
 */
exports.lift = function liftSails(userConfig, cb) {

	exports.load(userConfig, function() {
		exports.initialize(userConfig, cb);
	});
};
exports.hoist = exports.lift;
exports.start = exports.lift;

/**
 * Load the dependencies and app-specific components
 */
exports.load = function loadSails(userConfig, cb) {
	// If appPath not specified, use process.cwd() to get the app dir
	userConfig = userConfig || {};
	userConfig.appPath = userConfig.appPath || process.cwd();

	// Get config files (must be in /config)
	userConfig = _.extend(userConfig,modules.aggregate({
		dirname		: userConfig.appPath + '/config',
		exclude		: ['locales', 'local.js'],
		filter		: /(.+)\.js$/,
		identity	: false
	}));

	// Extend with local and (TODO) environment-specific config
	userConfig = _.extend(userConfig,modules.aggregate({
		dirname		: userConfig.appPath + '/config',
		filter		: /local\.js$/,
		identity	: false
	}));

	// Merge user config with defaults
	sails.config = _.extend(sails.config,configuration.build(configuration.defaults(userConfig), userConfig));

	// Initialize captains logger
	sails.log = require('./logger')(sails.config.log);

	// Pass logger down to submodules

	// For reference
	// var someOtherModulePath = require('path').dirname(require.resolve('someOtherModule')) + '/../package.json';

	// Validate user config
	sails.config = configuration.validate(sails.config, userConfig);

	// Indicate that server is starting
	sails.log("Starting server in " + sails.config.appPath + "...");

	// EXPERIMENTAL connect v2 support
	// see https://github.com/senchalabs/connect/issues/588
	//var connect = require('connect');
	//var connectCookie = require('cookie');
	//var cookieSecret = "k3yboard_kat";
	//parseCookie = function(cookie) {
	//	return connect.utils.parseSignedCookies(connectCookie.parse(decodeURIComponent(cookie)),cookieSecret);
	//}
	var connect = require('connect');
	parseCookie = connect.utils.parseCookie;
	ConnectSession = connect.middleware.session.Session;

	// Override the environment variable so express mirrors the sails env:
	process.env['NODE_ENV'] = sails.config.environment;

	// Load app's models
	// Case-insensitive, using filename to determine identity
	sails.models = modules.optional({
		dirname		: sails.config.paths.models,
		filter		: /(.+)\.js$/
	});

	// Augment models with room/socket logic (& bind context)
	for (var identity in sails.models) {
		sails.models[identity] = _.extend(sails.models[identity],pubsub);
		_.bindAll(sails.models[identity], 'subscribe', 'introduce', 'unsubscribe', 'publish', 'room');
	}

	// Load custom adapters
	// Case-insensitive, using filename to determine identity
	sails.adapters = modules.optional({
		dirname		: sails.config.paths.adapters,
		filter		: /(.+Adapter)\.js$/,
		replaceExpr	: /Adapter/
	});

	// Include default adapters automatically
	// (right now, just waterline-dirty)
	_.extend(sails.adapters, {
		'waterline-dirty': require('waterline-dirty')
	});

	// Start up waterline (ORM) and pass in adapters and models
	// as well as the sails logger and a copy of the default adapter configuration
	waterline({

		// Let waterline know about our app path
		appPath: userConfig.appPath,

		adapters: sails.adapters,

		collections: sails.models,

		log: sails.log,

		collection: _.clone(sails.config.modelDefaults) || { adapter: 'waterline-dirty' }

	}, function afterWaterlineInstantiated (err, instantiatedModules){
		if (err) throw new Error(err);

		// Make instantiated adapters and collections globally accessible
		sails.adapters = instantiatedModules.adapters;
		sails.models = sails.collections = instantiatedModules.collections;

		// Load app's service modules (case-insensitive)
		sails.services = modules.optional({
			dirname		: sails.config.paths.services,
			filter		: /(.+)\.js$/,
			caseSensitive: true
		});
		// Provide global access (if allowed in config)
		// TODO: support disablement of _, sails, and async globalization
		// if (sails.config.globals._) global['_'] = _;
		// if (sails.config.globals.async) global['async'] = async;
		// if (sails.config.globals.sails) global['sails'] = sails;
		if (sails.config.globals.services) {
			_.each(sails.services,function (service,identity) {
				var globalName = service.globalId || service.identity;
				global[globalName] = service;
			});
		}

		// Load app controllers
		sails.controllers = modules.optional({
			dirname		: sails.config.paths.controllers,
			filter		: /(.+)Controller\.js$/,
			replaceExpr	: /Controller/
		});

		// Get federated controllers where actions are specified each in their own file
		var federatedControllers = modules.optional({
			dirname			: sails.config.paths.controllers,
			pathFilter		: /(.+)\/(.+)\.js$/
		});
		sails.controllers = _.extend(sails.controllers,federatedControllers);

		// Load policy modules
		sails.policies = modules.optional({
			dirname		: sails.config.paths.policies,
			filter		: /(.+)\.js$/,
			replaceExpr	: null
		});

		// Run boostrap script
		if (sails.config.bootstrap) sails.config.bootstrap(afterBootstrap);
		else afterBootstrap();

		function afterBootstrap (err) {
			require('./express.js').configure();
			cb && cb(err);
		}
	});
};

// Get socket interpreter
var socketInterpreter = require("./interpreter");

// Initialize the app (start the servers)
exports.initialize = function initSails(userConfig, cb) {

	// Extend w/ data from Sails package.json
	var packageConfig = require('./package.js');
	sails.version = packageConfig.version;
	sails.dependencies = packageConfig.dependencies;

	// Listen for websocket connections (and rejects) through socket.io
	var io = sails.io = socketio.listen(sails.express.app);

	// Configure socket.io
	function commonSocketIOConfig() {
		io.set('log level', 0);
	}
	io.configure('development', function() {
		commonSocketIOConfig();
	});
	io.configure('production', function() {
		commonSocketIOConfig();
	});

	// logic modules USED TO BE HERE!!!

	// Load app policy tree
	sails.policies = _.extend({ "*" : true },sails.config.policies);

	// Load route config
	sails.routes = _.extend({},sails.config.routes);

	// Map Routes
	// Link Express HTTP requests to a function which handles them
	// *** NOTE: MUST BE AFTER app.configure in order for bodyparser to work ***
	require('./router').listen(function(url, fn, httpVerb) {
		// Use all,get,post,put,or delete conditionally based on http verb
		// null === *any* of the HTTP verbs
		if(!httpVerb) {
			sails.express.app.all(url, fn);
		} else {
			_.isFunction(sails.express.app[httpVerb]) && sails.express.app[httpVerb](url, fn);
		}
	});

	// Link Socket.io requests to a controller/action
	// When a socket.io client connects, listen for the actions in the routing table
	io.sockets.on('connection', function(socket) {
		sails.log.verbose("New socket.io client connected!", socket.id);

		// Cookie is socket.handshake.headers.cookie
		// Maybe do something w/ it later
		// Prune data from the session to avoid sharing anything inadvertently
		// By default, very restrictive
		var pruneFn = sails.config.sessionPruneFn || function(session) {
			return {};
		};

		// Respond w/ information about session
		socket.emit('sessionUpdated', pruneFn(socket.handshake.session));

		// Map routes
		socket.on('message', function(socketReq, fn) {
			socketInterpreter.route(socketReq, fn, socket);
		});
	});

	// Fire up app servers
    startServers();

	// Add beforeShutdown event
	var exiting;
	process.on('SIGINT', function() {
		beforeShutdown();
	});
	process.on('SIGTERM', function() {
		beforeShutdown();
	});
	process.on('exit', function() {
		if (!exiting) beforeShutdown();
	});
	function beforeShutdown() {
		exiting = true;
		if(_.isFunction(sails.config.beforeShutdown)) {
			sails.config.beforeShutdown();
		}
	}

	// start the ws(s):// and http(s):// servers
	function startServers() {

		// Start http(s):// server
		sails.express.server = sails.express.app.listen(sails.config.port);
		if(!sails.express.app.address()) {
			// '\nBut there was an error detecting sails.express.app.address().' +
			sails.log.error('Trying to start server on port ' + sails.config.port + '...');
			sails.log.error('But something else is already running on that port!');
			sails.log.error('Please disable the other server, or choose a different port, and try again.');
		} else {
			sails.log();
			sails.log.ship();
			sails.log('Sails lifted on port ' + sails.config.port + ' in ' + sails.config.environment + ' mode.');
		}

		// Configure auth for ws(s):// server
		io.set('authorization', function(data, accept) {
			// If a cookie was provided in the query string, use it.
			if (data.query.cookie) {
				data.headers.cookie = data.query.cookie;
			}

			// Attach authorization policy to socket event receiver
			if(data.headers.cookie) {
				// TODO: Support for Express 3.x, see:
				// https://gist.github.com/3337459
				// https://groups.google.com/forum/?fromgroups=#!topic/socket_io/pMuHFFZRfpQ

				// Solution found here in connect source code:
				// https://github.com/senchalabs/connect/blob/master/lib/middleware/session.js

				data.cookie = parseCookie(data.headers.cookie);

				data.sessionID = data.cookie[sails.config.session.key];
				data.sessionStore = sails.config.session.store;

				// (literally) get the session handshake from the session store
				sails.config.session.store.get(data.sessionID, function(err, session) {
					// An error occurred, so refuse the connection
					if(err) {
						accept('Error loading session from socket.io! \n' + err, false);
					}
					// Cookie is invalid, so regenerate a new one
					else if(!session) {
						data.session = new ConnectSession(data, {
							cookie: {
								// Prevent access from client-side javascript
								httpOnly: true
							}
						});
						sails.log.verbose("Generated new session....", data);
						accept(null, true);
					}

					// Save the session handshake and accept the connection
					else {
						// Create a session object, passing our just-acquired session handshake
						data.session = new ConnectSession(data, session);
						sails.log.verbose("Connected to existing session....");
						accept(null, true);
					}
				});
			} else {
				return accept('No cookie transmitted with socket.io connection.', false);
			}
		});

		// Trigger sails.initialize() callback if specified
		cb && cb(null, sails);
	}
};

/**
 * Kill the http and socket.io servers
 */
exports.lower = function lowerSails(cb) {
	sails.express.server.close();

	// This is probably unnecessary (seems to be killing it when express server is killed)
	// io.server.close();
};
exports.stop = exports.lower;
exports.close = exports.lower;
exports.kill = exports.lower;

// Export sails object
exports.sails = sails;
<|endoftext|>