UNPKG

filestack-js

Version:

Official JavaScript library for Filestack

750 lines (749 loc) 83.5 kB
"use strict"; /* * Copyright (c) 2019 by Filestack. * Some rights reserved. * * Licensed under the Apache License, Version 2.0 (the 'License'); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an 'AS IS' BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var config_1 = require("./../../config"); var cloud_1 = require("./cloud"); var utils = tslib_1.__importStar(require("./../utils/index.node")); var nock_1 = tslib_1.__importDefault(require("nock")); var store_1 = require("../utils/store"); var testApiKey = 'API_KEY'; var testTokSession = 'TOK_SESSION'; var testTokArchiveId = 'ARCHIVE_ID'; var testCloudToken = 'testCloudToken'; var testSecurity = { policy: 'examplePolicy', signature: 'exampleSignature', }; var sessionURls = config_1.config.urls; var testSession = { apikey: testApiKey, urls: sessionURls, }; var scope = (0, nock_1.default)(sessionURls.cloudApiUrl); scope.defaultReplyHeaders({ 'access-control-allow-origin': function (req) { var _a; return (_a = req.getHeader('origin')) === null || _a === void 0 ? void 0 : _a.toString(); }, 'access-control-allow-methods': function (req) { var _a; return (_a = req.getHeader('access-control-request-method')) === null || _a === void 0 ? void 0 : _a.toString(); }, 'access-control-allow-headers': function (req) { var _a; return (_a = req.getHeader('access-control-request-headers')) === null || _a === void 0 ? void 0 : _a.toString(); }, 'content-type': 'application/json', }); var mockTokInit = jest .fn() .mockName('tokInit') .mockReturnValue('init'); var mockTokStart = jest .fn() .mockName('tokStart') .mockReturnValue('start'); var mockTokStop = jest .fn() .mockName('tokStop') .mockReturnValue('stop'); var mockMetadata = jest .fn() .mockName('metadata') .mockReturnValue('metadata'); var mockList = jest .fn() .mockName('list') .mockImplementation(function (data) { if (data && data.clouds.token) { return { token: testCloudToken }; } return data; }); var mockLogout = jest .fn() .mockName('logout') .mockImplementation(function (url, params) { if (params.clouds && params.clouds.token) { return { token: testCloudToken }; } return params; }); var mockStore = jest .fn() .mockName('store') .mockImplementation(function (params) { if (params && params.clouds && params.clouds.token) { return JSON.stringify({ token: testCloudToken }); } return JSON.stringify(params); }); describe('cloud', function () { beforeEach(function () { scope .persist() .options(/.*/) .reply(204, '', { 'access-control-allow-headers': 'filestack-source,filestack-trace-id,filestack-trace-span', 'access-control-allow-methods': '*', 'access-control-allow-origin': '*', }); scope.post('/auth/logout').reply(200, mockLogout); scope.post('/folder/list').reply(200, function (_, data) { return mockList(data); }); scope.post('/store/').reply(200, function (_, data) { return mockStore(data); }); scope.post('/metadata').reply(200, mockMetadata); scope.post(/\/recording\/(audio|video)\/init/).reply(200, mockTokInit); scope.post(/\/recording\/(audio|video)\/start/).reply(200, mockTokStart); scope.post(/\/recording\/(audio|video)\/stop/).reply(200, mockTokStop); }); afterEach(function () { nock_1.default.cleanAll(); jest.clearAllMocks(); localStorage.clear(); }); describe('cancelToken', function () { var testDomain = 'http://delay.filestack.test'; var scopeD; beforeEach(function () { scopeD = (0, nock_1.default)(testDomain); scopeD .post('/store/') .delay(4000) .reply(200); scopeD .post('/folder/list') .delay(4000) .reply(200); scopeD .persist() .options(/.*/) .reply(204, '', { 'access-control-allow-headers': 'filestack-source,filestack-trace-id,filestack-trace-span', 'access-control-allow-methods': '*', 'access-control-allow-origin': '*', }); }); afterEach(function () { nock_1.default.cleanAll(); jest.clearAllMocks(); localStorage.clear(); }); it('Should cancel store request', function (done) { var sessionClone = JSON.parse(JSON.stringify(testSession)); sessionClone.urls.cloudApiUrl = testDomain; var token = {}; new cloud_1.CloudClient(sessionClone) .store('google', 'test', { filename: '1', location: 'gcs' }, {}, token) .then(function () { done('Request not canceled'); }) .catch(function (err) { expect(err).toEqual(expect.any(Error)); done(); }); setTimeout(function () { // @ts-ignore token.cancel(); }, 500); }); it('Should cancel list request', function (done) { var sessionClone = JSON.parse(JSON.stringify(testSession)); sessionClone.urls.cloudApiUrl = testDomain; var token = {}; new cloud_1.CloudClient(sessionClone) .list('google', token) .then(function () { done('Request not canceled'); }) .catch(function (err) { expect(err).toEqual(expect.any(Error)); done(); }); setTimeout(function () { // @ts-ignore token.cancel(); }, 500); }); }); describe('facebook inapp browser', function () { it('should set token to sessionStore when inapp browser is detected', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var client, token, store; return tslib_1.__generator(this, function (_a) { jest.spyOn(utils, 'isFacebook').mockReturnValue(true); client = new cloud_1.CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); token = 'test'; client.token = token; store = new store_1.Store(); expect(store.getItem(cloud_1.PICKER_KEY, store_1.STORE_TYPE.SESSION)).toEqual(token); store.setItem(cloud_1.PICKER_KEY, undefined, store_1.STORE_TYPE.SESSION); return [2 /*return*/]; }); }); }); it('should get token from sessionStore when inapp browser is detected', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var client, store, token; return tslib_1.__generator(this, function (_a) { jest.spyOn(utils, 'isFacebook').mockReturnValue(true); client = new cloud_1.CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); store = new store_1.Store(); token = 'test'; store.setItem(cloud_1.PICKER_KEY, token, store_1.STORE_TYPE.SESSION); expect(client.token).toEqual(token); store.setItem(cloud_1.PICKER_KEY, undefined, store_1.STORE_TYPE.SESSION); return [2 /*return*/]; }); }); }); it('should send appurl in list action', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, client, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; client = new cloud_1.CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(tslib_1.__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', appurl: 'http://localhost/?fs-tab=init', clouds: clouds, token: null, }); return [2 /*return*/]; } }); }); }); it('should send pass mimetypes to backend as string', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, client, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; client = new cloud_1.CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(tslib_1.__assign({}, clouds), {}, 'image/*')]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', appurl: 'http://localhost/?fs-tab=init', clouds: clouds, token: null, accept: ['image/*'], }); return [2 /*return*/]; } }); }); }); it('should send pass mimetypes to backend as array', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, client, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; client = new cloud_1.CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(tslib_1.__assign({}, clouds), {}, ['image/*'])]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', appurl: 'http://localhost/?fs-tab=init', clouds: clouds, token: null, accept: ['image/*'], }); return [2 /*return*/]; } }); }); }); }); it('should not send app url if urlsearch params is undefined', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, before, client, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; before = window.URLSearchParams; window.URLSearchParams = undefined; client = new cloud_1.CloudClient(Object.assign({}, testSession, { prefetch: { settings: { inapp_browser: true, }, pickerOptions: {}, }, })); return [4 /*yield*/, client.list(tslib_1.__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: clouds, token: null, }); window.URLSearchParams = before; return [2 /*return*/]; } }); }); }); describe('list', function () { it('should make correct list request', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; return [4 /*yield*/, new cloud_1.CloudClient(testSession).list(tslib_1.__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: clouds, }); return [2 /*return*/]; } }); }); }); it('should make correct list request with session cache', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; localStorage.setItem(cloud_1.PICKER_KEY, testCloudToken); return [4 /*yield*/, new cloud_1.CloudClient(testSession, { sessionCache: true, }).list(tslib_1.__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', token: testCloudToken, clouds: clouds, }); return [2 /*return*/]; } }); }); }); it('should set token on api token response', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { token: true }; return [4 /*yield*/, new cloud_1.CloudClient(testSession).list(tslib_1.__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual({ token: testCloudToken }); return [2 /*return*/]; } }); }); }); it('should cache session token to local storage', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { token: true }; return [4 /*yield*/, new cloud_1.CloudClient(testSession, { sessionCache: true }).list(tslib_1.__assign({}, clouds))]; case 1: res = _a.sent(); expect(localStorage.setItem).toHaveBeenCalledWith(cloud_1.PICKER_KEY, testCloudToken); expect(res).toEqual({ token: testCloudToken }); return [2 /*return*/]; } }); }); }); it('should make correct list request with security', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var clouds, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: clouds = { test: true }; return [4 /*yield*/, new cloud_1.CloudClient(tslib_1.__assign(tslib_1.__assign({}, testSession), testSecurity)).list(tslib_1.__assign({}, clouds))]; case 1: res = _a.sent(); expect(res).toEqual(tslib_1.__assign({ apikey: testApiKey, flow: 'web', clouds: clouds }, testSecurity)); return [2 /*return*/]; } }); }); }); }); describe('store', function () { it('should make correct basic request', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).store('google', 'test', { filename: '1' })]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: { google: { path: 'test', store: { filename: '1', location: 's3', }, }, }, }); return [2 /*return*/]; } }); }); }); it('should respect store location param', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).store('google', 'test', { filename: '1', location: 'gcs' })]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: { google: { path: 'test', store: { filename: '1', location: 'gcs', }, }, }, }); return [2 /*return*/]; } }); }); }); it('should respect store upload tags', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var uploadTags, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: uploadTags = { testTag: 'testTag' }; return [4 /*yield*/, new cloud_1.CloudClient(testSession).store('google', 'test', {}, null, null, uploadTags)]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', upload_tags: uploadTags, clouds: { google: { path: 'test', store: { location: 's3', }, }, }, }); return [2 /*return*/]; } }); }); }); it('should make correct basic with security', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res, excepted; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(tslib_1.__assign(tslib_1.__assign({}, testSession), testSecurity)).store('token', 'test', { filename: '1' })]; case 1: res = _a.sent(); excepted = tslib_1.__assign(tslib_1.__assign({}, testSecurity), { apikey: testApiKey, flow: 'web', clouds: { token: { path: 'test', store: { filename: '1', location: 's3', }, }, } }); expect(mockStore).toHaveBeenCalledWith(excepted); expect(res).toEqual(testCloudToken); return [2 /*return*/]; } }); }); }); it('should handle custom source', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var customSource, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: customSource = { customSourcePath: 'cs_path', customSourceContainer: 'cs_container', }; return [4 /*yield*/, new cloud_1.CloudClient(testSession).store('customsource', 'test', { filename: '1' }, customSource)]; case 1: res = _a.sent(); expect(res).toEqual({ apikey: testApiKey, flow: 'web', clouds: { customsource: tslib_1.__assign(tslib_1.__assign({}, customSource), { path: 'test', store: { filename: '1', location: 's3', } }), }, }); return [2 /*return*/]; } }); }); }); }); describe('logout', function () { it('should make correct request to logout', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: _a = expect; return [4 /*yield*/, new cloud_1.CloudClient(testSession).logout()]; case 1: _a.apply(void 0, [_b.sent()]).toEqual({ apikey: 'API_KEY', flow: 'web' }); return [2 /*return*/]; } }); }); }); it('should make correct request to logout with provided cloud', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: _a = expect; return [4 /*yield*/, new cloud_1.CloudClient(testSession).logout('google')]; case 1: _a.apply(void 0, [_b.sent()]).toEqual({ apikey: 'API_KEY', flow: 'web', clouds: { google: {} } }); return [2 /*return*/]; } }); }); }); it('should make correct request to logout and return correct response when cloud name is returned', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: _a = expect; return [4 /*yield*/, new cloud_1.CloudClient(testSession).logout('token')]; case 1: _a.apply(void 0, [_b.sent()]).toEqual('testCloudToken'); return [2 /*return*/]; } }); }); }); it('should make correct request to logout and clean session cache ', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: localStorage.setItem(cloud_1.PICKER_KEY, testCloudToken); return [4 /*yield*/, new cloud_1.CloudClient(testSession, { sessionCache: true }).logout()]; case 1: res = _a.sent(); expect(localStorage.removeItem).toHaveBeenCalledWith(cloud_1.PICKER_KEY); expect(res).toEqual({ apikey: 'API_KEY', flow: 'web', token: testCloudToken }); return [2 /*return*/]; } }); }); }); }); describe('metadata', function () { it('should make correct request', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var testUrl, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: testUrl = 'http://test.com'; return [4 /*yield*/, new cloud_1.CloudClient(testSession).metadata(testUrl)]; case 1: res = _a.sent(); expect(mockMetadata).toHaveBeenCalledWith(expect.any(String), { apikey: testApiKey, url: testUrl, }); expect(res).toEqual('metadata'); return [2 /*return*/]; } }); }); }); it('should make correct request with security', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var testUrl, res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: testUrl = 'http://test.com'; return [4 /*yield*/, new cloud_1.CloudClient(tslib_1.__assign(tslib_1.__assign({}, testSession), testSecurity)).metadata(testUrl)]; case 1: res = _a.sent(); expect(mockMetadata).toHaveBeenCalledWith(expect.any(String), tslib_1.__assign({ apikey: testApiKey, url: testUrl }, testSecurity)); expect(res).toEqual('metadata'); return [2 /*return*/]; } }); }); }); }); describe('OpenTok', function () { describe('tokInit', function () { it('should make correct request to api (audio)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).tokInit('audio')]; case 1: res = _a.sent(); expect(mockTokInit).toHaveBeenCalledWith(expect.any(String), ''); expect(res).toEqual('init'); return [2 /*return*/]; } }); }); }); it('should make correct request to api (video)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).tokInit('audio')]; case 1: res = _a.sent(); expect(mockTokInit).toHaveBeenCalledWith(expect.any(String), ''); expect(res).toEqual('init'); return [2 /*return*/]; } }); }); }); it('should throw on wrong type', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { expect(function () { new cloud_1.CloudClient(testSession) .tokInit('videoa') .then(function () { console.log('init'); }) .catch(function () { console.log('err'); }); }).toThrowError(); return [2 /*return*/]; }); }); }); }); describe('tokStart', function () { it('should make correct request to api (audio)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).tokStart('audio', 'key', testTokSession)]; case 1: res = _a.sent(); expect(mockTokStart).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession }); expect(res).toEqual('start'); return [2 /*return*/]; } }); }); }); it('should make correct request to api (video)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).tokStart('video', 'key', testTokSession)]; case 1: res = _a.sent(); expect(mockTokStart).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession }); expect(res).toEqual('start'); return [2 /*return*/]; } }); }); }); it('should throw on wrong type', function () { expect(function () { return new cloud_1.CloudClient(testSession).tokStart('videoa', 'key', testTokSession); }).toThrowError(); }); }); describe('tokStop', function () { it('should make correct request to api (audio)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).tokStop('audio', 'key', testTokSession, testTokArchiveId)]; case 1: res = _a.sent(); expect(mockTokStop).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession, archive_id: testTokArchiveId, }); expect(res).toEqual('stop'); return [2 /*return*/]; } }); }); }); it('should make correct request to api (video)', function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () { var res; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, new cloud_1.CloudClient(testSession).tokStop('video', 'key', testTokSession, testTokArchiveId)]; case 1: res = _a.sent(); expect(mockTokStop).toHaveBeenCalledWith(expect.any(String), { apikey: 'key', session_id: testTokSession, archive_id: testTokArchiveId, }); expect(res).toEqual('stop'); return [2 /*return*/]; } }); }); }); it('should throw on wrong type', function () { expect(function () { return new cloud_1.CloudClient(testSession).tokStop('videoa', 'key', testTokSession, testTokArchiveId); }).toThrowError(); }); }); }); }); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9saWIvYXBpL2Nsb3VkLnNwZWMuYnJvd3Nlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7Ozs7Ozs7Ozs7Ozs7OztHQWVHOzs7QUFFSCx5Q0FBd0M7QUFDeEMsaUNBQWtEO0FBQ2xELG1FQUErQztBQUMvQyxzREFBd0I7QUFDeEIsd0NBQW1EO0FBRW5ELElBQU0sVUFBVSxHQUFHLFNBQVMsQ0FBQztBQUM3QixJQUFNLGNBQWMsR0FBRyxhQUFhLENBQUM7QUFDckMsSUFBTSxnQkFBZ0IsR0FBRyxZQUFZLENBQUM7QUFFdEMsSUFBTSxjQUFjLEdBQUcsZ0JBQWdCLENBQUM7QUFFeEMsSUFBTSxZQUFZLEdBQUc7SUFDbkIsTUFBTSxFQUFFLGVBQWU7SUFDdkIsU0FBUyxFQUFFLGtCQUFrQjtDQUM5QixDQUFDO0FBRUYsSUFBTSxXQUFXLEdBQUcsZUFBTSxDQUFDLElBQUksQ0FBQztBQUNoQyxJQUFNLFdBQVcsR0FBRztJQUNsQixNQUFNLEVBQUUsVUFBVTtJQUNsQixJQUFJLEVBQUUsV0FBVztDQUNsQixDQUFDO0FBRUYsSUFBSSxLQUFLLEdBQUcsSUFBQSxjQUFJLEVBQUMsV0FBVyxDQUFDLFdBQVcsQ0FBQyxDQUFDO0FBRTFDLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQztJQUN4Qiw2QkFBNkIsRUFBRSxVQUFVLEdBQUcsWUFBSSxPQUFPLE1BQUEsR0FBRyxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsMENBQUUsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzdGLDhCQUE4QixFQUFFLFVBQVUsR0FBRyxZQUFJLE9BQU8sTUFBQSxHQUFHLENBQUMsU0FBUyxDQUFDLCtCQUErQixDQUFDLDBDQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQztJQUNySCw4QkFBOEIsRUFBRSxVQUFVLEdBQUcsWUFBSSxPQUFPLE1BQUEsR0FBRyxDQUFDLFNBQVMsQ0FBQyxnQ0FBZ0MsQ0FBQywwQ0FBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENB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