diff --git a/README.md b/README.md index 27ab00b..1be9b0a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Cloud JS Client jsDelivr links: - - client: `https://cdn.jsdelivr.net/gh/netsblox/client-auth/dist/cloud.js` + +- client: `https://cdn.jsdelivr.net/gh/netsblox/client-auth/dist/cloud.js` diff --git a/dist/cloud.js b/dist/cloud.js index 02c6ea4..94fc277 100644 --- a/dist/cloud.js +++ b/dist/cloud.js @@ -1,1645 +1,1883 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Cloud = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i * @license MIT */ -function compare(a, b) { - if (a === b) { - return 0; - } - - var x = a.length; - var y = b.length; - - for (var i = 0, len = Math.min(x, y); i < len; ++i) { - if (a[i] !== b[i]) { - x = a[i]; - y = b[i]; - break; - } - } - - if (x < y) { - return -1; - } - if (y < x) { - return 1; - } - return 0; -} -function isBuffer(b) { - if (global.Buffer && typeof global.Buffer.isBuffer === 'function') { - return global.Buffer.isBuffer(b); - } - return !!(b != null && b._isBuffer); -} - -// based on node assert, original notice: -// NB: The URL to the CommonJS spec is kept just for tradition. -// node-assert has evolved a lot since then, both in API and behavior. - -// http://wiki.commonjs.org/wiki/Unit_Testing/1.0 -// -// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! -// -// Originally from narwhal.js (http://narwhaljs.org) -// Copyright (c) 2009 Thomas Robinson <280north.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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -var util = require('util/'); -var hasOwn = Object.prototype.hasOwnProperty; -var pSlice = Array.prototype.slice; -var functionsHaveNames = (function () { - return function foo() {}.name === 'foo'; -}()); -function pToString (obj) { - return Object.prototype.toString.call(obj); -} -function isView(arrbuf) { - if (isBuffer(arrbuf)) { - return false; - } - if (typeof global.ArrayBuffer !== 'function') { - return false; - } - if (typeof ArrayBuffer.isView === 'function') { - return ArrayBuffer.isView(arrbuf); - } - if (!arrbuf) { - return false; - } - if (arrbuf instanceof DataView) { - return true; - } - if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { - return true; - } - return false; -} -// 1. The assert module provides functions that throw -// AssertionError's when particular conditions are not met. The -// assert module must conform to the following interface. - -var assert = module.exports = ok; - -// 2. The AssertionError is defined in assert. -// new assert.AssertionError({ message: message, -// actual: actual, -// expected: expected }) - -var regex = /\s*function\s+([^\(\s]*)\s*/; -// based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js -function getName(func) { - if (!util.isFunction(func)) { - return; - } - if (functionsHaveNames) { - return func.name; - } - var str = func.toString(); - var match = str.match(regex); - return match && match[1]; -} -assert.AssertionError = function AssertionError(options) { - this.name = 'AssertionError'; - this.actual = options.actual; - this.expected = options.expected; - this.operator = options.operator; - if (options.message) { - this.message = options.message; - this.generatedMessage = false; - } else { - this.message = getMessage(this); - this.generatedMessage = true; - } - var stackStartFunction = options.stackStartFunction || fail; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, stackStartFunction); - } else { - // non v8 browsers so we can have a stacktrace - var err = new Error(); - if (err.stack) { - var out = err.stack; - - // try to strip useless frames - var fn_name = getName(stackStartFunction); - var idx = out.indexOf('\n' + fn_name); - if (idx >= 0) { - // once we have located the function frame - // we need to strip out everything before it (and its line) - var next_line = out.indexOf('\n', idx + 1); - out = out.substring(next_line + 1); - } - - this.stack = out; - } - } -}; - -// assert.AssertionError instanceof Error -util.inherits(assert.AssertionError, Error); - -function truncate(s, n) { - if (typeof s === 'string') { - return s.length < n ? s : s.slice(0, n); - } else { - return s; - } -} -function inspect(something) { - if (functionsHaveNames || !util.isFunction(something)) { - return util.inspect(something); - } - var rawname = getName(something); - var name = rawname ? ': ' + rawname : ''; - return '[Function' + name + ']'; -} -function getMessage(self) { - return truncate(inspect(self.actual), 128) + ' ' + - self.operator + ' ' + - truncate(inspect(self.expected), 128); -} - -// At present only the three keys mentioned above are used and -// understood by the spec. Implementations or sub modules can pass -// other keys to the AssertionError's constructor - they will be -// ignored. - -// 3. All of the following functions must throw an AssertionError -// when a corresponding condition is not met, with a message that -// may be undefined if not provided. All assertion methods provide -// both the actual and expected values to the assertion error for -// display purposes. - -function fail(actual, expected, message, operator, stackStartFunction) { - throw new assert.AssertionError({ - message: message, - actual: actual, - expected: expected, - operator: operator, - stackStartFunction: stackStartFunction - }); -} - -// EXTENSION! allows for well behaved errors defined elsewhere. -assert.fail = fail; - -// 4. Pure assertion tests whether a value is truthy, as determined -// by !!guard. -// assert.ok(guard, message_opt); -// This statement is equivalent to assert.equal(true, !!guard, -// message_opt);. To test strictly for the value true, use -// assert.strictEqual(true, guard, message_opt);. - -function ok(value, message) { - if (!value) fail(value, true, message, '==', assert.ok); -} -assert.ok = ok; - -// 5. The equality assertion tests shallow, coercive equality with -// ==. -// assert.equal(actual, expected, message_opt); - -assert.equal = function equal(actual, expected, message) { - if (actual != expected) fail(actual, expected, message, '==', assert.equal); -}; - -// 6. The non-equality assertion tests for whether two objects are not equal -// with != assert.notEqual(actual, expected, message_opt); - -assert.notEqual = function notEqual(actual, expected, message) { - if (actual == expected) { - fail(actual, expected, message, '!=', assert.notEqual); - } -}; + function compare(a, b) { + if (a === b) { + return 0; + } + + var x = a.length; + var y = b.length; + + for (var i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + + if (x < y) { + return -1; + } + if (y < x) { + return 1; + } + return 0; + } + function isBuffer(b) { + if ( + global.Buffer && typeof global.Buffer.isBuffer === "function" + ) { + return global.Buffer.isBuffer(b); + } + return !!(b != null && b._isBuffer); + } -// 7. The equivalence assertion tests a deep equality relation. -// assert.deepEqual(actual, expected, message_opt); + // based on node assert, original notice: + // NB: The URL to the CommonJS spec is kept just for tradition. + // node-assert has evolved a lot since then, both in API and behavior. + + // http://wiki.commonjs.org/wiki/Unit_Testing/1.0 + // + // THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8! + // + // Originally from narwhal.js (http://narwhaljs.org) + // Copyright (c) 2009 Thomas Robinson <280north.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 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + var util = require("util/"); + var hasOwn = Object.prototype.hasOwnProperty; + var pSlice = Array.prototype.slice; + var functionsHaveNames = function () { + return function foo() {}.name === "foo"; + }(); + function pToString(obj) { + return Object.prototype.toString.call(obj); + } + function isView(arrbuf) { + if (isBuffer(arrbuf)) { + return false; + } + if (typeof global.ArrayBuffer !== "function") { + return false; + } + if (typeof ArrayBuffer.isView === "function") { + return ArrayBuffer.isView(arrbuf); + } + if (!arrbuf) { + return false; + } + if (arrbuf instanceof DataView) { + return true; + } + if (arrbuf.buffer && arrbuf.buffer instanceof ArrayBuffer) { + return true; + } + return false; + } + // 1. The assert module provides functions that throw + // AssertionError's when particular conditions are not met. The + // assert module must conform to the following interface. + + var assert = module.exports = ok; + + // 2. The AssertionError is defined in assert. + // new assert.AssertionError({ message: message, + // actual: actual, + // expected: expected }) + + var regex = /\s*function\s+([^\(\s]*)\s*/; + // based on https://github.com/ljharb/function.prototype.name/blob/adeeeec8bfcc6068b187d7d9fb3d5bb1d3a30899/implementation.js + function getName(func) { + if (!util.isFunction(func)) { + return; + } + if (functionsHaveNames) { + return func.name; + } + var str = func.toString(); + var match = str.match(regex); + return match && match[1]; + } + assert.AssertionError = function AssertionError(options) { + this.name = "AssertionError"; + this.actual = options.actual; + this.expected = options.expected; + this.operator = options.operator; + if (options.message) { + this.message = options.message; + this.generatedMessage = false; + } else { + this.message = getMessage(this); + this.generatedMessage = true; + } + var stackStartFunction = options.stackStartFunction || fail; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, stackStartFunction); + } else { + // non v8 browsers so we can have a stacktrace + var err = new Error(); + if (err.stack) { + var out = err.stack; + + // try to strip useless frames + var fn_name = getName(stackStartFunction); + var idx = out.indexOf("\n" + fn_name); + if (idx >= 0) { + // once we have located the function frame + // we need to strip out everything before it (and its line) + var next_line = out.indexOf("\n", idx + 1); + out = out.substring(next_line + 1); + } + + this.stack = out; + } + } + }; + + // assert.AssertionError instanceof Error + util.inherits(assert.AssertionError, Error); + + function truncate(s, n) { + if (typeof s === "string") { + return s.length < n ? s : s.slice(0, n); + } else { + return s; + } + } + function inspect(something) { + if (functionsHaveNames || !util.isFunction(something)) { + return util.inspect(something); + } + var rawname = getName(something); + var name = rawname ? ": " + rawname : ""; + return "[Function" + name + "]"; + } + function getMessage(self) { + return truncate(inspect(self.actual), 128) + " " + + self.operator + " " + + truncate(inspect(self.expected), 128); + } -assert.deepEqual = function deepEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'deepEqual', assert.deepEqual); - } -}; + // At present only the three keys mentioned above are used and + // understood by the spec. Implementations or sub modules can pass + // other keys to the AssertionError's constructor - they will be + // ignored. + + // 3. All of the following functions must throw an AssertionError + // when a corresponding condition is not met, with a message that + // may be undefined if not provided. All assertion methods provide + // both the actual and expected values to the assertion error for + // display purposes. + + function fail( + actual, + expected, + message, + operator, + stackStartFunction, + ) { + throw new assert.AssertionError({ + message: message, + actual: actual, + expected: expected, + operator: operator, + stackStartFunction: stackStartFunction, + }); + } -assert.deepStrictEqual = function deepStrictEqual(actual, expected, message) { - if (!_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'deepStrictEqual', assert.deepStrictEqual); - } -}; - -function _deepEqual(actual, expected, strict, memos) { - // 7.1. All identical values are equivalent, as determined by ===. - if (actual === expected) { - return true; - } else if (isBuffer(actual) && isBuffer(expected)) { - return compare(actual, expected) === 0; - - // 7.2. If the expected value is a Date object, the actual value is - // equivalent if it is also a Date object that refers to the same time. - } else if (util.isDate(actual) && util.isDate(expected)) { - return actual.getTime() === expected.getTime(); - - // 7.3 If the expected value is a RegExp object, the actual value is - // equivalent if it is also a RegExp object with the same source and - // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). - } else if (util.isRegExp(actual) && util.isRegExp(expected)) { - return actual.source === expected.source && - actual.global === expected.global && - actual.multiline === expected.multiline && - actual.lastIndex === expected.lastIndex && - actual.ignoreCase === expected.ignoreCase; - - // 7.4. Other pairs that do not both pass typeof value == 'object', - // equivalence is determined by ==. - } else if ((actual === null || typeof actual !== 'object') && - (expected === null || typeof expected !== 'object')) { - return strict ? actual === expected : actual == expected; - - // If both values are instances of typed arrays, wrap their underlying - // ArrayBuffers in a Buffer each to increase performance - // This optimization requires the arrays to have the same type as checked by - // Object.prototype.toString (aka pToString). Never perform binary - // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their - // bit patterns are not identical. - } else if (isView(actual) && isView(expected) && - pToString(actual) === pToString(expected) && - !(actual instanceof Float32Array || - actual instanceof Float64Array)) { - return compare(new Uint8Array(actual.buffer), - new Uint8Array(expected.buffer)) === 0; - - // 7.5 For all other Object pairs, including Array objects, equivalence is - // determined by having the same number of owned properties (as verified - // with Object.prototype.hasOwnProperty.call), the same set of keys - // (although not necessarily the same order), equivalent values for every - // corresponding key, and an identical 'prototype' property. Note: this - // accounts for both named and indexed properties on Arrays. - } else if (isBuffer(actual) !== isBuffer(expected)) { - return false; - } else { - memos = memos || {actual: [], expected: []}; - - var actualIndex = memos.actual.indexOf(actual); - if (actualIndex !== -1) { - if (actualIndex === memos.expected.indexOf(expected)) { - return true; - } - } + // EXTENSION! allows for well behaved errors defined elsewhere. + assert.fail = fail; - memos.actual.push(actual); - memos.expected.push(expected); + // 4. Pure assertion tests whether a value is truthy, as determined + // by !!guard. + // assert.ok(guard, message_opt); + // This statement is equivalent to assert.equal(true, !!guard, + // message_opt);. To test strictly for the value true, use + // assert.strictEqual(true, guard, message_opt);. - return objEquiv(actual, expected, strict, memos); - } -} - -function isArguments(object) { - return Object.prototype.toString.call(object) == '[object Arguments]'; -} - -function objEquiv(a, b, strict, actualVisitedObjects) { - if (a === null || a === undefined || b === null || b === undefined) - return false; - // if one is a primitive, the other must be same - if (util.isPrimitive(a) || util.isPrimitive(b)) - return a === b; - if (strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b)) - return false; - var aIsArgs = isArguments(a); - var bIsArgs = isArguments(b); - if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) - return false; - if (aIsArgs) { - a = pSlice.call(a); - b = pSlice.call(b); - return _deepEqual(a, b, strict); - } - var ka = objectKeys(a); - var kb = objectKeys(b); - var key, i; - // having the same number of owned properties (keys incorporates - // hasOwnProperty) - if (ka.length !== kb.length) - return false; - //the same set of keys (although not necessarily the same order), - ka.sort(); - kb.sort(); - //~~~cheap key test - for (i = ka.length - 1; i >= 0; i--) { - if (ka[i] !== kb[i]) - return false; - } - //equivalent values for every corresponding key, and - //~~~possibly expensive deep test - for (i = ka.length - 1; i >= 0; i--) { - key = ka[i]; - if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) - return false; - } - return true; -} + function ok(value, message) { + if (!value) fail(value, true, message, "==", assert.ok); + } + assert.ok = ok; + + // 5. The equality assertion tests shallow, coercive equality with + // ==. + // assert.equal(actual, expected, message_opt); + + assert.equal = function equal(actual, expected, message) { + if (actual != expected) { + fail(actual, expected, message, "==", assert.equal); + } + }; + + // 6. The non-equality assertion tests for whether two objects are not equal + // with != assert.notEqual(actual, expected, message_opt); + + assert.notEqual = function notEqual(actual, expected, message) { + if (actual == expected) { + fail(actual, expected, message, "!=", assert.notEqual); + } + }; + + // 7. The equivalence assertion tests a deep equality relation. + // assert.deepEqual(actual, expected, message_opt); + + assert.deepEqual = function deepEqual(actual, expected, message) { + if (!_deepEqual(actual, expected, false)) { + fail(actual, expected, message, "deepEqual", assert.deepEqual); + } + }; + + assert.deepStrictEqual = function deepStrictEqual( + actual, + expected, + message, + ) { + if (!_deepEqual(actual, expected, true)) { + fail( + actual, + expected, + message, + "deepStrictEqual", + assert.deepStrictEqual, + ); + } + }; + + function _deepEqual(actual, expected, strict, memos) { + // 7.1. All identical values are equivalent, as determined by ===. + if (actual === expected) { + return true; + } else if (isBuffer(actual) && isBuffer(expected)) { + return compare(actual, expected) === 0; + + // 7.2. If the expected value is a Date object, the actual value is + // equivalent if it is also a Date object that refers to the same time. + } else if (util.isDate(actual) && util.isDate(expected)) { + return actual.getTime() === expected.getTime(); + + // 7.3 If the expected value is a RegExp object, the actual value is + // equivalent if it is also a RegExp object with the same source and + // properties (`global`, `multiline`, `lastIndex`, `ignoreCase`). + } else if (util.isRegExp(actual) && util.isRegExp(expected)) { + return actual.source === expected.source && + actual.global === expected.global && + actual.multiline === expected.multiline && + actual.lastIndex === expected.lastIndex && + actual.ignoreCase === expected.ignoreCase; + + // 7.4. Other pairs that do not both pass typeof value == 'object', + // equivalence is determined by ==. + } else if ( + (actual === null || typeof actual !== "object") && + (expected === null || typeof expected !== "object") + ) { + return strict ? actual === expected : actual == expected; + + // If both values are instances of typed arrays, wrap their underlying + // ArrayBuffers in a Buffer each to increase performance + // This optimization requires the arrays to have the same type as checked by + // Object.prototype.toString (aka pToString). Never perform binary + // comparisons for Float*Arrays, though, since e.g. +0 === -0 but their + // bit patterns are not identical. + } else if ( + isView(actual) && isView(expected) && + pToString(actual) === pToString(expected) && + !(actual instanceof Float32Array || + actual instanceof Float64Array) + ) { + return compare( + new Uint8Array(actual.buffer), + new Uint8Array(expected.buffer), + ) === 0; + + // 7.5 For all other Object pairs, including Array objects, equivalence is + // determined by having the same number of owned properties (as verified + // with Object.prototype.hasOwnProperty.call), the same set of keys + // (although not necessarily the same order), equivalent values for every + // corresponding key, and an identical 'prototype' property. Note: this + // accounts for both named and indexed properties on Arrays. + } else if (isBuffer(actual) !== isBuffer(expected)) { + return false; + } else { + memos = memos || { actual: [], expected: [] }; + + var actualIndex = memos.actual.indexOf(actual); + if (actualIndex !== -1) { + if (actualIndex === memos.expected.indexOf(expected)) { + return true; + } + } -// 8. The non-equivalence assertion tests for any deep inequality. -// assert.notDeepEqual(actual, expected, message_opt); + memos.actual.push(actual); + memos.expected.push(expected); -assert.notDeepEqual = function notDeepEqual(actual, expected, message) { - if (_deepEqual(actual, expected, false)) { - fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual); - } -}; + return objEquiv(actual, expected, strict, memos); + } + } -assert.notDeepStrictEqual = notDeepStrictEqual; -function notDeepStrictEqual(actual, expected, message) { - if (_deepEqual(actual, expected, true)) { - fail(actual, expected, message, 'notDeepStrictEqual', notDeepStrictEqual); - } -} + function isArguments(object) { + return Object.prototype.toString.call(object) == + "[object Arguments]"; + } + function objEquiv(a, b, strict, actualVisitedObjects) { + if ( + a === null || a === undefined || b === null || b === undefined + ) { + return false; + } + // if one is a primitive, the other must be same + if (util.isPrimitive(a) || util.isPrimitive(b)) { + return a === b; + } + if ( + strict && Object.getPrototypeOf(a) !== Object.getPrototypeOf(b) + ) { + return false; + } + var aIsArgs = isArguments(a); + var bIsArgs = isArguments(b); + if ((aIsArgs && !bIsArgs) || (!aIsArgs && bIsArgs)) { + return false; + } + if (aIsArgs) { + a = pSlice.call(a); + b = pSlice.call(b); + return _deepEqual(a, b, strict); + } + var ka = objectKeys(a); + var kb = objectKeys(b); + var key, i; + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (ka.length !== kb.length) { + return false; + } + //the same set of keys (although not necessarily the same order), + ka.sort(); + kb.sort(); + //~~~cheap key test + for (i = ka.length - 1; i >= 0; i--) { + if (ka[i] !== kb[i]) { + return false; + } + } + //equivalent values for every corresponding key, and + //~~~possibly expensive deep test + for (i = ka.length - 1; i >= 0; i--) { + key = ka[i]; + if (!_deepEqual(a[key], b[key], strict, actualVisitedObjects)) { + return false; + } + } + return true; + } -// 9. The strict equality assertion tests strict equality, as determined by ===. -// assert.strictEqual(actual, expected, message_opt); + // 8. The non-equivalence assertion tests for any deep inequality. + // assert.notDeepEqual(actual, expected, message_opt); + + assert.notDeepEqual = function notDeepEqual( + actual, + expected, + message, + ) { + if (_deepEqual(actual, expected, false)) { + fail( + actual, + expected, + message, + "notDeepEqual", + assert.notDeepEqual, + ); + } + }; + + assert.notDeepStrictEqual = notDeepStrictEqual; + function notDeepStrictEqual(actual, expected, message) { + if (_deepEqual(actual, expected, true)) { + fail( + actual, + expected, + message, + "notDeepStrictEqual", + notDeepStrictEqual, + ); + } + } -assert.strictEqual = function strictEqual(actual, expected, message) { - if (actual !== expected) { - fail(actual, expected, message, '===', assert.strictEqual); - } -}; + // 9. The strict equality assertion tests strict equality, as determined by ===. + // assert.strictEqual(actual, expected, message_opt); + + assert.strictEqual = function strictEqual( + actual, + expected, + message, + ) { + if (actual !== expected) { + fail(actual, expected, message, "===", assert.strictEqual); + } + }; + + // 10. The strict non-equality assertion tests for strict inequality, as + // determined by !==. assert.notStrictEqual(actual, expected, message_opt); + + assert.notStrictEqual = function notStrictEqual( + actual, + expected, + message, + ) { + if (actual === expected) { + fail(actual, expected, message, "!==", assert.notStrictEqual); + } + }; + + function expectedException(actual, expected) { + if (!actual || !expected) { + return false; + } + + if ( + Object.prototype.toString.call(expected) == "[object RegExp]" + ) { + return expected.test(actual); + } + + try { + if (actual instanceof expected) { + return true; + } + } catch (e) { + // Ignore. The instanceof check doesn't work for arrow functions. + } -// 10. The strict non-equality assertion tests for strict inequality, as -// determined by !==. assert.notStrictEqual(actual, expected, message_opt); + if (Error.isPrototypeOf(expected)) { + return false; + } -assert.notStrictEqual = function notStrictEqual(actual, expected, message) { - if (actual === expected) { - fail(actual, expected, message, '!==', assert.notStrictEqual); - } -}; + return expected.call({}, actual) === true; + } -function expectedException(actual, expected) { - if (!actual || !expected) { - return false; - } + function _tryBlock(block) { + var error; + try { + block(); + } catch (e) { + error = e; + } + return error; + } - if (Object.prototype.toString.call(expected) == '[object RegExp]') { - return expected.test(actual); - } + function _throws(shouldThrow, block, expected, message) { + var actual; + + if (typeof block !== "function") { + throw new TypeError('"block" argument must be a function'); + } + + if (typeof expected === "string") { + message = expected; + expected = null; + } + + actual = _tryBlock(block); + + message = (expected && expected.name + ? " (" + expected.name + ")." + : ".") + + (message ? " " + message : "."); + + if (shouldThrow && !actual) { + fail(actual, expected, "Missing expected exception" + message); + } + + var userProvidedMessage = typeof message === "string"; + var isUnwantedException = !shouldThrow && util.isError(actual); + var isUnexpectedException = !shouldThrow && actual && !expected; + + if ( + (isUnwantedException && + userProvidedMessage && + expectedException(actual, expected)) || + isUnexpectedException + ) { + fail(actual, expected, "Got unwanted exception" + message); + } + + if ( + (shouldThrow && actual && expected && + !expectedException(actual, expected)) || + (!shouldThrow && actual) + ) { + throw actual; + } + } - try { - if (actual instanceof expected) { - return true; - } - } catch (e) { - // Ignore. The instanceof check doesn't work for arrow functions. - } + // 11. Expected to throw an error: + // assert.throws(block, Error_opt, message_opt); + + assert.throws = function ( + block, + /*optional*/ error, + /*optional*/ message, + ) { + _throws(true, block, error, message); + }; + + // EXTENSION! This is annoying to write outside this module. + assert.doesNotThrow = function ( + block, + /*optional*/ error, + /*optional*/ message, + ) { + _throws(false, block, error, message); + }; + + assert.ifError = function (err) { + if (err) { + throw err; + } + }; + + // Expose a strict only variant of assert + function strict(value, message) { + if (!value) { + fail(value, true, message, "==", strict); + } + } + assert.strict = objectAssign(strict, assert, { + equal: assert.strictEqual, + deepEqual: assert.deepStrictEqual, + notEqual: assert.notStrictEqual, + notDeepEqual: assert.notDeepStrictEqual, + }); + assert.strict.strict = assert.strict; - if (Error.isPrototypeOf(expected)) { - return false; - } + var objectKeys = Object.keys || function (obj) { + var keys = []; + for (var key in obj) { + if (hasOwn.call(obj, key)) { + keys.push(key); + } + } + return keys; + }; + }).call(this); + }).call( + this, + typeof global !== "undefined" + ? global + : typeof self !== "undefined" + ? self + : typeof window !== "undefined" + ? window + : {}, + ); + }, { "object-assign": 6, "util/": 4 }], + 2: [function (require, module, exports) { + if (typeof Object.create === "function") { + // implementation from standard node.js 'util' module + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + ctor.prototype = Object.create(superCtor.prototype, { + constructor: { + value: ctor, + enumerable: false, + writable: true, + configurable: true, + }, + }); + }; + } else { + // old school shim for old browsers + module.exports = function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; + }; + } + }, {}], + 3: [function (require, module, exports) { + module.exports = function isBuffer(arg) { + return arg && typeof arg === "object" && + typeof arg.copy === "function" && + typeof arg.fill === "function" && + typeof arg.readUInt8 === "function"; + }; + }, {}], + 4: [function (require, module, exports) { + (function (process, global) { + (function () { + // Copyright Joyent, Inc. and other Node contributors. + // + // Permission is hereby granted, free of charge, to any person obtaining a + // copy of this software and associated documentation files (the + // "Software"), to deal in the Software without restriction, including + // without limitation the rights to use, copy, modify, merge, publish, + // distribute, sublicense, and/or sell copies of the Software, and to permit + // persons to whom the Software is furnished to do so, subject to the + // following conditions: + // + // The above copyright notice and this permission notice shall be included + // in all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + // USE OR OTHER DEALINGS IN THE SOFTWARE. + + var formatRegExp = /%[sdj%]/g; + exports.format = function (f) { + if (!isString(f)) { + var objects = []; + for (var i = 0; i < arguments.length; i++) { + objects.push(inspect(arguments[i])); + } + return objects.join(" "); + } + + var i = 1; + var args = arguments; + var len = args.length; + var str = String(f).replace(formatRegExp, function (x) { + if (x === "%%") return "%"; + if (i >= len) return x; + switch (x) { + case "%s": + return String(args[i++]); + case "%d": + return Number(args[i++]); + case "%j": + try { + return JSON.stringify(args[i++]); + } catch (_) { + return "[Circular]"; + } + default: + return x; + } + }); + for (var x = args[i]; i < len; x = args[++i]) { + if (isNull(x) || !isObject(x)) { + str += " " + x; + } else { + str += " " + inspect(x); + } + } + return str; + }; + + // Mark that a method should not be used. + // Returns a modified function which warns once by default. + // If --no-deprecation is set, then it is a no-op. + exports.deprecate = function (fn, msg) { + // Allow for deprecating things in the process of starting up. + if (isUndefined(global.process)) { + return function () { + return exports.deprecate(fn, msg).apply(this, arguments); + }; + } + + if (process.noDeprecation === true) { + return fn; + } + + var warned = false; + function deprecated() { + if (!warned) { + if (process.throwDeprecation) { + throw new Error(msg); + } else if (process.traceDeprecation) { + console.trace(msg); + } else { + console.error(msg); + } + warned = true; + } + return fn.apply(this, arguments); + } + + return deprecated; + }; + + var debugs = {}; + var debugEnviron; + exports.debuglog = function (set) { + if (isUndefined(debugEnviron)) { + debugEnviron = process.env.NODE_DEBUG || ""; + } + set = set.toUpperCase(); + if (!debugs[set]) { + if (new RegExp("\\b" + set + "\\b", "i").test(debugEnviron)) { + var pid = process.pid; + debugs[set] = function () { + var msg = exports.format.apply(exports, arguments); + console.error("%s %d: %s", set, pid, msg); + }; + } else { + debugs[set] = function () {}; + } + } + return debugs[set]; + }; + + /** + * Echos the value of a value. Trys to print the value out + * in the best way possible given the different types. + * + * @param {Object} obj The object to print out. + * @param {Object} opts Optional options object that alters the output. + */ + /* legacy: obj, showHidden, depth, colors*/ + function inspect(obj, opts) { + // default options + var ctx = { + seen: [], + stylize: stylizeNoColor, + }; + // legacy... + if (arguments.length >= 3) ctx.depth = arguments[2]; + if (arguments.length >= 4) ctx.colors = arguments[3]; + if (isBoolean(opts)) { + // legacy... + ctx.showHidden = opts; + } else if (opts) { + // got an "options" object + exports._extend(ctx, opts); + } + // set default options + if (isUndefined(ctx.showHidden)) ctx.showHidden = false; + if (isUndefined(ctx.depth)) ctx.depth = 2; + if (isUndefined(ctx.colors)) ctx.colors = false; + if (isUndefined(ctx.customInspect)) ctx.customInspect = true; + if (ctx.colors) ctx.stylize = stylizeWithColor; + return formatValue(ctx, obj, ctx.depth); + } + exports.inspect = inspect; + + // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics + inspect.colors = { + "bold": [1, 22], + "italic": [3, 23], + "underline": [4, 24], + "inverse": [7, 27], + "white": [37, 39], + "grey": [90, 39], + "black": [30, 39], + "blue": [34, 39], + "cyan": [36, 39], + "green": [32, 39], + "magenta": [35, 39], + "red": [31, 39], + "yellow": [33, 39], + }; + + // Don't use 'blue' not visible on cmd.exe + inspect.styles = { + "special": "cyan", + "number": "yellow", + "boolean": "yellow", + "undefined": "grey", + "null": "bold", + "string": "green", + "date": "magenta", + // "name": intentionally not styling + "regexp": "red", + }; + + function stylizeWithColor(str, styleType) { + var style = inspect.styles[styleType]; + + if (style) { + return "\u001b[" + inspect.colors[style][0] + "m" + str + + "\u001b[" + inspect.colors[style][1] + "m"; + } else { + return str; + } + } - return expected.call({}, actual) === true; -} + function stylizeNoColor(str, styleType) { + return str; + } -function _tryBlock(block) { - var error; - try { - block(); - } catch (e) { - error = e; - } - return error; -} + function arrayToHash(array) { + var hash = {}; -function _throws(shouldThrow, block, expected, message) { - var actual; + array.forEach(function (val, idx) { + hash[val] = true; + }); - if (typeof block !== 'function') { - throw new TypeError('"block" argument must be a function'); - } + return hash; + } - if (typeof expected === 'string') { - message = expected; - expected = null; - } + function formatValue(ctx, value, recurseTimes) { + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it + if ( + ctx.customInspect && + value && + isFunction(value.inspect) && + // Filter out the util module, it's inspect function is special + value.inspect !== exports.inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value) + ) { + var ret = value.inspect(recurseTimes, ctx); + if (!isString(ret)) { + ret = formatValue(ctx, ret, recurseTimes); + } + return ret; + } + + // Primitive types cannot have properties + var primitive = formatPrimitive(ctx, value); + if (primitive) { + return primitive; + } + + // Look up the keys of the object. + var keys = Object.keys(value); + var visibleKeys = arrayToHash(keys); + + if (ctx.showHidden) { + keys = Object.getOwnPropertyNames(value); + } + + // IE doesn't make error fields non-enumerable + // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx + if ( + isError(value) && + (keys.indexOf("message") >= 0 || + keys.indexOf("description") >= 0) + ) { + return formatError(value); + } + + // Some type of object without properties can be shortcutted. + if (keys.length === 0) { + if (isFunction(value)) { + var name = value.name ? ": " + value.name : ""; + return ctx.stylize("[Function" + name + "]", "special"); + } + if (isRegExp(value)) { + return ctx.stylize( + RegExp.prototype.toString.call(value), + "regexp", + ); + } + if (isDate(value)) { + return ctx.stylize( + Date.prototype.toString.call(value), + "date", + ); + } + if (isError(value)) { + return formatError(value); + } + } + + var base = "", array = false, braces = ["{", "}"]; + + // Make Array say that they are Array + if (isArray(value)) { + array = true; + braces = ["[", "]"]; + } + + // Make functions say that they are functions + if (isFunction(value)) { + var n = value.name ? ": " + value.name : ""; + base = " [Function" + n + "]"; + } + + // Make RegExps say that they are RegExps + if (isRegExp(value)) { + base = " " + RegExp.prototype.toString.call(value); + } + + // Make dates with properties first say the date + if (isDate(value)) { + base = " " + Date.prototype.toUTCString.call(value); + } + + // Make error with message first say the error + if (isError(value)) { + base = " " + formatError(value); + } + + if (keys.length === 0 && (!array || value.length == 0)) { + return braces[0] + base + braces[1]; + } + + if (recurseTimes < 0) { + if (isRegExp(value)) { + return ctx.stylize( + RegExp.prototype.toString.call(value), + "regexp", + ); + } else { + return ctx.stylize("[Object]", "special"); + } + } + + ctx.seen.push(value); + + var output; + if (array) { + output = formatArray( + ctx, + value, + recurseTimes, + visibleKeys, + keys, + ); + } else { + output = keys.map(function (key) { + return formatProperty( + ctx, + value, + recurseTimes, + visibleKeys, + key, + array, + ); + }); + } - actual = _tryBlock(block); + ctx.seen.pop(); - message = (expected && expected.name ? ' (' + expected.name + ').' : '.') + - (message ? ' ' + message : '.'); + return reduceToSingleString(output, base, braces); + } - if (shouldThrow && !actual) { - fail(actual, expected, 'Missing expected exception' + message); - } + function formatPrimitive(ctx, value) { + if (isUndefined(value)) { + return ctx.stylize("undefined", "undefined"); + } + if (isString(value)) { + var simple = "'" + JSON.stringify(value).replace(/^"|"$/g, "") + .replace(/'/g, "\\'") + .replace(/\\"/g, '"') + + "'"; + return ctx.stylize(simple, "string"); + } + if (isNumber(value)) { + return ctx.stylize("" + value, "number"); + } + if (isBoolean(value)) { + return ctx.stylize("" + value, "boolean"); + } + // For some reason typeof null is "object", so special case here. + if (isNull(value)) { + return ctx.stylize("null", "null"); + } + } - var userProvidedMessage = typeof message === 'string'; - var isUnwantedException = !shouldThrow && util.isError(actual); - var isUnexpectedException = !shouldThrow && actual && !expected; + function formatError(value) { + return "[" + Error.prototype.toString.call(value) + "]"; + } - if ((isUnwantedException && - userProvidedMessage && - expectedException(actual, expected)) || - isUnexpectedException) { - fail(actual, expected, 'Got unwanted exception' + message); - } + function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { + var output = []; + for (var i = 0, l = value.length; i < l; ++i) { + if (hasOwnProperty(value, String(i))) { + output.push( + formatProperty( + ctx, + value, + recurseTimes, + visibleKeys, + String(i), + true, + ), + ); + } else { + output.push(""); + } + } + keys.forEach(function (key) { + if (!key.match(/^\d+$/)) { + output.push( + formatProperty( + ctx, + value, + recurseTimes, + visibleKeys, + key, + true, + ), + ); + } + }); + return output; + } - if ((shouldThrow && actual && expected && - !expectedException(actual, expected)) || (!shouldThrow && actual)) { - throw actual; - } -} - -// 11. Expected to throw an error: -// assert.throws(block, Error_opt, message_opt); - -assert.throws = function(block, /*optional*/error, /*optional*/message) { - _throws(true, block, error, message); -}; - -// EXTENSION! This is annoying to write outside this module. -assert.doesNotThrow = function(block, /*optional*/error, /*optional*/message) { - _throws(false, block, error, message); -}; - -assert.ifError = function(err) { if (err) throw err; }; - -// Expose a strict only variant of assert -function strict(value, message) { - if (!value) fail(value, true, message, '==', strict); -} -assert.strict = objectAssign(strict, assert, { - equal: assert.strictEqual, - deepEqual: assert.deepStrictEqual, - notEqual: assert.notStrictEqual, - notDeepEqual: assert.notDeepStrictEqual -}); -assert.strict.strict = assert.strict; + function formatProperty( + ctx, + value, + recurseTimes, + visibleKeys, + key, + array, + ) { + var name, str, desc; + desc = Object.getOwnPropertyDescriptor(value, key) || + { value: value[key] }; + if (desc.get) { + if (desc.set) { + str = ctx.stylize("[Getter/Setter]", "special"); + } else { + str = ctx.stylize("[Getter]", "special"); + } + } else { + if (desc.set) { + str = ctx.stylize("[Setter]", "special"); + } + } + if (!hasOwnProperty(visibleKeys, key)) { + name = "[" + key + "]"; + } + if (!str) { + if (ctx.seen.indexOf(desc.value) < 0) { + if (isNull(recurseTimes)) { + str = formatValue(ctx, desc.value, null); + } else { + str = formatValue(ctx, desc.value, recurseTimes - 1); + } + if (str.indexOf("\n") > -1) { + if (array) { + str = str.split("\n").map(function (line) { + return " " + line; + }).join("\n").substr(2); + } else { + str = "\n" + str.split("\n").map(function (line) { + return " " + line; + }).join("\n"); + } + } + } else { + str = ctx.stylize("[Circular]", "special"); + } + } + if (isUndefined(name)) { + if (array && key.match(/^\d+$/)) { + return str; + } + name = JSON.stringify("" + key); + if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { + name = name.substr(1, name.length - 2); + name = ctx.stylize(name, "name"); + } else { + name = name.replace(/'/g, "\\'") + .replace(/\\"/g, '"') + .replace(/(^"|"$)/g, "'"); + name = ctx.stylize(name, "string"); + } + } -var objectKeys = Object.keys || function (obj) { - var keys = []; - for (var key in obj) { - if (hasOwn.call(obj, key)) keys.push(key); - } - return keys; -}; - -}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"object-assign":6,"util/":4}],2:[function(require,module,exports){ -if (typeof Object.create === 'function') { - // implementation from standard node.js 'util' module - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - ctor.prototype = Object.create(superCtor.prototype, { - constructor: { - value: ctor, - enumerable: false, - writable: true, - configurable: true - } - }); - }; -} else { - // old school shim for old browsers - module.exports = function inherits(ctor, superCtor) { - ctor.super_ = superCtor - var TempCtor = function () {} - TempCtor.prototype = superCtor.prototype - ctor.prototype = new TempCtor() - ctor.prototype.constructor = ctor - } -} - -},{}],3:[function(require,module,exports){ -module.exports = function isBuffer(arg) { - return arg && typeof arg === 'object' - && typeof arg.copy === 'function' - && typeof arg.fill === 'function' - && typeof arg.readUInt8 === 'function'; -} -},{}],4:[function(require,module,exports){ -(function (process,global){(function (){ -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -var formatRegExp = /%[sdj%]/g; -exports.format = function(f) { - if (!isString(f)) { - var objects = []; - for (var i = 0; i < arguments.length; i++) { - objects.push(inspect(arguments[i])); - } - return objects.join(' '); - } + return name + ": " + str; + } - var i = 1; - var args = arguments; - var len = args.length; - var str = String(f).replace(formatRegExp, function(x) { - if (x === '%%') return '%'; - if (i >= len) return x; - switch (x) { - case '%s': return String(args[i++]); - case '%d': return Number(args[i++]); - case '%j': - try { - return JSON.stringify(args[i++]); - } catch (_) { - return '[Circular]'; - } - default: - return x; - } - }); - for (var x = args[i]; i < len; x = args[++i]) { - if (isNull(x) || !isObject(x)) { - str += ' ' + x; - } else { - str += ' ' + inspect(x); - } - } - return str; -}; - - -// Mark that a method should not be used. -// Returns a modified function which warns once by default. -// If --no-deprecation is set, then it is a no-op. -exports.deprecate = function(fn, msg) { - // Allow for deprecating things in the process of starting up. - if (isUndefined(global.process)) { - return function() { - return exports.deprecate(fn, msg).apply(this, arguments); - }; - } + function reduceToSingleString(output, base, braces) { + var numLinesEst = 0; + var length = output.reduce(function (prev, cur) { + numLinesEst++; + if (cur.indexOf("\n") >= 0) numLinesEst++; + return prev + cur.replace(/\u001b\[\d\d?m/g, "").length + 1; + }, 0); + + if (length > 60) { + return braces[0] + + (base === "" ? "" : base + "\n ") + + " " + + output.join(",\n ") + + " " + + braces[1]; + } + + return braces[0] + base + " " + output.join(", ") + " " + + braces[1]; + } - if (process.noDeprecation === true) { - return fn; - } + // NOTE: These type checking functions intentionally don't use `instanceof` + // because it is fragile and can be easily faked with `Object.create()`. + function isArray(ar) { + return Array.isArray(ar); + } + exports.isArray = isArray; - var warned = false; - function deprecated() { - if (!warned) { - if (process.throwDeprecation) { - throw new Error(msg); - } else if (process.traceDeprecation) { - console.trace(msg); - } else { - console.error(msg); - } - warned = true; - } - return fn.apply(this, arguments); - } + function isBoolean(arg) { + return typeof arg === "boolean"; + } + exports.isBoolean = isBoolean; - return deprecated; -}; - - -var debugs = {}; -var debugEnviron; -exports.debuglog = function(set) { - if (isUndefined(debugEnviron)) - debugEnviron = process.env.NODE_DEBUG || ''; - set = set.toUpperCase(); - if (!debugs[set]) { - if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) { - var pid = process.pid; - debugs[set] = function() { - var msg = exports.format.apply(exports, arguments); - console.error('%s %d: %s', set, pid, msg); - }; - } else { - debugs[set] = function() {}; - } - } - return debugs[set]; -}; + function isNull(arg) { + return arg === null; + } + exports.isNull = isNull; + function isNullOrUndefined(arg) { + return arg == null; + } + exports.isNullOrUndefined = isNullOrUndefined; -/** - * Echos the value of a value. Trys to print the value out - * in the best way possible given the different types. - * - * @param {Object} obj The object to print out. - * @param {Object} opts Optional options object that alters the output. - */ -/* legacy: obj, showHidden, depth, colors*/ -function inspect(obj, opts) { - // default options - var ctx = { - seen: [], - stylize: stylizeNoColor - }; - // legacy... - if (arguments.length >= 3) ctx.depth = arguments[2]; - if (arguments.length >= 4) ctx.colors = arguments[3]; - if (isBoolean(opts)) { - // legacy... - ctx.showHidden = opts; - } else if (opts) { - // got an "options" object - exports._extend(ctx, opts); - } - // set default options - if (isUndefined(ctx.showHidden)) ctx.showHidden = false; - if (isUndefined(ctx.depth)) ctx.depth = 2; - if (isUndefined(ctx.colors)) ctx.colors = false; - if (isUndefined(ctx.customInspect)) ctx.customInspect = true; - if (ctx.colors) ctx.stylize = stylizeWithColor; - return formatValue(ctx, obj, ctx.depth); -} -exports.inspect = inspect; - - -// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics -inspect.colors = { - 'bold' : [1, 22], - 'italic' : [3, 23], - 'underline' : [4, 24], - 'inverse' : [7, 27], - 'white' : [37, 39], - 'grey' : [90, 39], - 'black' : [30, 39], - 'blue' : [34, 39], - 'cyan' : [36, 39], - 'green' : [32, 39], - 'magenta' : [35, 39], - 'red' : [31, 39], - 'yellow' : [33, 39] -}; - -// Don't use 'blue' not visible on cmd.exe -inspect.styles = { - 'special': 'cyan', - 'number': 'yellow', - 'boolean': 'yellow', - 'undefined': 'grey', - 'null': 'bold', - 'string': 'green', - 'date': 'magenta', - // "name": intentionally not styling - 'regexp': 'red' -}; - - -function stylizeWithColor(str, styleType) { - var style = inspect.styles[styleType]; - - if (style) { - return '\u001b[' + inspect.colors[style][0] + 'm' + str + - '\u001b[' + inspect.colors[style][1] + 'm'; - } else { - return str; - } -} + function isNumber(arg) { + return typeof arg === "number"; + } + exports.isNumber = isNumber; + function isString(arg) { + return typeof arg === "string"; + } + exports.isString = isString; -function stylizeNoColor(str, styleType) { - return str; -} + function isSymbol(arg) { + return typeof arg === "symbol"; + } + exports.isSymbol = isSymbol; + function isUndefined(arg) { + return arg === void 0; + } + exports.isUndefined = isUndefined; -function arrayToHash(array) { - var hash = {}; + function isRegExp(re) { + return isObject(re) && objectToString(re) === "[object RegExp]"; + } + exports.isRegExp = isRegExp; - array.forEach(function(val, idx) { - hash[val] = true; - }); + function isObject(arg) { + return typeof arg === "object" && arg !== null; + } + exports.isObject = isObject; - return hash; -} + function isDate(d) { + return isObject(d) && objectToString(d) === "[object Date]"; + } + exports.isDate = isDate; + function isError(e) { + return isObject(e) && + (objectToString(e) === "[object Error]" || e instanceof Error); + } + exports.isError = isError; -function formatValue(ctx, value, recurseTimes) { - // Provide a hook for user-specified inspect functions. - // Check that value is an object with an inspect function on it - if (ctx.customInspect && - value && - isFunction(value.inspect) && - // Filter out the util module, it's inspect function is special - value.inspect !== exports.inspect && - // Also filter out any prototype objects using the circular check. - !(value.constructor && value.constructor.prototype === value)) { - var ret = value.inspect(recurseTimes, ctx); - if (!isString(ret)) { - ret = formatValue(ctx, ret, recurseTimes); - } - return ret; - } + function isFunction(arg) { + return typeof arg === "function"; + } + exports.isFunction = isFunction; + + function isPrimitive(arg) { + return arg === null || + typeof arg === "boolean" || + typeof arg === "number" || + typeof arg === "string" || + typeof arg === "symbol" || // ES6 symbol + typeof arg === "undefined"; + } + exports.isPrimitive = isPrimitive; - // Primitive types cannot have properties - var primitive = formatPrimitive(ctx, value); - if (primitive) { - return primitive; - } + exports.isBuffer = require("./support/isBuffer"); - // Look up the keys of the object. - var keys = Object.keys(value); - var visibleKeys = arrayToHash(keys); + function objectToString(o) { + return Object.prototype.toString.call(o); + } - if (ctx.showHidden) { - keys = Object.getOwnPropertyNames(value); - } + function pad(n) { + return n < 10 ? "0" + n.toString(10) : n.toString(10); + } - // IE doesn't make error fields non-enumerable - // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx - if (isError(value) - && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) { - return formatError(value); - } + var months = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ]; + + // 26 Feb 16:19:34 + function timestamp() { + var d = new Date(); + var time = [ + pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds()), + ].join(":"); + return [d.getDate(), months[d.getMonth()], time].join(" "); + } - // Some type of object without properties can be shortcutted. - if (keys.length === 0) { - if (isFunction(value)) { - var name = value.name ? ': ' + value.name : ''; - return ctx.stylize('[Function' + name + ']', 'special'); - } - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } - if (isDate(value)) { - return ctx.stylize(Date.prototype.toString.call(value), 'date'); - } - if (isError(value)) { - return formatError(value); - } - } + // log is just a thin wrapper to console.log that prepends a timestamp + exports.log = function () { + console.log( + "%s - %s", + timestamp(), + exports.format.apply(exports, arguments), + ); + }; + + /** + * Inherit the prototype methods from one constructor into another. + * + * The Function.prototype.inherits from lang.js rewritten as a standalone + * function (not on Function.prototype). NOTE: If this file is to be loaded + * during bootstrapping this function needs to be rewritten using some native + * functions as prototype setup using normal JavaScript does not work as + * expected during bootstrapping (see mirror.js in r114903). + * + * @param {function} ctor Constructor function which needs to inherit the + * prototype. + * @param {function} superCtor Constructor function to inherit prototype from. + */ + exports.inherits = require("inherits"); + + exports._extend = function (origin, add) { + // Don't do anything if add isn't an object + if (!add || !isObject(add)) return origin; + + var keys = Object.keys(add); + var i = keys.length; + while (i--) { + origin[keys[i]] = add[keys[i]]; + } + return origin; + }; + + function hasOwnProperty(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + } + }).call(this); + }).call( + this, + require("_process"), + typeof global !== "undefined" + ? global + : typeof self !== "undefined" + ? self + : typeof window !== "undefined" + ? window + : {}, + ); + }, { "./support/isBuffer": 3, "_process": 7, "inherits": 2 }], + 5: [function (require, module, exports) { + // the whatwg-fetch polyfill installs the fetch() function + // on the global object (window or self) + // + // Return that as the export for use in Webpack, Browserify etc. + require("whatwg-fetch"); + module.exports = self.fetch.bind(self); + }, { "whatwg-fetch": 8 }], + 6: [function (require, module, exports) { + /* +object-assign +(c) Sindre Sorhus +@license MIT + */ + + "use strict"; + /* eslint-disable no-unused-vars */ + var getOwnPropertySymbols = Object.getOwnPropertySymbols; + var hasOwnProperty = Object.prototype.hasOwnProperty; + var propIsEnumerable = Object.prototype.propertyIsEnumerable; + + function toObject(val) { + if (val === null || val === undefined) { + throw new TypeError( + "Object.assign cannot be called with null or undefined", + ); + } - var base = '', array = false, braces = ['{', '}']; + return Object(val); + } - // Make Array say that they are Array - if (isArray(value)) { - array = true; - braces = ['[', ']']; - } + function shouldUseNative() { + try { + if (!Object.assign) { + return false; + } - // Make functions say that they are functions - if (isFunction(value)) { - var n = value.name ? ': ' + value.name : ''; - base = ' [Function' + n + ']'; - } + // Detect buggy property enumeration order in older V8 versions. - // Make RegExps say that they are RegExps - if (isRegExp(value)) { - base = ' ' + RegExp.prototype.toString.call(value); - } + // https://bugs.chromium.org/p/v8/issues/detail?id=4118 + var test1 = new String("abc"); // eslint-disable-line no-new-wrappers + test1[5] = "de"; + if (Object.getOwnPropertyNames(test1)[0] === "5") { + return false; + } - // Make dates with properties first say the date - if (isDate(value)) { - base = ' ' + Date.prototype.toUTCString.call(value); - } + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test2 = {}; + for (var i = 0; i < 10; i++) { + test2["_" + String.fromCharCode(i)] = i; + } + var order2 = Object.getOwnPropertyNames(test2).map(function (n) { + return test2[n]; + }); + if (order2.join("") !== "0123456789") { + return false; + } - // Make error with message first say the error - if (isError(value)) { - base = ' ' + formatError(value); - } + // https://bugs.chromium.org/p/v8/issues/detail?id=3056 + var test3 = {}; + "abcdefghijklmnopqrst".split("").forEach(function (letter) { + test3[letter] = letter; + }); + if ( + Object.keys(Object.assign({}, test3)).join("") !== + "abcdefghijklmnopqrst" + ) { + return false; + } - if (keys.length === 0 && (!array || value.length == 0)) { - return braces[0] + base + braces[1]; - } + return true; + } catch (err) { + // We don't expect any of the above to throw, but better to be safe. + return false; + } + } - if (recurseTimes < 0) { - if (isRegExp(value)) { - return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp'); - } else { - return ctx.stylize('[Object]', 'special'); - } - } + module.exports = shouldUseNative() + ? Object.assign + : function (target, source) { + var from; + var to = toObject(target); + var symbols; - ctx.seen.push(value); + for (var s = 1; s < arguments.length; s++) { + from = Object(arguments[s]); - var output; - if (array) { - output = formatArray(ctx, value, recurseTimes, visibleKeys, keys); - } else { - output = keys.map(function(key) { - return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array); - }); - } + for (var key in from) { + if (hasOwnProperty.call(from, key)) { + to[key] = from[key]; + } + } + + if (getOwnPropertySymbols) { + symbols = getOwnPropertySymbols(from); + for (var i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(from, symbols[i])) { + to[symbols[i]] = from[symbols[i]]; + } + } + } + } - ctx.seen.pop(); + return to; + }; + }, {}], + 7: [function (require, module, exports) { + // shim for using process in browser + var process = module.exports = {}; - return reduceToSingleString(output, base, braces); -} + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + var cachedSetTimeout; + var cachedClearTimeout; -function formatPrimitive(ctx, value) { - if (isUndefined(value)) - return ctx.stylize('undefined', 'undefined'); - if (isString(value)) { - var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') - .replace(/'/g, "\\'") - .replace(/\\"/g, '"') + '\''; - return ctx.stylize(simple, 'string'); - } - if (isNumber(value)) - return ctx.stylize('' + value, 'number'); - if (isBoolean(value)) - return ctx.stylize('' + value, 'boolean'); - // For some reason typeof null is "object", so special case here. - if (isNull(value)) - return ctx.stylize('null', 'null'); -} - - -function formatError(value) { - return '[' + Error.prototype.toString.call(value) + ']'; -} - - -function formatArray(ctx, value, recurseTimes, visibleKeys, keys) { - var output = []; - for (var i = 0, l = value.length; i < l; ++i) { - if (hasOwnProperty(value, String(i))) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - String(i), true)); - } else { - output.push(''); - } - } - keys.forEach(function(key) { - if (!key.match(/^\d+$/)) { - output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, - key, true)); - } - }); - return output; -} - - -function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) { - var name, str, desc; - desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] }; - if (desc.get) { - if (desc.set) { - str = ctx.stylize('[Getter/Setter]', 'special'); - } else { - str = ctx.stylize('[Getter]', 'special'); - } - } else { - if (desc.set) { - str = ctx.stylize('[Setter]', 'special'); - } - } - if (!hasOwnProperty(visibleKeys, key)) { - name = '[' + key + ']'; - } - if (!str) { - if (ctx.seen.indexOf(desc.value) < 0) { - if (isNull(recurseTimes)) { - str = formatValue(ctx, desc.value, null); - } else { - str = formatValue(ctx, desc.value, recurseTimes - 1); - } - if (str.indexOf('\n') > -1) { - if (array) { - str = str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n').substr(2); - } else { - str = '\n' + str.split('\n').map(function(line) { - return ' ' + line; - }).join('\n'); + function defaultSetTimout() { + throw new Error("setTimeout has not been defined"); } - } - } else { - str = ctx.stylize('[Circular]', 'special'); - } - } - if (isUndefined(name)) { - if (array && key.match(/^\d+$/)) { - return str; - } - name = JSON.stringify('' + key); - if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { - name = name.substr(1, name.length - 2); - name = ctx.stylize(name, 'name'); - } else { - name = name.replace(/'/g, "\\'") - .replace(/\\"/g, '"') - .replace(/(^"|"$)/g, "'"); - name = ctx.stylize(name, 'string'); - } - } - - return name + ': ' + str; -} - - -function reduceToSingleString(output, base, braces) { - var numLinesEst = 0; - var length = output.reduce(function(prev, cur) { - numLinesEst++; - if (cur.indexOf('\n') >= 0) numLinesEst++; - return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1; - }, 0); - - if (length > 60) { - return braces[0] + - (base === '' ? '' : base + '\n ') + - ' ' + - output.join(',\n ') + - ' ' + - braces[1]; - } - - return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; -} - - -// NOTE: These type checking functions intentionally don't use `instanceof` -// because it is fragile and can be easily faked with `Object.create()`. -function isArray(ar) { - return Array.isArray(ar); -} -exports.isArray = isArray; - -function isBoolean(arg) { - return typeof arg === 'boolean'; -} -exports.isBoolean = isBoolean; - -function isNull(arg) { - return arg === null; -} -exports.isNull = isNull; - -function isNullOrUndefined(arg) { - return arg == null; -} -exports.isNullOrUndefined = isNullOrUndefined; - -function isNumber(arg) { - return typeof arg === 'number'; -} -exports.isNumber = isNumber; - -function isString(arg) { - return typeof arg === 'string'; -} -exports.isString = isString; - -function isSymbol(arg) { - return typeof arg === 'symbol'; -} -exports.isSymbol = isSymbol; - -function isUndefined(arg) { - return arg === void 0; -} -exports.isUndefined = isUndefined; - -function isRegExp(re) { - return isObject(re) && objectToString(re) === '[object RegExp]'; -} -exports.isRegExp = isRegExp; - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} -exports.isObject = isObject; - -function isDate(d) { - return isObject(d) && objectToString(d) === '[object Date]'; -} -exports.isDate = isDate; - -function isError(e) { - return isObject(e) && - (objectToString(e) === '[object Error]' || e instanceof Error); -} -exports.isError = isError; - -function isFunction(arg) { - return typeof arg === 'function'; -} -exports.isFunction = isFunction; - -function isPrimitive(arg) { - return arg === null || - typeof arg === 'boolean' || - typeof arg === 'number' || - typeof arg === 'string' || - typeof arg === 'symbol' || // ES6 symbol - typeof arg === 'undefined'; -} -exports.isPrimitive = isPrimitive; - -exports.isBuffer = require('./support/isBuffer'); - -function objectToString(o) { - return Object.prototype.toString.call(o); -} - - -function pad(n) { - return n < 10 ? '0' + n.toString(10) : n.toString(10); -} - - -var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', - 'Oct', 'Nov', 'Dec']; - -// 26 Feb 16:19:34 -function timestamp() { - var d = new Date(); - var time = [pad(d.getHours()), - pad(d.getMinutes()), - pad(d.getSeconds())].join(':'); - return [d.getDate(), months[d.getMonth()], time].join(' '); -} - - -// log is just a thin wrapper to console.log that prepends a timestamp -exports.log = function() { - console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments)); -}; - - -/** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be rewritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype. - * @param {function} superCtor Constructor function to inherit prototype from. - */ -exports.inherits = require('inherits'); - -exports._extend = function(origin, add) { - // Don't do anything if add isn't an object - if (!add || !isObject(add)) return origin; - - var keys = Object.keys(add); - var i = keys.length; - while (i--) { - origin[keys[i]] = add[keys[i]]; - } - return origin; -}; - -function hasOwnProperty(obj, prop) { - return Object.prototype.hasOwnProperty.call(obj, prop); -} - -}).call(this)}).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./support/isBuffer":3,"_process":7,"inherits":2}],5:[function(require,module,exports){ -// the whatwg-fetch polyfill installs the fetch() function -// on the global object (window or self) -// -// Return that as the export for use in Webpack, Browserify etc. -require('whatwg-fetch'); -module.exports = self.fetch.bind(self); - -},{"whatwg-fetch":8}],6:[function(require,module,exports){ -/* -object-assign -(c) Sindre Sorhus -@license MIT -*/ - -'use strict'; -/* eslint-disable no-unused-vars */ -var getOwnPropertySymbols = Object.getOwnPropertySymbols; -var hasOwnProperty = Object.prototype.hasOwnProperty; -var propIsEnumerable = Object.prototype.propertyIsEnumerable; - -function toObject(val) { - if (val === null || val === undefined) { - throw new TypeError('Object.assign cannot be called with null or undefined'); - } - - return Object(val); -} - -function shouldUseNative() { - try { - if (!Object.assign) { - return false; - } - - // Detect buggy property enumeration order in older V8 versions. - - // https://bugs.chromium.org/p/v8/issues/detail?id=4118 - var test1 = new String('abc'); // eslint-disable-line no-new-wrappers - test1[5] = 'de'; - if (Object.getOwnPropertyNames(test1)[0] === '5') { - return false; - } - - // https://bugs.chromium.org/p/v8/issues/detail?id=3056 - var test2 = {}; - for (var i = 0; i < 10; i++) { - test2['_' + String.fromCharCode(i)] = i; - } - var order2 = Object.getOwnPropertyNames(test2).map(function (n) { - return test2[n]; - }); - if (order2.join('') !== '0123456789') { - return false; - } - - // https://bugs.chromium.org/p/v8/issues/detail?id=3056 - var test3 = {}; - 'abcdefghijklmnopqrst'.split('').forEach(function (letter) { - test3[letter] = letter; - }); - if (Object.keys(Object.assign({}, test3)).join('') !== - 'abcdefghijklmnopqrst') { - return false; - } - - return true; - } catch (err) { - // We don't expect any of the above to throw, but better to be safe. - return false; - } -} - -module.exports = shouldUseNative() ? Object.assign : function (target, source) { - var from; - var to = toObject(target); - var symbols; - - for (var s = 1; s < arguments.length; s++) { - from = Object(arguments[s]); - - for (var key in from) { - if (hasOwnProperty.call(from, key)) { - to[key] = from[key]; - } - } - - if (getOwnPropertySymbols) { - symbols = getOwnPropertySymbols(from); - for (var i = 0; i < symbols.length; i++) { - if (propIsEnumerable.call(from, symbols[i])) { - to[symbols[i]] = from[symbols[i]]; - } - } - } - } - - return to; -}; - -},{}],7:[function(require,module,exports){ -// shim for using process in browser -var process = module.exports = {}; - -// cached from whatever global is present so that test runners that stub it -// don't break things. But we need to wrap it in a try catch in case it is -// wrapped in strict mode code which doesn't define any globals. It's inside a -// function because try/catches deoptimize in certain engines. - -var cachedSetTimeout; -var cachedClearTimeout; - -function defaultSetTimout() { - throw new Error('setTimeout has not been defined'); -} -function defaultClearTimeout () { - throw new Error('clearTimeout has not been defined'); -} -(function () { - try { - if (typeof setTimeout === 'function') { - cachedSetTimeout = setTimeout; - } else { + function defaultClearTimeout() { + throw new Error("clearTimeout has not been defined"); + } + (function () { + try { + if (typeof setTimeout === "function") { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === "function") { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + })(); + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ( + (cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && + setTimeout + ) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch (e) { + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch (e) { + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } } - } catch (e) { - cachedSetTimeout = defaultSetTimout; - } - try { - if (typeof clearTimeout === 'function') { + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ( + (cachedClearTimeout === defaultClearTimeout || + !cachedClearTimeout) && clearTimeout + ) { cachedClearTimeout = clearTimeout; - } else { - cachedClearTimeout = defaultClearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e) { + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e) { + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } } - } catch (e) { - cachedClearTimeout = defaultClearTimeout; - } -} ()) -function runTimeout(fun) { - if (cachedSetTimeout === setTimeout) { - //normal enviroments in sane situations - return setTimeout(fun, 0); - } - // if setTimeout wasn't available but was latter defined - if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { - cachedSetTimeout = setTimeout; - return setTimeout(fun, 0); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedSetTimeout(fun, 0); - } catch(e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedSetTimeout.call(null, fun, 0); - } catch(e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error - return cachedSetTimeout.call(this, fun, 0); + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while (len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); } - } + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; -} -function runClearTimeout(marker) { - if (cachedClearTimeout === clearTimeout) { - //normal enviroments in sane situations - return clearTimeout(marker); - } - // if clearTimeout wasn't available but was latter defined - if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { - cachedClearTimeout = clearTimeout; - return clearTimeout(marker); - } - try { - // when when somebody has screwed with setTimeout but no I.E. maddness - return cachedClearTimeout(marker); - } catch (e){ - try { - // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally - return cachedClearTimeout.call(null, marker); - } catch (e){ - // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. - // Some versions of I.E. have different rules for clearTimeout vs setTimeout - return cachedClearTimeout.call(this, marker); + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; } - } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = "browser"; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ""; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + process.prependListener = noop; + process.prependOnceListener = noop; + + process.listeners = function (name) { + return []; + }; + process.binding = function (name) { + throw new Error("process.binding is not supported"); + }; + process.cwd = function () { + return "/"; + }; + process.chdir = function (dir) { + throw new Error("process.chdir is not supported"); + }; + process.umask = function () { + return 0; + }; + }, {}], + 8: [function (require, module, exports) { + (function (global, factory) { + typeof exports === "object" && typeof module !== "undefined" + ? factory(exports) + : typeof define === "function" && define.amd + ? define(["exports"], factory) + : (factory(global.WHATWGFetch = {})); + })(this, function (exports) { + "use strict"; + + var global = (typeof globalThis !== "undefined" && globalThis) || + (typeof self !== "undefined" && self) || + (typeof global !== "undefined" && global); + + var support = { + searchParams: "URLSearchParams" in global, + iterable: "Symbol" in global && "iterator" in Symbol, + blob: "FileReader" in global && + "Blob" in global && + (function () { + try { + new Blob(); + return true; + } catch (e) { + return false; + } + })(), + formData: "FormData" in global, + arrayBuffer: "ArrayBuffer" in global, + }; -} -var queue = []; -var draining = false; -var currentQueue; -var queueIndex = -1; + function isDataView(obj) { + return obj && DataView.prototype.isPrototypeOf(obj); + } -function cleanUpNextTick() { - if (!draining || !currentQueue) { - return; - } - draining = false; - if (currentQueue.length) { - queue = currentQueue.concat(queue); - } else { - queueIndex = -1; - } - if (queue.length) { - drainQueue(); - } -} + if (support.arrayBuffer) { + var viewClasses = [ + "[object Int8Array]", + "[object Uint8Array]", + "[object Uint8ClampedArray]", + "[object Int16Array]", + "[object Uint16Array]", + "[object Int32Array]", + "[object Uint32Array]", + "[object Float32Array]", + "[object Float64Array]", + ]; + + var isArrayBufferView = ArrayBuffer.isView || + function (obj) { + return obj && + viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1; + }; + } -function drainQueue() { - if (draining) { - return; - } - var timeout = runTimeout(cleanUpNextTick); - draining = true; - - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - while (++queueIndex < len) { - if (currentQueue) { - currentQueue[queueIndex].run(); + function normalizeName(name) { + if (typeof name !== "string") { + name = String(name); } - } - queueIndex = -1; - len = queue.length; - } - currentQueue = null; - draining = false; - runClearTimeout(timeout); -} - -process.nextTick = function (fun) { - var args = new Array(arguments.length - 1); - if (arguments.length > 1) { - for (var i = 1; i < arguments.length; i++) { - args[i - 1] = arguments[i]; - } - } - queue.push(new Item(fun, args)); - if (queue.length === 1 && !draining) { - runTimeout(drainQueue); - } -}; - -// v8 likes predictible objects -function Item(fun, array) { - this.fun = fun; - this.array = array; -} -Item.prototype.run = function () { - this.fun.apply(null, this.array); -}; -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; -process.prependListener = noop; -process.prependOnceListener = noop; - -process.listeners = function (name) { return [] } - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],8:[function(require,module,exports){ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (factory((global.WHATWGFetch = {}))); -}(this, (function (exports) { 'use strict'; - - var global = - (typeof globalThis !== 'undefined' && globalThis) || - (typeof self !== 'undefined' && self) || - (typeof global !== 'undefined' && global); - - var support = { - searchParams: 'URLSearchParams' in global, - iterable: 'Symbol' in global && 'iterator' in Symbol, - blob: - 'FileReader' in global && - 'Blob' in global && - (function() { - try { - new Blob(); - return true - } catch (e) { - return false - } - })(), - formData: 'FormData' in global, - arrayBuffer: 'ArrayBuffer' in global - }; + if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === "") { + throw new TypeError( + 'Invalid character in header field name: "' + name + '"', + ); + } + return name.toLowerCase(); + } - function isDataView(obj) { - return obj && DataView.prototype.isPrototypeOf(obj) - } + function normalizeValue(value) { + if (typeof value !== "string") { + value = String(value); + } + return value; + } - if (support.arrayBuffer) { - var viewClasses = [ - '[object Int8Array]', - '[object Uint8Array]', - '[object Uint8ClampedArray]', - '[object Int16Array]', - '[object Uint16Array]', - '[object Int32Array]', - '[object Uint32Array]', - '[object Float32Array]', - '[object Float64Array]' - ]; - - var isArrayBufferView = - ArrayBuffer.isView || - function(obj) { - return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 - }; - } + // Build a destructive iterator for the value list + function iteratorFor(items) { + var iterator = { + next: function () { + var value = items.shift(); + return { done: value === undefined, value: value }; + }, + }; + + if (support.iterable) { + iterator[Symbol.iterator] = function () { + return iterator; + }; + } - function normalizeName(name) { - if (typeof name !== 'string') { - name = String(name); - } - if (/[^a-z0-9\-#$%&'*+.^_`|~!]/i.test(name) || name === '') { - throw new TypeError('Invalid character in header field name: "' + name + '"') - } - return name.toLowerCase() - } + return iterator; + } - function normalizeValue(value) { - if (typeof value !== 'string') { - value = String(value); - } - return value - } + function Headers(headers) { + this.map = {}; + + if (headers instanceof Headers) { + headers.forEach(function (value, name) { + this.append(name, value); + }, this); + } else if (Array.isArray(headers)) { + headers.forEach(function (header) { + this.append(header[0], header[1]); + }, this); + } else if (headers) { + Object.getOwnPropertyNames(headers).forEach(function (name) { + this.append(name, headers[name]); + }, this); + } + } - // Build a destructive iterator for the value list - function iteratorFor(items) { - var iterator = { - next: function() { - var value = items.shift(); - return {done: value === undefined, value: value} - } - }; + Headers.prototype.append = function (name, value) { + name = normalizeName(name); + value = normalizeValue(value); + var oldValue = this.map[name]; + this.map[name] = oldValue ? oldValue + ", " + value : value; + }; + + Headers.prototype["delete"] = function (name) { + delete this.map[normalizeName(name)]; + }; + + Headers.prototype.get = function (name) { + name = normalizeName(name); + return this.has(name) ? this.map[name] : null; + }; + + Headers.prototype.has = function (name) { + return this.map.hasOwnProperty(normalizeName(name)); + }; + + Headers.prototype.set = function (name, value) { + this.map[normalizeName(name)] = normalizeValue(value); + }; + + Headers.prototype.forEach = function (callback, thisArg) { + for (var name in this.map) { + if (this.map.hasOwnProperty(name)) { + callback.call(thisArg, this.map[name], name, this); + } + } + }; - if (support.iterable) { - iterator[Symbol.iterator] = function() { - return iterator - }; - } + Headers.prototype.keys = function () { + var items = []; + this.forEach(function (value, name) { + items.push(name); + }); + return iteratorFor(items); + }; - return iterator - } + Headers.prototype.values = function () { + var items = []; + this.forEach(function (value) { + items.push(value); + }); + return iteratorFor(items); + }; - function Headers(headers) { - this.map = {}; - - if (headers instanceof Headers) { - headers.forEach(function(value, name) { - this.append(name, value); - }, this); - } else if (Array.isArray(headers)) { - headers.forEach(function(header) { - this.append(header[0], header[1]); - }, this); - } else if (headers) { - Object.getOwnPropertyNames(headers).forEach(function(name) { - this.append(name, headers[name]); - }, this); - } - } + Headers.prototype.entries = function () { + var items = []; + this.forEach(function (value, name) { + items.push([name, value]); + }); + return iteratorFor(items); + }; - Headers.prototype.append = function(name, value) { - name = normalizeName(name); - value = normalizeValue(value); - var oldValue = this.map[name]; - this.map[name] = oldValue ? oldValue + ', ' + value : value; - }; - - Headers.prototype['delete'] = function(name) { - delete this.map[normalizeName(name)]; - }; - - Headers.prototype.get = function(name) { - name = normalizeName(name); - return this.has(name) ? this.map[name] : null - }; - - Headers.prototype.has = function(name) { - return this.map.hasOwnProperty(normalizeName(name)) - }; - - Headers.prototype.set = function(name, value) { - this.map[normalizeName(name)] = normalizeValue(value); - }; - - Headers.prototype.forEach = function(callback, thisArg) { - for (var name in this.map) { - if (this.map.hasOwnProperty(name)) { - callback.call(thisArg, this.map[name], name, this); - } - } - }; - - Headers.prototype.keys = function() { - var items = []; - this.forEach(function(value, name) { - items.push(name); - }); - return iteratorFor(items) - }; - - Headers.prototype.values = function() { - var items = []; - this.forEach(function(value) { - items.push(value); - }); - return iteratorFor(items) - }; - - Headers.prototype.entries = function() { - var items = []; - this.forEach(function(value, name) { - items.push([name, value]); - }); - return iteratorFor(items) - }; - - if (support.iterable) { - Headers.prototype[Symbol.iterator] = Headers.prototype.entries; - } + if (support.iterable) { + Headers.prototype[Symbol.iterator] = Headers.prototype.entries; + } - function consumed(body) { - if (body.bodyUsed) { - return Promise.reject(new TypeError('Already read')) - } - body.bodyUsed = true; - } + function consumed(body) { + if (body.bodyUsed) { + return Promise.reject(new TypeError("Already read")); + } + body.bodyUsed = true; + } - function fileReaderReady(reader) { - return new Promise(function(resolve, reject) { - reader.onload = function() { - resolve(reader.result); - }; - reader.onerror = function() { - reject(reader.error); - }; - }) - } + function fileReaderReady(reader) { + return new Promise(function (resolve, reject) { + reader.onload = function () { + resolve(reader.result); + }; + reader.onerror = function () { + reject(reader.error); + }; + }); + } - function readBlobAsArrayBuffer(blob) { - var reader = new FileReader(); - var promise = fileReaderReady(reader); - reader.readAsArrayBuffer(blob); - return promise - } + function readBlobAsArrayBuffer(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsArrayBuffer(blob); + return promise; + } - function readBlobAsText(blob) { - var reader = new FileReader(); - var promise = fileReaderReady(reader); - reader.readAsText(blob); - return promise - } + function readBlobAsText(blob) { + var reader = new FileReader(); + var promise = fileReaderReady(reader); + reader.readAsText(blob); + return promise; + } - function readArrayBufferAsText(buf) { - var view = new Uint8Array(buf); - var chars = new Array(view.length); + function readArrayBufferAsText(buf) { + var view = new Uint8Array(buf); + var chars = new Array(view.length); - for (var i = 0; i < view.length; i++) { - chars[i] = String.fromCharCode(view[i]); - } - return chars.join('') - } + for (var i = 0; i < view.length; i++) { + chars[i] = String.fromCharCode(view[i]); + } + return chars.join(""); + } - function bufferClone(buf) { - if (buf.slice) { - return buf.slice(0) - } else { - var view = new Uint8Array(buf.byteLength); - view.set(new Uint8Array(buf)); - return view.buffer - } - } + function bufferClone(buf) { + if (buf.slice) { + return buf.slice(0); + } else { + var view = new Uint8Array(buf.byteLength); + view.set(new Uint8Array(buf)); + return view.buffer; + } + } - function Body() { - this.bodyUsed = false; + function Body() { + this.bodyUsed = false; - this._initBody = function(body) { - /* + this._initBody = function (body) { + /* fetch-mock wraps the Response object in an ES6 Proxy to provide useful test harness features such as flush. However, on ES5 browsers without fetch or Proxy support pollyfills must be used; @@ -1649,992 +1887,1152 @@ process.umask = function() { return 0; }; semantic of setting Request.bodyUsed in the constructor before _initBody is called. */ - this.bodyUsed = this.bodyUsed; - this._bodyInit = body; - if (!body) { - this._bodyText = ''; - } else if (typeof body === 'string') { - this._bodyText = body; - } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { - this._bodyBlob = body; - } else if (support.formData && FormData.prototype.isPrototypeOf(body)) { - this._bodyFormData = body; - } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { - this._bodyText = body.toString(); - } else if (support.arrayBuffer && support.blob && isDataView(body)) { - this._bodyArrayBuffer = bufferClone(body.buffer); - // IE 10-11 can't handle a DataView body. - this._bodyInit = new Blob([this._bodyArrayBuffer]); - } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) { - this._bodyArrayBuffer = bufferClone(body); - } else { - this._bodyText = body = Object.prototype.toString.call(body); - } + this.bodyUsed = this.bodyUsed; + this._bodyInit = body; + if (!body) { + this._bodyText = ""; + } else if (typeof body === "string") { + this._bodyText = body; + } else if (support.blob && Blob.prototype.isPrototypeOf(body)) { + this._bodyBlob = body; + } else if ( + support.formData && FormData.prototype.isPrototypeOf(body) + ) { + this._bodyFormData = body; + } else if ( + support.searchParams && + URLSearchParams.prototype.isPrototypeOf(body) + ) { + this._bodyText = body.toString(); + } else if ( + support.arrayBuffer && support.blob && isDataView(body) + ) { + this._bodyArrayBuffer = bufferClone(body.buffer); + // IE 10-11 can't handle a DataView body. + this._bodyInit = new Blob([this._bodyArrayBuffer]); + } else if ( + support.arrayBuffer && + (ArrayBuffer.prototype.isPrototypeOf(body) || + isArrayBufferView(body)) + ) { + this._bodyArrayBuffer = bufferClone(body); + } else { + this._bodyText = body = Object.prototype.toString.call(body); + } + + if (!this.headers.get("content-type")) { + if (typeof body === "string") { + this.headers.set("content-type", "text/plain;charset=UTF-8"); + } else if (this._bodyBlob && this._bodyBlob.type) { + this.headers.set("content-type", this._bodyBlob.type); + } else if ( + support.searchParams && + URLSearchParams.prototype.isPrototypeOf(body) + ) { + this.headers.set( + "content-type", + "application/x-www-form-urlencoded;charset=UTF-8", + ); + } + } + }; + + if (support.blob) { + this.blob = function () { + var rejected = consumed(this); + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return Promise.resolve(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve(new Blob([this._bodyArrayBuffer])); + } else if (this._bodyFormData) { + throw new Error("could not read FormData body as blob"); + } else { + return Promise.resolve(new Blob([this._bodyText])); + } + }; + + this.arrayBuffer = function () { + if (this._bodyArrayBuffer) { + var isConsumed = consumed(this); + if (isConsumed) { + return isConsumed; + } + if (ArrayBuffer.isView(this._bodyArrayBuffer)) { + return Promise.resolve( + this._bodyArrayBuffer.buffer.slice( + this._bodyArrayBuffer.byteOffset, + this._bodyArrayBuffer.byteOffset + + this._bodyArrayBuffer.byteLength, + ), + ); + } else { + return Promise.resolve(this._bodyArrayBuffer); + } + } else { + return this.blob().then(readBlobAsArrayBuffer); + } + }; + } + + this.text = function () { + var rejected = consumed(this); + if (rejected) { + return rejected; + } + + if (this._bodyBlob) { + return readBlobAsText(this._bodyBlob); + } else if (this._bodyArrayBuffer) { + return Promise.resolve( + readArrayBufferAsText(this._bodyArrayBuffer), + ); + } else if (this._bodyFormData) { + throw new Error("could not read FormData body as text"); + } else { + return Promise.resolve(this._bodyText); + } + }; + + if (support.formData) { + this.formData = function () { + return this.text().then(decode); + }; + } + + this.json = function () { + return this.text().then(JSON.parse); + }; + + return this; + } + + // HTTP methods whose capitalization should be normalized + var methods = ["DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT"]; + + function normalizeMethod(method) { + var upcased = method.toUpperCase(); + return methods.indexOf(upcased) > -1 ? upcased : method; + } + + function Request(input, options) { + if (!(this instanceof Request)) { + throw new TypeError( + 'Please use the "new" operator, this DOM object constructor cannot be called as a function.', + ); + } + + options = options || {}; + var body = options.body; + + if (input instanceof Request) { + if (input.bodyUsed) { + throw new TypeError("Already read"); + } + this.url = input.url; + this.credentials = input.credentials; + if (!options.headers) { + this.headers = new Headers(input.headers); + } + this.method = input.method; + this.mode = input.mode; + this.signal = input.signal; + if (!body && input._bodyInit != null) { + body = input._bodyInit; + input.bodyUsed = true; + } + } else { + this.url = String(input); + } + + this.credentials = options.credentials || this.credentials || + "same-origin"; + if (options.headers || !this.headers) { + this.headers = new Headers(options.headers); + } + this.method = normalizeMethod( + options.method || this.method || "GET", + ); + this.mode = options.mode || this.mode || null; + this.signal = options.signal || this.signal; + this.referrer = null; + + if ((this.method === "GET" || this.method === "HEAD") && body) { + throw new TypeError("Body not allowed for GET or HEAD requests"); + } + this._initBody(body); + + if (this.method === "GET" || this.method === "HEAD") { + if ( + options.cache === "no-store" || options.cache === "no-cache" + ) { + // Search for a '_' parameter in the query string + var reParamSearch = /([?&])_=[^&]*/; + if (reParamSearch.test(this.url)) { + // If it already exists then set the value with the current time + this.url = this.url.replace( + reParamSearch, + "$1_=" + new Date().getTime(), + ); + } else { + // Otherwise add a new '_' parameter to the end with the current time + var reQueryString = /\?/; + this.url += (reQueryString.test(this.url) ? "&" : "?") + + "_=" + new Date().getTime(); + } + } + } + } - if (!this.headers.get('content-type')) { - if (typeof body === 'string') { - this.headers.set('content-type', 'text/plain;charset=UTF-8'); - } else if (this._bodyBlob && this._bodyBlob.type) { - this.headers.set('content-type', this._bodyBlob.type); - } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) { - this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8'); - } - } - }; + Request.prototype.clone = function () { + return new Request(this, { body: this._bodyInit }); + }; + + function decode(body) { + var form = new FormData(); + body + .trim() + .split("&") + .forEach(function (bytes) { + if (bytes) { + var split = bytes.split("="); + var name = split.shift().replace(/\+/g, " "); + var value = split.join("=").replace(/\+/g, " "); + form.append( + decodeURIComponent(name), + decodeURIComponent(value), + ); + } + }); + return form; + } - if (support.blob) { - this.blob = function() { - var rejected = consumed(this); - if (rejected) { - return rejected - } + function parseHeaders(rawHeaders) { + var headers = new Headers(); + // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space + // https://tools.ietf.org/html/rfc7230#section-3.2 + var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, " "); + // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill + // https://github.com/github/fetch/issues/748 + // https://github.com/zloirock/core-js/issues/751 + preProcessedHeaders + .split("\r") + .map(function (header) { + return header.indexOf("\n") === 0 + ? header.substr(1, header.length) + : header; + }) + .forEach(function (line) { + var parts = line.split(":"); + var key = parts.shift().trim(); + if (key) { + var value = parts.join(":").trim(); + headers.append(key, value); + } + }); + return headers; + } - if (this._bodyBlob) { - return Promise.resolve(this._bodyBlob) - } else if (this._bodyArrayBuffer) { - return Promise.resolve(new Blob([this._bodyArrayBuffer])) - } else if (this._bodyFormData) { - throw new Error('could not read FormData body as blob') - } else { - return Promise.resolve(new Blob([this._bodyText])) - } - }; - - this.arrayBuffer = function() { - if (this._bodyArrayBuffer) { - var isConsumed = consumed(this); - if (isConsumed) { - return isConsumed - } - if (ArrayBuffer.isView(this._bodyArrayBuffer)) { - return Promise.resolve( - this._bodyArrayBuffer.buffer.slice( - this._bodyArrayBuffer.byteOffset, - this._bodyArrayBuffer.byteOffset + this._bodyArrayBuffer.byteLength - ) - ) - } else { - return Promise.resolve(this._bodyArrayBuffer) + Body.call(Request.prototype); + + function Response(bodyInit, options) { + if (!(this instanceof Response)) { + throw new TypeError( + 'Please use the "new" operator, this DOM object constructor cannot be called as a function.', + ); + } + if (!options) { + options = {}; + } + + this.type = "default"; + this.status = options.status === undefined ? 200 : options.status; + this.ok = this.status >= 200 && this.status < 300; + this.statusText = options.statusText === undefined + ? "" + : "" + options.statusText; + this.headers = new Headers(options.headers); + this.url = options.url || ""; + this._initBody(bodyInit); } - } else { - return this.blob().then(readBlobAsArrayBuffer) - } - }; - } - this.text = function() { - var rejected = consumed(this); - if (rejected) { - return rejected - } + Body.call(Response.prototype); - if (this._bodyBlob) { - return readBlobAsText(this._bodyBlob) - } else if (this._bodyArrayBuffer) { - return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer)) - } else if (this._bodyFormData) { - throw new Error('could not read FormData body as text') - } else { - return Promise.resolve(this._bodyText) - } - }; + Response.prototype.clone = function () { + return new Response(this._bodyInit, { + status: this.status, + statusText: this.statusText, + headers: new Headers(this.headers), + url: this.url, + }); + }; - if (support.formData) { - this.formData = function() { - return this.text().then(decode) - }; - } + Response.error = function () { + var response = new Response(null, { status: 0, statusText: "" }); + response.type = "error"; + return response; + }; - this.json = function() { - return this.text().then(JSON.parse) - }; + var redirectStatuses = [301, 302, 303, 307, 308]; - return this - } + Response.redirect = function (url, status) { + if (redirectStatuses.indexOf(status) === -1) { + throw new RangeError("Invalid status code"); + } - // HTTP methods whose capitalization should be normalized - var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']; + return new Response(null, { + status: status, + headers: { location: url }, + }); + }; + + exports.DOMException = global.DOMException; + try { + new exports.DOMException(); + } catch (err) { + exports.DOMException = function (message, name) { + this.message = message; + this.name = name; + var error = Error(message); + this.stack = error.stack; + }; + exports.DOMException.prototype = Object.create(Error.prototype); + exports.DOMException.prototype.constructor = exports.DOMException; + } - function normalizeMethod(method) { - var upcased = method.toUpperCase(); - return methods.indexOf(upcased) > -1 ? upcased : method - } + function fetch(input, init) { + return new Promise(function (resolve, reject) { + var request = new Request(input, init); - function Request(input, options) { - if (!(this instanceof Request)) { - throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.') - } + if (request.signal && request.signal.aborted) { + return reject( + new exports.DOMException("Aborted", "AbortError"), + ); + } - options = options || {}; - var body = options.body; + var xhr = new XMLHttpRequest(); - if (input instanceof Request) { - if (input.bodyUsed) { - throw new TypeError('Already read') - } - this.url = input.url; - this.credentials = input.credentials; - if (!options.headers) { - this.headers = new Headers(input.headers); - } - this.method = input.method; - this.mode = input.mode; - this.signal = input.signal; - if (!body && input._bodyInit != null) { - body = input._bodyInit; - input.bodyUsed = true; - } - } else { - this.url = String(input); - } + function abortXhr() { + xhr.abort(); + } - this.credentials = options.credentials || this.credentials || 'same-origin'; - if (options.headers || !this.headers) { - this.headers = new Headers(options.headers); - } - this.method = normalizeMethod(options.method || this.method || 'GET'); - this.mode = options.mode || this.mode || null; - this.signal = options.signal || this.signal; - this.referrer = null; + xhr.onload = function () { + var options = { + status: xhr.status, + statusText: xhr.statusText, + headers: parseHeaders(xhr.getAllResponseHeaders() || ""), + }; + options.url = "responseURL" in xhr + ? xhr.responseURL + : options.headers.get("X-Request-URL"); + var body = "response" in xhr ? xhr.response : xhr.responseText; + setTimeout(function () { + resolve(new Response(body, options)); + }, 0); + }; + + xhr.onerror = function () { + setTimeout(function () { + reject(new TypeError("Network request failed")); + }, 0); + }; + + xhr.ontimeout = function () { + setTimeout(function () { + reject(new TypeError("Network request failed")); + }, 0); + }; + + xhr.onabort = function () { + setTimeout(function () { + reject(new exports.DOMException("Aborted", "AbortError")); + }, 0); + }; + + function fixUrl(url) { + try { + return url === "" && global.location.href + ? global.location.href + : url; + } catch (e) { + return url; + } + } + + xhr.open(request.method, fixUrl(request.url), true); + + if (request.credentials === "include") { + xhr.withCredentials = true; + } else if (request.credentials === "omit") { + xhr.withCredentials = false; + } + + if ("responseType" in xhr) { + if (support.blob) { + xhr.responseType = "blob"; + } else if ( + support.arrayBuffer && + request.headers.get("Content-Type") && + request.headers.get("Content-Type").indexOf( + "application/octet-stream", + ) !== -1 + ) { + xhr.responseType = "arraybuffer"; + } + } + + if ( + init && typeof init.headers === "object" && + !(init.headers instanceof Headers) + ) { + Object.getOwnPropertyNames(init.headers).forEach( + function (name) { + xhr.setRequestHeader( + name, + normalizeValue(init.headers[name]), + ); + }, + ); + } else { + request.headers.forEach(function (value, name) { + xhr.setRequestHeader(name, value); + }); + } - if ((this.method === 'GET' || this.method === 'HEAD') && body) { - throw new TypeError('Body not allowed for GET or HEAD requests') - } - this._initBody(body); - - if (this.method === 'GET' || this.method === 'HEAD') { - if (options.cache === 'no-store' || options.cache === 'no-cache') { - // Search for a '_' parameter in the query string - var reParamSearch = /([?&])_=[^&]*/; - if (reParamSearch.test(this.url)) { - // If it already exists then set the value with the current time - this.url = this.url.replace(reParamSearch, '$1_=' + new Date().getTime()); - } else { - // Otherwise add a new '_' parameter to the end with the current time - var reQueryString = /\?/; - this.url += (reQueryString.test(this.url) ? '&' : '?') + '_=' + new Date().getTime(); - } - } - } - } + if (request.signal) { + request.signal.addEventListener("abort", abortXhr); - Request.prototype.clone = function() { - return new Request(this, {body: this._bodyInit}) - }; - - function decode(body) { - var form = new FormData(); - body - .trim() - .split('&') - .forEach(function(bytes) { - if (bytes) { - var split = bytes.split('='); - var name = split.shift().replace(/\+/g, ' '); - var value = split.join('=').replace(/\+/g, ' '); - form.append(decodeURIComponent(name), decodeURIComponent(value)); - } - }); - return form - } + xhr.onreadystatechange = function () { + // DONE (success or failure) + if (xhr.readyState === 4) { + request.signal.removeEventListener("abort", abortXhr); + } + }; + } - function parseHeaders(rawHeaders) { - var headers = new Headers(); - // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space - // https://tools.ietf.org/html/rfc7230#section-3.2 - var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' '); - // Avoiding split via regex to work around a common IE11 bug with the core-js 3.6.0 regex polyfill - // https://github.com/github/fetch/issues/748 - // https://github.com/zloirock/core-js/issues/751 - preProcessedHeaders - .split('\r') - .map(function(header) { - return header.indexOf('\n') === 0 ? header.substr(1, header.length) : header - }) - .forEach(function(line) { - var parts = line.split(':'); - var key = parts.shift().trim(); - if (key) { - var value = parts.join(':').trim(); - headers.append(key, value); - } - }); - return headers - } + xhr.send( + typeof request._bodyInit === "undefined" + ? null + : request._bodyInit, + ); + }); + } - Body.call(Request.prototype); + fetch.polyfill = true; - function Response(bodyInit, options) { - if (!(this instanceof Response)) { - throw new TypeError('Please use the "new" operator, this DOM object constructor cannot be called as a function.') - } - if (!options) { - options = {}; - } + if (!global.fetch) { + global.fetch = fetch; + global.Headers = Headers; + global.Request = Request; + global.Response = Response; + } - this.type = 'default'; - this.status = options.status === undefined ? 200 : options.status; - this.ok = this.status >= 200 && this.status < 300; - this.statusText = options.statusText === undefined ? '' : '' + options.statusText; - this.headers = new Headers(options.headers); - this.url = options.url || ''; - this._initBody(bodyInit); - } + exports.Headers = Headers; + exports.Request = Request; + exports.Response = Response; + exports.fetch = fetch; - Body.call(Response.prototype); + Object.defineProperty(exports, "__esModule", { value: true }); + }); + }, {}], + 9: [function (require, module, exports) { + require("isomorphic-fetch"); + const assert = require("assert"); + + const defaultLocalizer = (text) => text; + const isNodeJs = typeof window === "undefined"; + + class Cloud { + constructor(url, clientId, username, localize = defaultLocalizer) { + this.clientId = clientId; + this.username = username; + this.projectId = null; + this.roleId = null; + this.url = url; + this.token = null; // only needed in NodeJs + this.localize = localize; + } - Response.prototype.clone = function() { - return new Response(this._bodyInit, { - status: this.status, - statusText: this.statusText, - headers: new Headers(this.headers), - url: this.url - }) - }; + clear() { + this.username = null; + this.token = null; + } - Response.error = function() { - var response = new Response(null, {status: 0, statusText: ''}); - response.type = 'error'; - return response - }; + hasProtocol() { + return this.url.toLowerCase().indexOf("http") === 0; + } - var redirectStatuses = [301, 302, 303, 307, 308]; + async resetPassword(username) { + const response = await this.fetch(`/users/${username}/password`, { + method: "POST", + }); + return await response.text(); + } - Response.redirect = function(url, status) { - if (redirectStatuses.indexOf(status) === -1) { - throw new RangeError('Invalid status code') - } + async login( + username, + password, + remember, // TODO: use this... + strategy = "NetsBlox", + ) { + const credentials = {}; + credentials[strategy] = { username, password }; + const body = { + credentials, + clientId: this.clientId, + }; + const response = await this.post("/users/login", body); + this.username = await response.text(); + if (isNodeJs) { + const cookie = response.headers.get("set-cookie"); + assert(cookie, new CloudError("No cookie received")); + this.token = cookie.split("=")[1].split(";").shift(); + } + return this.username; + } - return new Response(null, {status: status, headers: {location: url}}) - }; - - exports.DOMException = global.DOMException; - try { - new exports.DOMException(); - } catch (err) { - exports.DOMException = function(message, name) { - this.message = message; - this.name = name; - var error = Error(message); - this.stack = error.stack; - }; - exports.DOMException.prototype = Object.create(Error.prototype); - exports.DOMException.prototype.constructor = exports.DOMException; - } + async getProjectList() { + const response = await this.fetch( + `/projects/user/${this.username}`, + ); + return await response.json(); + } - function fetch(input, init) { - return new Promise(function(resolve, reject) { - var request = new Request(input, init); + async getSharedProjectList() { + const response = await this.fetch( + `/projects/shared/${this.username}`, + ); + return await response.json(); + } - if (request.signal && request.signal.aborted) { - return reject(new exports.DOMException('Aborted', 'AbortError')) - } + async changePassword( + oldPW, + newPW, + ) { + const body = JSON.stringify(newPW); + const response = await this.fetch( + `/users/${this.username}/password`, + { method: "PATCH", body }, + ); + return await response.text(); + } - var xhr = new XMLHttpRequest(); + parseResponse(src) { + var ans = [], + lines; + if (!src) return ans; + lines = src.split(" "); + lines.forEach(function (service) { + var entries = service.split("&"), + dict = {}; + entries.forEach(function (entry) { + var pair = entry.split("="), + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + dict[key] = val; + }); + ans.push(dict); + }); + return ans; + } - function abortXhr() { - xhr.abort(); - } + parseDict(src) { + var dict = {}; + if (!src) return dict; + src.split("&").forEach(function (entry) { + var pair = entry.split("="), + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + dict[key] = val; + }); + return dict; + } - xhr.onload = function() { - var options = { - status: xhr.status, - statusText: xhr.statusText, - headers: parseHeaders(xhr.getAllResponseHeaders() || '') - }; - options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL'); - var body = 'response' in xhr ? xhr.response : xhr.responseText; - setTimeout(function() { - resolve(new Response(body, options)); - }, 0); - }; - - xhr.onerror = function() { - setTimeout(function() { - reject(new TypeError('Network request failed')); - }, 0); - }; - - xhr.ontimeout = function() { - setTimeout(function() { - reject(new TypeError('Network request failed')); - }, 0); - }; - - xhr.onabort = function() { - setTimeout(function() { - reject(new exports.DOMException('Aborted', 'AbortError')); - }, 0); - }; - - function fixUrl(url) { - try { - return url === '' && global.location.href ? global.location.href : url - } catch (e) { - return url - } - } + encodeDict(dict) { + var str = "", + pair, + key; + if (!dict) return null; + for (key in dict) { + if (dict.hasOwnProperty(key)) { + pair = encodeURIComponent(key) + + "=" + + encodeURIComponent(dict[key]); + if (str.length > 0) { + str += "&"; + } + str += pair; + } + } + return str; + } - xhr.open(request.method, fixUrl(request.url), true); + async getUserData() { + const response = await this.fetch(`/users/${this.username}`); + return await response.json(); + } - if (request.credentials === 'include') { - xhr.withCredentials = true; - } else if (request.credentials === 'omit') { - xhr.withCredentials = false; - } + async addRole(name) { + const response = await this.post( + `/projects/id/${this.projectId}/`, + { name }, + ); + // TODO: should I request the new project state, too? + // I shouldn't have to since we should be subscribed to changes... + //return await response.json(); + } - if ('responseType' in xhr) { - if (support.blob) { - xhr.responseType = 'blob'; - } else if ( - support.arrayBuffer && - request.headers.get('Content-Type') && - request.headers.get('Content-Type').indexOf('application/octet-stream') !== -1 - ) { - xhr.responseType = 'arraybuffer'; - } - } + async saveRole(roleData) { + const url = `/projects/id/${this.projectId}/${this.roleId}`; + const options = { + method: "POST", + body: JSON.stringify(roleData), + }; + await this.fetch(url, options); + } - if (init && typeof init.headers === 'object' && !(init.headers instanceof Headers)) { - Object.getOwnPropertyNames(init.headers).forEach(function(name) { - xhr.setRequestHeader(name, normalizeValue(init.headers[name])); - }); - } else { - request.headers.forEach(function(value, name) { - xhr.setRequestHeader(name, value); - }); - } + async renameRole(roleId, name) { + const body = { + name, + clientId: this.clientId, + }; + const response = await this.patch( + `/projects/id/${this.projectId}/${roleId}`, + body, + ); + // TODO: error handling + //return await response.json(); + } - if (request.signal) { - request.signal.addEventListener('abort', abortXhr); + async renameProject(name) { + const body = { + name, + clientId: this.clientId, + }; + const response = await this.patch( + `/projects/id/${this.projectId}`, + body, + ); + } - xhr.onreadystatechange = function() { - // DONE (success or failure) - if (xhr.readyState === 4) { - request.signal.removeEventListener('abort', abortXhr); + async reportLatestRole(id, data) { + const clientId = this.clientId; + const options = { + method: "POST", + body: JSON.stringify({ id, data }), + }; + await this.fetch( + `/projects/id/${this.projectId}/${this.roleId}/latest?clientId=${clientId}`, + options, + ); } - }; - } - xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit); - }) - } + async cloneRole(roleId) { + const projectId = this.projectId; + const fetchRoleResponse = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest`, + ); + const { name, code, media } = await fetchRoleResponse.json(); + const response = await this.post(`/projects/id/${projectId}/`, { + name, + code, + media, + }); + } - fetch.polyfill = true; + async sendOccupantInvite(username, roleId) { + const body = { username, roleId }; + await this.post( + `/network/id/${this.projectId}/occupants/invite`, + body, + ); + } - if (!global.fetch) { - global.fetch = fetch; - global.Headers = Headers; - global.Request = Request; - global.Response = Response; - } + async evictOccupant(clientID) { + const method = "DELETE"; + await this.fetch( + `/network/id/${this.projectdId}/occupants/${clientID}`, + { method }, + ); + } - exports.Headers = Headers; - exports.Request = Request; - exports.Response = Response; - exports.fetch = fetch; + async getCollaboratorList() { + const response = await this.get( + `/projects/id/${this.projectId}/collaborators/`, + ); + return await response.json(); + } - Object.defineProperty(exports, '__esModule', { value: true }); + async getCollaboratorRequestList() { + const response = await this.get( + `/collaboration-invites/user/${this.username}/`, + ); + return await response.json(); + } -}))); + async sendCollaborateRequest(username) { + await this.post( + `/collaboration-invites/${this.projectId}/invite/${username}`, + ); + } -},{}],9:[function(require,module,exports){ -require('isomorphic-fetch'); -const assert = require('assert'); + async respondToCollaborateRequest(id, accepted) { + const newState = accepted ? "ACCEPTED" : "REJECTED"; + await this.post(`/collaboration-invites/id/${id}`, newState); + } -const defaultLocalizer = text => text; -const isNodeJs = typeof window === 'undefined'; + async removeCollaborator(username, projectId) { + await this.delete( + `/projects/id/${projectId}/collaborators/${username}`, + ); + } -class Cloud { - constructor(url, clientId, username, localize=defaultLocalizer) { - this.clientId = clientId; - this.username = username; - this.projectId = null; - this.roleId = null; - this.url = url; - this.token = null; // only needed in NodeJs - this.localize = localize; - } + async getOnlineFriendList() { + const response = await this.get(`/friends/${this.username}/online`); + return await response.json(); + } - clear() { - this.username = null; - this.token = null; - } + async getFriendList() { + const response = await this.get(`/friends/${this.username}/`); + return await response.json(); + } - hasProtocol() { - return this.url.toLowerCase().indexOf('http') === 0; - } + async getRole(projectId, roleId) { + const qs = this.clientId ? `clientId=${this.clientId}` : ""; + const response = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest?${qs}`, + ); + const project = await response.json(); + // TODO: Set the state here? + this.setLocalState(projectId, roleId); + return project; + } - async resetPassword(username) { - const response = await this.fetch(`/users/${username}/password`, {method: 'POST'}); - return await response.text(); - }; - - async login( - username, - password, - remember, // TODO: use this... - strategy = 'NetsBlox', - ) { - const credentials = {}; - credentials[strategy] = {username, password}; - const body = { - credentials, - clientId: this.clientId, - }; - const response = await this.post('/users/login', body); - this.username = await response.text(); - if (isNodeJs) { - const cookie = response.headers.get('set-cookie'); - assert(cookie, new CloudError('No cookie received')); - this.token = cookie.split('=')[1].split(';').shift(); - } - return this.username; - } + async getProjectMetadata(projectId) { + const response = await this.fetch( + `/projects/id/${projectId}/metadata`, + ); + const project = await response.json(); + return project; + } - async getProjectList() { - const response = await this.fetch(`/projects/user/${this.username}`); - return await response.json(); - } + async getProjectByName(owner, name) { + const response = await this.fetch( + `/projects/user/${owner}/${name}`, + ); + return await response.json(); + } - async getSharedProjectList() { - const response = await this.fetch(`/projects/shared/${this.username}`); - return await response.json(); - } + async getProjectMetadataByName(owner, name) { + const response = await this.fetch( + `/projects/user/${owner}/${name}/metadata`, + ); + return await response.json(); + } - async changePassword( - oldPW, - newPW, - ) { - const body = JSON.stringify(newPW); - const response = await this.fetch( - `/users/${this.username}/password`, - {method: 'PATCH', body} - ); - return await response.text(); - } + async startNetworkTrace(projectId) { + const response = await this.post(`/network/id/${projectId}/trace/`); + return await response.text(); + } - parseResponse(src) { - var ans = [], - lines; - if (!src) {return ans; } - lines = src.split(" "); - lines.forEach(function (service) { - var entries = service.split("&"), - dict = {}; - entries.forEach(function (entry) { - var pair = entry.split("="), - key = decodeURIComponent(pair[0]), - val = decodeURIComponent(pair[1]); - dict[key] = val; - }); - ans.push(dict); - }); - return ans; - }; - - parseDict(src) { - var dict = {}; - if (!src) {return dict; } - src.split("&").forEach(function (entry) { - var pair = entry.split("="), - key = decodeURIComponent(pair[0]), - val = decodeURIComponent(pair[1]); - dict[key] = val; - }); - return dict; - }; - - encodeDict(dict) { - var str = '', - pair, - key; - if (!dict) {return null; } - for (key in dict) { - if (dict.hasOwnProperty(key)) { - pair = encodeURIComponent(key) - + '=' - + encodeURIComponent(dict[key]); - if (str.length > 0) { - str += '&'; - } - str += pair; - } - } - return str; - }; - - async getUserData() { - const response = await this.fetch(`/users/${this.username}`); - return await response.json(); - }; - - async addRole(name) { - const response = await this.post(`/projects/id/${this.projectId}/`, {name}); - // TODO: should I request the new project state, too? - // I shouldn't have to since we should be subscribed to changes... - //return await response.json(); - }; - - async saveRole(roleData) { - - const url = `/projects/id/${this.projectId}/${this.roleId}`; - const options = { - method: 'POST', - body: JSON.stringify(roleData), - }; - await this.fetch(url, options); - }; + async stopNetworkTrace(projectId, traceId) { + const response = await this.post( + `/network/id/${projectId}/trace/${traceId}/stop`, + ); + return await response.text(); + } - async renameRole(roleId, name) { - const body = { - name, - clientId: this.clientId, - }; - const response = await this.patch(`/projects/id/${this.projectId}/${roleId}`, body); - // TODO: error handling - //return await response.json(); - }; - - async renameProject(name) { - const body = { - name, - clientId: this.clientId, - }; - const response = await this.patch(`/projects/id/${this.projectId}`, body); - }; - - async reportLatestRole(id, data) { - const clientId = this.clientId; - const options = { - method: 'POST', - body: JSON.stringify({id, data}) - }; - await this.fetch(`/projects/id/${this.projectId}/${this.roleId}/latest?clientId=${clientId}`, options); - }; - - async cloneRole(roleId) { - const projectId = this.projectId; - const fetchRoleResponse = await this.fetch(`/projects/id/${projectId}/${roleId}/latest`); - const {name, code, media} = await fetchRoleResponse.json(); - const response = await this.post(`/projects/id/${projectId}/`, {name, code, media}); - }; - - async sendOccupantInvite(username, roleId) { - const body = {username, roleId}; - await this.post(`/network/id/${this.projectId}/occupants/invite`, body); - }; - - async evictOccupant(clientID) { - const method = 'DELETE'; - await this.fetch(`/network/id/${this.projectdId}/occupants/${clientID}`, {method}); - }; - - async getCollaboratorList() { - const response = await this.get(`/projects/id/${this.projectId}/collaborators/`); - return await response.json(); - }; - - async getCollaboratorRequestList() { - const response = await this.get(`/collaboration-invites/user/${this.username}/`); - return await response.json(); - } + async getNetworkTrace(projectId, traceId) { + const response = await this.fetch( + `/network/id/${projectId}/trace/${traceId}`, + ); + return await response.json(); + } - async sendCollaborateRequest(username) { - await this.post(`/collaboration-invites/${this.projectId}/invite/${username}`); - }; + async getFriendRequestList() { + const response = await this.get( + `/friends/${this.username}/invites/`, + ); + return response.json(); + } - async respondToCollaborateRequest(id, accepted) { - const newState = accepted ? 'ACCEPTED' : 'REJECTED'; - await this.post(`/collaboration-invites/id/${id}`, newState); - }; + async sendFriendRequest(username) { + await this.post( + `/friends/${this.username}/invite/`, + username.trim(), + ); + } - async removeCollaborator(username, projectId) { - await this.delete(`/projects/id/${projectId}/collaborators/${username}`); - }; + async unfriend(username) { + username = encodeURIComponent(username.trim()); + await this.post(`/friends/${this.username}/unfriend/${username}`); + } - async getOnlineFriendList() { - const response = await this.get(`/friends/${this.username}/online`); - return await response.json(); - } + async respondToFriendRequest(sender, newState) { + sender = encodeURIComponent(sender); + await this.post( + `/friends/${this.username}/invites/${sender}`, + newState, + ); + } - async getFriendList() { - const response = await this.get(`/friends/${this.username}/`); - return await response.json(); - }; - - async getRole(projectId, roleId) { - const qs = this.clientId ? `clientId=${this.clientId}` : ''; - const response = await this.fetch(`/projects/id/${projectId}/${roleId}/latest?${qs}`); - const project = await response.json(); - // TODO: Set the state here? - this.setLocalState(projectId, roleId); - return project; - }; - - async getProjectMetadata(projectId) { - const response = await this.fetch(`/projects/id/${projectId}/metadata`); - const project = await response.json(); - return project; - }; - - async getProjectByName(owner, name) { - const response = await this.fetch(`/projects/user/${owner}/${name}`); - return await response.json(); - }; - - async getProjectMetadataByName(owner, name) { - const response = await this.fetch(`/projects/user/${owner}/${name}/metadata`); - return await response.json(); - }; - - async startNetworkTrace(projectId) { - const response = await this.post(`/network/id/${projectId}/trace/`); - return await response.text(); - }; - - async stopNetworkTrace(projectId, traceId) { - const response = await this.post(`/network/id/${projectId}/trace/${traceId}/stop`); - return await response.text(); - }; - - async getNetworkTrace(projectId, traceId) { - const response = await this.fetch(`/network/id/${projectId}/trace/${traceId}`); - return await response.json(); - }; - - async getFriendRequestList() { - const response = await this.get(`/friends/${this.username}/invites/`); - return response.json(); - } + async deleteRole(roleId) { + const method = "DELETE"; + await this.fetch(`/projects/id/${this.projectId}/${roleId}`, { + method, + }); + } - async sendFriendRequest(username) { - await this.post(`/friends/${this.username}/invite/`, username.trim()); - } + async deleteProject(projectId) { + const method = "DELETE"; + await this.fetch(`/projects/id/${projectId}`, { method }); + } - async unfriend(username) { - username = encodeURIComponent(username.trim()); - await this.post(`/friends/${this.username}/unfriend/${username}`); - } + async publishProject(projectId) { + const method = "POST"; + await this.fetch(`/projects/id/${projectId}/publish`, { method }); + } - async respondToFriendRequest(sender, newState) { - sender = encodeURIComponent(sender); - await this.post(`/friends/${this.username}/invites/${sender}`, newState); - } + async unpublishProject(projectId) { + const method = "POST"; + await this.fetch(`/projects/id/${projectId}/unpublish`, { method }); + } - async deleteRole(roleId) { - const method = 'DELETE'; - await this.fetch(`/projects/id/${this.projectId}/${roleId}`, {method}); - }; - - async deleteProject(projectId) { - const method = 'DELETE'; - await this.fetch(`/projects/id/${projectId}`, {method}); - }; - - async publishProject(projectId) { - const method = 'POST'; - await this.fetch(`/projects/id/${projectId}/publish`, {method}); - }; - - async unpublishProject(projectId) { - const method = 'POST'; - await this.fetch(`/projects/id/${projectId}/unpublish`, {method}); - }; - - reconnect(callback, errorCall) { - if (!this.username) { - this.message('You are not logged in'); - return; - } + reconnect(callback, errorCall) { + if (!this.username) { + this.message("You are not logged in"); + return; + } - // need to set 'api' from setClientState - let promise = this.setClientState(); - if (callback && errorCall) { - promise = promise.then(callback) + // need to set 'api' from setClientState + let promise = this.setClientState(); + if (callback && errorCall) { + promise = promise.then(callback) .catch(errorCall); - } - return promise; - }; - - async logout() { - const method = 'POST'; - await this.fetch('/users/logout', {method}); - this.clear(); - return true; - }; - - async signup( - username, - password, - email, - ) { - const body = { + } + return promise; + } + + async logout() { + const method = "POST"; + await this.fetch("/users/logout", { method }); + this.clear(); + return true; + } + + async signup( username, password, email, - }; - const response = await this.post('/users/create', body); - return await response.text(); - }; - - async saveProjectCopy() { - const response = await this.fetch(`/projects/${this.projectId}/latest`); - const xml = await response.text(); - const options = { - method: 'POST', - body: xml, // TODO: add options for allow rename? - }; - const saveResponse = await this.fetch(`/projects/`, options); + ) { + const body = { + username, + password, + email, + }; + const response = await this.post("/users/create", body); + return await response.text(); + } - // TODO: set the state with the network overlay - //this.setLocalState(response.projectId, this.roleId); + async saveProjectCopy() { + const response = await this.fetch( + `/projects/${this.projectId}/latest`, + ); + const xml = await response.text(); + const options = { + method: "POST", + body: xml, // TODO: add options for allow rename? + }; + const saveResponse = await this.fetch(`/projects/`, options); + + // TODO: set the state with the network overlay + //this.setLocalState(response.projectId, this.roleId); + + return saveResponse.status == 200; + } - return saveResponse.status == 200; - }; + async patch(url, body) { + const opts = { + method: "PATCH", + }; + if (body !== undefined) { + opts.body = JSON.stringify(body); + } + return await this.fetch(url, opts); + } - async patch(url, body) { - const opts = { - method: 'PATCH', - }; - if (body !== undefined) { - opts.body = JSON.stringify(body); - } - return await this.fetch(url, opts); - }; + async post(url, body) { + const opts = { + method: "POST", + }; + if (body !== undefined) { + opts.body = JSON.stringify(body); + } + return await this.fetch(url, opts); + } - async post(url, body) { - const opts = { - method: 'POST', - }; - if (body !== undefined) { - opts.body = JSON.stringify(body); - } - return await this.fetch(url, opts); - }; + async delete(url) { + const opts = { + method: "DELETE", + }; + return await this.fetch(url, opts); + } - async delete(url) { - const opts = { - method: 'DELETE', - }; - return await this.fetch(url, opts); - }; + async get(url) { + const opts = { + method: "GET", + }; + return await this.fetch(url, opts); + } - async get(url) { - const opts = { - method: 'GET', - }; - return await this.fetch(url, opts); - }; - - async fetch(url, opts={}) { - url = this.url + url; - opts.credentials = opts.credentials || 'include'; - opts.headers = opts.headers || {}; - opts.headers['Content-Type'] = opts.headers['Content-Type'] || 'application/json'; - if (this.token) { - opts.headers.cookie = `netsblox=${this.token}`; - } - const response = await fetch(url, opts); - if (response.status > 399) { + async fetch(url, opts = {}) { + url = this.url + url; + opts.credentials = opts.credentials || "include"; + opts.headers = opts.headers || {}; + opts.headers["Content-Type"] = opts.headers["Content-Type"] || + "application/json"; + if (this.token) { + opts.headers.cookie = `netsblox=${this.token}`; + } + const response = await fetch(url, opts); + if (response.status > 399) { + const text = await response.text(); + const error = new CloudError(text); + await this.onerror(error); + throw error; + } + return response; + } + + async onerror(response) { const text = await response.text(); - const error = new CloudError(text); - await this.onerror(error); - throw error; - } - return response; - } + throw new CloudError(text); + } - async onerror(response) { - const text = await response.text(); - throw new CloudError(text); - } + setLocalState(projectId, roleId) { + this.projectId = projectId; + this.roleId = roleId; + } - setLocalState(projectId, roleId) { - this.projectId = projectId; - this.roleId = roleId; - }; - - resetLocalState() { - this.setLocalState(null, null); - }; - - newProject(name=this.localize('untitled')) { - var myself = this; - - if (!this.newProjectRequest) { - const saveResponse = this.post(`/projects/`, {name, clientId: this.clientId}); - this.newProjectRequest = saveResponse - .then(response => response.json()) - .then(async metadata => { - const [roleId] = Object.keys(metadata.roles); - this.setClientState(metadata.id, roleId); - myself.newProjectRequest = null; - return metadata; + resetLocalState() { + this.setLocalState(null, null); + } + + newProject(name = this.localize("untitled")) { + var myself = this; + + if (!this.newProjectRequest) { + const saveResponse = this.post(`/projects/`, { + name, + clientId: this.clientId, + }); + this.newProjectRequest = saveResponse + .then((response) => response.json()) + .then(async (metadata) => { + const [roleId] = Object.keys(metadata.roles); + this.setClientState(metadata.id, roleId); + myself.newProjectRequest = null; + return metadata; }) - .catch(function(req) { - myself.resetLocalState(); - myself.newProjectRequest = null; - throw new Error(req.responseText); + .catch(function (req) { + myself.resetLocalState(); + myself.newProjectRequest = null; + throw new Error(req.responseText); }); - } - - return this.newProjectRequest; - }; + } - getClientState() { - return { - username: this.username, - clientId: this.clientId, - projectId: this.projectId, - roleId: this.roleId - }; - }; + return this.newProjectRequest; + } - async setClientState(projectId=this.projectId, roleId=this.roleId) { - var myself = this, - newProjectRequest = this.newProjectRequest || Promise.resolve(); + getClientState() { + return { + username: this.username, + clientId: this.clientId, + projectId: this.projectId, + roleId: this.roleId, + }; + } - this.projectId = projectId; - this.roleId = roleId; - return newProjectRequest - .then(async () => { + async setClientState( + projectId = this.projectId, + roleId = this.roleId, + ) { + var myself = this, + newProjectRequest = this.newProjectRequest || Promise.resolve(); + + this.projectId = projectId; + this.roleId = roleId; + return newProjectRequest + .then(async () => { const body = { - state: { - browser: { - projectId: this.projectId, - roleId: this.roleId, - } - } + state: { + browser: { + projectId: this.projectId, + roleId: this.roleId, + }, + }, }; await this.post(`/network/${this.clientId}/state`, body); // Only change the project ID if no other moves/newProjects/etc have occurred - }) - .catch(function(req) { - var connError = 'Could not connect to ' + myself.url; + }) + .catch(function (req) { + var connError = "Could not connect to " + myself.url; throw new Error(req.responseText || connError); - }); - }; + }); + } - setProjectName(name) { - const newProjectRequest = this.newProjectRequest || Promise.resolve(); + setProjectName(name) { + const newProjectRequest = this.newProjectRequest || + Promise.resolve(); + return newProjectRequest + .then(async () => { + await this.patch(`/projects/id/${this.projectId}`, { + name, + clientId: this.clientId, + }); + }); + } - return newProjectRequest - .then(async () => { - await this.patch(`/projects/id/${this.projectId}`, {name, clientId: this.clientId}); - }); - }; + async importProject(projectData) { + projectData.clientId = this.clientId; - async importProject(projectData) { - projectData.clientId = this.clientId; + const response = await this.post("/projects/", projectData); + return await response.json(); + } - const response = await this.post('/projects/', projectData); - return await response.json(); - }; + async linkAccount(username, password, type) { + await this.post(`/users/${this.username}/link/`, { + Snap: { username, password }, + }); + } - async linkAccount(username, password, type) { - await this.post(`/users/${this.username}/link/`, {Snap: {username, password}}); - }; + async unlinkAccount(account) { + await this.post(`/users/${this.username}/unlink/`, { + username, + strategy: "snap", + }); + } - async unlinkAccount(account) { - await this.post(`/users/${this.username}/unlink/`, {username, strategy: 'snap'}); - }; + async getProjectData(projectId = this.projectId) { + const response = await this.fetch( + `/projects/id/${projectId}/latest?clientId=${this.clientId}`, + ); + return await response.json(); + } - async getProjectData(projectId=this.projectId) { - const response = await this.fetch(`/projects/id/${projectId}/latest?clientId=${this.clientId}`); - return await response.json(); - }; + async exportRole(projectId = this.projectId, roleId = this.roleId) { + const response = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest?clientId=${this.clientId}`, + ); + return await response.text(); + } - async exportRole(projectId=this.projectId, roleId=this.roleId) { - const response = await this.fetch(`/projects/id/${projectId}/${roleId}/latest?clientId=${this.clientId}`); - return await response.text(); - }; + async viewUser(username = this.username) { + const response = await this.fetch(`/users/${username}`); + return await response.json(); + } - async viewUser(username=this.username) { - const response = await this.fetch(`/users/${username}`); - return await response.json(); - } + async whoAmI() { + const response = await this.get("/users/whoami"); + return await response.text(); + } - async whoAmI() { - const response = await this.get('/users/whoami'); - return await response.text(); - } + async getCommunityLibraryList() { + const response = await this.get("/libraries/community/"); + return await response.json(); + } - async getCommunityLibraryList() { - const response = await this.get('/libraries/community/'); - return await response.json(); - } + async getLibraryList() { + const response = await this.get( + `/libraries/user/${this.username}/`, + ); + return await response.json(); + } - async getLibraryList() { - const response = await this.get(`/libraries/user/${this.username}/`); - return await response.json(); - } + async saveLibrary(name, blocks, notes) { + const library = { + name, + notes, + blocks, + }; + const response = await this.post( + `/libraries/user/${this.username}/`, + library, + ); + return await response.json(); + } - async saveLibrary(name, blocks, notes) { - const library = { - name, notes, blocks - }; - const response = await this.post(`/libraries/user/${this.username}/`, library); - return await response.json(); - } + async getLibrary(username, name) { + name = encodeURIComponent(name); + const response = await this.get( + `/libraries/user/${username}/${name}`, + ); + return await response.text(); + } - async getLibrary(username, name) { - name = encodeURIComponent(name); - const response = await this.get(`/libraries/user/${username}/${name}`); - return await response.text(); - } + async deleteLibrary(name) { + name = encodeURIComponent(name); + return await this.delete( + `/libraries/user/${this.username}/${name}`, + ); + } - async deleteLibrary(name) { - name = encodeURIComponent(name); - return await this.delete(`/libraries/user/${this.username}/${name}`); - } + async publishLibrary(name) { + name = encodeURIComponent(name); + const response = await this.post( + `/libraries/user/${this.username}/${name}/publish`, + ); + return await response.json(); + } - async publishLibrary(name) { - name = encodeURIComponent(name); - const response = await this.post(`/libraries/user/${this.username}/${name}/publish`); - return await response.json(); - } + async unpublishLibrary(name) { + name = encodeURIComponent(name); + await this.post( + `/libraries/user/${this.username}/${name}/unpublish`, + ); + } - async unpublishLibrary(name) { - name = encodeURIComponent(name); - await this.post(`/libraries/user/${this.username}/${name}/unpublish`); - } + // Cloud: user messages (to be overridden) - // Cloud: user messages (to be overridden) + message(string) { + alert(string); + } - message(string) { - alert(string); - } + // TODO: legacy api used by other sites using NetsBlox authentication + async register() { + return this.signup(...arguments); + } - // TODO: legacy api used by other sites using NetsBlox authentication - async register() { - return this.signup(...arguments); - } + async checkLogin() { + try { + await this.whoAmI(); + return true; + } catch (err) { + return false; + } + } - async checkLogin() { - try { - await this.whoAmI(); - return true; - } catch (err) { - return false; + async getProfile() { + const profile = await this.viewUser(); + return profile; + } } - } - - async getProfile() { - const profile = await this.viewUser(); - return profile; - } -} -class CloudError extends Error { - constructor(label, message) { - super(message || label); - this.label = label; - } -} - -module.exports = Cloud; + class CloudError extends Error { + constructor(label, message) { + super(message || label); + this.label = label; + } + } -},{"assert":1,"isomorphic-fetch":5}]},{},[9])(9) + module.exports = Cloud; + }, { "assert": 1, "isomorphic-fetch": 5 }], + }, + {}, + [9], + )(9); }); diff --git a/examples/main.js b/examples/main.js index 2153772..006c0e9 100644 --- a/examples/main.js +++ b/examples/main.js @@ -1,51 +1,50 @@ -'use strict'; +"use strict"; /* global Cloud */ function showResults(title, content) { - document.getElementById('result-title').innerText = title; - document.getElementById('result-content').innerText = content; + document.getElementById("result-title").innerText = title; + document.getElementById("result-content").innerText = content; } window.onload = () => { // Set up the click listeners - let client = new Cloud(document.getElementById('url').value); - const loginBtn = document.getElementById('login-btn'); - const logoutBtn = document.getElementById('logout-btn'); - const testBtn = document.getElementById('test-btn'); + let client = new Cloud(document.getElementById("url").value); + const loginBtn = document.getElementById("login-btn"); + const logoutBtn = document.getElementById("logout-btn"); + const testBtn = document.getElementById("test-btn"); loginBtn.onclick = () => { - const url = document.getElementById('url').value; + const url = document.getElementById("url").value; client = new Cloud(url); - const username = document.getElementById('username').value; - const password = document.getElementById('password').value; + const username = document.getElementById("username").value; + const password = document.getElementById("password").value; client.login(username, password) - .then(username => { - showResults('response:', username); + .then((username) => { + showResults("response:", username); }) - .catch(err => { - showResults('response', err); + .catch((err) => { + showResults("response", err); }); }; testBtn.onclick = () => { - const url = document.getElementById('url').value; + const url = document.getElementById("url").value; client = new Cloud(url); // Try to login without a username and get the response client.getProfile() - .then(user => { - showResults('logged in user:', JSON.stringify(user)); + .then((user) => { + showResults("logged in user:", JSON.stringify(user)); }) .catch(() => { - showResults('Logged in?', false); + showResults("Logged in?", false); }); }; logoutBtn.onclick = () => { - const url = document.getElementById('url').value; + const url = document.getElementById("url").value; client = new Cloud(url); client.logout() - .then(response => { - showResults('response:', response); + .then((response) => { + showResults("response:", response); }); }; }; - diff --git a/package-lock.json b/package-lock.json index 4c82c87..543b0ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { - "name": "netsblox-client-auth", + "name": "netsblox-cloud-client", "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "netsblox-client-auth", + "name": "netsblox-cloud-client", "version": "2.0.0", "hasInstallScript": true, "license": "MIT", "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", + "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-typescript": "^11.0.0", "@types/isomorphic-fetch": "^0.0.36", @@ -94,6 +95,26 @@ "node": ">=10" } }, + "node_modules/@rollup/plugin-json": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz", + "integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", @@ -5635,6 +5656,15 @@ } } }, + "@rollup/plugin-json": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.0.0.tgz", + "integrity": "sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^5.0.1" + } + }, "@rollup/plugin-node-resolve": { "version": "15.0.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz", diff --git a/package.json b/package.json index bc11ddd..930c222 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,10 @@ { - "name": "netsblox-client-auth", + "name": "netsblox-cloud-client", "version": "2.0.0", - "description": "netsblox JS client helpers", + "description": "JS client for the netsblox cloud", "main": "main.js", "scripts": { - "build-watch": "babel src --watch --presets babel-preset-es2015 --out-dir dist", - "build": "babel src --presets babel-preset-es2015 --out-dir dist && npx browserify src/client.js -o dist/cloud.js -s Cloud", + "build": "npx rollup -c rollup.config.mjs", "test": "npx mocha --recursive test", "postinstall": "babel src --presets babel-preset-es2015 --out-dir dist" }, @@ -21,6 +20,7 @@ "homepage": "https://github.com/NetsBlox/client-auth#readme", "devDependencies": { "@rollup/plugin-commonjs": "^24.0.1", + "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.1", "@rollup/plugin-typescript": "^11.0.0", "@types/isomorphic-fetch": "^0.0.36", diff --git a/src/client.js b/src/client.js index 3c13488..ca5656f 100644 --- a/src/client.js +++ b/src/client.js @@ -1,539 +1,601 @@ -import 'isomorphic-fetch'; -import assert from 'assert'; -const defaultLocalizer = text => text; -const isNodeJs = typeof window === 'undefined'; +import "isomorphic-fetch"; +import assert from "assert"; +const defaultLocalizer = (text) => text; +const isNodeJs = typeof window === "undefined"; export default class Cloud { - constructor(url, clientId, username, localize = defaultLocalizer) { - this.clientId = clientId; - this.username = username; - this.projectId = null; - this.roleId = null; - this.url = url; - this.token = null; // only needed in NodeJs - this.localize = localize; - } - clear() { - this.username = null; - this.token = null; - } - hasProtocol() { - return this.url.toLowerCase().indexOf('http') === 0; - } - async resetPassword(username) { - const response = await this.fetch(`/users/${username}/password`, { method: 'POST' }); - return await response.text(); - } - ; - async login(username, password, remember, // TODO: use this... - strategy = 'NetsBlox') { - const credentials = {}; - credentials[strategy] = { username, password }; - const body = { - credentials, - clientId: this.clientId, - }; - const response = await this.post('/users/login', body); - this.username = await response.text(); - if (isNodeJs) { - const cookie = response.headers.get('set-cookie'); - assert(cookie, new CloudError('No cookie received')); - this.token = cookie.split('=')[1].split(';').shift(); - } - return this.username; - } - async getProjectList() { - const response = await this.fetch(`/projects/user/${this.username}`); - return await response.json(); - } - async getSharedProjectList() { - const response = await this.fetch(`/projects/shared/${this.username}`); - return await response.json(); - } - async changePassword(oldPW, newPW) { - const body = JSON.stringify(newPW); - const response = await this.fetch(`/users/${this.username}/password`, { method: 'PATCH', body }); - return await response.text(); - } - parseResponse(src) { - var ans = [], lines; - if (!src) { - return ans; - } - lines = src.split(" "); - lines.forEach(function (service) { - var entries = service.split("&"), dict = {}; - entries.forEach(function (entry) { - var pair = entry.split("="), key = decodeURIComponent(pair[0]), val = decodeURIComponent(pair[1]); - dict[key] = val; - }); - ans.push(dict); - }); - return ans; - } - ; - parseDict(src) { - var dict = {}; - if (!src) { - return dict; + constructor(url, clientId, username, localize = defaultLocalizer) { + this.clientId = clientId; + this.username = username; + this.projectId = null; + this.roleId = null; + this.url = url; + this.token = null; // only needed in NodeJs + this.localize = localize; + } + clear() { + this.username = null; + this.token = null; + } + hasProtocol() { + return this.url.toLowerCase().indexOf("http") === 0; + } + async resetPassword(username) { + const response = await this.fetch(`/users/${username}/password`, { + method: "POST", + }); + return await response.text(); + } + + async login( + username, + password, + remember, // TODO: use this... + strategy = "NetsBlox", + ) { + const credentials = {}; + credentials[strategy] = { username, password }; + const body = { + credentials, + clientId: this.clientId, + }; + const response = await this.post("/users/login", body); + this.username = await response.text(); + if (isNodeJs) { + const cookie = response.headers.get("set-cookie"); + assert(cookie, new CloudError("No cookie received")); + this.token = cookie.split("=")[1].split(";").shift(); + } + return this.username; + } + async getProjectList() { + const response = await this.fetch(`/projects/user/${this.username}`); + return await response.json(); + } + async getSharedProjectList() { + const response = await this.fetch(`/projects/shared/${this.username}`); + return await response.json(); + } + async changePassword(oldPW, newPW) { + const body = JSON.stringify(newPW); + const response = await this.fetch(`/users/${this.username}/password`, { + method: "PATCH", + body, + }); + return await response.text(); + } + parseResponse(src) { + var ans = [], lines; + if (!src) { + return ans; + } + lines = src.split(" "); + lines.forEach(function (service) { + var entries = service.split("&"), dict = {}; + entries.forEach(function (entry) { + var pair = entry.split("="), + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + dict[key] = val; + }); + ans.push(dict); + }); + return ans; + } + + parseDict(src) { + var dict = {}; + if (!src) { + return dict; + } + src.split("&").forEach(function (entry) { + var pair = entry.split("="), + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + dict[key] = val; + }); + return dict; + } + + encodeDict(dict) { + var str = "", pair, key; + if (!dict) { + return null; + } + for (key in dict) { + if (dict.hasOwnProperty(key)) { + pair = encodeURIComponent(key) + + "=" + + encodeURIComponent(dict[key]); + if (str.length > 0) { + str += "&"; } - src.split("&").forEach(function (entry) { - var pair = entry.split("="), key = decodeURIComponent(pair[0]), val = decodeURIComponent(pair[1]); - dict[key] = val; + str += pair; + } + } + return str; + } + + async getUserData() { + const response = await this.fetch(`/users/${this.username}`); + return await response.json(); + } + + async addRole(name) { + const response = await this.post(`/projects/id/${this.projectId}/`, { + name, + }); + // TODO: should I request the new project state, too? + // I shouldn't have to since we should be subscribed to changes... + //return await response.json(); + } + + async saveRole(roleData) { + const url = `/projects/id/${this.projectId}/${this.roleId}`; + const options = { + method: "POST", + body: JSON.stringify(roleData), + }; + await this.fetch(url, options); + } + + async renameRole(roleId, name) { + const body = { + name, + clientId: this.clientId, + }; + const response = await this.patch( + `/projects/id/${this.projectId}/${roleId}`, + body, + ); + // TODO: error handling + //return await response.json(); + } + + async renameProject(name) { + const body = { + name, + clientId: this.clientId, + }; + const response = await this.patch(`/projects/id/${this.projectId}`, body); + } + + async reportLatestRole(id, data) { + const clientId = this.clientId; + const options = { + method: "POST", + body: JSON.stringify({ id, data }), + }; + await this.fetch( + `/projects/id/${this.projectId}/${this.roleId}/latest?clientId=${clientId}`, + options, + ); + } + + async cloneRole(roleId) { + const projectId = this.projectId; + const fetchRoleResponse = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest`, + ); + const { name, code, media } = await fetchRoleResponse.json(); + const response = await this.post(`/projects/id/${projectId}/`, { + name, + code, + media, + }); + } + + async sendOccupantInvite(username, roleId) { + const body = { username, roleId }; + await this.post(`/network/id/${this.projectId}/occupants/invite`, body); + } + + async evictOccupant(clientID) { + const method = "DELETE"; + await this.fetch(`/network/id/${this.projectId}/occupants/${clientID}`, { + method, + }); + } + + async getCollaboratorList() { + const response = await this.get( + `/projects/id/${this.projectId}/collaborators/`, + ); + return await response.json(); + } + + async getCollaboratorRequestList() { + const response = await this.get( + `/collaboration-invites/user/${this.username}/`, + ); + return await response.json(); + } + async sendCollaborateRequest(username) { + await this.post( + `/collaboration-invites/${this.projectId}/invite/${username}`, + ); + } + + async respondToCollaborateRequest(id, accepted) { + const newState = accepted ? "ACCEPTED" : "REJECTED"; + await this.post(`/collaboration-invites/id/${id}`, newState); + } + + async removeCollaborator(username, projectId) { + await this.delete(`/projects/id/${projectId}/collaborators/${username}`); + } + + async getOnlineFriendList() { + const response = await this.get(`/friends/${this.username}/online`); + return await response.json(); + } + async getFriendList() { + const response = await this.get(`/friends/${this.username}/`); + return await response.json(); + } + + async getRole(projectId, roleId) { + const qs = this.clientId ? `clientId=${this.clientId}` : ""; + const response = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest?${qs}`, + ); + const project = await response.json(); + // TODO: Set the state here? + this.setLocalState(projectId, roleId); + return project; + } + + async getProjectMetadata(projectId) { + const response = await this.fetch(`/projects/id/${projectId}/metadata`); + const project = await response.json(); + return project; + } + + async getProjectByName(owner, name) { + const response = await this.fetch(`/projects/user/${owner}/${name}`); + return await response.json(); + } + + async getProjectMetadataByName(owner, name) { + const response = await this.fetch( + `/projects/user/${owner}/${name}/metadata`, + ); + return await response.json(); + } + + async startNetworkTrace(projectId) { + const response = await this.post(`/network/id/${projectId}/trace/`); + return await response.text(); + } + + async stopNetworkTrace(projectId, traceId) { + const response = await this.post( + `/network/id/${projectId}/trace/${traceId}/stop`, + ); + return await response.text(); + } + + async getNetworkTrace(projectId, traceId) { + const response = await this.fetch( + `/network/id/${projectId}/trace/${traceId}`, + ); + return await response.json(); + } + + async getFriendRequestList() { + const response = await this.get(`/friends/${this.username}/invites/`); + return response.json(); + } + async sendFriendRequest(username) { + await this.post(`/friends/${this.username}/invite/`, username.trim()); + } + async unfriend(username) { + username = encodeURIComponent(username.trim()); + await this.post(`/friends/${this.username}/unfriend/${username}`); + } + async respondToFriendRequest(sender, newState) { + sender = encodeURIComponent(sender); + await this.post(`/friends/${this.username}/invites/${sender}`, newState); + } + async deleteRole(roleId) { + const method = "DELETE"; + await this.fetch(`/projects/id/${this.projectId}/${roleId}`, { method }); + } + + async deleteProject(projectId) { + const method = "DELETE"; + await this.fetch(`/projects/id/${projectId}`, { method }); + } + + async publishProject(projectId) { + const method = "POST"; + await this.fetch(`/projects/id/${projectId}/publish`, { method }); + } + + async unpublishProject(projectId) { + const method = "POST"; + await this.fetch(`/projects/id/${projectId}/unpublish`, { method }); + } + + reconnect(callback, errorCall) { + if (!this.username) { + this.message("You are not logged in"); + return; + } + // need to set 'api' from setClientState + let promise = this.setClientState(); + if (callback && errorCall) { + promise = promise.then(callback) + .catch(errorCall); + } + return promise; + } + + async logout() { + const method = "POST"; + await this.fetch("/users/logout", { method }); + this.clear(); + return true; + } + + async signup(username, password, email) { + const body = { + username, + password, + email, + }; + const response = await this.post("/users/create", body); + return await response.text(); + } + + async saveProjectCopy() { + const response = await this.fetch(`/projects/${this.projectId}/latest`); + const xml = await response.text(); + const options = { + method: "POST", + body: xml, // TODO: add options for allow rename? + }; + const saveResponse = await this.fetch(`/projects/`, options); + // TODO: set the state with the network overlay + //this.setLocalState(response.projectId, this.roleId); + return saveResponse.status == 200; + } + + async patch(url, body = undefined) { + const opts = { + method: "PATCH", + }; + if (body !== undefined) { + opts.body = JSON.stringify(body); + } + return await this.fetch(url, opts); + } + + async post(url, body = undefined) { + const opts = { + method: "POST", + }; + if (body !== undefined) { + opts.body = JSON.stringify(body); + } + return await this.fetch(url, opts); + } + + async delete(url) { + const opts = { + method: "DELETE", + }; + return await this.fetch(url, opts); + } + + async get(url) { + const opts = { + method: "GET", + }; + return await this.fetch(url, opts); + } + + async fetch(url, opts) { + opts = opts || {}; + url = this.url + url; + opts.credentials = opts.credentials || "include"; + opts.headers = { + "Content-Type": "application/json", + }; + if (this.token) { + opts.headers["cookie"] = `netsblox=${this.token}`; + } + const response = await fetch(url, opts); + if (response.status > 399) { + const text = await response.text(); + const error = new CloudError(text); + await this.onerror(error); + throw error; + } + return response; + } + async onerror(response) { + const text = await response.text(); + throw new CloudError(text); + } + setLocalState(projectId, roleId) { + this.projectId = projectId; + this.roleId = roleId; + } + + resetLocalState() { + this.setLocalState(null, null); + } + + newProject(name = this.localize("untitled")) { + var myself = this; + if (!this.newProjectRequest) { + const saveResponse = this.post(`/projects/`, { + name, + clientId: this.clientId, + }); + this.newProjectRequest = saveResponse + .then((response) => response.json()) + .then(async (metadata) => { + const [roleId] = Object.keys(metadata.roles); + this.setClientState(metadata.id, roleId); + myself.newProjectRequest = null; + return metadata; + }) + .catch(function (req) { + myself.resetLocalState(); + myself.newProjectRequest = null; + throw new Error(req.responseText); }); - return dict; - } - ; - encodeDict(dict) { - var str = '', pair, key; - if (!dict) { - return null; - } - for (key in dict) { - if (dict.hasOwnProperty(key)) { - pair = encodeURIComponent(key) - + '=' - + encodeURIComponent(dict[key]); - if (str.length > 0) { - str += '&'; - } - str += pair; - } - } - return str; - } - ; - async getUserData() { - const response = await this.fetch(`/users/${this.username}`); - return await response.json(); - } - ; - async addRole(name) { - const response = await this.post(`/projects/id/${this.projectId}/`, { name }); - // TODO: should I request the new project state, too? - // I shouldn't have to since we should be subscribed to changes... - //return await response.json(); - } - ; - async saveRole(roleData) { - const url = `/projects/id/${this.projectId}/${this.roleId}`; - const options = { - method: 'POST', - body: JSON.stringify(roleData), - }; - await this.fetch(url, options); - } - ; - async renameRole(roleId, name) { - const body = { - name, - clientId: this.clientId, - }; - const response = await this.patch(`/projects/id/${this.projectId}/${roleId}`, body); - // TODO: error handling - //return await response.json(); } - ; - async renameProject(name) { + return this.newProjectRequest; + } + + getClientState() { + return { + username: this.username, + clientId: this.clientId, + projectId: this.projectId, + roleId: this.roleId, + }; + } + + async setClientState(projectId = this.projectId, roleId = this.roleId) { + var myself = this, + newProjectRequest = this.newProjectRequest || Promise.resolve(); + this.projectId = projectId; + this.roleId = roleId; + return newProjectRequest + .then(async () => { const body = { - name, - clientId: this.clientId, + state: { + browser: { + projectId: this.projectId, + roleId: this.roleId, + }, + }, }; - const response = await this.patch(`/projects/id/${this.projectId}`, body); - } - ; - async reportLatestRole(id, data) { - const clientId = this.clientId; - const options = { - method: 'POST', - body: JSON.stringify({ id, data }) - }; - await this.fetch(`/projects/id/${this.projectId}/${this.roleId}/latest?clientId=${clientId}`, options); - } - ; - async cloneRole(roleId) { - const projectId = this.projectId; - const fetchRoleResponse = await this.fetch(`/projects/id/${projectId}/${roleId}/latest`); - const { name, code, media } = await fetchRoleResponse.json(); - const response = await this.post(`/projects/id/${projectId}/`, { name, code, media }); - } - ; - async sendOccupantInvite(username, roleId) { - const body = { username, roleId }; - await this.post(`/network/id/${this.projectId}/occupants/invite`, body); - } - ; - async evictOccupant(clientID) { - const method = 'DELETE'; - await this.fetch(`/network/id/${this.projectId}/occupants/${clientID}`, { method }); - } - ; - async getCollaboratorList() { - const response = await this.get(`/projects/id/${this.projectId}/collaborators/`); - return await response.json(); - } - ; - async getCollaboratorRequestList() { - const response = await this.get(`/collaboration-invites/user/${this.username}/`); - return await response.json(); - } - async sendCollaborateRequest(username) { - await this.post(`/collaboration-invites/${this.projectId}/invite/${username}`); - } - ; - async respondToCollaborateRequest(id, accepted) { - const newState = accepted ? 'ACCEPTED' : 'REJECTED'; - await this.post(`/collaboration-invites/id/${id}`, newState); - } - ; - async removeCollaborator(username, projectId) { - await this.delete(`/projects/id/${projectId}/collaborators/${username}`); - } - ; - async getOnlineFriendList() { - const response = await this.get(`/friends/${this.username}/online`); - return await response.json(); - } - async getFriendList() { - const response = await this.get(`/friends/${this.username}/`); - return await response.json(); - } - ; - async getRole(projectId, roleId) { - const qs = this.clientId ? `clientId=${this.clientId}` : ''; - const response = await this.fetch(`/projects/id/${projectId}/${roleId}/latest?${qs}`); - const project = await response.json(); - // TODO: Set the state here? - this.setLocalState(projectId, roleId); - return project; - } - ; - async getProjectMetadata(projectId) { - const response = await this.fetch(`/projects/id/${projectId}/metadata`); - const project = await response.json(); - return project; - } - ; - async getProjectByName(owner, name) { - const response = await this.fetch(`/projects/user/${owner}/${name}`); - return await response.json(); - } - ; - async getProjectMetadataByName(owner, name) { - const response = await this.fetch(`/projects/user/${owner}/${name}/metadata`); - return await response.json(); - } - ; - async startNetworkTrace(projectId) { - const response = await this.post(`/network/id/${projectId}/trace/`); - return await response.text(); - } - ; - async stopNetworkTrace(projectId, traceId) { - const response = await this.post(`/network/id/${projectId}/trace/${traceId}/stop`); - return await response.text(); - } - ; - async getNetworkTrace(projectId, traceId) { - const response = await this.fetch(`/network/id/${projectId}/trace/${traceId}`); - return await response.json(); - } - ; - async getFriendRequestList() { - const response = await this.get(`/friends/${this.username}/invites/`); - return response.json(); - } - async sendFriendRequest(username) { - await this.post(`/friends/${this.username}/invite/`, username.trim()); - } - async unfriend(username) { - username = encodeURIComponent(username.trim()); - await this.post(`/friends/${this.username}/unfriend/${username}`); - } - async respondToFriendRequest(sender, newState) { - sender = encodeURIComponent(sender); - await this.post(`/friends/${this.username}/invites/${sender}`, newState); - } - async deleteRole(roleId) { - const method = 'DELETE'; - await this.fetch(`/projects/id/${this.projectId}/${roleId}`, { method }); - } - ; - async deleteProject(projectId) { - const method = 'DELETE'; - await this.fetch(`/projects/id/${projectId}`, { method }); - } - ; - async publishProject(projectId) { - const method = 'POST'; - await this.fetch(`/projects/id/${projectId}/publish`, { method }); - } - ; - async unpublishProject(projectId) { - const method = 'POST'; - await this.fetch(`/projects/id/${projectId}/unpublish`, { method }); - } - ; - reconnect(callback, errorCall) { - if (!this.username) { - this.message('You are not logged in'); - return; - } - // need to set 'api' from setClientState - let promise = this.setClientState(); - if (callback && errorCall) { - promise = promise.then(callback) - .catch(errorCall); - } - return promise; - } - ; - async logout() { - const method = 'POST'; - await this.fetch('/users/logout', { method }); - this.clear(); - return true; - } - ; - async signup(username, password, email) { - const body = { - username, - password, - email, - }; - const response = await this.post('/users/create', body); - return await response.text(); - } - ; - async saveProjectCopy() { - const response = await this.fetch(`/projects/${this.projectId}/latest`); - const xml = await response.text(); - const options = { - method: 'POST', - body: xml, // TODO: add options for allow rename? - }; - const saveResponse = await this.fetch(`/projects/`, options); - // TODO: set the state with the network overlay - //this.setLocalState(response.projectId, this.roleId); - return saveResponse.status == 200; - } - ; - async patch(url, body = undefined) { - const opts = { - method: 'PATCH', - }; - if (body !== undefined) { - opts.body = JSON.stringify(body); - } - return await this.fetch(url, opts); - } - ; - async post(url, body = undefined) { - const opts = { - method: 'POST', - }; - if (body !== undefined) { - opts.body = JSON.stringify(body); - } - return await this.fetch(url, opts); - } - ; - async delete(url) { - const opts = { - method: 'DELETE', - }; - return await this.fetch(url, opts); - } - ; - async get(url) { - const opts = { - method: 'GET', - }; - return await this.fetch(url, opts); - } - ; - async fetch(url, opts) { - opts = opts || {}; - url = this.url + url; - opts.credentials = opts.credentials || 'include'; - opts.headers = { - 'Content-Type': 'application/json' - }; - if (this.token) { - opts.headers['cookie'] = `netsblox=${this.token}`; - } - const response = await fetch(url, opts); - if (response.status > 399) { - const text = await response.text(); - const error = new CloudError(text); - await this.onerror(error); - throw error; - } - return response; - } - async onerror(response) { - const text = await response.text(); - throw new CloudError(text); - } - setLocalState(projectId, roleId) { - this.projectId = projectId; - this.roleId = roleId; - } - ; - resetLocalState() { - this.setLocalState(null, null); - } - ; - newProject(name = this.localize('untitled')) { - var myself = this; - if (!this.newProjectRequest) { - const saveResponse = this.post(`/projects/`, { name, clientId: this.clientId }); - this.newProjectRequest = saveResponse - .then(response => response.json()) - .then(async (metadata) => { - const [roleId] = Object.keys(metadata.roles); - this.setClientState(metadata.id, roleId); - myself.newProjectRequest = null; - return metadata; - }) - .catch(function (req) { - myself.resetLocalState(); - myself.newProjectRequest = null; - throw new Error(req.responseText); - }); - } - return this.newProjectRequest; - } - ; - getClientState() { - return { - username: this.username, - clientId: this.clientId, - projectId: this.projectId, - roleId: this.roleId - }; - } - ; - async setClientState(projectId = this.projectId, roleId = this.roleId) { - var myself = this, newProjectRequest = this.newProjectRequest || Promise.resolve(); - this.projectId = projectId; - this.roleId = roleId; - return newProjectRequest - .then(async () => { - const body = { - state: { - browser: { - projectId: this.projectId, - roleId: this.roleId, - } - } - }; - await this.post(`/network/${this.clientId}/state`, body); - // Only change the project ID if no other moves/newProjects/etc have occurred - }) - .catch(function (req) { - var connError = 'Could not connect to ' + myself.url; - throw new Error(req.responseText || connError); - }); - } - ; - setProjectName(name) { - const newProjectRequest = this.newProjectRequest || Promise.resolve(); - return newProjectRequest - .then(async () => { - await this.patch(`/projects/id/${this.projectId}`, { name, clientId: this.clientId }); + await this.post(`/network/${this.clientId}/state`, body); + // Only change the project ID if no other moves/newProjects/etc have occurred + }) + .catch(function (req) { + var connError = "Could not connect to " + myself.url; + throw new Error(req.responseText || connError); + }); + } + + setProjectName(name) { + const newProjectRequest = this.newProjectRequest || Promise.resolve(); + return newProjectRequest + .then(async () => { + await this.patch(`/projects/id/${this.projectId}`, { + name, + clientId: this.clientId, }); - } - ; - async importProject(projectData) { - projectData.clientId = this.clientId; - const response = await this.post('/projects/', projectData); - return await response.json(); - } - ; - async linkAccount(username, password, type) { - await this.post(`/users/${this.username}/link/`, { Snap: { username, password } }); - } - ; - async unlinkAccount(account) { - await this.post(`/users/${this.username}/unlink/`, account); - } - ; - async getProjectData(projectId = this.projectId) { - const response = await this.fetch(`/projects/id/${projectId}/latest?clientId=${this.clientId}`); - return await response.json(); - } - ; - async exportRole(projectId = this.projectId, roleId = this.roleId) { - const response = await this.fetch(`/projects/id/${projectId}/${roleId}/latest?clientId=${this.clientId}`); - return await response.text(); - } - ; - async viewUser(username = this.username) { - const response = await this.fetch(`/users/${username}`); - return await response.json(); - } - async whoAmI() { - const response = await this.get('/users/whoami'); - return await response.text(); - } - async getCommunityLibraryList() { - const response = await this.get('/libraries/community/'); - return await response.json(); - } - async getLibraryList() { - const response = await this.get(`/libraries/user/${this.username}/`); - return await response.json(); - } - async saveLibrary(name, blocks, notes) { - const library = { - name, notes, blocks - }; - const response = await this.post(`/libraries/user/${this.username}/`, library); - return await response.json(); - } - async getLibrary(username, name) { - name = encodeURIComponent(name); - const response = await this.get(`/libraries/user/${username}/${name}`); - return await response.text(); - } - async deleteLibrary(name) { - name = encodeURIComponent(name); - return await this.delete(`/libraries/user/${this.username}/${name}`); - } - async publishLibrary(name) { - name = encodeURIComponent(name); - const response = await this.post(`/libraries/user/${this.username}/${name}/publish`); - return await response.json(); - } - async unpublishLibrary(name) { - name = encodeURIComponent(name); - await this.post(`/libraries/user/${this.username}/${name}/unpublish`); - } - // Cloud: user messages (to be overridden) - message(string) { - alert(string); - } - // legacy api used by other sites using NetsBlox authentication - async register(username, password, email) { - return this.signup(username, password, email); - } - async checkLogin() { - try { - await this.whoAmI(); - return true; - } - catch (err) { - return false; - } - } - async getProfile() { - const profile = await this.viewUser(); - return profile; - } + }); + } + + async importProject(projectData) { + projectData.clientId = this.clientId; + const response = await this.post("/projects/", projectData); + return await response.json(); + } + + async linkAccount(username, password, type) { + await this.post(`/users/${this.username}/link/`, { + Snap: { username, password }, + }); + } + + async unlinkAccount(account) { + await this.post(`/users/${this.username}/unlink/`, account); + } + + async getProjectData(projectId = this.projectId) { + const response = await this.fetch( + `/projects/id/${projectId}/latest?clientId=${this.clientId}`, + ); + return await response.json(); + } + + async exportRole(projectId = this.projectId, roleId = this.roleId) { + const response = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest?clientId=${this.clientId}`, + ); + return await response.text(); + } + + async viewUser(username = this.username) { + const response = await this.fetch(`/users/${username}`); + return await response.json(); + } + async whoAmI() { + const response = await this.get("/users/whoami"); + return await response.text(); + } + async getCommunityLibraryList() { + const response = await this.get("/libraries/community/"); + return await response.json(); + } + async getLibraryList() { + const response = await this.get(`/libraries/user/${this.username}/`); + return await response.json(); + } + async saveLibrary(name, blocks, notes) { + const library = { + name, + notes, + blocks, + }; + const response = await this.post( + `/libraries/user/${this.username}/`, + library, + ); + return await response.json(); + } + async getLibrary(username, name) { + name = encodeURIComponent(name); + const response = await this.get(`/libraries/user/${username}/${name}`); + return await response.text(); + } + async deleteLibrary(name) { + name = encodeURIComponent(name); + return await this.delete(`/libraries/user/${this.username}/${name}`); + } + async publishLibrary(name) { + name = encodeURIComponent(name); + const response = await this.post( + `/libraries/user/${this.username}/${name}/publish`, + ); + return await response.json(); + } + async unpublishLibrary(name) { + name = encodeURIComponent(name); + await this.post(`/libraries/user/${this.username}/${name}/unpublish`); + } + // Cloud: user messages (to be overridden) + message(string) { + alert(string); + } + // legacy api used by other sites using NetsBlox authentication + async register(username, password, email) { + return this.signup(username, password, email); + } + async checkLogin() { + try { + await this.whoAmI(); + return true; + } catch (err) { + return false; + } + } + async getProfile() { + const profile = await this.viewUser(); + return profile; + } } export class CloudError extends Error { - constructor(label, message = undefined) { - super(message || label); - this.label = label; - } + constructor(label, message = undefined) { + super(message || label); + this.label = label; + } } diff --git a/src/client.ts b/src/client.ts index 8f5d46f..6385a42 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,619 +1,669 @@ -import 'isomorphic-fetch'; -import assert from 'assert'; +import "isomorphic-fetch"; +import assert from "assert"; -const defaultLocalizer = text => text; -const isNodeJs = typeof window === 'undefined'; +const defaultLocalizer = (text) => text; +const isNodeJs = typeof window === "undefined"; interface LinkedAccount { - username: string; - strategy: string; + username: string; + strategy: string; } export default class Cloud { - url: string; - clientId: string; - username: string; - projectId: string | null; - roleId: string | null; - newProjectRequest: Promise | undefined; - localize: (text: string) => string; - token: string | null; - - constructor(url, clientId, username, localize=defaultLocalizer) { - this.clientId = clientId; - this.username = username; - this.projectId = null; - this.roleId = null; - this.url = url; - this.token = null; // only needed in NodeJs - this.localize = localize; + url: string; + clientId: string; + username: string; + projectId: string | null; + roleId: string | null; + newProjectRequest: Promise | undefined; + localize: (text: string) => string; + token: string | null; + + constructor(url, clientId, username, localize = defaultLocalizer) { + this.clientId = clientId; + this.username = username; + this.projectId = null; + this.roleId = null; + this.url = url; + this.token = null; // only needed in NodeJs + this.localize = localize; + } + + clear() { + this.username = null; + this.token = null; + } + + hasProtocol() { + return this.url.toLowerCase().indexOf("http") === 0; + } + + async resetPassword(username) { + const response = await this.fetch(`/users/${username}/password`, { + method: "POST", + }); + return await response.text(); + } + + async login( + username, + password, + remember, // TODO: use this... + strategy = "NetsBlox", + ) { + const credentials = {}; + credentials[strategy] = { username, password }; + const body = { + credentials, + clientId: this.clientId, + }; + const response = await this.post("/users/login", body); + this.username = await response.text(); + if (isNodeJs) { + const cookie = response.headers.get("set-cookie"); + assert(cookie, new CloudError("No cookie received")); + this.token = cookie.split("=")[1].split(";").shift(); } - - clear() { - this.username = null; - this.token = null; - } - - hasProtocol() { - return this.url.toLowerCase().indexOf('http') === 0; - } - - async resetPassword(username) { - const response = await this.fetch(`/users/${username}/password`, {method: 'POST'}); - return await response.text(); - }; - - async login( - username, - password, - remember, // TODO: use this... - strategy = 'NetsBlox', - ) { - const credentials = {}; - credentials[strategy] = {username, password}; - const body = { - credentials, - clientId: this.clientId, - }; - const response = await this.post('/users/login', body); - this.username = await response.text(); - if (isNodeJs) { - const cookie = response.headers.get('set-cookie'); - assert(cookie, new CloudError('No cookie received')); - this.token = cookie.split('=')[1].split(';').shift(); + return this.username; + } + + async getProjectList() { + const response = await this.fetch(`/projects/user/${this.username}`); + return await response.json(); + } + + async getSharedProjectList() { + const response = await this.fetch(`/projects/shared/${this.username}`); + return await response.json(); + } + + async changePassword( + oldPW, + newPW, + ) { + const body = JSON.stringify(newPW); + const response = await this.fetch( + `/users/${this.username}/password`, + { method: "PATCH", body }, + ); + return await response.text(); + } + + parseResponse(src) { + var ans = [], + lines; + if (!src) return ans; + lines = src.split(" "); + lines.forEach(function (service) { + var entries = service.split("&"), + dict = {}; + entries.forEach(function (entry) { + var pair = entry.split("="), + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + dict[key] = val; + }); + ans.push(dict); + }); + return ans; + } + + parseDict(src) { + var dict = {}; + if (!src) return dict; + src.split("&").forEach(function (entry) { + var pair = entry.split("="), + key = decodeURIComponent(pair[0]), + val = decodeURIComponent(pair[1]); + dict[key] = val; + }); + return dict; + } + + encodeDict(dict) { + var str = "", + pair, + key; + if (!dict) return null; + for (key in dict) { + if (dict.hasOwnProperty(key)) { + pair = encodeURIComponent(key) + + "=" + + encodeURIComponent(dict[key]); + if (str.length > 0) { + str += "&"; } - return this.username; + str += pair; + } } - - async getProjectList() { - const response = await this.fetch(`/projects/user/${this.username}`); - return await response.json(); + return str; + } + + async getUserData() { + const response = await this.fetch(`/users/${this.username}`); + return await response.json(); + } + + async addRole(name) { + const response = await this.post(`/projects/id/${this.projectId}/`, { + name, + }); + // TODO: should I request the new project state, too? + // I shouldn't have to since we should be subscribed to changes... + //return await response.json(); + } + + async saveRole(roleData) { + const url = `/projects/id/${this.projectId}/${this.roleId}`; + const options = { + method: "POST", + body: JSON.stringify(roleData), + }; + await this.fetch(url, options); + } + + async renameRole(roleId, name) { + const body = { + name, + clientId: this.clientId, + }; + const response = await this.patch( + `/projects/id/${this.projectId}/${roleId}`, + body, + ); + // TODO: error handling + //return await response.json(); + } + + async renameProject(name) { + const body = { + name, + clientId: this.clientId, + }; + const response = await this.patch(`/projects/id/${this.projectId}`, body); + } + + async reportLatestRole(id, data) { + const clientId = this.clientId; + const options = { + method: "POST", + body: JSON.stringify({ id, data }), + }; + await this.fetch( + `/projects/id/${this.projectId}/${this.roleId}/latest?clientId=${clientId}`, + options, + ); + } + + async cloneRole(roleId) { + const projectId = this.projectId; + const fetchRoleResponse = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest`, + ); + const { name, code, media } = await fetchRoleResponse.json(); + const response = await this.post(`/projects/id/${projectId}/`, { + name, + code, + media, + }); + } + + async sendOccupantInvite(username, roleId) { + const body = { username, roleId }; + await this.post(`/network/id/${this.projectId}/occupants/invite`, body); + } + + async evictOccupant(clientID) { + const method = "DELETE"; + await this.fetch(`/network/id/${this.projectId}/occupants/${clientID}`, { + method, + }); + } + + async getCollaboratorList() { + const response = await this.get( + `/projects/id/${this.projectId}/collaborators/`, + ); + return await response.json(); + } + + async getCollaboratorRequestList() { + const response = await this.get( + `/collaboration-invites/user/${this.username}/`, + ); + return await response.json(); + } + + async sendCollaborateRequest(username) { + await this.post( + `/collaboration-invites/${this.projectId}/invite/${username}`, + ); + } + + async respondToCollaborateRequest(id, accepted) { + const newState = accepted ? "ACCEPTED" : "REJECTED"; + await this.post(`/collaboration-invites/id/${id}`, newState); + } + + async removeCollaborator(username, projectId) { + await this.delete(`/projects/id/${projectId}/collaborators/${username}`); + } + + async getOnlineFriendList() { + const response = await this.get(`/friends/${this.username}/online`); + return await response.json(); + } + + async getFriendList() { + const response = await this.get(`/friends/${this.username}/`); + return await response.json(); + } + + async getRole(projectId, roleId) { + const qs = this.clientId ? `clientId=${this.clientId}` : ""; + const response = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest?${qs}`, + ); + const project = await response.json(); + // TODO: Set the state here? + this.setLocalState(projectId, roleId); + return project; + } + + async getProjectMetadata(projectId) { + const response = await this.fetch(`/projects/id/${projectId}/metadata`); + const project = await response.json(); + return project; + } + + async getProjectByName(owner, name) { + const response = await this.fetch(`/projects/user/${owner}/${name}`); + return await response.json(); + } + + async getProjectMetadataByName(owner, name) { + const response = await this.fetch( + `/projects/user/${owner}/${name}/metadata`, + ); + return await response.json(); + } + + async startNetworkTrace(projectId) { + const response = await this.post(`/network/id/${projectId}/trace/`); + return await response.text(); + } + + async stopNetworkTrace(projectId, traceId) { + const response = await this.post( + `/network/id/${projectId}/trace/${traceId}/stop`, + ); + return await response.text(); + } + + async getNetworkTrace(projectId, traceId) { + const response = await this.fetch( + `/network/id/${projectId}/trace/${traceId}`, + ); + return await response.json(); + } + + async getFriendRequestList() { + const response = await this.get(`/friends/${this.username}/invites/`); + return response.json(); + } + + async sendFriendRequest(username) { + await this.post(`/friends/${this.username}/invite/`, username.trim()); + } + + async unfriend(username) { + username = encodeURIComponent(username.trim()); + await this.post(`/friends/${this.username}/unfriend/${username}`); + } + + async respondToFriendRequest(sender, newState) { + sender = encodeURIComponent(sender); + await this.post(`/friends/${this.username}/invites/${sender}`, newState); + } + + async deleteRole(roleId) { + const method = "DELETE"; + await this.fetch(`/projects/id/${this.projectId}/${roleId}`, { method }); + } + + async deleteProject(projectId) { + const method = "DELETE"; + await this.fetch(`/projects/id/${projectId}`, { method }); + } + + async publishProject(projectId) { + const method = "POST"; + await this.fetch(`/projects/id/${projectId}/publish`, { method }); + } + + async unpublishProject(projectId) { + const method = "POST"; + await this.fetch(`/projects/id/${projectId}/unpublish`, { method }); + } + + reconnect(callback, errorCall) { + if (!this.username) { + this.message("You are not logged in"); + return; } - async getSharedProjectList() { - const response = await this.fetch(`/projects/shared/${this.username}`); - return await response.json(); + // need to set 'api' from setClientState + let promise = this.setClientState(); + if (callback && errorCall) { + promise = promise.then(callback) + .catch(errorCall); } - - async changePassword( - oldPW, - newPW, - ) { - const body = JSON.stringify(newPW); - const response = await this.fetch( - `/users/${this.username}/password`, - {method: 'PATCH', body} - ); - return await response.text(); + return promise; + } + + async logout() { + const method = "POST"; + await this.fetch("/users/logout", { method }); + this.clear(); + return true; + } + + async signup( + username: string, + password: string, + email: string, + ) { + const body = { + username, + password, + email, + }; + const response = await this.post("/users/create", body); + return await response.text(); + } + + async saveProjectCopy() { + const response = await this.fetch(`/projects/${this.projectId}/latest`); + const xml = await response.text(); + const options = { + method: "POST", + body: xml, // TODO: add options for allow rename? + }; + const saveResponse = await this.fetch(`/projects/`, options); + + // TODO: set the state with the network overlay + //this.setLocalState(response.projectId, this.roleId); + + return saveResponse.status == 200; + } + + async patch(url: string, body = undefined) { + const opts: RequestInit = { + method: "PATCH", + }; + if (body !== undefined) { + opts.body = JSON.stringify(body); } + return await this.fetch(url, opts); + } - parseResponse(src) { - var ans = [], - lines; - if (!src) {return ans; } - lines = src.split(" "); - lines.forEach(function (service) { - var entries = service.split("&"), - dict = {}; - entries.forEach(function (entry) { - var pair = entry.split("="), - key = decodeURIComponent(pair[0]), - val = decodeURIComponent(pair[1]); - dict[key] = val; - }); - ans.push(dict); - }); - return ans; - }; - - parseDict(src) { - var dict = {}; - if (!src) {return dict; } - src.split("&").forEach(function (entry) { - var pair = entry.split("="), - key = decodeURIComponent(pair[0]), - val = decodeURIComponent(pair[1]); - dict[key] = val; - }); - return dict; - }; - - encodeDict(dict) { - var str = '', - pair, - key; - if (!dict) {return null; } - for (key in dict) { - if (dict.hasOwnProperty(key)) { - pair = encodeURIComponent(key) - + '=' - + encodeURIComponent(dict[key]); - if (str.length > 0) { - str += '&'; - } - str += pair; - } - } - return str; - }; - - async getUserData() { - const response = await this.fetch(`/users/${this.username}`); - return await response.json(); - }; - - async addRole(name) { - const response = await this.post(`/projects/id/${this.projectId}/`, {name}); - // TODO: should I request the new project state, too? - // I shouldn't have to since we should be subscribed to changes... - //return await response.json(); - }; - - async saveRole(roleData) { - - const url = `/projects/id/${this.projectId}/${this.roleId}`; - const options = { - method: 'POST', - body: JSON.stringify(roleData), - }; - await this.fetch(url, options); + async post(url, body = undefined) { + const opts: RequestInit = { + method: "POST", }; - - async renameRole(roleId, name) { - const body = { - name, - clientId: this.clientId, - }; - const response = await this.patch(`/projects/id/${this.projectId}/${roleId}`, body); - // TODO: error handling - //return await response.json(); - }; - - async renameProject(name) { - const body = { - name, - clientId: this.clientId, - }; - const response = await this.patch(`/projects/id/${this.projectId}`, body); - }; - - async reportLatestRole(id, data) { - const clientId = this.clientId; - const options = { - method: 'POST', - body: JSON.stringify({id, data}) - }; - await this.fetch(`/projects/id/${this.projectId}/${this.roleId}/latest?clientId=${clientId}`, options); - }; - - async cloneRole(roleId) { - const projectId = this.projectId; - const fetchRoleResponse = await this.fetch(`/projects/id/${projectId}/${roleId}/latest`); - const {name, code, media} = await fetchRoleResponse.json(); - const response = await this.post(`/projects/id/${projectId}/`, {name, code, media}); - }; - - async sendOccupantInvite(username, roleId) { - const body = {username, roleId}; - await this.post(`/network/id/${this.projectId}/occupants/invite`, body); - }; - - async evictOccupant(clientID) { - const method = 'DELETE'; - await this.fetch(`/network/id/${this.projectId}/occupants/${clientID}`, {method}); - }; - - async getCollaboratorList() { - const response = await this.get(`/projects/id/${this.projectId}/collaborators/`); - return await response.json(); - }; - - async getCollaboratorRequestList() { - const response = await this.get(`/collaboration-invites/user/${this.username}/`); - return await response.json(); + if (body !== undefined) { + opts.body = JSON.stringify(body); } + return await this.fetch(url, opts); + } - async sendCollaborateRequest(username) { - await this.post(`/collaboration-invites/${this.projectId}/invite/${username}`); + async delete(url) { + const opts = { + method: "DELETE", }; + return await this.fetch(url, opts); + } - async respondToCollaborateRequest(id, accepted) { - const newState = accepted ? 'ACCEPTED' : 'REJECTED'; - await this.post(`/collaboration-invites/id/${id}`, newState); + async get(url) { + const opts = { + method: "GET", }; + return await this.fetch(url, opts); + } - async removeCollaborator(username, projectId) { - await this.delete(`/projects/id/${projectId}/collaborators/${username}`); + async fetch(url, opts?: RequestInit) { + opts = opts || {}; + url = this.url + url; + opts.credentials = opts.credentials || "include"; + opts.headers = { + "Content-Type": "application/json", }; - - async getOnlineFriendList() { - const response = await this.get(`/friends/${this.username}/online`); - return await response.json(); + if (this.token) { + opts.headers["cookie"] = `netsblox=${this.token}`; } - - async getFriendList() { - const response = await this.get(`/friends/${this.username}/`); - return await response.json(); - }; - - async getRole(projectId, roleId) { - const qs = this.clientId ? `clientId=${this.clientId}` : ''; - const response = await this.fetch(`/projects/id/${projectId}/${roleId}/latest?${qs}`); - const project = await response.json(); - // TODO: Set the state here? - this.setLocalState(projectId, roleId); - return project; - }; - - async getProjectMetadata(projectId) { - const response = await this.fetch(`/projects/id/${projectId}/metadata`); - const project = await response.json(); - return project; - }; - - async getProjectByName(owner, name) { - const response = await this.fetch(`/projects/user/${owner}/${name}`); - return await response.json(); - }; - - async getProjectMetadataByName(owner, name) { - const response = await this.fetch(`/projects/user/${owner}/${name}/metadata`); - return await response.json(); - }; - - async startNetworkTrace(projectId) { - const response = await this.post(`/network/id/${projectId}/trace/`); - return await response.text(); - }; - - async stopNetworkTrace(projectId, traceId) { - const response = await this.post(`/network/id/${projectId}/trace/${traceId}/stop`); - return await response.text(); - }; - - async getNetworkTrace(projectId, traceId) { - const response = await this.fetch(`/network/id/${projectId}/trace/${traceId}`); - return await response.json(); - }; - - async getFriendRequestList() { - const response = await this.get(`/friends/${this.username}/invites/`); - return response.json(); - } - - async sendFriendRequest(username) { - await this.post(`/friends/${this.username}/invite/`, username.trim()); - } - - async unfriend(username) { - username = encodeURIComponent(username.trim()); - await this.post(`/friends/${this.username}/unfriend/${username}`); + const response = await fetch(url, opts); + if (response.status > 399) { + const text = await response.text(); + const error = new CloudError(text); + await this.onerror(error); + throw error; } - - async respondToFriendRequest(sender, newState) { - sender = encodeURIComponent(sender); - await this.post(`/friends/${this.username}/invites/${sender}`, newState); + return response; + } + + async onerror(response) { + const text = await response.text(); + throw new CloudError(text); + } + + setLocalState(projectId, roleId) { + this.projectId = projectId; + this.roleId = roleId; + } + + resetLocalState() { + this.setLocalState(null, null); + } + + newProject(name = this.localize("untitled")) { + var myself = this; + + if (!this.newProjectRequest) { + const saveResponse = this.post(`/projects/`, { + name, + clientId: this.clientId, + }); + this.newProjectRequest = saveResponse + .then((response) => response.json()) + .then(async (metadata) => { + const [roleId] = Object.keys(metadata.roles); + this.setClientState(metadata.id, roleId); + myself.newProjectRequest = null; + return metadata; + }) + .catch(function (req) { + myself.resetLocalState(); + myself.newProjectRequest = null; + throw new Error(req.responseText); + }); } - async deleteRole(roleId) { - const method = 'DELETE'; - await this.fetch(`/projects/id/${this.projectId}/${roleId}`, {method}); - }; - - async deleteProject(projectId) { - const method = 'DELETE'; - await this.fetch(`/projects/id/${projectId}`, {method}); - }; - - async publishProject(projectId) { - const method = 'POST'; - await this.fetch(`/projects/id/${projectId}/publish`, {method}); - }; - - async unpublishProject(projectId) { - const method = 'POST'; - await this.fetch(`/projects/id/${projectId}/unpublish`, {method}); - }; - - reconnect(callback, errorCall) { - if (!this.username) { - this.message('You are not logged in'); - return; - } + return this.newProjectRequest; + } - // need to set 'api' from setClientState - let promise = this.setClientState(); - if (callback && errorCall) { - promise = promise.then(callback) - .catch(errorCall); - } - return promise; + getClientState() { + return { + username: this.username, + clientId: this.clientId, + projectId: this.projectId, + roleId: this.roleId, }; + } - async logout() { - const method = 'POST'; - await this.fetch('/users/logout', {method}); - this.clear(); - return true; - }; + async setClientState(projectId = this.projectId, roleId = this.roleId) { + var myself = this, + newProjectRequest = this.newProjectRequest || Promise.resolve(); - async signup( - username: string, - password: string, - email: string, - ) { + this.projectId = projectId; + this.roleId = roleId; + return newProjectRequest + .then(async () => { const body = { - username, - password, - email, + state: { + browser: { + projectId: this.projectId, + roleId: this.roleId, + }, + }, }; - const response = await this.post('/users/create', body); - return await response.text(); - }; - - async saveProjectCopy() { - const response = await this.fetch(`/projects/${this.projectId}/latest`); - const xml = await response.text(); - const options = { - method: 'POST', - body: xml, // TODO: add options for allow rename? - }; - const saveResponse = await this.fetch(`/projects/`, options); - - // TODO: set the state with the network overlay - //this.setLocalState(response.projectId, this.roleId); - - return saveResponse.status == 200; - }; - - async patch(url: string, body = undefined) { - const opts: RequestInit = { - method: 'PATCH', - }; - if (body !== undefined) { - opts.body = JSON.stringify(body); - } - return await this.fetch(url, opts); - }; - - async post(url, body=undefined) { - const opts: RequestInit = { - method: 'POST', - }; - if (body !== undefined) { - opts.body = JSON.stringify(body); - } - return await this.fetch(url, opts); - }; - - async delete(url) { - const opts = { - method: 'DELETE', - }; - return await this.fetch(url, opts); - }; - - async get(url) { - const opts = { - method: 'GET', - }; - return await this.fetch(url, opts); - }; - - async fetch(url, opts?: RequestInit) { - opts = opts || {}; - url = this.url + url; - opts.credentials = opts.credentials || 'include'; - opts.headers = { - 'Content-Type': 'application/json' - }; - if (this.token) { - opts.headers['cookie'] = `netsblox=${this.token}`; - } - const response = await fetch(url, opts); - if (response.status > 399) { - const text = await response.text(); - const error = new CloudError(text); - await this.onerror(error); - throw error; - } - return response; - } - - async onerror(response) { - const text = await response.text(); - throw new CloudError(text); - } - - setLocalState(projectId, roleId) { - this.projectId = projectId; - this.roleId = roleId; - }; - - resetLocalState() { - this.setLocalState(null, null); - }; - - newProject(name=this.localize('untitled')) { - var myself = this; - - if (!this.newProjectRequest) { - const saveResponse = this.post(`/projects/`, {name, clientId: this.clientId}); - this.newProjectRequest = saveResponse - .then(response => response.json()) - .then(async metadata => { - const [roleId] = Object.keys(metadata.roles); - this.setClientState(metadata.id, roleId); - myself.newProjectRequest = null; - return metadata; - }) - .catch(function(req) { - myself.resetLocalState(); - myself.newProjectRequest = null; - throw new Error(req.responseText); - }); - } - - return this.newProjectRequest; - }; - - getClientState() { - return { - username: this.username, - clientId: this.clientId, - projectId: this.projectId, - roleId: this.roleId - }; - }; - - async setClientState(projectId=this.projectId, roleId=this.roleId) { - var myself = this, - newProjectRequest = this.newProjectRequest || Promise.resolve(); - - this.projectId = projectId; - this.roleId = roleId; - return newProjectRequest - .then(async () => { - const body = { - state: { - browser: { - projectId: this.projectId, - roleId: this.roleId, - } - } - }; - await this.post(`/network/${this.clientId}/state`, body); - // Only change the project ID if no other moves/newProjects/etc have occurred - }) - .catch(function(req) { - var connError = 'Could not connect to ' + myself.url; - throw new Error(req.responseText || connError); - }); - }; - - setProjectName(name) { - const newProjectRequest = this.newProjectRequest || Promise.resolve(); - - - return newProjectRequest - .then(async () => { - await this.patch(`/projects/id/${this.projectId}`, {name, clientId: this.clientId}); - }); - }; - - async importProject(projectData) { - projectData.clientId = this.clientId; - - const response = await this.post('/projects/', projectData); - return await response.json(); - }; - - async linkAccount(username, password, type) { - await this.post(`/users/${this.username}/link/`, {Snap: {username, password}}); - }; - - async unlinkAccount(account: LinkedAccount) { - await this.post(`/users/${this.username}/unlink/`, account); - }; - - async getProjectData(projectId=this.projectId) { - const response = await this.fetch(`/projects/id/${projectId}/latest?clientId=${this.clientId}`); - return await response.json(); - }; - - async exportRole(projectId=this.projectId, roleId=this.roleId) { - const response = await this.fetch(`/projects/id/${projectId}/${roleId}/latest?clientId=${this.clientId}`); - return await response.text(); - }; - - async viewUser(username=this.username) { - const response = await this.fetch(`/users/${username}`); - return await response.json(); - } - - async whoAmI() { - const response = await this.get('/users/whoami'); - return await response.text(); - } - - async getCommunityLibraryList() { - const response = await this.get('/libraries/community/'); - return await response.json(); - } - - async getLibraryList() { - const response = await this.get(`/libraries/user/${this.username}/`); - return await response.json(); - } - - async saveLibrary(name, blocks, notes) { - const library = { - name, notes, blocks - }; - const response = await this.post(`/libraries/user/${this.username}/`, library); - return await response.json(); - } - - async getLibrary(username, name) { - name = encodeURIComponent(name); - const response = await this.get(`/libraries/user/${username}/${name}`); - return await response.text(); - } - - async deleteLibrary(name) { - name = encodeURIComponent(name); - return await this.delete(`/libraries/user/${this.username}/${name}`); - } - - async publishLibrary(name) { - name = encodeURIComponent(name); - const response = await this.post(`/libraries/user/${this.username}/${name}/publish`); - return await response.json(); - } - - async unpublishLibrary(name) { - name = encodeURIComponent(name); - await this.post(`/libraries/user/${this.username}/${name}/unpublish`); - } - - // Cloud: user messages (to be overridden) - - message(string) { - alert(string); - } - - // legacy api used by other sites using NetsBlox authentication - async register( - username: string, - password: string, - email: string, - ) { - return this.signup( - username, - password, - email); - } - - async checkLogin() { - try { - await this.whoAmI(); - return true; - } catch (err) { - return false; - } + await this.post(`/network/${this.clientId}/state`, body); + // Only change the project ID if no other moves/newProjects/etc have occurred + }) + .catch(function (req) { + var connError = "Could not connect to " + myself.url; + throw new Error(req.responseText || connError); + }); + } + + setProjectName(name) { + const newProjectRequest = this.newProjectRequest || Promise.resolve(); + + return newProjectRequest + .then(async () => { + await this.patch(`/projects/id/${this.projectId}`, { + name, + clientId: this.clientId, + }); + }); + } + + async importProject(projectData) { + projectData.clientId = this.clientId; + + const response = await this.post("/projects/", projectData); + return await response.json(); + } + + async linkAccount(username, password, type) { + await this.post(`/users/${this.username}/link/`, { + Snap: { username, password }, + }); + } + + async unlinkAccount(account: LinkedAccount) { + await this.post(`/users/${this.username}/unlink/`, account); + } + + async getProjectData(projectId = this.projectId) { + const response = await this.fetch( + `/projects/id/${projectId}/latest?clientId=${this.clientId}`, + ); + return await response.json(); + } + + async exportRole(projectId = this.projectId, roleId = this.roleId) { + const response = await this.fetch( + `/projects/id/${projectId}/${roleId}/latest?clientId=${this.clientId}`, + ); + return await response.text(); + } + + async viewUser(username = this.username) { + const response = await this.fetch(`/users/${username}`); + return await response.json(); + } + + async whoAmI() { + const response = await this.get("/users/whoami"); + return await response.text(); + } + + async getCommunityLibraryList() { + const response = await this.get("/libraries/community/"); + return await response.json(); + } + + async getLibraryList() { + const response = await this.get(`/libraries/user/${this.username}/`); + return await response.json(); + } + + async saveLibrary(name, blocks, notes) { + const library = { + name, + notes, + blocks, + }; + const response = await this.post( + `/libraries/user/${this.username}/`, + library, + ); + return await response.json(); + } + + async getLibrary(username, name) { + name = encodeURIComponent(name); + const response = await this.get(`/libraries/user/${username}/${name}`); + return await response.text(); + } + + async deleteLibrary(name) { + name = encodeURIComponent(name); + return await this.delete(`/libraries/user/${this.username}/${name}`); + } + + async publishLibrary(name) { + name = encodeURIComponent(name); + const response = await this.post( + `/libraries/user/${this.username}/${name}/publish`, + ); + return await response.json(); + } + + async unpublishLibrary(name) { + name = encodeURIComponent(name); + await this.post(`/libraries/user/${this.username}/${name}/unpublish`); + } + + // Cloud: user messages (to be overridden) + + message(string) { + alert(string); + } + + // legacy api used by other sites using NetsBlox authentication + async register( + username: string, + password: string, + email: string, + ) { + return this.signup( + username, + password, + email, + ); + } + + async checkLogin() { + try { + await this.whoAmI(); + return true; + } catch (err) { + return false; } + } - async getProfile() { - const profile = await this.viewUser(); - return profile; - } + async getProfile() { + const profile = await this.viewUser(); + return profile; + } } export class CloudError extends Error { - label: string; + label: string; - constructor(label, message = undefined) { - super(message || label); - this.label = label; - } + constructor(label, message = undefined) { + super(message || label); + this.label = label; + } } diff --git a/test/client.spec.js b/test/client.spec.js index 0742c73..c24ab77 100644 --- a/test/client.spec.js +++ b/test/client.spec.js @@ -1,74 +1,73 @@ -const CloudClient = require('../src/client'); -const assert = require('assert'); +const CloudClient = require("../src/client"); +const assert = require("assert"); -const client = new CloudClient('http://localhost:7777'); +const client = new CloudClient("http://localhost:7777"); -describe('localize', function() { - it('should have localize fn (default: identity)', function() { - assert.equal(client.localize('abc'), 'abc'); - }); +describe("localize", function () { + it("should have localize fn (default: identity)", function () { + assert.equal(client.localize("abc"), "abc"); + }); }); -describe('login', function() { - it('should login on success', async function() { - const username = await client.login('test', 'password'); - assert.equal(username, 'test'); - }); +describe("login", function () { + it("should login on success", async function () { + const username = await client.login("test", "password"); + assert.equal(username, "test"); + }); - it('should maintain state', async function() { - await client.login('test', 'password'); - const {username} = await client.viewUser(); - assert.equal(username, 'test'); - }); + it("should maintain state", async function () { + await client.login("test", "password"); + const { username } = await client.viewUser(); + assert.equal(username, "test"); + }); - it('should throw error on bad password', async function() { - await assert.rejects( - client.login('test', 'incorrectPassword') - ); - }); + it("should throw error on bad password", async function () { + await assert.rejects( + client.login("test", "incorrectPassword"), + ); + }); - it('should throw error on invalid URL', async function() { - const client = new CloudClient('http://localhost:da7777'); - await assert.rejects(client.login('test', 'password')); - }); + it("should throw error on invalid URL", async function () { + const client = new CloudClient("http://localhost:da7777"); + await assert.rejects(client.login("test", "password")); + }); }); -describe('logout', function() { - before(async () => { - await client.login('test', 'password'); - }); +describe("logout", function () { + before(async () => { + await client.login("test", "password"); + }); - it('should logout', async function() { - await client.logout(); - await assert.rejects(client.viewUser()); - }); + it("should logout", async function () { + await client.logout(); + await assert.rejects(client.viewUser()); + }); }); -describe('register', function() { - it('should create new user', async function() { - client.clear(); - await client.register('testUser', 'testUser@netsblox.org'); - }); +describe("register", function () { + it("should create new user", async function () { + client.clear(); + await client.register("testUser", "testUser@netsblox.org"); + }); }); -describe('checkLogin', function() { - it('should report true when logged in', async function() { - const username = await client.login('test', 'password'); - assert(await client.checkLogin()); - }); - - it('should report false when not logged in', async function() { - await client.logout(); - assert(!await client.checkLogin()); - }); +describe("checkLogin", function () { + it("should report true when logged in", async function () { + const username = await client.login("test", "password"); + assert(await client.checkLogin()); + }); + it("should report false when not logged in", async function () { + await client.logout(); + assert(!await client.checkLogin()); + }); }); -describe('getProfile', function() { - it('should get the user profile', async function() { - const username = await client.login('test', 'password'); - const profile = await client.getProfile(); - assert.equal(profile.username, 'test'); - assert(profile.email); - }); +describe("getProfile", function () { + it("should get the user profile", async function () { + const username = await client.login("test", "password"); + const profile = await client.getProfile(); + assert.equal(profile.username, "test"); + assert(profile.email); + }); }); diff --git a/tsconfig.json b/tsconfig.json index 3c0ab33..7c27fe8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es2020", - "moduleResolution": "node", + "moduleResolution": "node" }, "include": ["src/**/*.ts"] }