@iota-pico/api
Version:
IOTA Pico Framework API
316 lines • 25.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const arrayHelper_1 = require("@iota-pico/core/dist/helpers/arrayHelper");
const numberHelper_1 = require("@iota-pico/core/dist/helpers/numberHelper");
const objectHelper_1 = require("@iota-pico/core/dist/helpers/objectHelper");
const stringHelper_1 = require("@iota-pico/core/dist/helpers/stringHelper");
const nullLogger_1 = require("@iota-pico/core/dist/loggers/nullLogger");
const apiError_1 = require("../error/apiError");
/**
* Default implementation of an api client.
*/
class ApiClient {
/**
* Create an instance of ApiClient.
* @param networkClient The network client to communicate through.
* @param apiVersion The API version to send with the requests
* @param additionalHeaders Extra headers to send with the requests.
* @param logger Logger to send communication info to.
*/
constructor(networkClient, apiVersion = "1", additionalHeaders, logger) {
if (objectHelper_1.ObjectHelper.isEmpty(networkClient)) {
throw new apiError_1.ApiError("The networkClient must be defined");
}
if (stringHelper_1.StringHelper.isEmpty(apiVersion)) {
throw new apiError_1.ApiError("The apiVersion must not be empty");
}
this._networkClient = networkClient;
this._apiVersion = apiVersion;
this._additionalHeaders = additionalHeaders;
this._logger = logger || new nullLogger_1.NullLogger();
}
/**
* Returns information about your node.
* @returns Promise which resolves to the getNodeInfo response object or rejects with error.
*/
async getNodeInfo() {
return this.sendCommand("getNodeInfo", {});
}
/**
* Returns the set of neighbors you are connected with, as well as their activity count.
* The activity counter is reset after restarting IRI.
* @returns Promise which resolves to the getNeighbors response object or rejects with error.
*/
async getNeighbors() {
return this.sendCommand("getNeighbors", {});
}
/**
* Add a list of neighbors to your node. It should be noted that this is only temporary,
* and the added neighbors will be removed from your set of neighbors after you relaunch IRI.
* @param request The add neighbours request object.
* @returns Promise which resolves to the addNeighbors response object or rejects with error.
*/
async addNeighbors(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.uris)) {
throw new apiError_1.ApiError("The request.uris must not be empty");
}
return this.sendCommand("addNeighbors", request);
}
/**
* Removes a list of neighbors from your node. This is only temporary, and if you have your
* neighbors added via the command line, they will be retained after you restart your node.
* @param request The remove neighbours request object.
* @returns Promise which resolves to the removeNeighbors response object or rejects with error.
*/
async removeNeighbors(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.uris)) {
throw new apiError_1.ApiError("The request.uris must not be empty");
}
return this.sendCommand("removeNeighbors", request);
}
/**
* Returns the list of tips.
* @returns Promise which resolves to the getTips response object or rejects with error.
*/
async getTips() {
return this.sendCommand("getTips", {});
}
/**
* Find the transactions which match the specified input and return. All input values are lists,
* for which a list of return values (transaction hashes), in the same order, is returned for all
* individual elements. The input fields can either be bundles, addresses, tags or approvees.
* Using multiple of these input fields returns the intersection of the values.
* @param request The find transactions request object.
* @returns Promise which resolves to the findTransactions response object or rejects with error.
*/
async findTransactions(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
const bundlesEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.bundles);
const addressesEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.addresses);
const tagsEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.tags);
const approveesEmpty = arrayHelper_1.ArrayHelper.isEmpty(request.approvees);
if (bundlesEmpty && addressesEmpty && tagsEmpty && approveesEmpty) {
throw new apiError_1.ApiError("One of the bundle, addresses, tags or approvees must not be empty");
}
return this.sendCommand("findTransactions", request);
}
/**
* Returns the raw transaction data (trytes) of a specific transaction.
* These trytes can then be easily converted into the actual transaction object.
* @param request The get trytes request object.
* @returns Promise which resolves to the findTransactions response object or rejects with error.
*/
async getTrytes(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.hashes)) {
throw new apiError_1.ApiError("The request.hashes must not be empty");
}
return this.sendCommand("getTrytes", request);
}
/**
* Get the inclusion states of a set of transactions. This is for determining if a transaction
* was accepted and confirmed by the network or not. You can search for multiple tips (and thus,
* milestones) to get past inclusion states of transactions.
* @param request The get inclusion states request object.
* @returns Promise which resolves to the getInclusionStates response object or rejects with error.
*/
async getInclusionStates(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.transactions)) {
throw new apiError_1.ApiError("The request.transactions must not be empty");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.tips)) {
throw new apiError_1.ApiError("The request.tips must not be empty");
}
return this.sendCommand("getInclusionStates", request);
}
/**
* Returns the confirmed balance which a list of addresses have at the latest confirmed milestone.
* In addition to the balances, it also returns the milestone as well as the index with which the
* confirmed balance was determined. The balances is returned as a list in the same order as the
* addresses were provided as input.
* @param request The getBalances request object.
* @returns Promise which resolves to the getBalances response object or rejects with error.
*/
async getBalances(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.addresses)) {
throw new apiError_1.ApiError("The request.addresses must not be empty");
}
if (!numberHelper_1.NumberHelper.isInteger(request.threshold)) {
throw new apiError_1.ApiError("The request.threshold must be a valid number");
}
return this.sendCommand("getBalances", request);
}
/**
* Tip selection which returns trunkTransaction and branchTransaction. The input value is depth,
* which basically determines how many bundles to go back to for finding the transactions to approve.
* The higher your depth value, the more "babysitting" you do for the network (as you have to confirm more transactions).
* @param request The getTransactionsToApprove request object.
* @returns Promise which resolves to the getTransactionsToApprove response object or rejects with error.
*/
async getTransactionsToApprove(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (!numberHelper_1.NumberHelper.isInteger(request.depth)) {
throw new apiError_1.ApiError("The request.depth must be a valid number");
}
if (!objectHelper_1.ObjectHelper.isEmpty(request.reference) && stringHelper_1.StringHelper.isEmpty(request.reference)) {
throw new apiError_1.ApiError("The request.reference must be a non empty string");
}
if (!objectHelper_1.ObjectHelper.isEmpty(request.numWalks) && !numberHelper_1.NumberHelper.isInteger(request.numWalks)) {
throw new apiError_1.ApiError("The request.numWalks must be a valid number");
}
return this.sendCommand("getTransactionsToApprove", request);
}
/**
* Attaches the specified transactions (trytes) to the Tangle by doing Proof of Work. You need to supply
* branchTransaction as well as trunkTransaction (basically the tips which you're going to validate and
* reference with this transaction) - both of which you'll get through the getTransactionsToApprove API call.
* @param request The attachToTangle request object.
* @returns Promise which resolves to the attachToTangle response object or rejects with error.
*/
async attachToTangle(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (stringHelper_1.StringHelper.isEmpty(request.trunkTransaction)) {
throw new apiError_1.ApiError("The request.trunkTransaction must not be empty");
}
if (stringHelper_1.StringHelper.isEmpty(request.branchTransaction)) {
throw new apiError_1.ApiError("The request.branchTransaction must not be empty");
}
if (!numberHelper_1.NumberHelper.isInteger(request.minWeightMagnitude)) {
throw new apiError_1.ApiError("The request.minWeightMagnitude must be a valid number");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.trytes)) {
throw new apiError_1.ApiError("The request.trytes must not be empty");
}
return this.sendCommand("attachToTangle", request);
}
/**
* Interrupts and completely aborts the attachToTangle process
* @returns Promise which resolves with empty response object or rejects with error.
*/
async interruptAttachingToTangle() {
return this.sendCommand("interruptAttachingToTangle", {});
}
/**
* Broadcast a list of transactions to all neighbors. The input trytes for this call are provided by attachToTangle.
* @param request The broadcastTransactions request object.
* @returns Promise which resolves with empty response object or rejects with error.
*/
async broadcastTransactions(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.trytes)) {
throw new apiError_1.ApiError("The request.trytes must not be empty");
}
return this.sendCommand("broadcastTransactions", request);
}
/**
* Store transactions into the local storage. The trytes to be used for this call are returned by attachToTangle.
* @param request The storeTransactions request object.
* @returns Promise which resolves with empty response object or rejects with error.
*/
async storeTransactions(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.trytes)) {
throw new apiError_1.ApiError("The request.trytes must not be empty");
}
return this.sendCommand("storeTransactions", request);
}
/**
* Get transactions with missing references.
* @param request The getMissingTransactions request object.
* @returns Promise which resolves to the getMissingTransactions response object or rejects with error.
*/
async getMissingTransactions() {
return this.sendCommand("getMissingTransactions", {});
}
/**
* Check the consistency of tail hashes.
* @param request The checkConsistency request object.
* @returns Promise which resolves to the checkConsistency response object or rejects with error.
*/
async checkConsistency(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.tails)) {
throw new apiError_1.ApiError("The request.tails must not be empty");
}
return this.sendCommand("checkConsistency", request);
}
/**
* Have the requested addresses been spent from already.
* @param request The wereAddressesSpentFrom request object.
* @returns Promise which resolves to the wereAddressesSpentFrom response object or rejects with error.
*/
async wereAddressesSpentFrom(request) {
if (objectHelper_1.ObjectHelper.isEmpty(request)) {
throw new apiError_1.ApiError("The request must be defined");
}
if (arrayHelper_1.ArrayHelper.isEmpty(request.addresses)) {
throw new apiError_1.ApiError("The request.addresses must not be empty");
}
return this.sendCommand("wereAddressesSpentFrom", request);
}
/* @internal */
async sendCommand(command, request) {
this._logger.info(`===> ApiClient::${command}`, request);
Object.defineProperty(request, "command", {
value: command,
enumerable: true
});
return this._networkClient.postJson(request, undefined, this.createHeaders())
.then((response) => {
this._logger.info(`===> ${command}`, response);
return response;
})
.catch((err) => {
this._logger.error(`===> ${command} Error`, err);
if (err.additional && err.additional.errorResponse) {
try {
const apiError = JSON.parse(err.additional.errorResponse);
if (apiError.error) {
delete err.additional.errorResponse;
err.additional.apiError = apiError.error;
}
else if (apiError.exception) {
delete err.additional.errorResponse;
err.additional.apiError = apiError.exception;
}
}
catch (e) {
}
}
throw err;
});
}
/* @internal */
createHeaders() {
const headers = this._additionalHeaders || {};
headers["X-IOTA-API-Version"] = this._apiVersion;
return headers;
}
}
exports.ApiClient = ApiClient;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBpQ2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9hcGlDbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLDBFQUF1RTtBQUN2RSw0RUFBeUU7QUFDekUsNEVBQXlFO0FBQ3pFLDRFQUF5RTtBQUd6RSx3RUFBcUU7QUFDckUsZ0RBQTZDO0FBOEI3Qzs7R0FFRztBQUNILE1BQWEsU0FBUztJQVVsQjs7Ozs7O09BTUc7SUFDSCxZQUFZLGFBQTZCLEVBQUUsYUFBcUIsR0FBRyxFQUFFLGlCQUFnRCxFQUFFLE1BQWdCO1FBQ25JLElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLEVBQUU7WUFDckMsTUFBTSxJQUFJLG1CQUFRLENBQUMsbUNBQW1DLENBQUMsQ0FBQztTQUMzRDtRQUNELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLEVBQUU7WUFDbEMsTUFBTSxJQUFJLG1CQUFRLENBQUMsa0NBQWtDLENBQUMsQ0FBQztTQUMxRDtRQUNELElBQUksQ0FBQyxjQUFjLEdBQUcsYUFBYSxDQUFDO1FBQ3BDLElBQUksQ0FBQyxXQUFXLEdBQUcsVUFBVSxDQUFDO1FBQzlCLElBQUksQ0FBQyxrQkFBa0IsR0FBRyxpQkFBaUIsQ0FBQztRQUM1QyxJQUFJLENBQUMsT0FBTyxHQUFHLE1BQU0sSUFBSSxJQUFJLHVCQUFVLEVBQUUsQ0FBQztJQUM5QyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLFdBQVc7UUFDcEIsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUEyQixhQUFhLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDekUsQ0FBQztJQUVEOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsWUFBWTtRQUNyQixPQUFPLElBQUksQ0FBQyxXQUFXLENBQTRCLGNBQWMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsWUFBWSxDQUFDLE9BQTZCO1FBQ25ELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7WUFDL0IsTUFBTSxJQUFJLG1CQUFRLENBQUMsNkJBQTZCLENBQUMsQ0FBQztTQUNyRDtRQUNELElBQUkseUJBQVcsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ25DLE1BQU0sSUFBSSxtQkFBUSxDQUFDLG9DQUFvQyxDQUFDLENBQUM7U0FDNUQ7UUFDRCxPQUFPLElBQUksQ0FBQyxXQUFXLENBQThDLGNBQWMsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNsRyxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsZUFBZSxDQUFDLE9BQWdDO1FBQ3pELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7WUFDL0IsTUFBTSxJQUFJLG1CQUFRLENBQUMsNkJBQTZCLENBQUMsQ0FBQztTQUNyRDtRQUNELElBQUkseUJBQVcsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxFQUFFO1lBQ25DLE1BQU0sSUFBSSxtQkFBUSxDQUFDLG9DQUFvQyxDQUFDLENBQUM7U0FDNUQ7UUFDRCxPQUFPLElBQUksQ0FBQyxXQUFXLENBQW9ELGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzNHLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsT0FBTztRQUNoQixPQUFPLElBQUksQ0FBQyxXQUFXLENBQXVCLFNBQVMsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNJLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFpQztRQUMzRCxJQUFJLDJCQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQy9CLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZCQUE2QixDQUFDLENBQUM7U0FDckQ7UUFDRCxNQUFNLFlBQVksR0FBRyx5QkFBVyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDMUQsTUFBTSxjQUFjLEdBQUcseUJBQVcsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzlELE1BQU0sU0FBUyxHQUFHLHlCQUFXLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNwRCxNQUFNLGNBQWMsR0FBRyx5QkFBVyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDOUQsSUFBSSxZQUFZLElBQUksY0FBYyxJQUFJLFNBQVMsSUFBSSxjQUFjLEVBQUU7WUFDL0QsTUFBTSxJQUFJLG1CQUFRLENBQUMsbUVBQW1FLENBQUMsQ0FBQztTQUMzRjtRQUNELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBc0Qsa0JBQWtCLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDOUcsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksS0FBSyxDQUFDLFNBQVMsQ0FBQyxPQUEwQjtRQUM3QyxJQUFJLDJCQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQy9CLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZCQUE2QixDQUFDLENBQUM7U0FDckQ7UUFDRCxJQUFJLHlCQUFXLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNyQyxNQUFNLElBQUksbUJBQVEsQ0FBQyxzQ0FBc0MsQ0FBQyxDQUFDO1NBQzlEO1FBQ0QsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUF3QyxXQUFXLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekYsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxPQUFtQztRQUMvRCxJQUFJLDJCQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQy9CLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZCQUE2QixDQUFDLENBQUM7U0FDckQ7UUFDRCxJQUFJLHlCQUFXLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsRUFBRTtZQUMzQyxNQUFNLElBQUksbUJBQVEsQ0FBQyw0Q0FBNEMsQ0FBQyxDQUFDO1NBQ3BFO1FBQ0QsSUFBSSx5QkFBVyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLEVBQUU7WUFDbkMsTUFBTSxJQUFJLG1CQUFRLENBQUMsb0NBQW9DLENBQUMsQ0FBQztTQUM1RDtRQUNELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBMEQsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDcEgsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSSxLQUFLLENBQUMsV0FBVyxDQUFDLE9BQTRCO1FBQ2pELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7WUFDL0IsTUFBTSxJQUFJLG1CQUFRLENBQUMsNkJBQTZCLENBQUMsQ0FBQztTQUNyRDtRQUNELElBQUkseUJBQVcsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFO1lBQ3hDLE1BQU0sSUFBSSxtQkFBUSxDQUFDLHlDQUF5QyxDQUFDLENBQUM7U0FDakU7UUFDRCxJQUFJLENBQUMsMkJBQVksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFO1lBQzVDLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDhDQUE4QyxDQUFDLENBQUM7U0FDdEU7UUFDRCxPQUFPLElBQUksQ0FBQyxXQUFXLENBQTRDLGFBQWEsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUMvRixDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksS0FBSyxDQUFDLHdCQUF3QixDQUFDLE9BQXlDO1FBQzNFLElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7WUFDL0IsTUFBTSxJQUFJLG1CQUFRLENBQUMsNkJBQTZCLENBQUMsQ0FBQztTQUNyRDtRQUNELElBQUksQ0FBQywyQkFBWSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLEVBQUU7WUFDeEMsTUFBTSxJQUFJLG1CQUFRLENBQUMsMENBQTBDLENBQUMsQ0FBQztTQUNsRTtRQUNELElBQUksQ0FBQywyQkFBWSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFO1lBQ3JGLE1BQU0sSUFBSSxtQkFBUSxDQUFDLGtEQUFrRCxDQUFDLENBQUM7U0FDMUU7UUFDRCxJQUFJLENBQUMsMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsMkJBQVksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxFQUFFO1lBQ3RGLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZDQUE2QyxDQUFDLENBQUM7U0FDckU7UUFDRCxPQUFPLElBQUksQ0FBQyxXQUFXLENBQXNFLDBCQUEwQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3RJLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxLQUFLLENBQUMsY0FBYyxDQUFDLE9BQStCO1FBQ3ZELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUU7WUFDL0IsTUFBTSxJQUFJLG1CQUFRLENBQUMsNkJBQTZCLENBQUMsQ0FBQztTQUNyRDtRQUNELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLGdCQUFnQixDQUFDLEVBQUU7WUFDaEQsTUFBTSxJQUFJLG1CQUFRLENBQUMsZ0RBQWdELENBQUMsQ0FBQztTQUN4RTtRQUNELElBQUksMkJBQVksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLGlCQUFpQixDQUFDLEVBQUU7WUFDakQsTUFBTSxJQUFJLG1CQUFRLENBQUMsaURBQWlELENBQUMsQ0FBQztTQUN6RTtRQUNELElBQUksQ0FBQywyQkFBWSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsRUFBRTtZQUNyRCxNQUFNLElBQUksbUJBQVEsQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1NBQy9FO1FBQ0QsSUFBSSx5QkFBVyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEVBQUU7WUFDckMsTUFBTSxJQUFJLG1CQUFRLENBQUMsc0NBQXNDLENBQUMsQ0FBQztTQUM5RDtRQUNELE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBa0QsZ0JBQWdCLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDeEcsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQywwQkFBMEI7UUFDbkMsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFzQiw0QkFBNEIsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNuRixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxPQUFzQztRQUNyRSxJQUFJLDJCQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQy9CLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZCQUE2QixDQUFDLENBQUM7U0FDckQ7UUFDRCxJQUFJLHlCQUFXLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNyQyxNQUFNLElBQUksbUJBQVEsQ0FBQyxzQ0FBc0MsQ0FBQyxDQUFDO1NBQzlEO1FBQ0QsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFpRCx1QkFBdUIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUM5RyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxpQkFBaUIsQ0FBQyxPQUFrQztRQUM3RCxJQUFJLDJCQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQy9CLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZCQUE2QixDQUFDLENBQUM7U0FDckQ7UUFDRCxJQUFJLHlCQUFXLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsRUFBRTtZQUNyQyxNQUFNLElBQUksbUJBQVEsQ0FBQyxzQ0FBc0MsQ0FBQyxDQUFDO1NBQzlEO1FBQ0QsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUE2QyxtQkFBbUIsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUN0RyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxzQkFBc0I7UUFDL0IsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFzQyx3QkFBd0IsRUFBRSxFQUFFLENBQUMsQ0FBQztJQUMvRixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxPQUFpQztRQUMzRCxJQUFJLDJCQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQy9CLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZCQUE2QixDQUFDLENBQUM7U0FDckQ7UUFDRCxJQUFJLHlCQUFXLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRTtZQUNwQyxNQUFNLElBQUksbUJBQVEsQ0FBQyxxQ0FBcUMsQ0FBQyxDQUFDO1NBQzdEO1FBQ0QsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFzRCxrQkFBa0IsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUM5RyxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxPQUF1QztRQUN2RSxJQUFJLDJCQUFZLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFO1lBQy9CLE1BQU0sSUFBSSxtQkFBUSxDQUFDLDZCQUE2QixDQUFDLENBQUM7U0FDckQ7UUFDRCxJQUFJLHlCQUFXLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRTtZQUN4QyxNQUFNLElBQUksbUJBQVEsQ0FBQyx5Q0FBeUMsQ0FBQyxDQUFDO1NBQ2pFO1FBQ0QsT0FBTyxJQUFJLENBQUMsV0FBVyxDQUFrRSx3QkFBd0IsRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNoSSxDQUFDO0lBRUQsZUFBZTtJQUNQLEtBQUssQ0FBQyxXQUFXLENBQStCLE9BQWUsRUFBRSxPQUFVO1FBQy9FLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLG1CQUFtQixPQUFPLEVBQUUsRUFBRSxPQUFPLENBQUMsQ0FBQztRQUN6RCxNQUFNLENBQUMsY0FBYyxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUU7WUFDdEMsS0FBSyxFQUFFLE9BQU87WUFDZCxVQUFVLEVBQUUsSUFBSTtTQUNuQixDQUFDLENBQUM7UUFDSCxPQUFPLElBQUksQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFPLE9BQU8sRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO2FBQzlFLElBQUksQ0FBQyxDQUFDLFFBQVEsRUFBRSxFQUFFO1lBQ2YsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxPQUFPLEVBQUUsRUFBRSxRQUFRLENBQUMsQ0FBQztZQUMvQyxPQUFPLFFBQVEsQ0FBQztRQUNwQixDQUFDLENBQUM7YUFDRCxLQUFLLENBQUMsQ0FBQyxHQUFhLEVBQUUsRUFBRTtZQUNyQixJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxRQUFRLE9BQU8sUUFBUSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1lBQ2pELElBQUksR0FBRyxDQUFDLFVBQVUsSUFBSSxHQUFHLENBQUMsVUFBVSxDQUFDLGFBQWEsRUFBRTtnQkFDaEQsSUFBSTtvQkFDQSxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDLENBQUM7b0JBQzFELElBQUksUUFBUSxDQUFDLEtBQUssRUFBRTt3QkFDaEIsT0FBTyxHQUFHLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQzt3QkFDcEMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQztxQkFDNUM7eUJBQU0sSUFBSSxRQUFRLENBQUMsU0FBUyxFQUFFO3dCQUMzQixPQUFPLEdBQUcsQ0FBQyxVQUFVLENBQUMsYUFBYSxDQUFDO3dCQUNwQyxHQUFHLENBQUMsVUFBVSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUMsU0FBUyxDQUFDO3FCQUNoRDtpQkFDSjtnQkFBQyxPQUFPLENBQUMsRUFBRTtpQkFDWDthQUNKO1lBQ0QsTUFBTSxHQUFHLENBQUM7UUFDZCxDQUFDLENBQUMsQ0FBQztJQUNYLENBQUM7SUFFRCxlQUFlO0lBQ1AsYUFBYTtRQUNqQixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsa0JBQWtCLElBQUksRUFBRSxDQUFDO1FBQzlDLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUM7UUFDakQsT0FBTyxPQUFPLENBQUM7SUFDbkIsQ0FBQztDQUNKO0FBelVELDhCQXlVQyJ9