Merge branch 'master' into fix-unfurler-stray-slashes

This commit is contained in:
wiz 2022-10-30 02:05:17 +09:00 committed by GitHub
commit 23a4ab461e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 76737 additions and 16341 deletions

View File

@ -4,7 +4,7 @@ import logger from '../logger';
import { Common } from './common'; import { Common } from './common';
class DatabaseMigration { class DatabaseMigration {
private static currentVersion = 40; private static currentVersion = 41;
private queryTimeout = 120000; private queryTimeout = 120000;
private statisticsAddedIndexed = false; private statisticsAddedIndexed = false;
private uniqueLogs: string[] = []; private uniqueLogs: string[] = [];
@ -348,6 +348,10 @@ class DatabaseMigration {
await this.$executeQuery('ALTER TABLE `nodes` ADD channels int(11) unsigned DEFAULT NULL'); await this.$executeQuery('ALTER TABLE `nodes` ADD channels int(11) unsigned DEFAULT NULL');
await this.$executeQuery('ALTER TABLE `nodes` ADD INDEX `capacity` (`capacity`);'); await this.$executeQuery('ALTER TABLE `nodes` ADD INDEX `capacity` (`capacity`);');
} }
if (databaseSchemaVersion < 41 && isBitcoin === true) {
await this.$executeQuery('UPDATE channels SET closing_reason = NULL WHERE closing_reason = 1');
}
} }
/** /**

View File

@ -289,6 +289,24 @@ class NetworkSyncService {
1. Mutually closed 1. Mutually closed
2. Forced closed 2. Forced closed
3. Forced closed with penalty 3. Forced closed with penalty
outputs contain revocation script? yes force close w/ penalty = 3
no
outputs contain other lightning script?
no yes
sequence starts with 0x80
and force close = 2
locktime starts with 0x20?
no
mutual close = 1
*/ */
private async $runClosedChannelsForensics(): Promise<void> { private async $runClosedChannelsForensics(): Promise<void> {
@ -326,36 +344,31 @@ class NetworkSyncService {
lightningScriptReasons.push(lightningScript); lightningScriptReasons.push(lightningScript);
} }
} }
if (lightningScriptReasons.length === outspends.length const filteredReasons = lightningScriptReasons.filter((r) => r !== 1);
&& lightningScriptReasons.filter((r) => r === 1).length === outspends.length) { if (filteredReasons.length) {
reason = 1; if (filteredReasons.some((r) => r === 2 || r === 4)) {
} else { reason = 3;
const filteredReasons = lightningScriptReasons.filter((r) => r !== 1);
if (filteredReasons.length) {
if (filteredReasons.some((r) => r === 2 || r === 4)) {
reason = 3;
} else {
reason = 2;
}
} else { } else {
/* reason = 2;
We can detect a commitment transaction (force close) by reading Sequence and Locktime }
https://github.com/lightning/bolts/blob/master/03-transactions.md#commitment-transaction } else {
*/ /*
let closingTx: IEsploraApi.Transaction | undefined; We can detect a commitment transaction (force close) by reading Sequence and Locktime
try { https://github.com/lightning/bolts/blob/master/03-transactions.md#commitment-transaction
closingTx = await bitcoinApi.$getRawTransaction(channel.closing_transaction_id); */
} catch (e) { let closingTx: IEsploraApi.Transaction | undefined;
logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + channel.closing_transaction_id}. Reason ${e instanceof Error ? e.message : e}`); try {
continue; closingTx = await bitcoinApi.$getRawTransaction(channel.closing_transaction_id);
} } catch (e) {
const sequenceHex: string = closingTx.vin[0].sequence.toString(16); logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + channel.closing_transaction_id}. Reason ${e instanceof Error ? e.message : e}`);
const locktimeHex: string = closingTx.locktime.toString(16); continue;
if (sequenceHex.substring(0, 2) === '80' && locktimeHex.substring(0, 2) === '20') { }
reason = 2; // Here we can't be sure if it's a penalty or not const sequenceHex: string = closingTx.vin[0].sequence.toString(16);
} else { const locktimeHex: string = closingTx.locktime.toString(16);
reason = 1; if (sequenceHex.substring(0, 2) === '80' && locktimeHex.substring(0, 2) === '20') {
} reason = 2; // Here we can't be sure if it's a penalty or not
} else {
reason = 1;
} }
} }
if (reason) { if (reason) {

View File

@ -11,11 +11,15 @@
[showZoom]="false" [showZoom]="false"
></app-mempool-graph> ></app-mempool-graph>
</div> </div>
<div class="blockchain-wrapper"> <div class="blockchain-wrapper" [dir]="timeLtr ? 'rtl' : 'ltr'" [class.time-ltr]="timeLtr">
<div class="position-container"> <div class="position-container">
<app-mempool-blocks></app-mempool-blocks> <span>
<app-blockchain-blocks></app-blockchain-blocks> <div class="blocks-wrapper">
<div id="divider"></div> <app-mempool-blocks></app-mempool-blocks>
<app-blockchain-blocks></app-blockchain-blocks>
</div>
<div id="divider"></div>
</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -31,8 +31,9 @@
.position-container { .position-container {
position: absolute; position: absolute;
left: 50%; left: 0;
bottom: 170px; bottom: 170px;
transform: translateX(50vw);
} }
#divider { #divider {
@ -47,9 +48,33 @@
top: -28px; top: -28px;
} }
} }
&.time-ltr {
.blocks-wrapper {
transform: scaleX(-1);
}
}
} }
:host-context(.ltr-layout) {
.blockchain-wrapper.time-ltr .blocks-wrapper,
.blockchain-wrapper .blocks-wrapper {
direction: ltr;
}
}
:host-context(.rtl-layout) {
.blockchain-wrapper.time-ltr .blocks-wrapper,
.blockchain-wrapper .blocks-wrapper {
direction: rtl;
}
}
.tv-container { .tv-container {
display: flex; display: flex;
margin-top: 0px; margin-top: 0px;
flex-direction: column; flex-direction: column;
} }

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, OnDestroy } from '@angular/core';
import { WebsocketService } from '../../services/websocket.service'; import { WebsocketService } from '../../services/websocket.service';
import { OptimizedMempoolStats } from '../../interfaces/node-api.interface'; import { OptimizedMempoolStats } from '../../interfaces/node-api.interface';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
@ -6,7 +6,7 @@ import { ApiService } from '../../services/api.service';
import { SeoService } from '../../services/seo.service'; import { SeoService } from '../../services/seo.service';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { map, scan, startWith, switchMap, tap } from 'rxjs/operators'; import { map, scan, startWith, switchMap, tap } from 'rxjs/operators';
import { interval, merge, Observable } from 'rxjs'; import { interval, merge, Observable, Subscription } from 'rxjs';
import { ChangeDetectionStrategy } from '@angular/core'; import { ChangeDetectionStrategy } from '@angular/core';
@Component({ @Component({
@ -15,11 +15,13 @@ import { ChangeDetectionStrategy } from '@angular/core';
styleUrls: ['./television.component.scss'], styleUrls: ['./television.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class TelevisionComponent implements OnInit { export class TelevisionComponent implements OnInit, OnDestroy {
mempoolStats: OptimizedMempoolStats[] = []; mempoolStats: OptimizedMempoolStats[] = [];
statsSubscription$: Observable<OptimizedMempoolStats[]>; statsSubscription$: Observable<OptimizedMempoolStats[]>;
fragment: string; fragment: string;
timeLtrSubscription: Subscription;
timeLtr: boolean = this.stateService.timeLtr.value;
constructor( constructor(
private websocketService: WebsocketService, private websocketService: WebsocketService,
@ -37,6 +39,10 @@ export class TelevisionComponent implements OnInit {
this.seoService.setTitle($localize`:@@46ce8155c9ab953edeec97e8950b5a21e67d7c4e:TV view`); this.seoService.setTitle($localize`:@@46ce8155c9ab953edeec97e8950b5a21e67d7c4e:TV view`);
this.websocketService.want(['blocks', 'live-2h-chart', 'mempool-blocks']); this.websocketService.want(['blocks', 'live-2h-chart', 'mempool-blocks']);
this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {
this.timeLtr = !!ltr;
});
this.statsSubscription$ = merge( this.statsSubscription$ = merge(
this.stateService.live2Chart$.pipe(map(stats => [stats])), this.stateService.live2Chart$.pipe(map(stats => [stats])),
this.route.fragment this.route.fragment
@ -70,4 +76,8 @@ export class TelevisionComponent implements OnInit {
}) })
); );
} }
ngOnDestroy() {
this.timeLtrSubscription.unsubscribe();
}
} }

View File

@ -210,8 +210,6 @@
[network]="network" [network]="network"
[tooltip]="true" [tooltip]="true"
[inputIndex]="inputIndex" [outputIndex]="outputIndex" [inputIndex]="inputIndex" [outputIndex]="outputIndex"
(selectInput)="selectInput($event)"
(selectOutput)="selectOutput($event)"
> >
</tx-bowtie-graph> </tx-bowtie-graph>
</div> </div>

View File

@ -18,6 +18,7 @@ import { ApiService } from '../../services/api.service';
import { SeoService } from '../../services/seo.service'; import { SeoService } from '../../services/seo.service';
import { BlockExtended, CpfpInfo } from '../../interfaces/node-api.interface'; import { BlockExtended, CpfpInfo } from '../../interfaces/node-api.interface';
import { LiquidUnblinding } from './liquid-ublinding'; import { LiquidUnblinding } from './liquid-ublinding';
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
@Component({ @Component({
selector: 'app-transaction', selector: 'app-transaction',
@ -40,6 +41,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
txReplacedSubscription: Subscription; txReplacedSubscription: Subscription;
blocksSubscription: Subscription; blocksSubscription: Subscription;
queryParamsSubscription: Subscription; queryParamsSubscription: Subscription;
urlFragmentSubscription: Subscription;
fragmentParams: URLSearchParams;
rbfTransaction: undefined | Transaction; rbfTransaction: undefined | Transaction;
cpfpInfo: CpfpInfo | null; cpfpInfo: CpfpInfo | null;
showCpfpDetails = false; showCpfpDetails = false;
@ -67,6 +70,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private relativeUrlPipe: RelativeUrlPipe,
private electrsApiService: ElectrsApiService, private electrsApiService: ElectrsApiService,
private stateService: StateService, private stateService: StateService,
private websocketService: WebsocketService, private websocketService: WebsocketService,
@ -93,6 +97,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
map((da) => da.timeAvg) map((da) => da.timeAvg)
); );
this.urlFragmentSubscription = this.route.fragment.subscribe((fragment) => {
this.fragmentParams = new URLSearchParams(fragment || '');
const vin = parseInt(this.fragmentParams.get('vin'), 10);
const vout = parseInt(this.fragmentParams.get('vout'), 10);
this.inputIndex = (!isNaN(vin) && vin >= 0) ? vin : null;
this.outputIndex = (!isNaN(vout) && vout >= 0) ? vout : null;
});
this.fetchCpfpSubscription = this.fetchCpfp$ this.fetchCpfpSubscription = this.fetchCpfp$
.pipe( .pipe(
switchMap((txId) => switchMap((txId) =>
@ -132,13 +144,29 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
switchMap((params: ParamMap) => { switchMap((params: ParamMap) => {
const urlMatch = (params.get('id') || '').split(':'); const urlMatch = (params.get('id') || '').split(':');
if (urlMatch.length === 2 && urlMatch[1].length === 64) { if (urlMatch.length === 2 && urlMatch[1].length === 64) {
this.inputIndex = parseInt(urlMatch[0], 10); const vin = parseInt(urlMatch[0], 10);
this.outputIndex = null;
this.txId = urlMatch[1]; this.txId = urlMatch[1];
// rewrite legacy vin syntax
if (!isNaN(vin)) {
this.fragmentParams.set('vin', vin.toString());
this.fragmentParams.delete('vout');
}
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.txId], {
queryParamsHandling: 'merge',
fragment: this.fragmentParams.toString(),
});
} else { } else {
this.txId = urlMatch[0]; this.txId = urlMatch[0];
this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10); const vout = parseInt(urlMatch[1], 10);
this.inputIndex = null; if (urlMatch.length > 1 && !isNaN(vout)) {
// rewrite legacy vout syntax
this.fragmentParams.set('vout', vout.toString());
this.fragmentParams.delete('vin');
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.txId], {
queryParamsHandling: 'merge',
fragment: this.fragmentParams.toString(),
});
}
} }
this.seoService.setTitle( this.seoService.setTitle(
$localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:` $localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`
@ -222,6 +250,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.fetchCpfp$.next(this.tx.txid); this.fetchCpfp$.next(this.tx.txid);
} }
} }
setTimeout(() => { this.applyFragment(); }, 0);
}, },
(error) => { (error) => {
this.error = error; this.error = error;
@ -359,14 +388,15 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.graphExpanded = false; this.graphExpanded = false;
} }
selectInput(input) { // simulate normal anchor fragment behavior
this.inputIndex = input; applyFragment(): void {
this.outputIndex = null; const anchor = Array.from(this.fragmentParams.entries()).find(([frag, value]) => value === '');
} if (anchor) {
const anchorElement = document.getElementById(anchor[0]);
selectOutput(output) { if (anchorElement) {
this.outputIndex = output; anchorElement.scrollIntoView();
this.inputIndex = null; }
}
} }
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
@ -383,6 +413,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
this.blocksSubscription.unsubscribe(); this.blocksSubscription.unsubscribe();
this.queryParamsSubscription.unsubscribe(); this.queryParamsSubscription.unsubscribe();
this.flowPrefSubscription.unsubscribe(); this.flowPrefSubscription.unsubscribe();
this.urlFragmentSubscription.unsubscribe();
this.leaveTransaction(); this.leaveTransaction();
} }
} }

View File

@ -43,7 +43,7 @@
</ng-template> </ng-template>
</ng-template> </ng-template>
<ng-template #defaultPrevout> <ng-template #defaultPrevout>
<a [routerLink]="['/tx/' | relativeUrl, vin.txid + ':' + vin.vout]" class="red"> <a [routerLink]="['/tx/' | relativeUrl, vin.txid]" [fragment]="'vout=' + vin.vout" class="red">
<fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon> <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
</a> </a>
</ng-template> </ng-template>
@ -220,7 +220,7 @@
<fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon> <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
</span> </span>
<ng-template #spent> <ng-template #spent>
<a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].vin + ':' + tx._outspends[vindex].txid]" class="red"> <a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].txid]" [fragment]="'vin=' + tx._outspends[vindex].vin" class="red">
<fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon> <fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
</a> </a>
<ng-template #outputNoTxId> <ng-template #outputNoTxId>

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core'; import { Component, OnInit, Input, OnChanges, HostListener } from '@angular/core';
import { StateService } from '../../services/state.service'; import { StateService } from '../../services/state.service';
import { Outspend, Transaction } from '../../interfaces/electrs.interface'; import { Outspend, Transaction } from '../../interfaces/electrs.interface';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
@ -43,9 +43,6 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
@Input() inputIndex: number; @Input() inputIndex: number;
@Input() outputIndex: number; @Input() outputIndex: number;
@Output() selectInput = new EventEmitter<number>();
@Output() selectOutput = new EventEmitter<number>();
inputData: Xput[]; inputData: Xput[];
outputData: Xput[]; outputData: Xput[];
inputs: SvgLine[]; inputs: SvgLine[];
@ -368,24 +365,42 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
onClick(event, side, index): void { onClick(event, side, index): void {
if (side === 'input') { if (side === 'input') {
const input = this.tx.vin[index]; const input = this.tx.vin[index];
if (input && input.txid && input.vout != null) { if (input && !input.is_coinbase && !input.is_pegin && input.txid && input.vout != null) {
this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid + ':' + input.vout], { this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid], {
queryParamsHandling: 'merge', queryParamsHandling: 'merge',
fragment: 'flow' fragment: (new URLSearchParams({
flow: '',
vout: input.vout.toString(),
})).toString(),
});
} else if (index != null) {
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.tx.txid], {
queryParamsHandling: 'merge',
fragment: (new URLSearchParams({
flow: '',
vin: index.toString(),
})).toString(),
}); });
} else {
this.selectInput.emit(index);
} }
} else { } else {
const output = this.tx.vout[index]; const output = this.tx.vout[index];
const outspend = this.outspends[index]; const outspend = this.outspends[index];
if (output && outspend && outspend.spent && outspend.txid) { if (output && outspend && outspend.spent && outspend.txid) {
this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.vin + ':' + outspend.txid], { this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.txid], {
queryParamsHandling: 'merge', queryParamsHandling: 'merge',
fragment: 'flow' fragment: (new URLSearchParams({
flow: '',
vin: outspend.vin.toString(),
})).toString(),
});
} else if (index != null) {
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.tx.txid], {
queryParamsHandling: 'merge',
fragment: (new URLSearchParams({
flow: '',
vout: index.toString(),
})).toString(),
}); });
} else {
this.selectOutput.emit(index);
} }
} }
} }

View File

@ -13,7 +13,10 @@
<ng-template #notFullyTaproot> <ng-template #notFullyTaproot>
<span *ngIf="segwitGains.realizedTaprootGains && segwitGains.potentialTaprootGains; else noTaproot" class="badge badge-warning mr-1" i18n-ngbTooltip="Tooltip about fees that saved and could be saved with taproot" ngbTooltip="This transaction uses Taproot and already saved at least {{ segwitGains.realizedTaprootGains * 100 | number: '1.0-0' }}% on fees, but could save an additional {{ segwitGains.potentialTaprootGains * 100 | number: '1.0-0' }}% by fully using Taproot" placement="bottom" i18n="tx-features.tag.taproot|Taproot">Taproot</span> <span *ngIf="segwitGains.realizedTaprootGains && segwitGains.potentialTaprootGains; else noTaproot" class="badge badge-warning mr-1" i18n-ngbTooltip="Tooltip about fees that saved and could be saved with taproot" ngbTooltip="This transaction uses Taproot and already saved at least {{ segwitGains.realizedTaprootGains * 100 | number: '1.0-0' }}% on fees, but could save an additional {{ segwitGains.potentialTaprootGains * 100 | number: '1.0-0' }}% by fully using Taproot" placement="bottom" i18n="tx-features.tag.taproot|Taproot">Taproot</span>
<ng-template #noTaproot> <ng-template #noTaproot>
<span *ngIf="segwitGains.potentialTaprootGains; else taprootButNoGains" class="badge badge-danger mr-1" i18n-ngbTooltip="Tooltip about fees that could be saved with taproot" ngbTooltip="This transaction could save {{ segwitGains.potentialTaprootGains * 100 | number: '1.0-0' }}% on fees by using Taproot" placement="bottom"><del i18n="tx-features.tag.taproot|Taproot">Taproot</del></span> <span *ngIf="segwitGains.potentialTaprootGains && segwitGains.potentialTaprootGains > 0; else negativeTaprootGains" class="badge badge-danger mr-1" i18n-ngbTooltip="Tooltip about fees that could be saved with taproot" ngbTooltip="This transaction could save {{ segwitGains.potentialTaprootGains * 100 | number: '1.0-0' }}% on fees by using Taproot" placement="bottom"><del i18n="tx-features.tag.taproot|Taproot">Taproot</del></span>
<ng-template #negativeTaprootGains>
<span *ngIf="!isTaproot; else taprootButNoGains" class="badge badge-danger mr-1" i18n-ngbTooltip="Tooltip about using taproot" ngbTooltip="This transaction does not use Taproot" placement="bottom"><del i18n="tx-features.tag.taproot|Taproot">Taproot</del></span>
</ng-template>
<ng-template #taprootButNoGains> <ng-template #taprootButNoGains>
<span *ngIf="isTaproot" class="badge badge-success mr-1" i18n-ngbTooltip="Tooltip about taproot" ngbTooltip="This transaction uses Taproot" placement="bottom" i18n="tx-features.tag.taproot|Taproot">Taproot</span> <span *ngIf="isTaproot" class="badge badge-success mr-1" i18n-ngbTooltip="Tooltip about taproot" ngbTooltip="This transaction uses Taproot" placement="bottom" i18n="tx-features.tag.taproot|Taproot">Taproot</span>
</ng-template> </ng-template>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,8 @@
], ],
"lib": [ "lib": [
"es2018", "es2018",
"dom" "dom",
"dom.iterable"
] ]
}, },
"angularCompilerOptions": { "angularCompilerOptions": {