Revert "Add Loop version information"

pull/1298/head
ShahanaFarooqui 7 months ago committed by GitHub
parent 40295bac43
commit 282d8b120a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -124,7 +124,7 @@
"indent": ["error", 2, { "SwitchCase": 1, "MemberExpression": 1, "ArrayExpression": "off" }],
"keyword-spacing": ["error", { "before": true, "after": true, "overrides": { "this": { "before": false }}}],
"lines-around-comment": "error",
"max-depth": ["error", { "max": 7 }],
"max-depth": ["error", { "max": 6 }],
"max-nested-callbacks": "error",
"max-statements-per-line": ["error", { "max": 3 }],
"no-array-constructor": "error",

@ -40,7 +40,7 @@ export const listChannels = (req, res, next) => {
request(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') {
channel.alias = channel.channel_id.substring(0, 20);
channel.alias = channel.id.substring(0, 20);
}
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;

@ -16,12 +16,14 @@ export const simplifyAllChannels = (selNode, channels) => {
nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '',
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.channelFlags && channel.data.commitments.params.channelFlags.announceChannel ? channel.data.commitments.params.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.active[0].localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.active[0].localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toRemote / 1000) : 0,
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.channelFlags && channel.data.commitments.channelFlags.announceChannel ? channel.data.commitments.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0,
shortChannelId: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.shortChannelId ? channel.data.channelUpdate.shortChannelId : '',
isInitiator: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.localParams && channel.data.commitments.params.localParams.isInitiator ? channel.data.commitments.params.localParams.isInitiator : false,
isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false,
buried: channel.data && channel.data.buried ? channel.data.buried : false,
feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,
feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0,
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
alias: ''
});
@ -157,6 +159,7 @@ export const closeChannel = (req, res, next) => {
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
// options.form = { sourceNodeId: req.params.source, targetNodeId: req.params.target, amountMsat: req.params.amount, ignoreNodeIds: req.params.ignore };
export const circularRebalance = (req, res, next) => {
const crInvDescription = 'Circular rebalancing invoice for ' + (req.body.amountMsat / 1000) + ' Sats';
options = common.getOptions(req);

@ -6,7 +6,13 @@ const logger = Logger;
const common = Common;
export const loopOut = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping Out..' });
options.uri = '/v1/loop/out';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out';
options.body = {
amt: req.body.amount,
sweep_conf_target: req.body.targetConf,
@ -35,7 +41,13 @@ export const loopOut = (req, res, next) => {
};
export const loopOutTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms..' });
options.uri = '/v1/loop/out/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Terms Received', data: body });
res.status(200).json(body);
@ -46,7 +58,13 @@ export const loopOutTerms = (req, res, next) => {
};
export const loopOutQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Quotes..' });
options.uri = '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Quote URL', data: options.url });
request(options).then((quoteRes) => {
quoteRes.amount = +req.params.amount;
@ -60,13 +78,19 @@ export const loopOutQuote = (req, res, next) => {
};
export const loopOutTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms & Quotes..' });
options.uri = '/v1/loop/out/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Terms Received', data: terms });
const options1 = options;
const options2 = options;
options1.uri = '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = common.getSwapServerOptions(req);
const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -88,7 +112,13 @@ export const loopOutTermsAndQuotes = (req, res, next) => {
};
export const loopIn = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping In..' });
options.uri = '/v1/loop/in';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in';
options.body = {
amt: req.body.amount,
max_swap_fee: req.body.swapFee,
@ -106,7 +136,13 @@ export const loopIn = (req, res, next) => {
};
export const loopInTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms..' });
options.uri = '/v1/loop/in/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Terms Received', data: body });
res.status(200).json(body);
@ -117,7 +153,13 @@ export const loopInTerms = (req, res, next) => {
};
export const loopInQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Quotes..' });
options.uri = '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Quote Options', data: options.url });
request(options).then((body) => {
body.amount = +req.params.amount;
@ -131,13 +173,19 @@ export const loopInQuote = (req, res, next) => {
};
export const loopInTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms & Quotes..' });
options.uri = '/v1/loop/in/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Terms Received', data: terms });
const options1 = options;
const options2 = options;
options1.uri = '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = common.getSwapServerOptions(req);
const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -159,12 +207,13 @@ export const loopInTermsAndQuotes = (req, res, next) => {
};
export const swaps = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting List Swaps..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.uri = '/v1/loop/swaps';
options.url = options.url + '/v1/loop/swaps';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body });
res.status(200).json(body.swaps);
@ -175,29 +224,18 @@ export const swaps = (req, res, next) => {
};
export const swap = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Swap Information..' });
options.uri = '/v1/loop/swap/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Information..' });
options = common.setSwapServerOptions(req);
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Loop Info Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
const err = common.handleError({ statusCode: 500, message: 'Get Swap Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.uri = '/v1/loop/info';
options.url = options.url + '/v1/loop/swap/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Information Received', data: body });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Loop Info Error', req.session.selectedNode);
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -137,25 +137,3 @@ export const CollectionFieldsEnum = { ...OfferFieldsEnum, ...PageSettingsFieldsE
export const LNDCollection = [CollectionsEnum.PAGE_SETTINGS];
export const ECLCollection = [CollectionsEnum.PAGE_SETTINGS];
export const CLNCollection = [CollectionsEnum.PAGE_SETTINGS, CollectionsEnum.OFFERS];
export const ECL_UPDATED_DB = [
{
pageId: 'peers_channels',
tables: [
{
tableId: 'open_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'pending_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'inactive_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
}
]
}
];

@ -1,9 +1,8 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { loopInfo, loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
import { loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
const router = Router();
router.get('/info', isAuthenticated, loopInfo);
router.get('/in/terms', isAuthenticated, loopInTerms);
router.get('/in/quote/:amount', isAuthenticated, loopInQuote);
router.get('/in/termsAndQuotes', isAuthenticated, loopInTermsAndQuotes);

@ -10,7 +10,6 @@ import sharedRoutes from '../routes/shared/index.js';
import lndRoutes from '../routes/lnd/index.js';
import clnRoutes from '../routes/cln/index.js';
import eclRoutes from '../routes/eclair/index.js';
import { Database } from './database.js';
import { Common } from './common.js';
import { Logger } from './logger.js';
import { CLWSClient } from '../controllers/cln/webSocketClient.js';
@ -25,7 +24,6 @@ export class ExpressApplication {
this.eclWsClient = ECLWSClient;
this.clWsClient = CLWSClient;
this.lndWsClient = LNDWSClient;
this.databaseService = Database;
this.directoryName = dirname(fileURLToPath(import.meta.url));
this.getApp = () => this.app;
this.setCORS = () => { CORS.mount(this.app); };
@ -81,7 +79,6 @@ export class ExpressApplication {
this.setCORS();
this.setCSRF();
this.setApplicationRoutes();
this.databaseService.migrateDatabase();
}
}
export default ExpressApplication;

@ -29,10 +29,9 @@ export class CommonService {
{ name: 'JAN', days: 31 }, { name: 'FEB', days: 28 }, { name: 'MAR', days: 31 }, { name: 'APR', days: 30 }, { name: 'MAY', days: 31 }, { name: 'JUN', days: 30 },
{ name: 'JUL', days: 31 }, { name: 'AUG', days: 31 }, { name: 'SEP', days: 30 }, { name: 'OCT', days: 31 }, { name: 'NOV', days: 30 }, { name: 'DEC', days: 31 }
];
this.setSwapServerOptions = (req) => {
this.getSwapServerOptions = (req) => {
const swapOptions = {
baseUrl: req.session.selectedNode.swap_server_url,
uri: '',
url: req.session.selectedNode.swap_server_url,
rejectUnauthorized: false,
json: true,
headers: { 'Grpc-Metadata-macaroon': '' }

@ -2,7 +2,7 @@ import * as fs from 'fs';
import { join, sep } from 'path';
import { Common } from '../utils/common.js';
import { Logger } from '../utils/logger.js';
import { CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection, ECL_UPDATED_DB } from '../models/database.model.js';
import { validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
export class DatabaseService {
constructor() {
this.common = Common;
@ -10,58 +10,6 @@ export class DatabaseService {
this.dbDirectory = join(this.common.db_directory_path, 'database');
this.nodeDatabase = {};
}
migrateDatabase() {
this.common.nodes?.map((node) => {
if (node.ln_implementation === 'ECL') {
this.nodeDatabase[node.index] = { adapter: null, data: {} };
this.nodeDatabase[node.index].adapter = new DatabaseAdapter(this.dbDirectory, node);
this.fetchNodeData(node);
if (this.nodeDatabase[node.index].data.PageSettings) {
try {
const currPageSettings = JSON.parse(JSON.stringify(this.nodeDatabase[node.index].data.PageSettings));
ECL_UPDATED_DB.forEach((updatePage) => {
const foundPageDB = this.nodeDatabase[node.index].data.PageSettings.find((currPage) => currPage.pageId === updatePage.pageId);
if (foundPageDB) {
updatePage.tables.forEach((updateTable) => {
const foundTableDB = foundPageDB.tables.find((currTable) => currTable.tableId === updateTable.tableId);
if (foundTableDB) {
updateTable.removed.forEach((colToBeRemoved) => {
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === colToBeRemoved);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === colToBeRemoved);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1);
}
});
updateTable.renamed.forEach((colToBeRenamed) => {
const [oldName, newName] = colToBeRenamed.split(':');
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === oldName);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === oldName);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1, newName);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1, newName);
}
});
}
});
}
});
if (currPageSettings !== this.nodeDatabase[node.index].data.PageSettings) {
this.saveDatabase(node, CollectionsEnum.PAGE_SETTINGS);
}
}
catch (err) {
this.logger.log({ selectedNode: node, level: 'ERROR', fileName: 'Database', msg: 'Database Migration Error', error: err });
}
}
}
return true;
});
}
loadDatabase(session) {
const { id, selectedNode } = session;
try {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1113,93 +1113,163 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
d3-array
ISC
Copyright 2010-2023 Mike Bostock
BSD-3-Clause
Copyright 2010-2020 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-brush
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-color
ISC
Copyright 2010-2022 Mike Bostock
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-dispatch
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-drag
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-ease
BSD-3-Clause
Copyright 2010-2021 Mike Bostock
Copyright 2010-2016 Mike Bostock
Copyright 2001 Robert Penner
All rights reserved.
@ -1230,88 +1300,157 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-format
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright 2010-2015 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-interpolate
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-scale
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright 2010-2015 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-selection
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright (c) 2010-2018, Michael Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-time
ISC
Copyright 2010-2022 Mike Bostock
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-time-format
@ -1346,37 +1485,96 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-timer
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright 2010-2016 Mike Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d3-transition
ISC
Copyright 2010-2021 Mike Bostock
BSD-3-Clause
Copyright (c) 2010-2015, Michael Bostock
All rights reserved.
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name Michael Bostock may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
TERMS OF USE - EASING EQUATIONS
Open source under the BSD License.
Copyright 2001 Robert Penner
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of the author nor the names of contributors may be used to
endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
des.js
@ -1551,23 +1749,6 @@ PERFORMANCE OF THIS SOFTWARE.
internmap
ISC
Copyright 2021 Mike Bostock
Permission to use, copy, modify, and/or distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright notice
and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
material-icons
Apache-2.0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -13,6 +13,6 @@
<style>html{width:100%;height:99%;line-height:1.5;overflow-x:hidden;font-family:Roboto,sans-serif!important;font-size:95%}@media only screen and (max-width: 56.25em){html{font-size:90%}}@media only screen and (max-width: 37.5em){html{font-size:80%}}body{box-sizing:border-box;height:100%;margin:0;overflow:hidden}*{margin:0;padding:0}@font-face{font-family:Roboto;src:url(Roboto-Thin.f7a95c9c5999532c.woff2) format("woff2"),url(Roboto-Thin.c13c157cb81e8ebb.woff) format("woff");font-weight:100;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-ThinItalic.b0e084abf689f393.woff2) format("woff2"),url(Roboto-ThinItalic.1111028df6cea564.woff) format("woff");font-weight:100;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Light.0e01b6cd13b3857f.woff2) format("woff2"),url(Roboto-Light.603ca9a537b88428.woff) format("woff");font-weight:300;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-LightItalic.232ef4b20215f720.woff2) format("woff2"),url(Roboto-LightItalic.1b5e142f787151c8.woff) format("woff");font-weight:300;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Regular.475ba9e4e2d63456.woff2) format("woff2"),url(Roboto-Regular.bcefbfee882bc1cb.woff) format("woff");font-weight:400;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-RegularItalic.e3a9ebdaac06bbc4.woff2) format("woff2"),url(Roboto-RegularItalic.0668fae6af0cf8c2.woff) format("woff");font-weight:400;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Medium.457532032ceb0168.woff2) format("woff2"),url(Roboto-Medium.6e1ae5f0b324a0aa.woff) format("woff");font-weight:500;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-MediumItalic.872f7060602d55d2.woff2) format("woff2"),url(Roboto-MediumItalic.e06fb533801cbb08.woff) format("woff");font-weight:500;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Bold.447291a88c067396.woff2) format("woff2"),url(Roboto-Bold.fc482e6133cf5e26.woff) format("woff");font-weight:700;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BoldItalic.1b15168ef6fa4e16.woff2) format("woff2"),url(Roboto-BoldItalic.e26ba339b06f09f7.woff) format("woff");font-weight:700;font-style:italic}@font-face{font-family:Roboto;src:url(Roboto-Black.2eaa390d458c877d.woff2) format("woff2"),url(Roboto-Black.b25f67ad8583da68.woff) format("woff");font-weight:900;font-style:normal}@font-face{font-family:Roboto;src:url(Roboto-BlackItalic.7dc03ee444552bc5.woff2) format("woff2"),url(Roboto-BlackItalic.c8dc642467cb3099.woff) format("woff");font-weight:900;font-style:italic}</style><link rel="stylesheet" href="styles.a04c018645a5044a.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles.a04c018645a5044a.css"></noscript></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.c2bb514ce598134b.js" type="module"></script><script src="polyfills.aa01d8f6b94657cb.js" type="module"></script><script src="main.8ef8f2009574bb18.js" type="module"></script>
<script src="runtime.9c1e0f691dfda3dd.js" type="module"></script><script src="polyfills.aa01d8f6b94657cb.js" type="module"></script><script src="main.3ccfe42677016a42.js" type="module"></script>
</body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
(()=>{"use strict";var e,v={},m={};function r(e){var f=m[e];if(void 0!==f)return f.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(f,t,i,o)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,o]=e[n],s=!0,d=0;d<t.length;d++)(!1&o||a>=o)&&Object.keys(r.O).every(b=>r.O[b](t[d]))?t.splice(d--,1):(s=!1,o<a&&(a=o));if(s){e.splice(n--,1);var u=i();void 0!==u&&(f=u)}}return f}o=o||0;for(var n=e.length;n>0&&e[n-1][2]>o;n--)e[n]=e[n-1];e[n]=[t,i,o]},r.d=(e,f)=>{for(var t in f)r.o(f,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:f[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((f,t)=>(r.f[t](e,f),f),[])),r.u=e=>e+"."+{167:"a3774800f5f9ed5a",267:"5508f97536cb5708",315:"d20113f8d2f54786",636:"eaef3bec0eb4cb7a"}[e]+".js",r.miniCssF=e=>{},r.o=(e,f)=>Object.prototype.hasOwnProperty.call(e,f),(()=>{var e={},f="RTLApp:";r.l=(t,i,o,n)=>{if(e[t])e[t].push(i);else{var a,s;if(void 0!==o)for(var d=document.getElementsByTagName("script"),u=0;u<d.length;u++){var l=d[u];if(l.getAttribute("src")==t||l.getAttribute("data-webpack")==f+o){a=l;break}}a||(s=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",f+o),a.src=r.tu(t)),e[t]=[i];var c=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(c.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=c.bind(null,a.onerror),a.onload=c.bind(null,a.onload),s&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:f=>f},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,o)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)o.push(n[2]);else if(666!=i){var a=new Promise((l,c)=>n=e[i]=[l,c]);o.push(n[2]=a);var s=r.p+r.u(i),d=new Error;r.l(s,l=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var c=l&&("load"===l.type?"missing":l.type),p=l&&l.target&&l.target.src;d.message="Loading chunk "+i+" failed.\n("+c+": "+p+")",d.name="ChunkLoadError",d.type=c,d.request=p,n[1](d)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var f=(i,o)=>{var d,u,[n,a,s]=o,l=0;if(n.some(p=>0!==e[p])){for(d in a)r.o(a,d)&&(r.m[d]=a[d]);if(s)var c=s(r)}for(i&&i(o);l<n.length;l++)r.o(e,u=n[l])&&e[u]&&e[u][0](),e[u]=0;return r.O(c)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(f.bind(null,0)),t.push=f.bind(null,t.push.bind(t))})()})();

@ -1 +0,0 @@
(()=>{"use strict";var e,v={},m={};function r(e){var o=m[e];if(void 0!==o)return o.exports;var t=m[e]={id:e,loaded:!1,exports:{}};return v[e].call(t.exports,t,t.exports,r),t.loaded=!0,t.exports}r.m=v,e=[],r.O=(o,t,i,f)=>{if(!t){var a=1/0;for(n=0;n<e.length;n++){for(var[t,i,f]=e[n],c=!0,l=0;l<t.length;l++)(!1&f||a>=f)&&Object.keys(r.O).every(b=>r.O[b](t[l]))?t.splice(l--,1):(c=!1,f<a&&(a=f));if(c){e.splice(n--,1);var d=i();void 0!==d&&(o=d)}}return o}f=f||0;for(var n=e.length;n>0&&e[n-1][2]>f;n--)e[n]=e[n-1];e[n]=[t,i,f]},r.d=(e,o)=>{for(var t in o)r.o(o,t)&&!r.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((o,t)=>(r.f[t](e,o),o),[])),r.u=e=>e+"."+{167:"28293ff5a3323208",267:"3050ada6a6d9afd8",315:"25112d0bface4bf2",636:"b883c00cebb300aa"}[e]+".js",r.miniCssF=e=>{},r.o=(e,o)=>Object.prototype.hasOwnProperty.call(e,o),(()=>{var e={},o="RTLApp:";r.l=(t,i,f,n)=>{if(e[t])e[t].push(i);else{var a,c;if(void 0!==f)for(var l=document.getElementsByTagName("script"),d=0;d<l.length;d++){var u=l[d];if(u.getAttribute("src")==t||u.getAttribute("data-webpack")==o+f){a=u;break}}a||(c=!0,(a=document.createElement("script")).type="module",a.charset="utf-8",a.timeout=120,r.nc&&a.setAttribute("nonce",r.nc),a.setAttribute("data-webpack",o+f),a.src=r.tu(t)),e[t]=[i];var s=(g,b)=>{a.onerror=a.onload=null,clearTimeout(p);var h=e[t];if(delete e[t],a.parentNode&&a.parentNode.removeChild(a),h&&h.forEach(y=>y(b)),g)return g(b)},p=setTimeout(s.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=s.bind(null,a.onerror),a.onload=s.bind(null,a.onload),c&&document.head.appendChild(a)}}})(),r.r=e=>{typeof Symbol<"u"&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:o=>o},typeof trustedTypes<"u"&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("angular#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="",(()=>{var e={666:0};r.f.j=(i,f)=>{var n=r.o(e,i)?e[i]:void 0;if(0!==n)if(n)f.push(n[2]);else if(666!=i){var a=new Promise((u,s)=>n=e[i]=[u,s]);f.push(n[2]=a);var c=r.p+r.u(i),l=new Error;r.l(c,u=>{if(r.o(e,i)&&(0!==(n=e[i])&&(e[i]=void 0),n)){var s=u&&("load"===u.type?"missing":u.type),p=u&&u.target&&u.target.src;l.message="Loading chunk "+i+" failed.\n("+s+": "+p+")",l.name="ChunkLoadError",l.type=s,l.request=p,n[1](l)}},"chunk-"+i,i)}else e[i]=0},r.O.j=i=>0===e[i];var o=(i,f)=>{var l,d,[n,a,c]=f,u=0;if(n.some(p=>0!==e[p])){for(l in a)r.o(a,l)&&(r.m[l]=a[l]);if(c)var s=c(r)}for(i&&i(f);u<n.length;u++)r.o(e,d=n[u])&&e[d]&&e[d][0](),e[d]=0;return r.O(s)},t=self.webpackChunkRTLApp=self.webpackChunkRTLApp||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))})()})();

4
package-lock.json generated

@ -1,12 +1,12 @@
{
"name": "rtl",
"version": "0.14.1-beta",
"version": "0.14.0-beta",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "rtl",
"version": "0.14.1-beta",
"version": "0.14.0-beta",
"license": "MIT",
"dependencies": {
"@ngrx/effects": "^15.0.0",

@ -1,6 +1,6 @@
{
"name": "rtl",
"version": "0.14.1-beta",
"version": "0.14.0-beta",
"license": "MIT",
"type": "module",
"scripts": {

@ -35,7 +35,7 @@ export const listChannels = (req, res, next) => {
options.url = req.session.selectedNode.ln_server_url + '/v1/channel/listPeerChannels';
request(options).then((body) => {
body?.map((channel) => {
if (!channel.alias || channel.alias === '') { channel.alias = channel.channel_id.substring(0, 20); }
if (!channel.alias || channel.alias === '') { channel.alias = channel.id.substring(0, 20); }
const local = channel.to_us_msat || 0;
const remote = (channel.total_msat - local) || 0;
const total = channel.total_msat || 0;

@ -19,12 +19,14 @@ export const simplifyAllChannels = (selNode: CommonSelectedNode, channels) => {
nodeId: channel.nodeId ? channel.nodeId : '',
channelId: channel.channelId ? channel.channelId : '',
state: channel.state ? channel.state : '',
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.channelFlags && channel.data.commitments.params.channelFlags.announceChannel ? channel.data.commitments.params.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.active[0].localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.active[0].localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.active[0].localCommit.spec.toRemote / 1000) : 0,
announceChannel: channel.data && channel.data.commitments && channel.data.commitments.channelFlags && channel.data.commitments.channelFlags.announceChannel ? channel.data.commitments.channelFlags.announceChannel : false,
toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal / 1000) : 0,
toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote / 1000) : 0,
shortChannelId: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.shortChannelId ? channel.data.channelUpdate.shortChannelId : '',
isInitiator: channel.data && channel.data.commitments && channel.data.commitments.params && channel.data.commitments.params.localParams && channel.data.commitments.params.localParams.isInitiator ? channel.data.commitments.params.localParams.isInitiator : false,
isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false,
buried: channel.data && channel.data.buried ? channel.data.buried : false,
feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,
feeRatePerKw: (channel.data.commitments.localCommit.spec.feeratePerKw) ? channel.data.commitments.localCommit.spec.feeratePerKw : 0,
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
alias: ''
});
@ -153,6 +155,8 @@ export const closeChannel = (req, res, next) => {
});
};
// options.form = { sourceNodeId: req.params.source, targetNodeId: req.params.target, amountMsat: req.params.amount, ignoreNodeIds: req.params.ignore };
export const circularRebalance = (req, res, next) => {
const crInvDescription = 'Circular rebalancing invoice for ' + (req.body.amountMsat / 1000) + ' Sats';
options = common.getOptions(req);

@ -7,7 +7,13 @@ const common: CommonService = Common;
export const loopOut = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping Out..' });
options.uri = '/v1/loop/out';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out';
options.body = {
amt: req.body.amount,
sweep_conf_target: req.body.targetConf,
@ -33,7 +39,13 @@ export const loopOut = (req, res, next) => {
export const loopOutTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms..' });
options.uri = '/v1/loop/out/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Out Terms Received', data: body });
res.status(200).json(body);
@ -45,7 +57,13 @@ export const loopOutTerms = (req, res, next) => {
export const loopOutQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Quotes..' });
options.uri = '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Quote URL', data: options.url });
request(options).then((quoteRes) => {
quoteRes.amount = +req.params.amount;
@ -60,12 +78,18 @@ export const loopOutQuote = (req, res, next) => {
export const loopOutTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Out Terms & Quotes..' });
options.uri = '/v1/loop/out/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop Out Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/out/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Terms Received', data: terms });
const options1 = options; const options2 = options;
options1.uri = '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/out/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/out/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Out Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -88,7 +112,13 @@ export const loopOutTermsAndQuotes = (req, res, next) => {
export const loopIn = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Looping In..' });
options.uri = '/v1/loop/in';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in';
options.body = {
amt: req.body.amount,
max_swap_fee: req.body.swapFee,
@ -107,7 +137,13 @@ export const loopIn = (req, res, next) => {
export const loopInTerms = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms..' });
options.uri = '/v1/loop/in/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop In Terms Received', data: body });
res.status(200).json(body);
@ -119,7 +155,13 @@ export const loopInTerms = (req, res, next) => {
export const loopInQuote = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Quotes..' });
options.uri = '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/quote/' + req.params.amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Quote Options', data: options.url });
request(options).then((body) => {
body.amount = +req.params.amount;
@ -134,12 +176,18 @@ export const loopInQuote = (req, res, next) => {
export const loopInTermsAndQuotes = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop In Terms & Quotes..' });
options.uri = '/v1/loop/in/terms';
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Loop In Terms & Quotes Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.url = options.url + '/v1/loop/in/terms';
request(options).then((terms) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Terms Received', data: terms });
const options1 = options; const options2 = options;
options1.uri = '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.uri = '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
const options1 = common.getSwapServerOptions(req); const options2 = common.getSwapServerOptions(req);
options1.url = options1.url + '/v1/loop/in/quote/' + terms.min_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
options2.url = options2.url + '/v1/loop/in/quote/' + terms.max_swap_amount + '?conf_target=' + (req.query.targetConf ? req.query.targetConf : '2') + '&swap_publication_deadline=' + req.query.swapPublicationDeadline;
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Min Quote Options', data: options1 });
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop In Max Quote Options', data: options2 });
return Promise.all([request(options1), request(options2)]).then((values) => {
@ -162,12 +210,13 @@ export const loopInTermsAndQuotes = (req, res, next) => {
export const swaps = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting List Swaps..' });
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'List Swaps Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.uri = '/v1/loop/swaps';
options.url = options.url + '/v1/loop/swaps';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'DEBUG', fileName: 'Loop', msg: 'Loop Swaps Received', data: body });
res.status(200).json(body.swaps);
@ -179,30 +228,18 @@ export const swaps = (req, res, next) => {
export const swap = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Swap Information..' });
options.uri = '/v1/loop/swap/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};
export const loopInfo = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Getting Loop Information..' });
options = common.setSwapServerOptions(req);
options = common.getSwapServerOptions(req);
if (options.url === '') {
const errMsg = 'Loop Server URL is missing in the configuration.';
const err = common.handleError({ statusCode: 500, message: 'Get Loop Info Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
const err = common.handleError({ statusCode: 500, message: 'Get Swap Error', error: errMsg }, 'Loop', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
}
options.uri = '/v1/loop/info';
options.url = options.url + '/v1/loop/swap/' + req.params.id;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Information Received', data: body });
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'Loop', msg: 'Loop Swap Information Received', data: body });
res.status(200).json(body);
}).catch((errRes) => {
const err = common.handleError(errRes, 'Loop', 'Get Loop Info Error', req.session.selectedNode);
const err = common.handleError(errRes, 'Loop', 'Get Swap Error', req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.message, error: err.error });
});
};

@ -154,26 +154,3 @@ export const CollectionFieldsEnum = { ...OfferFieldsEnum, ...PageSettingsFieldsE
export const LNDCollection = [CollectionsEnum.PAGE_SETTINGS];
export const ECLCollection = [CollectionsEnum.PAGE_SETTINGS];
export const CLNCollection = [CollectionsEnum.PAGE_SETTINGS, CollectionsEnum.OFFERS];
export const ECL_UPDATED_DB = [
{
pageId: 'peers_channels',
tables: [
{
tableId: 'open_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'pending_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
},
{
tableId: 'inactive_channels',
removed: ['buried', 'feeRatePerKw'],
renamed: ['isFunder:isInitiator']
}
]
}
];

@ -1,11 +1,10 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { loopInfo, loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
import { loopInTerms, loopInQuote, loopInTermsAndQuotes, loopIn, loopOutTerms, loopOutQuote, loopOutTermsAndQuotes, loopOut, swaps, swap } from '../../controllers/shared/loop.js';
const router = Router();
router.get('/info', isAuthenticated, loopInfo);
router.get('/in/terms', isAuthenticated, loopInTerms);
router.get('/in/quote/:amount', isAuthenticated, loopInQuote);
router.get('/in/termsAndQuotes', isAuthenticated, loopInTermsAndQuotes);

@ -11,7 +11,6 @@ import sharedRoutes from '../routes/shared/index.js';
import lndRoutes from '../routes/lnd/index.js';
import clnRoutes from '../routes/cln/index.js';
import eclRoutes from '../routes/eclair/index.js';
import { Database, DatabaseService } from './database.js';
import { Common, CommonService } from './common.js';
import { Logger, LoggerService } from './logger.js';
import { CLWSClient, CLWebSocketClient } from '../controllers/cln/webSocketClient.js';
@ -28,7 +27,6 @@ export class ExpressApplication {
public eclWsClient: ECLWebSocketClient = ECLWSClient;
public clWsClient: CLWebSocketClient = CLWSClient;
public lndWsClient: LNDWebSocketClient = LNDWSClient;
public databaseService: DatabaseService = Database;
public directoryName = dirname(fileURLToPath(import.meta.url));
constructor() {
@ -42,7 +40,6 @@ export class ExpressApplication {
this.setCORS();
this.setCSRF();
this.setApplicationRoutes();
this.databaseService.migrateDatabase();
}
public getApp = () => this.app;

@ -34,10 +34,9 @@ export class CommonService {
constructor() { }
public setSwapServerOptions = (req) => {
public getSwapServerOptions = (req) => {
const swapOptions = {
baseUrl: req.session.selectedNode.swap_server_url,
uri: '',
url: req.session.selectedNode.swap_server_url,
rejectUnauthorized: false,
json: true,
headers: { 'Grpc-Metadata-macaroon': '' }

@ -2,7 +2,7 @@ import * as fs from 'fs';
import { join, sep } from 'path';
import { Common, CommonService } from '../utils/common.js';
import { Logger, LoggerService } from '../utils/logger.js';
import { Collections, CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection, ECL_UPDATED_DB } from '../models/database.model.js';
import { Collections, CollectionsEnum, validateDocument, LNDCollection, ECLCollection, CLNCollection } from '../models/database.model.js';
import { CommonSelectedNode } from '../models/config.model.js';
export class DatabaseService {
@ -14,58 +14,6 @@ export class DatabaseService {
constructor() { }
migrateDatabase() {
this.common.nodes?.map((node: any) => {
if (node.ln_implementation === 'ECL') {
this.nodeDatabase[node.index] = { adapter: null, data: {} };
this.nodeDatabase[node.index].adapter = new DatabaseAdapter(this.dbDirectory, node);
this.fetchNodeData(node);
if (this.nodeDatabase[node.index].data.PageSettings) {
try {
const currPageSettings = JSON.parse(JSON.stringify(this.nodeDatabase[node.index].data.PageSettings));
ECL_UPDATED_DB.forEach((updatePage) => {
const foundPageDB = this.nodeDatabase[node.index].data.PageSettings.find((currPage) => currPage.pageId === updatePage.pageId);
if (foundPageDB) {
updatePage.tables.forEach((updateTable) => {
const foundTableDB = foundPageDB.tables.find((currTable) => currTable.tableId === updateTable.tableId);
if (foundTableDB) {
updateTable.removed.forEach((colToBeRemoved) => {
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === colToBeRemoved);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === colToBeRemoved);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1);
}
});
updateTable.renamed.forEach((colToBeRenamed) => {
const [oldName, newName] = colToBeRenamed.split(':');
const foundIndex = foundTableDB.columnSelection.findIndex((col) => col === oldName);
const foundIndexSM = foundTableDB.columnSelectionSM.findIndex((col) => col === oldName);
if (foundIndex >= 0) {
foundTableDB.columnSelection?.splice(foundIndex, 1, newName);
}
if (foundIndexSM >= 0) {
foundTableDB.columnSelectionSM?.splice(foundIndexSM, 1, newName);
}
});
}
});
}
});
if (currPageSettings !== this.nodeDatabase[node.index].data.PageSettings) {
this.saveDatabase(node, CollectionsEnum.PAGE_SETTINGS);
}
} catch (err) {
this.logger.log({ selectedNode: node, level: 'ERROR', fileName: 'Database', msg: 'Database Migration Error', error: err });
}
}
}
return true;
});
}
loadDatabase(session: any) {
const { id, selectedNode } = session;
try {
@ -250,7 +198,7 @@ export class DatabaseAdapter {
private dbFilePath = '';
private userSessions = [];
constructor(public dbDirectoryPath: string, public selNode: CommonSelectedNode = null, public id: string = '') {
constructor(public dbDirectoryPath: string, private selNode: CommonSelectedNode = null, private id: string = '') {
this.dbFilePath = dbDirectoryPath + sep + 'node-' + selNode.index;
// For backward compatibility Start
const oldFilePath = dbDirectoryPath + sep + 'rtldb-node-' + selNode.index + '.json';

@ -10,12 +10,6 @@
<h4 fxLayoutAlign="start" class="dashboard-info-title">Transactions</h4>
<div class="overflow-wrap dashboard-info-value">{{fees?.totalTxCount | number}}</div>
</div>
<div *ngIf="!fees?.totalTxCount">
<h4 fxLayoutAlign="start" class="dashboard-info-title">Transactions</h4>
<a class="overflow-wrap dashboard-info-value" [routerLink]="['../routing']">
Go to Routing
</a>
</div>
</div>
</div>
<ng-template #errorBlock>

@ -21,7 +21,7 @@ import { RTLState } from '../../store/rtl.state';
import { addUpdateOfferBookmark, fetchBalance, fetchChannels, fetchFeeRates, fetchFees, fetchInvoices, fetchLocalRemoteBalance,
fetchPayments, fetchPeers, fetchUTXOs, setLookup, setPeers, setQueryRoutes, updateCLAPICallStatus, updateInvoice, setOfferInvoice,
sendPaymentStatus, setForwardingHistory, fetchPageSettings } from './cln.actions';
import { allAPIsCallStatus } from './cln.selector';
import { allAPIsCallStatus, clnNodeInformation, nodeInfoAndBalance } from './cln.selector';
import { ApiCallsListCL } from '../../shared/models/apiCallsPayload';
import { CLNOfferInformationComponent } from '../transactions/offers/offer-information-modal/offer-information.component';
@ -30,7 +30,7 @@ export class CLNEffects implements OnDestroy {
CHILD_API_URL = API_URL + '/cln';
API_VERION = '';
CLN_VERISON = '';
NODE_VERISON = '';
private flgInitialized = false;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
@ -97,7 +97,7 @@ export class CLNEffects implements OnDestroy {
map((info) => {
this.logger.info(info);
this.API_VERION = info.api_version || '';
this.CLN_VERISON = info.version || '';
this.NODE_VERISON = info.version || '';
if (info.chains && info.chains.length && info.chains[0] &&
(typeof info.chains[0] === 'object' && info.chains[0].hasOwnProperty('chain') && info?.chains[0].chain &&
(info?.chains[0].chain.toLowerCase().indexOf('bitcoin') < 0 && info?.chains[0].chain.toLowerCase().indexOf('liquid') < 0)
@ -331,7 +331,7 @@ export class CLNEffects implements OnDestroy {
mergeMap(() => {
this.store.dispatch(updateCLAPICallStatus({ payload: { action: 'FetchChannels', status: APICallStatusEnum.INITIATED } }));
const listChannelsEndpoint =
this.commonService.isVersionCompatible(this.CLN_VERISON, '23.02') &&
this.commonService.isVersionCompatible(this.NODE_VERISON, '23.02') &&
this.commonService.isVersionCompatible(this.API_VERION, '0.10.3') ?
'/listPeerChannels' : '/listChannels';
return this.httpClient.get<Channel[]>(this.CHILD_API_URL + API_END_POINTS.CHANNELS_API + listChannelsEndpoint);

@ -39,20 +39,24 @@
</div>
<mat-divider class="my-1" [inset]="true"></mat-divider>
<div fxLayout="row">
<div fxFlex="100">
<h4 fxLayoutAlign="start" class="font-bold-500">State</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.state | titlecase}}</span>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Private</h4>
<span class="overflow-wrap foreground-secondary-text">{{!channel.announceChannel ? 'Yes' : 'No'}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Funder</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.isFunder ? 'Yes' : 'No'}}</span>
</div>
</div>
<mat-divider class="my-1" [inset]="true"></mat-divider>
<div fxLayout="row">
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Private</h4>
<span class="overflow-wrap foreground-secondary-text">{{!channel.announceChannel ? 'Yes' : 'No'}}</span>
<h4 fxLayoutAlign="start" class="font-bold-500">State</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.state | titlecase}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Initiator</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.isInitiator ? 'Yes' : 'No'}}</span>
<h4 fxLayoutAlign="start" class="font-bold-500">Buried</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.buried ? 'Yes' : 'No'}}</span>
</div>
</div>
<mat-divider class="my-1" [inset]="true"></mat-divider>

@ -56,9 +56,13 @@
</div>
</td>
</ng-container>
<ng-container matColumnDef="isInitiator">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Initiator</th>
<td *matCellDef="let channel" mat-cell>{{channel?.isInitiator ? 'Yes' : 'No'}}</td>
<ng-container matColumnDef="isFunder">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Funder</th>
<td *matCellDef="let channel" mat-cell>{{channel?.isFunder ? 'Yes' : 'No'}}</td>
</ng-container>
<ng-container matColumnDef="buried">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Buried</th>
<td *matCellDef="let channel" mat-cell>{{channel?.buried ? 'Yes' : 'No'}}</td>
</ng-container>
<ng-container matColumnDef="toLocal">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">Local Balance (Sats)</th>
@ -70,6 +74,11 @@
<td *matCellDef="let channel" mat-cell><span fxLayoutAlign="end center">
{{channel?.toRemote | number:'1.0-0'}} </span></td>
</ng-container>
<ng-container matColumnDef="feeRatePerKw">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">Local Fee/KW</th>
<td *matCellDef="let channel" mat-cell><span fxLayoutAlign="end center">
{{channel?.feeRatePerKw | number:'1.0-0'}} </span></td>
</ng-container>
<ng-container matColumnDef="balancedness">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Balance Score</th>
<td *matCellDef="let channel" mat-cell>

@ -52,9 +52,13 @@
</div>
</td>
</ng-container>
<ng-container matColumnDef="isInitiator">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Initiator</th>
<td *matCellDef="let channel" mat-cell>{{channel?.isInitiator ? 'Yes' : 'No'}}</td>
<ng-container matColumnDef="isFunder">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Funder</th>
<td *matCellDef="let channel" mat-cell>{{channel?.isFunder ? 'Yes' : 'No'}}</td>
</ng-container>
<ng-container matColumnDef="buried">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Buried</th>
<td *matCellDef="let channel" mat-cell>{{channel?.buried ? 'Yes' : 'No'}}</td>
</ng-container>
<ng-container matColumnDef="feeBaseMsat">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">Base Fee (mSats)</th>
@ -76,6 +80,11 @@
<td *matCellDef="let channel" mat-cell><span fxLayoutAlign="end center">
{{channel?.toRemote | number:'1.0-0'}} </span></td>
</ng-container>
<ng-container matColumnDef="feeRatePerKw">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">Fee/KW</th>
<td *matCellDef="let channel" mat-cell><span fxLayoutAlign="end center">
{{channel?.feeRatePerKw | number:'1.0-0'}} </span></td>
</ng-container>
<ng-container matColumnDef="balancedness">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Balance Score</th>
<td *matCellDef="let channel" mat-cell>

@ -48,9 +48,13 @@
</div>
</td>
</ng-container>
<ng-container matColumnDef="isInitiator">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Initiator</th>
<td *matCellDef="let channel" mat-cell>{{channel?.isInitiator ? 'Yes' : 'No'}}</td>
<ng-container matColumnDef="isFunder">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Funder</th>
<td *matCellDef="let channel" mat-cell>{{channel?.isFunder ? 'Yes' : 'No'}}</td>
</ng-container>
<ng-container matColumnDef="buried">
<th *matHeaderCellDef mat-header-cell mat-sort-header>Buried</th>
<td *matCellDef="let channel" mat-cell>{{channel?.buried ? 'Yes' : 'No'}}</td>
</ng-container>
<ng-container matColumnDef="toLocal">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">Local Balance (Sats)</th>
@ -62,6 +66,11 @@
<td *matCellDef="let channel" mat-cell><span fxLayoutAlign="end center">
{{channel?.toRemote | number:'1.0-0'}} </span></td>
</ng-container>
<ng-container matColumnDef="feeRatePerKw">
<th *matHeaderCellDef mat-header-cell mat-sort-header arrowPosition="before">Fee/KW</th>
<td *matCellDef="let channel" mat-cell><span fxLayoutAlign="end center">
{{channel?.feeRatePerKw | number:'1.0-0'}} </span></td>
</ng-container>
<ng-container matColumnDef="actions">
<th *matHeaderCellDef mat-header-cell>
<div class="bordered-box table-actions-select" fxLayoutAlign="center center">

@ -3,7 +3,7 @@
}
.info-graphics-container {
max-height: 30rem;
min-height: 30rem;
max-height: 60rem;
min-height: 60rem;
overflow-x: hidden;
}

@ -3,7 +3,7 @@
}
.info-graphics-container {
max-height: 30rem;
min-height: 30rem;
max-height: 60rem;
min-height: 60rem;
overflow-x: hidden;
}

@ -1,6 +1,6 @@
<div fxLayout="row" fxLayoutAlign="start center" class="page-title-container">
<fa-icon class="page-title-img mr-1" [icon]="faInfinity"></fa-icon>
<span class="page-title">Loop (v{{loopInfo?.version || ' Unknown'}})</span>
<span class="page-title">Loop</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>

@ -5,13 +5,13 @@ import { takeUntil, filter } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faInfinity } from '@fortawesome/free-solid-svg-icons';
import { LoopTypeEnum, UI_MESSAGES } from '../../../services/consts-enums-functions';
import { LoopTypeEnum } from '../../../services/consts-enums-functions';
import { LoopModalComponent } from './loop-modal/loop-modal.component';
import { LoopQuote, LoopSwapStatus } from '../../../models/loopModels';
import { LoopService } from '../../../services/loop.service';
import { RTLState } from '../../../../store/rtl.state';
import { openAlert, closeSpinner, openSpinner } from '../../../../store/rtl.actions';
import { openAlert } from '../../../../store/rtl.actions';
@Component({
selector: 'rtl-loop',
@ -21,7 +21,6 @@ import { openAlert, closeSpinner, openSpinner } from '../../../../store/rtl.acti
export class LoopComponent implements OnInit, OnDestroy {
public faInfinity = faInfinity;
public loopInfo: any = null;
private targetConf = 2;
public inAmount = 250000;
public quotes: LoopQuote[] = [];
@ -33,26 +32,11 @@ export class LoopComponent implements OnInit, OnDestroy {
public flgLoading: Array<Boolean | 'error'> = [true];
public links = [{ link: 'loopout', name: 'Loop Out' }, { link: 'loopin', name: 'Loop In' }];
public activeTab = this.links[0];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private router: Router, private loopService: LoopService, private store: Store<RTLState>) { }
ngOnInit() {
this.store.dispatch(openSpinner({ payload: UI_MESSAGES.GET_LOOP_INFO }));
this.loopService.getLoopInfo().pipe(takeUntil(this.unSubs[4])).
subscribe({
next: (loopInfoResponse: any) => {
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_LOOP_INFO }));
this.loopInfo = loopInfoResponse;
if (this.loopInfo && this.loopInfo.version) {
this.loopInfo.version = this.loopInfo.version.split(' ')[0];
}
},
error: (err) => {
this.store.dispatch(closeSpinner({ payload: UI_MESSAGES.GET_LOOP_INFO }));
this.loopInfo.version = ' Unknown';
}
});
this.loopService.listSwaps();
const linkFound = this.links.find((link) => this.router.url.includes(link.link));
this.activeTab = linkFound ? linkFound : this.links[0];

@ -111,8 +111,10 @@ export interface Channel {
toRemote?: number;
shortChannelId?: string;
announceChannel?: boolean;
isInitiator?: boolean;
isFunder?: boolean;
buried?: boolean;
feeBaseMsat?: number;
feeRatePerKw?: number;
feeProportionalMillionths?: number;
balancedness?: number;
}

@ -298,19 +298,11 @@ export class CommonService implements OnDestroy {
isVersionCompatible(currentVersion, checkVersion) {
// Check for newer CLN version style compatibility
if (currentVersion) {
const match = currentVersion.match(/v?(?<version>\d+(?:\.\d+)*)/);
if (match && match.groups && match.groups.version) {
this.logger.info('Current Version: ' + match.groups.version);
this.logger.info('Checking Compatiblility with Version: ' + checkVersion);
const versionsArr = match.groups.version.split('.') || [];
const checkVersionsArr = checkVersion.split('.');
return (+versionsArr[0] > +checkVersionsArr[0]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] > +checkVersionsArr[1]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] === +checkVersionsArr[1] && +versionsArr[2] >= +checkVersionsArr[2]);
} else {
this.logger.error('Invalid Version String: ' + currentVersion);
return false;
}
const versionsArr = currentVersion.trim()?.replace('v', '').split('-')[0].split('.') || [];
const checkVersionsArr = checkVersion.split('.');
return (+versionsArr[0] > +checkVersionsArr[0]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] > +checkVersionsArr[1]) ||
(+versionsArr[0] === +checkVersionsArr[0] && +versionsArr[1] === +checkVersionsArr[1] && +versionsArr[2] >= +checkVersionsArr[2]);
}
return false;
}

@ -12,7 +12,7 @@ export const HOUR_SECONDS = 3600;
export const DEFAULT_INVOICE_EXPIRY = HOUR_SECONDS * 24 * 7;
export const VERSION = '0.14.1-beta';
export const VERSION = '0.14.0-beta';
export const API_URL = isDevMode() ? 'http://localhost:3000/rtl/api' : './api';
@ -357,7 +357,6 @@ export const UI_MESSAGES = {
VERIFY_MESSAGE: 'Verifying Message...',
BUMP_FEE: 'Bumping Fee...',
LEASE_UTXO: 'Leasing UTXO...',
GET_LOOP_INFO: 'Getting Loop Info...',
GET_LOOP_SWAPS: 'Getting List Swaps...',
GET_FORWARDING_HISTORY: 'Getting Forwarding History...',
GET_LOOKUP_DETAILS: 'Getting Lookup Details...',
@ -1196,20 +1195,20 @@ export const ECL_PAGE_DEFS: ECLPageDefinitions = {
peers_channels: {
open_channels: {
maxColumns: 8,
allowedColumns: [{ column:'shortChannelId' }, { column:'channelId' }, { column:'alias' }, { column:'nodeId' }, { column:'isInitiator', label: 'Initiator' },
{ column:'feeBaseMsat', label: 'Base Fee' }, { column:'feeProportionalMillionths', label: 'Fee Rate' }, { column:'toLocal', label: 'Local Balance' }, { column:'toRemote', label: 'Remote Balance' },
{ column:'balancedness', label: 'Balance Score' }]
allowedColumns: [{ column:'shortChannelId' }, { column:'channelId' }, { column:'alias' }, { column:'nodeId' }, { column:'isFunder', label: 'Funder' },
{ column:'buried' }, { column:'feeBaseMsat', label: 'Base Fee' }, { column:'feeProportionalMillionths', label: 'Fee Rate' }, { column:'toLocal', label: 'Local Balance' }, { column:'toRemote', label: 'Remote Balance' },
{ column:'feeRatePerKw', label: 'Fee/KW' }, { column:'balancedness', label: 'Balance Score' }]
},
pending_channels: {
maxColumns: 7,
allowedColumns: [{ column:'state' }, { column:'channelId' }, { column:'alias' }, { column:'nodeId' }, { column:'isInitiator', label: 'Initiator' },
{ column:'toLocal', label: 'Local Balance' }, { column:'toRemote', label: 'Remote Balance' }]
allowedColumns: [{ column:'state' }, { column:'channelId' }, { column:'alias' }, { column:'nodeId' }, { column:'isFunder', label: 'Funder' },
{ column:'buried' }, { column:'toLocal', label: 'Local Balance' }, { column:'toRemote', label: 'Remote Balance' }, { column:'feeRatePerKw', label: 'Fee/KW' }]
},
inactive_channels: {
maxColumns: 8,
allowedColumns: [{ column:'state' }, { column:'shortChannelId' }, { column:'channelId' }, { column:'alias' }, { column:'nodeId' },
{ column:'isInitiator', label: 'Initiator' }, { column:'toLocal', label: 'Local Balance' },
{ column:'toRemote', label: 'Remote Balance' }, { column:'balancedness', label: 'Balance Score' }]
{ column:'isFunder', label: 'Funder' }, { column:'buried' }, { column:'toLocal', label: 'Local Balance' },
{ column:'toRemote', label: 'Remote Balance' }, { column:'feeRatePerKw', label: 'Fee/KW' }, { column:'balancedness', label: 'Balance Score' }]
},
peers: {
maxColumns: 4,

@ -19,15 +19,10 @@ export class LoopService implements OnDestroy {
private loopUrl = '';
private swaps: LoopSwapStatus[] = [];
public swapsChanged = new BehaviorSubject<LoopSwapStatus[]>([]);
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private httpClient: HttpClient, private logger: LoggerService, private store: Store<RTLState>, private commonService: CommonService) { }
getLoopInfo() {
this.loopUrl = API_URL + API_END_POINTS.LOOP_API + '/info';
return this.httpClient.get<any>(this.loopUrl);
}
getSwapsList() {
return this.swaps;
}

Loading…
Cancel
Save