From 900448aedce1449c9349de52343399d68c03a526 Mon Sep 17 00:00:00 2001 From: "Kevin C. Coram" Date: Fri, 9 Oct 2020 12:21:21 -0400 Subject: [PATCH] Implement client->server chat --- apps/api/src/app/app.controller.spec.ts | 22 -- apps/api/src/app/app.controller.ts | 15 - apps/api/src/app/app.gateway.spec.ts | 18 ++ apps/api/src/app/app.gateway.ts | 35 ++ apps/api/src/app/app.module.ts | 6 +- apps/api/src/app/app.service.spec.ts | 13 +- apps/api/src/app/app.service.ts | 13 +- apps/chat-room/src/app/app.component.html | 25 +- apps/chat-room/src/app/app.component.scss | 144 ++------- apps/chat-room/src/app/app.component.spec.ts | 29 +- apps/chat-room/src/app/app.component.ts | 30 +- apps/chat-room/src/app/app.module.ts | 16 +- apps/chat-room/src/app/messages.service.ts | 23 ++ libs/api-interfaces/src/index.ts | 1 + libs/api-interfaces/src/lib/api-interfaces.ts | 3 + libs/api-interfaces/src/lib/constants.ts | 2 + package.json | 4 + yarn.lock | 300 +++++++++++++++++- 18 files changed, 506 insertions(+), 193 deletions(-) delete mode 100644 apps/api/src/app/app.controller.spec.ts delete mode 100644 apps/api/src/app/app.controller.ts create mode 100644 apps/api/src/app/app.gateway.spec.ts create mode 100644 apps/api/src/app/app.gateway.ts create mode 100644 apps/chat-room/src/app/messages.service.ts create mode 100644 libs/api-interfaces/src/lib/constants.ts diff --git a/apps/api/src/app/app.controller.spec.ts b/apps/api/src/app/app.controller.spec.ts deleted file mode 100644 index b991ea7..0000000 --- a/apps/api/src/app/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; - -import { AppController } from './app.controller'; -import { AppService } from './app.service'; - -describe('AppController', () => { - let app: TestingModule; - - beforeAll(async () => { - app = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - }); - - describe('getData', () => { - it('should return "Welcome to api!"', () => { - const appController = app.get(AppController); - expect(appController.getData()).toEqual({ message: 'Welcome to api!' }); - }); - }); -}); diff --git a/apps/api/src/app/app.controller.ts b/apps/api/src/app/app.controller.ts deleted file mode 100644 index 6818101..0000000 --- a/apps/api/src/app/app.controller.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; - -import { Message } from '@chat-room/api-interfaces'; - -import { AppService } from './app.service'; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get('hello') - getData(): Message { - return this.appService.getData(); - } -} diff --git a/apps/api/src/app/app.gateway.spec.ts b/apps/api/src/app/app.gateway.spec.ts new file mode 100644 index 0000000..ce7a9b9 --- /dev/null +++ b/apps/api/src/app/app.gateway.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AppGateway } from './app.gateway'; + +describe('AppGateway', () => { + let gateway: AppGateway; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [AppGateway], + }).compile(); + + gateway = module.get(AppGateway); + }); + + it('should be defined', () => { + expect(gateway).toBeDefined(); + }); +}); diff --git a/apps/api/src/app/app.gateway.ts b/apps/api/src/app/app.gateway.ts new file mode 100644 index 0000000..c121916 --- /dev/null +++ b/apps/api/src/app/app.gateway.ts @@ -0,0 +1,35 @@ +import { + Message, + MESSAGE_TO_CLIENT, + MESSAGE_TO_SERVER, +} from '@chat-room/api-interfaces'; +import { Logger } from '@nestjs/common'; +import { + OnGatewayConnection, + SubscribeMessage, + WebSocketGateway, + WebSocketServer, +} from '@nestjs/websockets'; +import { Server } from 'http'; +import { AppService } from './app.service'; + +@WebSocketGateway(4001) +export class AppGateway implements OnGatewayConnection { + @WebSocketServer() + wss: Server; + + private logger: Logger = new Logger('AppGateway'); + + constructor(private appService: AppService) {} + + handleConnection(client: any, ...args: any[]) { + client.emit(MESSAGE_TO_CLIENT, this.appService.getMessages()); + } + + @SubscribeMessage(MESSAGE_TO_SERVER) + handleMessage(client: any, payload: Message): void { + this.logger.log(payload); + this.appService.addMessage(payload); + this.wss.emit(MESSAGE_TO_CLIENT, this.appService.getMessages()); + } +} diff --git a/apps/api/src/app/app.module.ts b/apps/api/src/app/app.module.ts index 6a9bc16..e9196c1 100644 --- a/apps/api/src/app/app.module.ts +++ b/apps/api/src/app/app.module.ts @@ -1,11 +1,11 @@ import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; import { AppService } from './app.service'; +import { AppGateway } from './app.gateway'; @Module({ imports: [], - controllers: [AppController], - providers: [AppService], + controllers: [], + providers: [AppService, AppGateway], }) export class AppModule {} diff --git a/apps/api/src/app/app.service.spec.ts b/apps/api/src/app/app.service.spec.ts index 884b5c9..64dab4c 100644 --- a/apps/api/src/app/app.service.spec.ts +++ b/apps/api/src/app/app.service.spec.ts @@ -1,6 +1,7 @@ import { Test } from '@nestjs/testing'; import { AppService } from './app.service'; +import { Message } from '@chat-room/api-interfaces'; describe('AppService', () => { let service: AppService; @@ -13,9 +14,15 @@ describe('AppService', () => { service = app.get(AppService); }); - describe('getData', () => { - it('should return "Welcome to api!"', () => { - expect(service.getData()).toEqual({ message: 'Welcome to api!' }); + describe('addMessage', () => { + it('should return message', () => { + const message: Message = { + creationDate: new Date().toISOString(), + message: 'Test message', + user: 'Test User', + color: 'Test color', + }; + expect(service.addMessage(message)).toBe(message); }); }); }); diff --git a/apps/api/src/app/app.service.ts b/apps/api/src/app/app.service.ts index 84ef513..67535f3 100644 --- a/apps/api/src/app/app.service.ts +++ b/apps/api/src/app/app.service.ts @@ -3,7 +3,16 @@ import { Message } from '@chat-room/api-interfaces'; @Injectable() export class AppService { - getData(): Message { - return { message: 'Welcome to api!' }; + private messages: Message[] = []; + + getMessages(): Message[] { + return this.messages; + } + + addMessage(message: Message): Message { + this.messages.push(message); + // Only keep the last 60 messages + this.messages = this.messages.reverse().slice(0, 60).reverse(); + return message; } } diff --git a/apps/chat-room/src/app/app.component.html b/apps/chat-room/src/app/app.component.html index 1b70cf7..0bc02a1 100644 --- a/apps/chat-room/src/app/app.component.html +++ b/apps/chat-room/src/app/app.component.html @@ -1,8 +1,19 @@ -
-

Welcome to chat-room!

- +
+
+ {{ message.user }} : + {{ message.message }} +
+
+
+ + +
-
Message: {{ hello$ | async | json }}
diff --git a/apps/chat-room/src/app/app.component.scss b/apps/chat-room/src/app/app.component.scss index f222adf..ef4917e 100644 --- a/apps/chat-room/src/app/app.component.scss +++ b/apps/chat-room/src/app/app.component.scss @@ -1,133 +1,31 @@ -/* - * Remove template code below - */ -:host { - display: block; - font-family: sans-serif; - min-width: 300px; - max-width: 600px; - margin: 50px auto; -} -.gutter-left { - margin-left: 9px; -} - -.col-span-2 { - grid-column: span 2; -} - -.flex { +.messages-container{ display: flex; - align-items: center; - justify-content: center; + flex-flow: column-reverse nowrap; + margin: 0px; + border: 1px solid black; + min-height: 80vh; + max-height: 80vh; + overflow-y: scroll; } -header { - background-color: #143055; - color: white; - padding: 5px; - border-radius: 3px; +.message { + padding: 0.3rem; + &:nth-child(odd){ + background: #EEEEEE; + } + &:nth-child(even) { + background: #FFFFFF; + } } -main { - padding: 0 36px; +input { + width: 100%; + margin: 0px; + margin-top: 1rem; } -p { - text-align: center; -} - -h1 { - text-align: center; - margin-left: 18px; - font-size: 24px; -} - -h2 { - text-align: center; - font-size: 20px; - margin: 40px 0 10px 0; -} - -.resources { - text-align: center; - list-style: none; - padding: 0; +.input-section { display: grid; - grid-gap: 9px; - grid-template-columns: 1fr 1fr; -} - -.resource { - color: #0094ba; - height: 36px; - background-color: rgba(0, 0, 0, 0); - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 4px; - padding: 3px 9px; - text-decoration: none; -} - -.resource:hover { - background-color: rgba(68, 138, 255, 0.04); -} - -pre { - padding: 9px; - border-radius: 4px; - background-color: black; - color: #eee; -} - -details { - border-radius: 4px; - color: #333; - background-color: rgba(0, 0, 0, 0); - border: 1px solid rgba(0, 0, 0, 0.12); - padding: 3px 9px; - margin-bottom: 9px; -} - -summary { - cursor: pointer; - outline: none; - height: 36px; - line-height: 36px; -} - -.github-star-container { - margin-top: 12px; - line-height: 20px; -} - -.github-star-container a { - display: flex; - align-items: center; - text-decoration: none; - color: #333; -} - -.github-star-badge { - color: #24292e; - display: flex; - align-items: center; - font-size: 12px; - padding: 3px 10px; - border: 1px solid rgba(27, 31, 35, 0.2); - border-radius: 3px; - background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%); - margin-left: 4px; - font-weight: 600; -} - -.github-star-badge:hover { - background-image: linear-gradient(-180deg, #f0f3f6, #e6ebf1 90%); - border-color: rgba(27, 31, 35, 0.35); - background-position: -0.5em; -} -.github-star-badge .material-icons { - height: 16px; - width: 16px; - margin-right: 4px; + grid-template-columns: 1fr 200px 30px; } diff --git a/apps/chat-room/src/app/app.component.spec.ts b/apps/chat-room/src/app/app.component.spec.ts index 5729e41..5618985 100644 --- a/apps/chat-room/src/app/app.component.spec.ts +++ b/apps/chat-room/src/app/app.component.spec.ts @@ -1,15 +1,28 @@ -import { Component } from '@angular/core'; -import { TestBed, async } from '@angular/core/testing'; +import { TestBed, waitForAsync } from '@angular/core/testing'; import { HttpClientModule } from '@angular/common/http'; +import { FormsModule } from '@angular/forms'; +import { MockService } from 'ng-mocks'; import { AppComponent } from './app.component'; +import { MessagesService } from './messages.service'; +import { of } from 'rxjs'; describe('AppComponent', () => { - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [AppComponent], - imports: [HttpClientModule], - }).compileComponents(); - })); + beforeEach( + waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AppComponent], + imports: [HttpClientModule, FormsModule], + providers: [ + { provide: MessagesService, useValue: MockService(MessagesService) }, + ], + }).compileComponents(); + }) + ); + + beforeEach(() => { + const messageService = TestBed.inject(MessagesService); + jest.spyOn(messageService, 'getMessages').mockReturnValue(of([])); + }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); diff --git a/apps/chat-room/src/app/app.component.ts b/apps/chat-room/src/app/app.component.ts index c27351c..8647cc5 100644 --- a/apps/chat-room/src/app/app.component.ts +++ b/apps/chat-room/src/app/app.component.ts @@ -1,5 +1,7 @@ import { Component } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import { MessagesService } from './messages.service'; +import { Observable } from 'rxjs'; +import { map, tap } from 'rxjs/operators'; import { Message } from '@chat-room/api-interfaces'; @Component({ @@ -8,6 +10,28 @@ import { Message } from '@chat-room/api-interfaces'; styleUrls: ['./app.component.scss'], }) export class AppComponent { - hello$ = this.http.get('/api/hello'); - constructor(private http: HttpClient) {} + messages$: Observable; + currentMessage = ''; + user = 'anonymous' + new Date().getTime(); + color = '#333333'; + + constructor(private messagesService: MessagesService) { + this.messages$ = this.messagesService + .getMessages() + .pipe(map((messages) => messages.reverse())); + } + + sendMessage() { + this.messagesService.addMessage({ + message: this.currentMessage, + user: this.user, + color: this.color, + creationDate: new Date().toISOString(), + }); + this.currentMessage = ''; + } + + trackByFn(index, item: Message) { + return item.creationDate.toString(); + } } diff --git a/apps/chat-room/src/app/app.module.ts b/apps/chat-room/src/app/app.module.ts index cb0a85a..ea392df 100644 --- a/apps/chat-room/src/app/app.module.ts +++ b/apps/chat-room/src/app/app.module.ts @@ -1,12 +1,24 @@ +import { HttpClientModule } from '@angular/common/http'; import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; +import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io'; import { AppComponent } from './app.component'; -import { HttpClientModule } from '@angular/common/http'; +import { FormsModule } from '@angular/forms'; + +const config: SocketIoConfig = { + url: ':4001', + options: {}, +}; @NgModule({ declarations: [AppComponent], - imports: [BrowserModule, HttpClientModule], + imports: [ + BrowserModule, + HttpClientModule, + FormsModule, + SocketIoModule.forRoot(config), + ], providers: [], bootstrap: [AppComponent], }) diff --git a/apps/chat-room/src/app/messages.service.ts b/apps/chat-room/src/app/messages.service.ts new file mode 100644 index 0000000..f174dff --- /dev/null +++ b/apps/chat-room/src/app/messages.service.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { + Message, + MESSAGE_TO_CLIENT, + MESSAGE_TO_SERVER, +} from '@chat-room/api-interfaces'; +import { Socket } from 'ngx-socket-io'; + +@Injectable({ + providedIn: 'root', +}) +export class MessagesService { + constructor(private socket: Socket) {} + + addMessage(message: Message): void { + this.socket.emit(MESSAGE_TO_SERVER, message); + } + + getMessages(): Observable { + return this.socket.fromEvent(MESSAGE_TO_CLIENT); + } +} diff --git a/libs/api-interfaces/src/index.ts b/libs/api-interfaces/src/index.ts index 3bc02d6..8d3dd56 100644 --- a/libs/api-interfaces/src/index.ts +++ b/libs/api-interfaces/src/index.ts @@ -1 +1,2 @@ export * from './lib/api-interfaces'; +export * from './lib/constants'; \ No newline at end of file diff --git a/libs/api-interfaces/src/lib/api-interfaces.ts b/libs/api-interfaces/src/lib/api-interfaces.ts index 0f1e936..969bd11 100644 --- a/libs/api-interfaces/src/lib/api-interfaces.ts +++ b/libs/api-interfaces/src/lib/api-interfaces.ts @@ -1,3 +1,6 @@ export interface Message { + creationDate: string; message: string; + user: string; + color: string; } diff --git a/libs/api-interfaces/src/lib/constants.ts b/libs/api-interfaces/src/lib/constants.ts new file mode 100644 index 0000000..edae0a6 --- /dev/null +++ b/libs/api-interfaces/src/lib/constants.ts @@ -0,0 +1,2 @@ +export const MESSAGE_TO_SERVER = 'messageToServer'; +export const MESSAGE_TO_CLIENT = 'messageToClient'; \ No newline at end of file diff --git a/package.json b/package.json index 5f9fde2..c882a54 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,10 @@ "@nestjs/common": "^7.0.0", "@nestjs/core": "^7.0.0", "@nestjs/platform-express": "^7.0.0", + "@nestjs/platform-socket.io": "^7.4.4", + "@nestjs/websockets": "^7.4.4", "@nrwl/angular": "10.3.0", + "ngx-socket-io": "^3.2.0", "reflect-metadata": "^0.1.13", "rxjs": "~6.5.5", "zone.js": "^0.10.2" @@ -71,6 +74,7 @@ "eslint-plugin-cypress": "^2.10.3", "jest": "26.2.2", "jest-preset-angular": "8.3.1", + "ng-mocks": "^10.2.0", "prettier": "2.0.4", "ts-jest": "26.4.0", "ts-node": "~7.0.0", diff --git a/yarn.lock b/yarn.lock index b3164fc..f54dec7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1530,6 +1530,14 @@ multer "1.4.2" tslib "2.0.1" +"@nestjs/platform-socket.io@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@nestjs/platform-socket.io/-/platform-socket.io-7.4.4.tgz#4dd70ed21043430c403baf9ca9d7869717e6a800" + integrity sha512-ameDjwT19Ctj9UQ86+mnCcjsT0S6d9Mft5Iwmp3UXc6tmWxLSAZPCAawkbfSXxpkBFI9Qy8LqmZhT7L1o9iIIw== + dependencies: + socket.io "2.3.0" + tslib "2.0.1" + "@nestjs/schematics@^7.0.0": version "7.1.2" resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-7.1.2.tgz#678058436f578fc1e933da9db327a223b1ab8a73" @@ -1548,6 +1556,14 @@ optional "0.1.4" tslib "2.0.1" +"@nestjs/websockets@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@nestjs/websockets/-/websockets-7.4.4.tgz#2530f2f3d2271e2880be58246271f4c7d505f167" + integrity sha512-XT2lRnCiw2d6IpscRFcjjkUFA0FgZYaE3On1pmW8FvQSWlHnu3Izl/TgyySJZNDKpRnmnh4IGFA4GZ6VML//1A== + dependencies: + iterare "1.2.1" + tslib "2.0.1" + "@ngtools/webpack@10.1.6": version "10.1.6" resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-10.1.6.tgz#f9b1b253b4f3a3944966e5ed3529e3ff252772c6" @@ -2443,6 +2459,11 @@ adjust-sourcemap-loader@2.0.0: object-path "0.11.4" regex-parser "2.2.10" +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + agent-base@4, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" @@ -2691,6 +2712,11 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + arrify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -2919,16 +2945,36 @@ babel-preset-jest@^26.5.0: babel-plugin-jest-hoist "^26.5.0" babel-preset-current-node-syntax "^0.1.3" +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-arraybuffer@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" + integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= + +base64-arraybuffer@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + base64-js@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== +base64id@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -2954,6 +3000,13 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= + dependencies: + callsite "1.0.0" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -2976,6 +3029,11 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + bluebird@3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de" @@ -3295,6 +3353,11 @@ caller-path@^2.0.0: dependencies: caller-callsite "^2.0.0" +callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= + callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" @@ -3705,11 +3768,26 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= -component-emitter@^1.2.1: +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + +component-emitter@^1.2.1, component-emitter@~1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + compose-function@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" @@ -3801,6 +3879,11 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + cookie@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" @@ -3853,6 +3936,11 @@ core-js@3.6.4: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.4.tgz#440a83536b458114b9cb2ac1580ba377dc470647" integrity sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw== +core-js@^3.0.0: + version "3.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" + integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -4246,7 +4334,7 @@ debug@3.1.0, debug@=3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@4.1.1: +debug@4.1.1, debug@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -4623,6 +4711,46 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" +engine.io-client@~3.4.0: + version "3.4.4" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.4.tgz#77d8003f502b0782dd792b073a4d2cf7ca5ab967" + integrity sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ== + dependencies: + component-emitter "~1.3.0" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + ws "~6.1.0" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-parser@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" + integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.4" + blob "0.0.5" + has-binary2 "~1.0.2" + +engine.io@~3.4.0: + version "3.4.2" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.4.2.tgz#8fc84ee00388e3e228645e0a7d3dfaeed5bd122c" + integrity sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg== + dependencies: + accepts "~1.3.4" + base64id "2.0.0" + cookie "0.3.1" + debug "~4.1.0" + engine.io-parser "~2.2.0" + ws "^7.1.2" + enhanced-resolve@4.3.0, enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0, enhanced-resolve@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz#3b806f3bfafc1ec7de69551ef93cca46c1704126" @@ -5686,6 +5814,18 @@ has-ansi@^2.0.0: dependencies: ansi-regex "^2.0.0" +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + dependencies: + isarray "2.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -6051,6 +6191,11 @@ indexes-of@^1.0.1: resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -6490,6 +6635,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -7908,6 +8058,23 @@ next-tick@~1.0.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= +ng-mocks@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/ng-mocks/-/ng-mocks-10.2.0.tgz#82bb97a8a72f8f857d893a951cf76edbe5814f1d" + integrity sha512-aEbNKbXYXNFRH1iedokACPBnZpiWrpG09Szkcc3RpcFa1gb46wwWBQUlgNxuc52MX/RZurVLpdn38nkKh3iSKA== + +ngx-socket-io@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ngx-socket-io/-/ngx-socket-io-3.2.0.tgz#afd9e6f8937e4b6d4cf2492e29cc8f01cc5d1b44" + integrity sha512-gxphw9yiHvnCavZutCRG6JCYNeguIon5Fxo4+8VCnMThtEy45vrZ3UmczR1ZH9vjJTkoaAS8YtqG8TdqDu5DkQ== + dependencies: + core-js "^3.0.0" + reflect-metadata "^0.1.10" + socket.io "^2.2.0" + socket.io-client "^2.2.0" + tslib "^1.9.0" + zone.js "^0.10.1" + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -8169,6 +8336,11 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1 resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= + object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -8563,6 +8735,30 @@ parse5@6.0.1, parse5@^6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== +parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + dependencies: + better-assert "~1.0.0" + +parseqs@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" + integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== + +parseuri@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + dependencies: + better-assert "~1.0.0" + +parseuri@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" + integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -9465,7 +9661,7 @@ readdirp@~3.4.0: dependencies: picomatch "^2.2.1" -reflect-metadata@^0.1.13, reflect-metadata@^0.1.2: +reflect-metadata@^0.1.10, reflect-metadata@^0.1.13, reflect-metadata@^0.1.2: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== @@ -10161,6 +10357,78 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socket.io-adapter@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" + integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== + +socket.io-client@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4" + integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~4.1.0" + engine.io-client "~3.4.0" + has-binary2 "~1.0.2" + has-cors "1.1.0" + indexof "0.0.1" + object-component "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-client@^2.2.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.1.tgz#91a4038ef4d03c19967bb3c646fec6e0eaa78cff" + integrity sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ== + dependencies: + backo2 "1.0.2" + component-bind "1.0.0" + component-emitter "~1.3.0" + debug "~3.1.0" + engine.io-client "~3.4.0" + has-binary2 "~1.0.2" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-parser@~3.3.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.1.tgz#f07d9c8cb3fb92633aa93e76d98fd3a334623199" + integrity sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ== + dependencies: + component-emitter "~1.3.0" + debug "~3.1.0" + isarray "2.0.1" + +socket.io-parser@~3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.1.tgz#b06af838302975837eab2dc980037da24054d64a" + integrity sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A== + dependencies: + component-emitter "1.2.1" + debug "~4.1.0" + isarray "2.0.1" + +socket.io@2.3.0, socket.io@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.3.0.tgz#cd762ed6a4faeca59bc1f3e243c0969311eb73fb" + integrity sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg== + dependencies: + debug "~4.1.0" + engine.io "~3.4.0" + has-binary2 "~1.0.2" + socket.io-adapter "~1.1.0" + socket.io-client "2.3.0" + socket.io-parser "~3.4.0" + sockjs-client@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" @@ -10880,6 +11148,11 @@ tmpl@1.0.x: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -11784,11 +12057,18 @@ ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.2.3: +ws@^7.1.2, ws@^7.2.3: version "7.3.1" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== +ws@~6.1.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" + integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== + dependencies: + async-limiter "~1.0.0" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -11799,6 +12079,11 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -11903,12 +12188,17 @@ yauzl@^2.10.0: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= + yn@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= -zone.js@^0.10.2: +zone.js@^0.10.1, zone.js@^0.10.2: version "0.10.3" resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg==