firebase-bolt
Version:
Firebase Bolt Security and Modeling Language Compiler
194 lines (192 loc) • 23.3 kB
JavaScript
;
exports.__esModule = true;
/*
* Firebase helper functions for REST API (using Promises).
*
* Copyright 2015 Google Inc. All Rights Reserved.
*
* 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 https = require("https");
var util = require("./util");
var querystring = require("querystring");
var uuid = require('node-uuid');
var FirebaseTokenGenerator = require('firebase-token-generator');
var FIREBASE_HOST = 'firebaseio.com';
var DEBUG_HEADER = 'x-firebase-auth-debug';
exports.RULES_LOCATION = '/.settings/rules';
exports.TIMESTAMP = {
'.sv': 'timestamp'
};
var Client = /** @class */ (function () {
function Client(appName, authToken, uid) {
this.appName = appName;
this.authToken = authToken;
this.uid = uid;
this.debug = false;
}
Client.prototype.setDebug = function (debug) {
if (debug === void 0) { debug = true; }
this.debug = debug;
return this;
};
Client.prototype.get = function (location) {
return this.request({ method: 'GET' }, location);
};
Client.prototype.put = function (location, content) {
return this.request({ method: 'PUT', print: 'silent' }, location, content);
};
Client.prototype.request = function (opt, path, content) {
var options = {
hostname: this.appName + '.' + FIREBASE_HOST,
path: path + '.json',
method: opt.method
};
var query = {};
if (opt.print) {
query.print = opt.print;
}
if (this.authToken) {
query.auth = this.authToken;
}
if (Object.keys(query).length > 0) {
options.path += '?' + querystring.stringify(query);
}
content = util.prettyJSON(content);
return request(options, content, this.debug).then(function (body) {
return body === '' ? null : JSON.parse(body);
});
};
return Client;
}());
exports.Client = Client;
var ridNext = 0;
function request(options, content, debug) {
ridNext += 1;
var rid = ridNext;
function log(s) {
if (debug) {
console.error('Request<' + rid + '>: ' + s);
}
}
log('Request: ' + util.prettyJSON(options));
if (content) {
log('Body: \'' + content + '\'');
}
return new Promise(function (resolve, reject) {
// TODO: Why isn't this argument typed as per https.request?
var req = https.request(options, function (res) {
var chunks = [];
res.on('data', function (body) {
chunks.push(body);
});
res.on('end', function () {
var result = chunks.join('');
log('Result (' + res.statusCode + '): \'' + result + '\'');
var message = 'Status = ' + res.statusCode + ' ' + result;
// Dump debug information if present for both successful and failed
// requests.
if (res.headers[DEBUG_HEADER]) {
var formattedHeader = res.headers[DEBUG_HEADER].split(' /').join('\n /');
log(formattedHeader);
message += '\n' + formattedHeader;
}
if (res.statusCode && Math.floor(res.statusCode / 100) !== 2) {
reject(new Error(message));
}
else {
resolve(result);
}
});
});
if (content) {
req.write(content, 'utf8');
}
req.end();
req.on('error', function (error) {
log('Request error: ' + error);
reject(error);
});
});
}
// opts { debug: Boolean, admin: Boolean }
function generateUidAuthToken(secret, opts) {
opts = util.extend({ debug: false, admin: false }, opts);
var tokenGenerator = new FirebaseTokenGenerator(secret);
var uid = uuid.v4();
var token = tokenGenerator.createToken({ uid: uid }, opts);
return { uid: uid, token: token };
}
exports.generateUidAuthToken = generateUidAuthToken;
/**
* Fancy ID generator that creates 20-character string identifiers with the
* following properties:
*
* 1. They're based on timestamp so that they sort *after* any existing ids.
* 2. They contain 72-bits of random data after the timestamp so that IDs won't
* collide with other clients' IDs.
* 3. They sort *lexicographically* (so the timestamp is converted to characters
* that will sort properly).
* 4. They're monotonically increasing. Even if you generate more than one in
* the same timestamp, the latter ones will sort after the former ones. We do
* this by using the previous random bits but "incrementing" them by 1 (only in
* the case of a timestamp collision).
*/
// Modeled after base64 web-safe chars, but ordered by ASCII.
var PUSH_CHARS = '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
// Timestamp of last push, used to prevent local collisions if you push twice in
// one ms.
var lastPushTime = 0;
// We generate 72-bits of randomness which get turned into 12 characters and
// appended to the timestamp to prevent collisions with other clients. We store
// the last characters we generated because in the event of a collision, we'll
// use those same characters except "incremented" by one.
var lastRandChars = [];
function generatePushID() {
var now = new Date().getTime();
var duplicateTime = (now === lastPushTime);
lastPushTime = now;
var timeStampChars = new Array(8);
for (var i = 7; i >= 0; i--) {
timeStampChars[i] = PUSH_CHARS.charAt(now % 64);
// NOTE: Can't use << here because javascript will convert to int and lose
// the upper bits.
now = Math.floor(now / 64);
}
if (now !== 0) {
throw new Error('We should have converted the entire timestamp.');
}
var id = timeStampChars.join('');
if (!duplicateTime) {
for (i = 0; i < 12; i++) {
lastRandChars[i] = Math.floor(Math.random() * 64);
}
}
else {
// If the timestamp hasn't changed since last push, use the same random
// number, except incremented by 1.
for (i = 11; i >= 0 && lastRandChars[i] === 63; i--) {
lastRandChars[i] = 0;
}
lastRandChars[i]++;
}
for (i = 0; i < 12; i++) {
id += PUSH_CHARS.charAt(lastRandChars[i]);
}
if (id.length !== 20) {
throw new Error('Length should be 20.');
}
return id;
}
exports.generatePushID = generatePushID;
//# sourceMappingURL=data:application/json;charset=utf8;base64,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SUFBSSxLQUFLLENBQUMsZ0RBQWdELENBQUMsQ0FBQztLQUNuRTtJQUVELElBQUksRUFBRSxHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUM7SUFFakMsSUFBSSxDQUFDLGFBQWEsRUFBRTtRQUNsQixLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLEVBQUUsRUFBRTtZQUN2QixhQUFhLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7U0FDbkQ7S0FDRjtTQUFNO1FBQ0wsdUVBQXVFO1FBQ3ZFLG1DQUFtQztRQUNuQyxLQUFLLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSSxhQUFhLENBQUMsQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFO1lBQ25ELGFBQWEsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7U0FDdEI7UUFDRCxhQUFhLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztLQUNwQjtJQUNELEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsRUFBRSxFQUFFO1FBQ3ZCLEVBQUUsSUFBSSxVQUFVLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0tBQzNDO0lBQ0QsSUFBSSxFQUFFLENBQUMsTUFBTSxLQUFLLEVBQUUsRUFBRTtRQUNwQixNQUFNLElBQUksS0FBSyxDQUFDLHNCQUFzQixDQUFDLENBQUM7S0FDekM7SUFFRCxPQUFPLEVBQUUsQ0FBQztBQUNaLENBQUM7QUF0Q0Qsd0NBc0NDIiwiZmlsZSI6ImZpcmViYXNlLXJlc3QuanMiLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuICogRmlyZWJhc2UgaGVscGVyIGZ1bmN0aW9ucyBmb3IgUkVTVCBBUEkgKHVzaW5nIFByb21pc2VzKS5cbiAqXG4gKiBDb3B5cmlnaHQgMjAxNSBHb29nbGUgSW5jLiBBbGwgUmlnaHRzIFJlc2VydmVkLlxuICpcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG4gKlxuICogVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZVxuICogZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gXCJBUyBJU1wiIEJBU0lTLFxuICogV0lUSE9VVCBXQVJSQU5USUVTIE9SIENPTkRJVElPTlMgT0YgQU5ZIEtJTkQsIGVpdGhlciBleHByZXNzIG9yIGltcGxpZWQuXG4gKiBTZWUgdGhlIExpY2Vuc2UgZm9yIHRoZSBzcGVjaWZpYyBsYW5ndWFnZSBnb3Zlcm5pbmcgcGVybWlzc2lvbnMgYW5kXG4gKiBsaW1pdGF0aW9ucyB1bmRlciB0aGUgTGljZW5zZS5cbiAqL1xuaW1wb3J0IGh0dHBzID0gcmVxdWlyZSgnaHR0cHMnKTtcbmltcG9ydCBodHRwID0gcmVxdWlyZSgnaHR0cCcpO1xuaW1wb3J0ICogYXMgdXRpbCBmcm9tICcuL3V0aWwnO1xuaW1wb3J0IHF1ZXJ5c3RyaW5nID0gcmVxdWlyZSgncXVlcnlzdHJpbmcnKTtcbmxldCB1dWlkID0gcmVxdWlyZSgnbm9kZS11dWlkJyk7XG52YXIgRmlyZWJhc2VUb2tlbkdlbmVyYXRvciA9IHJlcXVpcmUoJ2ZpcmViYXNlLXRva2VuLWdlbmVyYXRvcicpO1xuXG52YXIgRklSRUJBU0VfSE9TVCA9ICdmaXJlYmFzZWlvLmNvbSc7XG52YXIgREVCVUdfSEVBREVSID0gJ3gtZmlyZWJhc2UtYXV0aC1kZWJ1Zyc7XG5cbmV4cG9ydCB2YXIgUlVMRVNfTE9DQVRJT04gPSAnLy5zZXR0aW5ncy9ydWxlcyc7XG5leHBvcnQgdmFyIFRJTUVTVEFNUCA9IHtcbiAgJy5zdic6ICd0aW1lc3RhbXAnXG59O1xuXG5leHBvcnQgdHlwZSBSZXF1ZXN0T3B0aW9ucyA9IHtcbiAgbWV0aG9kOiBzdHJpbmcsXG4gIHByaW50Pzogc3RyaW5nO1xufVxuXG5leHBvcnQgY2xhc3MgQ2xpZW50IHtcbiAgcHJpdmF0ZSBkZWJ1ZyA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgICAgcHJpdmF0ZSBhcHBOYW1lOiBzdHJpbmcsIHByaXZhdGUgYXV0aFRva2VuPzogc3RyaW5nLFxuICAgICAgcHVibGljIHVpZD86IHN0cmluZykge31cblxuICBzZXREZWJ1ZyhkZWJ1ZyA9IHRydWUpIHtcbiAgICB0aGlzLmRlYnVnID0gZGVidWc7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBnZXQobG9jYXRpb246IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgcmV0dXJuIHRoaXMucmVxdWVzdCh7bWV0aG9kOiAnR0VUJ30sIGxvY2F0aW9uKTtcbiAgfVxuXG4gIHB1dChsb2NhdGlvbjogc3RyaW5nLCBjb250ZW50OiBhbnkpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiB0aGlzLnJlcXVlc3Qoe21ldGhvZDogJ1BVVCcsIHByaW50OiAnc2lsZW50J30sIGxvY2F0aW9uLCBjb250ZW50KTtcbiAgfVxuXG4gIHJlcXVlc3Qob3B0OiBSZXF1ZXN0T3B0aW9ucywgcGF0aDogc3RyaW5nLCBjb250ZW50PzogYW55KSB7XG4gICAgdmFyIG9wdGlvbnMgPSB7XG4gICAgICBob3N0bmFtZTogdGhpcy5hcHBOYW1lICsgJy4nICsgRklSRUJBU0VfSE9TVCxcbiAgICAgIHBhdGg6IHBhdGggKyAnLmpzb24nLFxuICAgICAgbWV0aG9kOiBvcHQubWV0aG9kLFxuICAgIH07XG5cbiAgICB2YXIgcXVlcnk6IGFueSA9IHt9O1xuICAgIGlmIChvcHQucHJpbnQpIHtcbiAgICAgIHF1ZXJ5LnByaW50ID0gb3B0LnByaW50O1xuICAgIH1cblxuICAgIGlmICh0aGlzLmF1dGhUb2tlbikge1xuICAgICAgcXVlcnkuYXV0aCA9IHRoaXMuYXV0aFRva2VuO1xuICAgIH1cblxuICAgIGlmIChPYmplY3Qua2V5cyhxdWVyeSkubGVuZ3RoID4gMCkge1xuICAgICAgb3B0aW9ucy5wYXRoICs9ICc/JyArIHF1ZXJ5c3RyaW5nLnN0cmluZ2lmeShxdWVyeSk7XG4gICAgfVxuXG4gICAgY29udGVudCA9IHV0aWwucHJldHR5SlNPTihjb250ZW50KTtcblxuICAgIHJldHVybiByZXF1ZXN0KG9wdGlvbnMsIGNvbnRlbnQsIHRoaXMuZGVidWcpLnRoZW4oZnVuY3Rpb24oYm9keTogc3RyaW5nKSB7XG4gICAgICByZXR1cm4gYm9keSA9PT0gJycgPyBudWxsIDogSlNPTi5wYXJzZShib2R5KTtcbiAgICB9KTtcbiAgfVxufVxuXG52YXIgcmlkTmV4dCA9IDA7XG5cbmZ1bmN0aW9uIHJlcXVlc3Qob3B0aW9uczogYW55LCBjb250ZW50OiBhbnksIGRlYnVnOiBib29sZWFuKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgcmlkTmV4dCArPSAxO1xuICB2YXIgcmlkID0gcmlkTmV4dDtcblxuICBmdW5jdGlvbiBsb2coczogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKGRlYnVnKSB7XG4gICAgICBjb25zb2xlLmVycm9yKCdSZXF1ZXN0PCcgKyByaWQgKyAnPjogJyArIHMpO1xuICAgIH1cbiAgfVxuXG4gIGxvZygnUmVxdWVzdDogJyArIHV0aWwucHJldHR5SlNPTihvcHRpb25zKSk7XG4gIGlmIChjb250ZW50KSB7XG4gICAgbG9nKCdCb2R5OiBcXCcnICsgY29udGVudCArICdcXCcnKTtcbiAgfVxuXG4gIHJldHVybiBuZXcgUHJvbWlzZShmdW5jdGlvbihyZXNvbHZlLCByZWplY3QpIHtcbiAgICAvLyBUT0RPOiBXaHkgaXNuJ3QgdGhpcyBhcmd1bWVudCB0eXBlZCBhcyBwZXIgaHR0cHMucmVxdWVzdD9cbiAgICB2YXIgcmVxID0gaHR0cHMucmVxdWVzdChvcHRpb25zLCBmdW5jdGlvbihyZXM6IGh0dHAuQ2xpZW50UmVzcG9uc2UpIHtcbiAgICAgIHZhciBjaHVua3MgPSA8c3RyaW5nW10+W107XG5cbiAgICAgIHJlcy5vbignZGF0YScsIGZ1bmN0aW9uKGJvZHk6IHN0cmluZykge1xuICAgICAgICBjaHVua3MucHVzaChib2R5KTtcbiAgICAgIH0pO1xuXG4gICAgICByZXMub24oJ2VuZCcsIGZ1bmN0aW9uKCkge1xuICAgICAgICB2YXIgcmVzdWx0OiBzdHJpbmcgPSBjaHVua3Muam9pbignJyk7XG4gICAgICAgIGxvZygnUmVzdWx0ICgnICsgcmVzLnN0YXR1c0NvZGUgKyAnKTogXFwnJyArIHJlc3VsdCArICdcXCcnKTtcbiAgICAgICAgbGV0IG1lc3NhZ2UgPSAnU3RhdHVzID0gJyArIHJlcy5zdGF0dXNDb2RlICsgJyAnICsgcmVzdWx0O1xuXG4gICAgICAgIC8vIER1bXAgZGVidWcgaW5mb3JtYXRpb24gaWYgcHJlc2VudCBmb3IgYm90aCBzdWNjZXNzZnVsIGFuZCBmYWlsZWRcbiAgICAgICAgLy8gcmVxdWVzdHMuXG4gICAgICAgIGlmIChyZXMuaGVhZGVyc1tERUJVR19IRUFERVJdKSB7XG4gICAgICAgICAgbGV0IGZvcm1hdHRlZEhlYWRlciA9XG4gICAgICAgICAgICAgIHJlcy5oZWFkZXJzW0RFQlVHX0hFQURFUl0uc3BsaXQoJyAvJykuam9pbignXFxuICAvJyk7XG4gICAgICAgICAgbG9nKGZvcm1hdHRlZEhlYWRlcik7XG4gICAgICAgICAgbWVzc2FnZSArPSAnXFxuJyArIGZvcm1hdHRlZEhlYWRlcjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChyZXMuc3RhdHVzQ29kZSAmJiBNYXRoLmZsb29yKHJlcy5zdGF0dXNDb2RlIC8gMTAwKSAhPT0gMikge1xuICAgICAgICAgIHJlamVjdChuZXcgRXJyb3IobWVzc2FnZSkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJlc29sdmUocmVzdWx0KTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICBpZiAoY29udGVudCkge1xuICAgICAgcmVxLndyaXRlKGNvbnRlbnQsICd1dGY4Jyk7XG4gICAgfVxuICAgIHJlcS5lbmQoKTtcblxuICAgIHJlcS5vbignZXJyb3InLCBmdW5jdGlvbihlcnJvcjogRXJyb3IpIHtcbiAgICAgIGxvZygnUmVxdWVzdCBlcnJvcjogJyArIGVycm9yKTtcbiAgICAgIHJlamVjdChlcnJvcik7XG4gICAgfSk7XG4gIH0pO1xufVxuXG4vLyBvcHRzIHsgZGVidWc6IEJvb2xlYW4sIGFkbWluOiBCb29sZWFuIH1cbmV4cG9ydCBmdW5jdGlvbiBnZW5lcmF0ZVVpZEF1dGhUb2tlbihzZWNyZXQ6IHN0cmluZywgb3B0czogYW55KSB7XG4gIG9wdHMgPSB1dGlsLmV4dGVuZCh7ZGVidWc6IGZhbHNlLCBhZG1pbjogZmFsc2V9LCBvcHRzKTtcblxuICB2YXIgdG9rZW5HZW5lcmF0b3IgPSBuZXcgRmlyZWJhc2VUb2tlbkdlbmVyYXRvcihzZWNyZXQpO1xuICB2YXIgdWlkID0gdXVpZC52NCgpO1xuICB2YXIgdG9rZW4gPSB0b2tlbkdlbmVyYXRvci5jcmVhdGVUb2tlbih7dWlkOiB1aWR9LCBvcHRzKTtcbiAgcmV0dXJuIHt1aWQ6IHVpZCwgdG9rZW46IHRva2VufTtcbn1cblxuLyoqXG4gKiBGYW5jeSBJRCBnZW5lcmF0b3IgdGhhdCBjcmVhdGVzIDIwLWNoYXJhY3RlciBzdHJpbmcgaWRlbnRpZmllcnMgd2l0aCB0aGVcbiAqIGZvbGxvd2luZyBwcm9wZXJ0aWVzOlxuICpcbiAqIDEuIFRoZXkncmUgYmFzZWQgb24gdGltZXN0YW1wIHNvIHRoYXQgdGhleSBzb3J0ICphZnRlciogYW55IGV4aXN0aW5nIGlkcy5cbiAqIDIuIFRoZXkgY29udGFpbiA3Mi1iaXRzIG9mIHJhbmRvbSBkYXRhIGFmdGVyIHRoZSB0aW1lc3RhbXAgc28gdGhhdCBJRHMgd29uJ3RcbiAqIGNvbGxpZGUgd2l0aCBvdGhlciBjbGllbnRzJyBJRHMuXG4gKiAzLiBUaGV5IHNvcnQgKmxleGljb2dyYXBoaWNhbGx5KiAoc28gdGhlIHRpbWVzdGFtcCBpcyBjb252ZXJ0ZWQgdG8gY2hhcmFjdGVyc1xuICogdGhhdCB3aWxsIHNvcnQgcHJvcGVybHkpLlxuICogNC4gVGhleSdyZSBtb25vdG9uaWNhbGx5IGluY3JlYXNpbmcuICBFdmVuIGlmIHlvdSBnZW5lcmF0ZSBtb3JlIHRoYW4gb25lIGluXG4gKiB0aGUgc2FtZSB0aW1lc3RhbXAsIHRoZSBsYXR0ZXIgb25lcyB3aWxsIHNvcnQgYWZ0ZXIgdGhlIGZvcm1lciBvbmVzLiAgV2UgZG9cbiAqIHRoaXMgYnkgdXNpbmcgdGhlIHByZXZpb3VzIHJhbmRvbSBiaXRzIGJ1dCBcImluY3JlbWVudGluZ1wiIHRoZW0gYnkgMSAob25seSBpblxuICogdGhlIGNhc2Ugb2YgYSB0aW1lc3RhbXAgY29sbGlzaW9uKS5cbiAqL1xuXG4vLyBNb2RlbGVkIGFmdGVyIGJhc2U2NCB3ZWItc2FmZSBjaGFycywgYnV0IG9yZGVyZWQgYnkgQVNDSUkuXG52YXIgUFVTSF9DSEFSUyA9XG4gICAgJy0wMTIzNDU2Nzg5QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpfYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXonO1xuXG4vLyBUaW1lc3RhbXAgb2YgbGFzdCBwdXNoLCB1c2VkIHRvIHByZXZlbnQgbG9jYWwgY29sbGlzaW9ucyBpZiB5b3UgcHVzaCB0d2ljZSBpblxuLy8gb25lIG1zLlxudmFyIGxhc3RQdXNoVGltZSA9IDA7XG5cbi8vIFdlIGdlbmVyYXRlIDcyLWJpdHMgb2YgcmFuZG9tbmVzcyB3aGljaCBnZXQgdHVybmVkIGludG8gMTIgY2hhcmFjdGVycyBhbmRcbi8vIGFwcGVuZGVkIHRvIHRoZSB0aW1lc3RhbXAgdG8gcHJldmVudCBjb2xsaXNpb25zIHdpdGggb3RoZXIgY2xpZW50cy4gIFdlIHN0b3JlXG4vLyB0aGUgbGFzdCBjaGFyYWN0ZXJzIHdlIGdlbmVyYXRlZCBiZWNhdXNlIGluIHRoZSBldmVudCBvZiBhIGNvbGxpc2lvbiwgd2UnbGxcbi8vIHVzZSB0aG9zZSBzYW1lIGNoYXJhY3RlcnMgZXhjZXB0IFwiaW5jcmVtZW50ZWRcIiBieSBvbmUuXG52YXIgbGFzdFJhbmRDaGFycyA9IDxudW1iZXJbXT5bXTtcblxuZXhwb3J0IGZ1bmN0aW9uIGdlbmVyYXRlUHVzaElEKCk6IHN0cmluZyB7XG4gIHZhciBub3cgPSBuZXcgRGF0ZSgpLmdldFRpbWUoKTtcbiAgdmFyIGR1cGxpY2F0ZVRpbWUgPSAobm93ID09PSBsYXN0UHVzaFRpbWUpO1xuICBsYXN0UHVzaFRpbWUgPSBub3c7XG5cbiAgdmFyIHRpbWVTdGFtcENoYXJzID0gbmV3IEFycmF5KDgpO1xuICBmb3IgKHZhciBpID0gNzsgaSA+PSAwOyBpLS0pIHtcbiAgICB0aW1lU3RhbXBDaGFyc1tpXSA9IFBVU0hfQ0hBUlMuY2hhckF0KG5vdyAlIDY0KTtcbiAgICAvLyBOT1RFOiBDYW4ndCB1c2UgPDwgaGVyZSBiZWNhdXNlIGphdmFzY3JpcHQgd2lsbCBjb252ZXJ0IHRvIGludCBhbmQgbG9zZVxuICAgIC8vIHRoZSB1cHBlciBiaXRzLlxuICAgIG5vdyA9IE1hdGguZmxvb3Iobm93IC8gNjQpO1xuICB9XG4gIGlmIChub3cgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ1dlIHNob3VsZCBoYXZlIGNvbnZlcnRlZCB0aGUgZW50aXJlIHRpbWVzdGFtcC4nKTtcbiAgfVxuXG4gIHZhciBpZCA9IHRpbWVTdGFtcENoYXJzLmpvaW4oJycpO1xuXG4gIGlmICghZHVwbGljYXRlVGltZSkge1xuICAgIGZvciAoaSA9IDA7IGkgPCAxMjsgaSsrKSB7XG4gICAgICBsYXN0UmFuZENoYXJzW2ldID0gTWF0aC5mbG9vcihNYXRoLnJhbmRvbSgpICogNjQpO1xuICAgIH1cbiAgfSBlbHNlIHtcbiAgICAvLyBJZiB0aGUgdGltZXN0YW1wIGhhc24ndCBjaGFuZ2VkIHNpbmNlIGxhc3QgcHVzaCwgdXNlIHRoZSBzYW1lIHJhbmRvbVxuICAgIC8vIG51bWJlciwgZXhjZXB0IGluY3JlbWVudGVkIGJ5IDEuXG4gICAgZm9yIChpID0gMTE7IGkgPj0gMCAmJiBsYXN0UmFuZENoYXJzW2ldID09PSA2MzsgaS0tKSB7XG4gICAgICBsYXN0UmFuZENoYXJzW2ldID0gMDtcbiAgICB9XG4gICAgbGFzdFJhbmRDaGFyc1tpXSsrO1xuICB9XG4gIGZvciAoaSA9IDA7IGkgPCAxMjsgaSsrKSB7XG4gICAgaWQgKz0gUFVTSF9DSEFSUy5jaGFyQXQobGFzdFJhbmRDaGFyc1tpXSk7XG4gIH1cbiAgaWYgKGlkLmxlbmd0aCAhPT0gMjApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0xlbmd0aCBzaG91bGQgYmUgMjAuJyk7XG4gIH1cblxuICByZXR1cm4gaWQ7XG59XG4iXX0=