filestack-js
Version:
Official JavaScript library for Filestack
750 lines (749 loc) • 83.5 kB
JavaScript
"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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