owltech
Version:
This a backend for OwlTech Company
1,468 lines (1,458 loc) • 607 kB
JavaScript
import firebase from '@firebase/app';
import { __extends, __awaiter, __generator } from 'tslib';
import { stringify, jsonEval, contains, assert, base64, stringToByteArray, Sha1, isNodeSdk, forEach, stringLength, errorPrefix, validateArgCount, validateCallback, Deferred, assertionError, safeGet, clone, map, getCount, getAnyKey, every, validateContextObject, isEmpty, getValues, findValue, findKey, deepCopy, isMobileCordova, base64Encode, CONSTANTS, isAdmin, isValidFormat, isReactNative, querystring } from '@firebase/util';
import { Logger, LogLevel } from '@firebase/logger';
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Wraps a DOM Storage object and:
* - automatically encode objects as JSON strings before storing them to allow us to store arbitrary types.
* - prefixes names with "firebase:" to avoid collisions with app data.
*
* We automatically (see storage.js) create two such wrappers, one for sessionStorage,
* and one for localStorage.
*
* @constructor
*/
var DOMStorageWrapper = /** @class */ (function () {
/**
* @param {Storage} domStorage_ The underlying storage object (e.g. localStorage or sessionStorage)
*/
function DOMStorageWrapper(domStorage_) {
this.domStorage_ = domStorage_;
// Use a prefix to avoid collisions with other stuff saved by the app.
this.prefix_ = 'firebase:';
}
/**
* @param {string} key The key to save the value under
* @param {?Object} value The value being stored, or null to remove the key.
*/
DOMStorageWrapper.prototype.set = function (key, value) {
if (value == null) {
this.domStorage_.removeItem(this.prefixedName_(key));
}
else {
this.domStorage_.setItem(this.prefixedName_(key), stringify(value));
}
};
/**
* @param {string} key
* @return {*} The value that was stored under this key, or null
*/
DOMStorageWrapper.prototype.get = function (key) {
var storedVal = this.domStorage_.getItem(this.prefixedName_(key));
if (storedVal == null) {
return null;
}
else {
return jsonEval(storedVal);
}
};
/**
* @param {string} key
*/
DOMStorageWrapper.prototype.remove = function (key) {
this.domStorage_.removeItem(this.prefixedName_(key));
};
/**
* @param {string} name
* @return {string}
*/
DOMStorageWrapper.prototype.prefixedName_ = function (name) {
return this.prefix_ + name;
};
DOMStorageWrapper.prototype.toString = function () {
return this.domStorage_.toString();
};
return DOMStorageWrapper;
}());
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* An in-memory storage implementation that matches the API of DOMStorageWrapper
* (TODO: create interface for both to implement).
*
* @constructor
*/
var MemoryStorage = /** @class */ (function () {
function MemoryStorage() {
this.cache_ = {};
this.isInMemoryStorage = true;
}
MemoryStorage.prototype.set = function (key, value) {
if (value == null) {
delete this.cache_[key];
}
else {
this.cache_[key] = value;
}
};
MemoryStorage.prototype.get = function (key) {
if (contains(this.cache_, key)) {
return this.cache_[key];
}
return null;
};
MemoryStorage.prototype.remove = function (key) {
delete this.cache_[key];
};
return MemoryStorage;
}());
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Helper to create a DOMStorageWrapper or else fall back to MemoryStorage.
* TODO: Once MemoryStorage and DOMStorageWrapper have a shared interface this method annotation should change
* to reflect this type
*
* @param {string} domStorageName Name of the underlying storage object
* (e.g. 'localStorage' or 'sessionStorage').
* @return {?} Turning off type information until a common interface is defined.
*/
var createStoragefor = function (domStorageName) {
try {
// NOTE: just accessing "localStorage" or "window['localStorage']" may throw a security exception,
// so it must be inside the try/catch.
if (typeof window !== 'undefined' &&
typeof window[domStorageName] !== 'undefined') {
// Need to test cache. Just because it's here doesn't mean it works
var domStorage = window[domStorageName];
domStorage.setItem('firebase:sentinel', 'cache');
domStorage.removeItem('firebase:sentinel');
return new DOMStorageWrapper(domStorage);
}
}
catch (e) { }
// Failed to create wrapper. Just return in-memory storage.
// TODO: log?
return new MemoryStorage();
};
/** A storage object that lasts across sessions */
var PersistentStorage = createStoragefor('localStorage');
/** A storage object that only lasts one session */
var SessionStorage = createStoragefor('sessionStorage');
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var logClient = new Logger('@firebase/database');
/**
* Returns a locally-unique ID (generated by just incrementing up from 0 each time its called).
* @type {function(): number} Generated ID.
*/
var LUIDGenerator = (function () {
var id = 1;
return function () {
return id++;
};
})();
/**
* Sha1 hash of the input string
* @param {!string} str The string to hash
* @return {!string} The resulting hash
*/
var sha1 = function (str) {
var utf8Bytes = stringToByteArray(str);
var sha1 = new Sha1();
sha1.update(utf8Bytes);
var sha1Bytes = sha1.digest();
return base64.encodeByteArray(sha1Bytes);
};
/**
* @param {...*} var_args
* @return {string}
* @private
*/
var buildLogMessage_ = function () {
var var_args = [];
for (var _i = 0; _i < arguments.length; _i++) {
var_args[_i] = arguments[_i];
}
var message = '';
for (var i = 0; i < var_args.length; i++) {
if (Array.isArray(var_args[i]) ||
(var_args[i] &&
typeof var_args[i] === 'object' &&
typeof var_args[i].length === 'number')) {
message += buildLogMessage_.apply(null, var_args[i]);
}
else if (typeof var_args[i] === 'object') {
message += stringify(var_args[i]);
}
else {
message += var_args[i];
}
message += ' ';
}
return message;
};
/**
* Use this for all debug messages in Firebase.
* @type {?function(string)}
*/
var logger = null;
/**
* Flag to check for log availability on first log message
* @type {boolean}
* @private
*/
var firstLog_ = true;
/**
* The implementation of Firebase.enableLogging (defined here to break dependencies)
* @param {boolean|?function(string)} logger_ A flag to turn on logging, or a custom logger
* @param {boolean=} persistent Whether or not to persist logging settings across refreshes
*/
var enableLogging = function (logger_, persistent) {
assert(!persistent || (logger_ === true || logger_ === false), "Can't turn on custom loggers persistently.");
if (logger_ === true) {
logClient.logLevel = LogLevel.VERBOSE;
logger = logClient.log.bind(logClient);
if (persistent)
SessionStorage.set('logging_enabled', true);
}
else if (typeof logger_ === 'function') {
logger = logger_;
}
else {
logger = null;
SessionStorage.remove('logging_enabled');
}
};
/**
*
* @param {...(string|Arguments)} var_args
*/
var log = function () {
var var_args = [];
for (var _i = 0; _i < arguments.length; _i++) {
var_args[_i] = arguments[_i];
}
if (firstLog_ === true) {
firstLog_ = false;
if (logger === null && SessionStorage.get('logging_enabled') === true)
enableLogging(true);
}
if (logger) {
var message = buildLogMessage_.apply(null, var_args);
logger(message);
}
};
/**
* @param {!string} prefix
* @return {function(...[*])}
*/
var logWrapper = function (prefix) {
return function () {
var var_args = [];
for (var _i = 0; _i < arguments.length; _i++) {
var_args[_i] = arguments[_i];
}
log.apply(void 0, [prefix].concat(var_args));
};
};
/**
* @param {...string} var_args
*/
var error = function () {
var var_args = [];
for (var _i = 0; _i < arguments.length; _i++) {
var_args[_i] = arguments[_i];
}
var message = 'FIREBASE INTERNAL ERROR: ' + buildLogMessage_.apply(void 0, var_args);
logClient.error(message);
};
/**
* @param {...string} var_args
*/
var fatal = function () {
var var_args = [];
for (var _i = 0; _i < arguments.length; _i++) {
var_args[_i] = arguments[_i];
}
var message = "FIREBASE FATAL ERROR: " + buildLogMessage_.apply(void 0, var_args);
logClient.error(message);
throw new Error(message);
};
/**
* @param {...*} var_args
*/
var warn = function () {
var var_args = [];
for (var _i = 0; _i < arguments.length; _i++) {
var_args[_i] = arguments[_i];
}
var message = 'FIREBASE WARNING: ' + buildLogMessage_.apply(void 0, var_args);
logClient.warn(message);
};
/**
* Logs a warning if the containing page uses https. Called when a call to new Firebase
* does not use https.
*/
var warnIfPageIsSecure = function () {
// Be very careful accessing browser globals. Who knows what may or may not exist.
if (typeof window !== 'undefined' &&
window.location &&
window.location.protocol &&
window.location.protocol.indexOf('https:') !== -1) {
warn('Insecure Firebase access from a secure page. ' +
'Please use https in calls to new Firebase().');
}
};
/**
* Returns true if data is NaN, or +/- Infinity.
* @param {*} data
* @return {boolean}
*/
var isInvalidJSONNumber = function (data) {
return (typeof data === 'number' &&
(data != data || // NaN
data == Number.POSITIVE_INFINITY ||
data == Number.NEGATIVE_INFINITY));
};
/**
* @param {function()} fn
*/
var executeWhenDOMReady = function (fn) {
if (isNodeSdk() || document.readyState === 'complete') {
fn();
}
else {
// Modeled after jQuery. Try DOMContentLoaded and onreadystatechange (which
// fire before onload), but fall back to onload.
var called_1 = false;
var wrappedFn_1 = function () {
if (!document.body) {
setTimeout(wrappedFn_1, Math.floor(10));
return;
}
if (!called_1) {
called_1 = true;
fn();
}
};
if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', wrappedFn_1, false);
// fallback to onload.
window.addEventListener('load', wrappedFn_1, false);
}
else if (document.attachEvent) {
// IE.
document.attachEvent('onreadystatechange', function () {
if (document.readyState === 'complete')
wrappedFn_1();
});
// fallback to onload.
window.attachEvent('onload', wrappedFn_1);
// jQuery has an extra hack for IE that we could employ (based on
// http://javascript.nwbox.com/IEContentLoaded/) But it looks really old.
// I'm hoping we don't need it.
}
}
};
/**
* Minimum key name. Invalid for actual data, used as a marker to sort before any valid names
* @type {!string}
*/
var MIN_NAME = '[MIN_NAME]';
/**
* Maximum key name. Invalid for actual data, used as a marker to sort above any valid names
* @type {!string}
*/
var MAX_NAME = '[MAX_NAME]';
/**
* Compares valid Firebase key names, plus min and max name
* @param {!string} a
* @param {!string} b
* @return {!number}
*/
var nameCompare = function (a, b) {
if (a === b) {
return 0;
}
else if (a === MIN_NAME || b === MAX_NAME) {
return -1;
}
else if (b === MIN_NAME || a === MAX_NAME) {
return 1;
}
else {
var aAsInt = tryParseInt(a), bAsInt = tryParseInt(b);
if (aAsInt !== null) {
if (bAsInt !== null) {
return aAsInt - bAsInt == 0 ? a.length - b.length : aAsInt - bAsInt;
}
else {
return -1;
}
}
else if (bAsInt !== null) {
return 1;
}
else {
return a < b ? -1 : 1;
}
}
};
/**
* @param {!string} a
* @param {!string} b
* @return {!number} comparison result.
*/
var stringCompare = function (a, b) {
if (a === b) {
return 0;
}
else if (a < b) {
return -1;
}
else {
return 1;
}
};
/**
* @param {string} key
* @param {Object} obj
* @return {*}
*/
var requireKey = function (key, obj) {
if (obj && key in obj) {
return obj[key];
}
else {
throw new Error('Missing required key (' + key + ') in object: ' + stringify(obj));
}
};
/**
* @param {*} obj
* @return {string}
*/
var ObjectToUniqueKey = function (obj) {
if (typeof obj !== 'object' || obj === null)
return stringify(obj);
var keys = [];
for (var k in obj) {
keys.push(k);
}
// Export as json, but with the keys sorted.
keys.sort();
var key = '{';
for (var i = 0; i < keys.length; i++) {
if (i !== 0)
key += ',';
key += stringify(keys[i]);
key += ':';
key += ObjectToUniqueKey(obj[keys[i]]);
}
key += '}';
return key;
};
/**
* Splits a string into a number of smaller segments of maximum size
* @param {!string} str The string
* @param {!number} segsize The maximum number of chars in the string.
* @return {Array.<string>} The string, split into appropriately-sized chunks
*/
var splitStringBySize = function (str, segsize) {
var len = str.length;
if (len <= segsize) {
return [str];
}
var dataSegs = [];
for (var c = 0; c < len; c += segsize) {
if (c + segsize > len) {
dataSegs.push(str.substring(c, len));
}
else {
dataSegs.push(str.substring(c, c + segsize));
}
}
return dataSegs;
};
/**
* Apply a function to each (key, value) pair in an object or
* apply a function to each (index, value) pair in an array
* @param {!(Object|Array)} obj The object or array to iterate over
* @param {function(?, ?)} fn The function to apply
*/
var each = function (obj, fn) {
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; ++i) {
fn(i, obj[i]);
}
}
else {
/**
* in the conversion of code we removed the goog.object.forEach
* function which did a value,key callback. We standardized on
* a single impl that does a key, value callback. So we invert
* to not have to touch the `each` code points
*/
forEach(obj, function (key, val) { return fn(val, key); });
}
};
/**
* Borrowed from http://hg.secondlife.com/llsd/src/tip/js/typedarray.js (MIT License)
* I made one modification at the end and removed the NaN / Infinity
* handling (since it seemed broken [caused an overflow] and we don't need it). See MJL comments.
* @param {!number} v A double
* @return {string}
*/
var doubleToIEEE754String = function (v) {
assert(!isInvalidJSONNumber(v), 'Invalid JSON number'); // MJL
var ebits = 11, fbits = 52;
var bias = (1 << (ebits - 1)) - 1, s, e, f, ln, i, bits, str;
// Compute sign, exponent, fraction
// Skip NaN / Infinity handling --MJL.
if (v === 0) {
e = 0;
f = 0;
s = 1 / v === -Infinity ? 1 : 0;
}
else {
s = v < 0;
v = Math.abs(v);
if (v >= Math.pow(2, 1 - bias)) {
// Normalized
ln = Math.min(Math.floor(Math.log(v) / Math.LN2), bias);
e = ln + bias;
f = Math.round(v * Math.pow(2, fbits - ln) - Math.pow(2, fbits));
}
else {
// Denormalized
e = 0;
f = Math.round(v / Math.pow(2, 1 - bias - fbits));
}
}
// Pack sign, exponent, fraction
bits = [];
for (i = fbits; i; i -= 1) {
bits.push(f % 2 ? 1 : 0);
f = Math.floor(f / 2);
}
for (i = ebits; i; i -= 1) {
bits.push(e % 2 ? 1 : 0);
e = Math.floor(e / 2);
}
bits.push(s ? 1 : 0);
bits.reverse();
str = bits.join('');
// Return the data as a hex string. --MJL
var hexByteString = '';
for (i = 0; i < 64; i += 8) {
var hexByte = parseInt(str.substr(i, 8), 2).toString(16);
if (hexByte.length === 1)
hexByte = '0' + hexByte;
hexByteString = hexByteString + hexByte;
}
return hexByteString.toLowerCase();
};
/**
* Used to detect if we're in a Chrome content script (which executes in an
* isolated environment where long-polling doesn't work).
* @return {boolean}
*/
var isChromeExtensionContentScript = function () {
return !!(typeof window === 'object' &&
window['chrome'] &&
window['chrome']['extension'] &&
!/^chrome/.test(window.location.href));
};
/**
* Used to detect if we're in a Windows 8 Store app.
* @return {boolean}
*/
var isWindowsStoreApp = function () {
// Check for the presence of a couple WinRT globals
return typeof Windows === 'object' && typeof Windows.UI === 'object';
};
/**
* Converts a server error code to a Javascript Error
* @param {!string} code
* @param {!Query} query
* @return {Error}
*/
var errorForServerCode = function (code, query) {
var reason = 'Unknown Error';
if (code === 'too_big') {
reason =
'The data requested exceeds the maximum size ' +
'that can be accessed with a single request.';
}
else if (code == 'permission_denied') {
reason = "Client doesn't have permission to access the desired data.";
}
else if (code == 'unavailable') {
reason = 'The service is unavailable';
}
var error = new Error(code + ' at ' + query.path.toString() + ': ' + reason);
error.code = code.toUpperCase();
return error;
};
/**
* Used to test for integer-looking strings
* @type {RegExp}
* @private
*/
var INTEGER_REGEXP_ = new RegExp('^-?\\d{1,10}$');
/**
* If the string contains a 32-bit integer, return it. Else return null.
* @param {!string} str
* @return {?number}
*/
var tryParseInt = function (str) {
if (INTEGER_REGEXP_.test(str)) {
var intVal = Number(str);
if (intVal >= -2147483648 && intVal <= 2147483647) {
return intVal;
}
}
return null;
};
/**
* Helper to run some code but catch any exceptions and re-throw them later.
* Useful for preventing user callbacks from breaking internal code.
*
* Re-throwing the exception from a setTimeout is a little evil, but it's very
* convenient (we don't have to try to figure out when is a safe point to
* re-throw it), and the behavior seems reasonable:
*
* * If you aren't pausing on exceptions, you get an error in the console with
* the correct stack trace.
* * If you're pausing on all exceptions, the debugger will pause on your
* exception and then again when we rethrow it.
* * If you're only pausing on uncaught exceptions, the debugger will only pause
* on us re-throwing it.
*
* @param {!function()} fn The code to guard.
*/
var exceptionGuard = function (fn) {
try {
fn();
}
catch (e) {
// Re-throw exception when it's safe.
setTimeout(function () {
// It used to be that "throw e" would result in a good console error with
// relevant context, but as of Chrome 39, you just get the firebase.js
// file/line number where we re-throw it, which is useless. So we log
// e.stack explicitly.
var stack = e.stack || '';
warn('Exception was thrown by user callback.', stack);
throw e;
}, Math.floor(0));
}
};
/**
* @return {boolean} true if we think we're currently being crawled.
*/
var beingCrawled = function () {
var userAgent = (typeof window === 'object' &&
window['navigator'] &&
window['navigator']['userAgent']) ||
'';
// For now we whitelist the most popular crawlers. We should refine this to be the set of crawlers we
// believe to support JavaScript/AJAX rendering.
// NOTE: Google Webmaster Tools doesn't really belong, but their "This is how a visitor to your website
// would have seen the page" is flaky if we don't treat it as a crawler.
return (userAgent.search(/googlebot|google webmaster tools|bingbot|yahoo! slurp|baiduspider|yandexbot|duckduckbot/i) >= 0);
};
/**
* Same as setTimeout() except on Node.JS it will /not/ prevent the process from exiting.
*
* It is removed with clearTimeout() as normal.
*
* @param {Function} fn Function to run.
* @param {number} time Milliseconds to wait before running.
* @return {number|Object} The setTimeout() return value.
*/
var setTimeoutNonBlocking = function (fn, time) {
var timeout = setTimeout(fn, time);
if (typeof timeout === 'object' && timeout['unref']) {
timeout['unref']();
}
return timeout;
};
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* An immutable object representing a parsed path. It's immutable so that you
* can pass them around to other functions without worrying about them changing
* it.
*/
var Path = /** @class */ (function () {
/**
* @param {string|Array.<string>} pathOrString Path string to parse,
* or another path, or the raw tokens array
* @param {number=} pieceNum
*/
function Path(pathOrString, pieceNum) {
if (pieceNum === void 0) {
this.pieces_ = pathOrString.split('/');
// Remove empty pieces.
var copyTo = 0;
for (var i = 0; i < this.pieces_.length; i++) {
if (this.pieces_[i].length > 0) {
this.pieces_[copyTo] = this.pieces_[i];
copyTo++;
}
}
this.pieces_.length = copyTo;
this.pieceNum_ = 0;
}
else {
this.pieces_ = pathOrString;
this.pieceNum_ = pieceNum;
}
}
Object.defineProperty(Path, "Empty", {
/**
* Singleton to represent an empty path
*
* @const
*/
get: function () {
return new Path('');
},
enumerable: true,
configurable: true
});
Path.prototype.getFront = function () {
if (this.pieceNum_ >= this.pieces_.length)
return null;
return this.pieces_[this.pieceNum_];
};
/**
* @return {number} The number of segments in this path
*/
Path.prototype.getLength = function () {
return this.pieces_.length - this.pieceNum_;
};
/**
* @return {!Path}
*/
Path.prototype.popFront = function () {
var pieceNum = this.pieceNum_;
if (pieceNum < this.pieces_.length) {
pieceNum++;
}
return new Path(this.pieces_, pieceNum);
};
/**
* @return {?string}
*/
Path.prototype.getBack = function () {
if (this.pieceNum_ < this.pieces_.length)
return this.pieces_[this.pieces_.length - 1];
return null;
};
Path.prototype.toString = function () {
var pathString = '';
for (var i = this.pieceNum_; i < this.pieces_.length; i++) {
if (this.pieces_[i] !== '')
pathString += '/' + this.pieces_[i];
}
return pathString || '/';
};
Path.prototype.toUrlEncodedString = function () {
var pathString = '';
for (var i = this.pieceNum_; i < this.pieces_.length; i++) {
if (this.pieces_[i] !== '')
pathString += '/' + encodeURIComponent(String(this.pieces_[i]));
}
return pathString || '/';
};
/**
* Shallow copy of the parts of the path.
*
* @param {number=} begin
* @return {!Array<string>}
*/
Path.prototype.slice = function (begin) {
if (begin === void 0) { begin = 0; }
return this.pieces_.slice(this.pieceNum_ + begin);
};
/**
* @return {?Path}
*/
Path.prototype.parent = function () {
if (this.pieceNum_ >= this.pieces_.length)
return null;
var pieces = [];
for (var i = this.pieceNum_; i < this.pieces_.length - 1; i++)
pieces.push(this.pieces_[i]);
return new Path(pieces, 0);
};
/**
* @param {string|!Path} childPathObj
* @return {!Path}
*/
Path.prototype.child = function (childPathObj) {
var pieces = [];
for (var i = this.pieceNum_; i < this.pieces_.length; i++)
pieces.push(this.pieces_[i]);
if (childPathObj instanceof Path) {
for (var i = childPathObj.pieceNum_; i < childPathObj.pieces_.length; i++) {
pieces.push(childPathObj.pieces_[i]);
}
}
else {
var childPieces = childPathObj.split('/');
for (var i = 0; i < childPieces.length; i++) {
if (childPieces[i].length > 0)
pieces.push(childPieces[i]);
}
}
return new Path(pieces, 0);
};
/**
* @return {boolean} True if there are no segments in this path
*/
Path.prototype.isEmpty = function () {
return this.pieceNum_ >= this.pieces_.length;
};
/**
* @param {!Path} outerPath
* @param {!Path} innerPath
* @return {!Path} The path from outerPath to innerPath
*/
Path.relativePath = function (outerPath, innerPath) {
var outer = outerPath.getFront(), inner = innerPath.getFront();
if (outer === null) {
return innerPath;
}
else if (outer === inner) {
return Path.relativePath(outerPath.popFront(), innerPath.popFront());
}
else {
throw new Error('INTERNAL ERROR: innerPath (' +
innerPath +
') is not within ' +
'outerPath (' +
outerPath +
')');
}
};
/**
* @param {!Path} left
* @param {!Path} right
* @return {number} -1, 0, 1 if left is less, equal, or greater than the right.
*/
Path.comparePaths = function (left, right) {
var leftKeys = left.slice();
var rightKeys = right.slice();
for (var i = 0; i < leftKeys.length && i < rightKeys.length; i++) {
var cmp = nameCompare(leftKeys[i], rightKeys[i]);
if (cmp !== 0)
return cmp;
}
if (leftKeys.length === rightKeys.length)
return 0;
return leftKeys.length < rightKeys.length ? -1 : 1;
};
/**
*
* @param {Path} other
* @return {boolean} true if paths are the same.
*/
Path.prototype.equals = function (other) {
if (this.getLength() !== other.getLength()) {
return false;
}
for (var i = this.pieceNum_, j = other.pieceNum_; i <= this.pieces_.length; i++, j++) {
if (this.pieces_[i] !== other.pieces_[j]) {
return false;
}
}
return true;
};
/**
*
* @param {!Path} other
* @return {boolean} True if this path is a parent (or the same as) other
*/
Path.prototype.contains = function (other) {
var i = this.pieceNum_;
var j = other.pieceNum_;
if (this.getLength() > other.getLength()) {
return false;
}
while (i < this.pieces_.length) {
if (this.pieces_[i] !== other.pieces_[j]) {
return false;
}
++i;
++j;
}
return true;
};
return Path;
}()); // end Path
/**
* Dynamic (mutable) path used to count path lengths.
*
* This class is used to efficiently check paths for valid
* length (in UTF8 bytes) and depth (used in path validation).
*
* Throws Error exception if path is ever invalid.
*
* The definition of a path always begins with '/'.
*/
var ValidationPath = /** @class */ (function () {
/**
* @param {!Path} path Initial Path.
* @param {string} errorPrefix_ Prefix for any error messages.
*/
function ValidationPath(path, errorPrefix_) {
this.errorPrefix_ = errorPrefix_;
/** @type {!Array<string>} */
this.parts_ = path.slice();
/** @type {number} Initialize to number of '/' chars needed in path. */
this.byteLength_ = Math.max(1, this.parts_.length);
for (var i = 0; i < this.parts_.length; i++) {
this.byteLength_ += stringLength(this.parts_[i]);
}
this.checkValid_();
}
Object.defineProperty(ValidationPath, "MAX_PATH_DEPTH", {
/** @const {number} Maximum key depth. */
get: function () {
return 32;
},
enumerable: true,
configurable: true
});
Object.defineProperty(ValidationPath, "MAX_PATH_LENGTH_BYTES", {
/** @const {number} Maximum number of (UTF8) bytes in a Firebase path. */
get: function () {
return 768;
},
enumerable: true,
configurable: true
});
/** @param {string} child */
ValidationPath.prototype.push = function (child) {
// Count the needed '/'
if (this.parts_.length > 0) {
this.byteLength_ += 1;
}
this.parts_.push(child);
this.byteLength_ += stringLength(child);
this.checkValid_();
};
ValidationPath.prototype.pop = function () {
var last = this.parts_.pop();
this.byteLength_ -= stringLength(last);
// Un-count the previous '/'
if (this.parts_.length > 0) {
this.byteLength_ -= 1;
}
};
ValidationPath.prototype.checkValid_ = function () {
if (this.byteLength_ > ValidationPath.MAX_PATH_LENGTH_BYTES) {
throw new Error(this.errorPrefix_ +
'has a key path longer than ' +
ValidationPath.MAX_PATH_LENGTH_BYTES +
' bytes (' +
this.byteLength_ +
').');
}
if (this.parts_.length > ValidationPath.MAX_PATH_DEPTH) {
throw new Error(this.errorPrefix_ +
'path specified exceeds the maximum depth that can be written (' +
ValidationPath.MAX_PATH_DEPTH +
') or object contains a cycle ' +
this.toErrorString());
}
};
/**
* String for use in error messages - uses '.' notation for path.
*
* @return {string}
*/
ValidationPath.prototype.toErrorString = function () {
if (this.parts_.length == 0) {
return '';
}
return "in property '" + this.parts_.join('.') + "'";
};
return ValidationPath;
}());
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var PROTOCOL_VERSION = '5';
var VERSION_PARAM = 'v';
var TRANSPORT_SESSION_PARAM = 's';
var REFERER_PARAM = 'r';
var FORGE_REF = 'f';
var FORGE_DOMAIN = 'firebaseio.com';
var LAST_SESSION_PARAM = 'ls';
var WEBSOCKET = 'websocket';
var LONG_POLLING = 'long_polling';
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A class that holds metadata about a Repo object
*
* @constructor
*/
var RepoInfo = /** @class */ (function () {
/**
* @param {string} host Hostname portion of the url for the repo
* @param {boolean} secure Whether or not this repo is accessed over ssl
* @param {string} namespace The namespace represented by the repo
* @param {boolean} webSocketOnly Whether to prefer websockets over all other transports (used by Nest).
* @param {string=} persistenceKey Override the default session persistence storage key
*/
function RepoInfo(host, secure, namespace, webSocketOnly, persistenceKey) {
if (persistenceKey === void 0) { persistenceKey = ''; }
this.secure = secure;
this.namespace = namespace;
this.webSocketOnly = webSocketOnly;
this.persistenceKey = persistenceKey;
this.host = host.toLowerCase();
this.domain = this.host.substr(this.host.indexOf('.') + 1);
this.internalHost = PersistentStorage.get('host:' + host) || this.host;
}
RepoInfo.prototype.needsQueryParam = function () {
return this.host !== this.internalHost || this.isCustomHost();
};
RepoInfo.prototype.isCacheableHost = function () {
return this.internalHost.substr(0, 2) === 's-';
};
RepoInfo.prototype.isDemoHost = function () {
return this.domain === 'firebaseio-demo.com';
};
RepoInfo.prototype.isCustomHost = function () {
return (this.domain !== 'firebaseio.com' && this.domain !== 'firebaseio-demo.com');
};
RepoInfo.prototype.updateHost = function (newHost) {
if (newHost !== this.internalHost) {
this.internalHost = newHost;
if (this.isCacheableHost()) {
PersistentStorage.set('host:' + this.host, this.internalHost);
}
}
};
/**
* Returns the websocket URL for this repo
* @param {string} type of connection
* @param {Object} params list
* @return {string} The URL for this repo
*/
RepoInfo.prototype.connectionURL = function (type, params) {
assert(typeof type === 'string', 'typeof type must == string');
assert(typeof params === 'object', 'typeof params must == object');
var connURL;
if (type === WEBSOCKET) {
connURL =
(this.secure ? 'wss://' : 'ws://') + this.internalHost + '/.ws?';
}
else if (type === LONG_POLLING) {
connURL =
(this.secure ? 'https://' : 'http://') + this.internalHost + '/.lp?';
}
else {
throw new Error('Unknown connection type: ' + type);
}
if (this.needsQueryParam()) {
params['ns'] = this.namespace;
}
var pairs = [];
forEach(params, function (key, value) {
pairs.push(key + '=' + value);
});
return connURL + pairs.join('&');
};
/** @return {string} */
RepoInfo.prototype.toString = function () {
var str = this.toURLString();
if (this.persistenceKey) {
str += '<' + this.persistenceKey + '>';
}
return str;
};
/** @return {string} */
RepoInfo.prototype.toURLString = function () {
return (this.secure ? 'https://' : 'http://') + this.host;
};
return RepoInfo;
}());
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @param {!string} pathString
* @return {string}
*/
function decodePath(pathString) {
var pathStringDecoded = '';
var pieces = pathString.split('/');
for (var i = 0; i < pieces.length; i++) {
if (pieces[i].length > 0) {
var piece = pieces[i];
try {
piece = decodeURIComponent(piece.replace(/\+/g, ' '));
}
catch (e) { }
pathStringDecoded += '/' + piece;
}
}
return pathStringDecoded;
}
/**
* @param {!string} queryString
* @return {!{[key:string]:string}} key value hash
*/
function decodeQuery(queryString) {
var results = {};
if (queryString.charAt(0) === '?') {
queryString = queryString.substring(1);
}
for (var _i = 0, _a = queryString.split('&'); _i < _a.length; _i++) {
var segment = _a[_i];
if (segment.length === 0) {
continue;
}
var kv = segment.split('=');
if (kv.length === 2) {
results[decodeURIComponent(kv[0])] = decodeURIComponent(kv[1]);
}
else {
warn("Invalid query segment '" + segment + "' in query '" + queryString + "'");
}
}
return results;
}
/**
*
* @param {!string} dataURL
* @return {{repoInfo: !RepoInfo, path: !Path}}
*/
var parseRepoInfo = function (dataURL) {
var parsedUrl = parseURL(dataURL), namespace = parsedUrl.subdomain;
if (parsedUrl.domain === 'firebase') {
fatal(parsedUrl.host +
' is no longer supported. ' +
'Please use <YOUR FIREBASE>.firebaseio.com instead');
}
// Catch common error of uninitialized namespace value.
if ((!namespace || namespace == 'undefined') &&
parsedUrl.domain !== 'localhost') {
fatal('Cannot parse Firebase url. Please use https://<YOUR FIREBASE>.firebaseio.com');
}
if (!parsedUrl.secure) {
warnIfPageIsSecure();
}
var webSocketOnly = parsedUrl.scheme === 'ws' || parsedUrl.scheme === 'wss';
return {
repoInfo: new RepoInfo(parsedUrl.host, parsedUrl.secure, namespace, webSocketOnly),
path: new Path(parsedUrl.pathString)
};
};
/**
*
* @param {!string} dataURL
* @return {{host: string, port: number, domain: string, subdomain: string, secure: boolean, scheme: string, pathString: string}}
*/
var parseURL = function (dataURL) {
// Default to empty strings in the event of a malformed string.
var host = '', domain = '', subdomain = '', pathString = '';
// Always default to SSL, unless otherwise specified.
var secure = true, scheme = 'https', port = 443;
// Don't do any validation here. The caller is responsible for validating the result of parsing.
if (typeof dataURL === 'string') {
// Parse scheme.
var colonInd = dataURL.indexOf('//');
if (colonInd >= 0) {
scheme = dataURL.substring(0, colonInd - 1);
dataURL = dataURL.substring(colonInd + 2);
}
// Parse host, path, and query string.
var slashInd = dataURL.indexOf('/');
if (slashInd === -1) {
slashInd = dataURL.length;
}
var questionMarkInd = dataURL.indexOf('?');
if (questionMarkInd === -1) {
questionMarkInd = dataURL.length;
}
host = dataURL.substring(0, Math.min(slashInd, questionMarkInd));
if (slashInd < questionMarkInd) {
// For pathString, questionMarkInd will always come after slashInd
pathString = decodePath(dataURL.substring(slashInd, questionMarkInd));
}
var queryParams = decodeQuery(dataURL.substring(Math.min(dataURL.length, questionMarkInd)));
// If we have a port, use scheme for determining if it's secure.
colonInd = host.indexOf(':');
if (colonInd >= 0) {
secure = scheme === 'https' || scheme === 'wss';
port = parseInt(host.substring(colonInd + 1), 10);
}
else {
colonInd = dataURL.length;
}
var parts = host.split('.');
if (parts.length === 3) {
// Normalize namespaces to lowercase to share storage / connection.
domain = parts[1];
subdomain = parts[0].toLowerCase();
}
else if (parts.length === 2) {
domain = parts[0];
}
else if (parts[0].slice(0, colonInd).toLowerCase() === 'localhost') {
domain = 'localhost';
}
// Support `ns` query param if subdomain not already set
if (subdomain === '' && 'ns' in queryParams) {
subdomain = queryParams['ns'];
}
}
return {
host: host,
port: port,
domain: domain,
subdomain: subdomain,
secure: secure,
scheme: scheme,
pathString: pathString
};
};
/**
* @license
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* True for invalid Firebase keys
* @type {RegExp}
* @private
*/
var INVALID_KEY_REGEX_ = /[\[\].#$\/\u0000-\u001F\u007F]/;
/**
* True for invalid Firebase paths.
* Allows '/' in paths.
* @type {RegExp}
* @private
*/
var INVALID_PATH_REGEX_ = /[\[\].#$\u0000-\u001F\u007F]/;
/**
* Maximum number of characters to allow in leaf value
* @type {number}
* @private
*/
var MAX_LEAF_SIZE_ = 10 * 1024 * 1024;
/**
* @param {*} key
* @return {boolean}
*/
var isValidKey = function (key) {
return (typeof key === 'string' && key.length !== 0 && !INVALID_KEY_REGEX_.test(key));
};
/**
* @param {string} pathString
* @return {boolean}
*/
var isValidPathString = function (pathString) {
return (typeof pathString === 'string' &&
pathString.length !== 0 &&
!INVALID_PATH_REGEX_.test(pathString));
};
/**
* @param {string} pathString
* @return {boolean}
*/
var isValidRootPathString = function (pathString) {
if (pathString) {
// Allow '/.info/' at the beginning.
pathString = pathString.replace(/^\/*\.info(\/|$)/, '/');
}
return isValidPathString(pathString);
};
/**
* @param {*} priority
* @return {boolean}
*/
var isValidPriority = function (priority) {
return (priority === null ||
typeof priority === 'string' ||
(typeof priority === 'number' && !isInvalidJSONNumber(priority)) ||
(priority && typeof priority === 'object' && contains(priority, '.sv')));
};
/**
* Pre-validate a datum passed as an argument to Firebase function.
*
* @param {string} fnName
* @param {number} argumentNumber
* @param {*} data
* @param {!Path} path
* @param {boolean} optional
*/
var validateFirebaseDataArg = function (fnName, argumentNumber, data, path, optional) {
if (optional && data === undefined)
return;
validateFirebaseData(errorPrefix(fnName, argumentNumber, optional), data, path);
};
/**
* Validate a data object client-side before sending to server.
*
* @param {string} errorPrefix
* @param {*} data
* @param {!Path|!ValidationPath} path_
*/
var validateFirebaseData = function (errorPrefix, data, path_) {
var path = path_ instanceof Path ? new ValidationPath(path_, errorPrefix) : path_;
if (data === undefined) {
throw new Error(errorPrefix + 'contains undefined ' + path.toErrorString());
}
if (typeof data === 'function') {
throw new Error(errorPrefix +
'contains a function ' +
path.toErrorString() +
' with contents = ' +
data.toString());
}
if (isInvalidJSONNumber(data)) {
throw new Error(errorPrefix + 'contains ' + data.toString() + ' ' + path.toErrorString());
}
// Check max leaf size, but try to avoid the utf8 conversion if we can.
if (typeof data === 'string' &&
data.length > MAX_LEAF_SIZE_ / 3 &&
stringLength(data) > MAX_LEAF_SIZE_) {
throw new Error(errorPrefix +
'contains a string greater than ' +
MAX_LEAF_SIZE_ +
' utf8 bytes ' +
path.toErrorString() +
" ('" +
data.substring(0, 50) +
"...')");
}
// TODO = Perf = Consider combining the recursive validation of keys into NodeFromJSON
// to save extra walking of large objects.
if (data && typeof data === 'object') {
var hasDotValue_1 = false, hasActualChild_1 = false;
forEach(data, function (key, value) {
if (key === '.value') {
hasDotValue_1 = true;
}