oss-client
Version:
Aliyun OSS(Object Storage Service) Node.js Client
891 lines • 66.7 kB
JavaScript
import { createReadStream, createWriteStream } from 'node:fs';
import { strict as assert } from 'node:assert';
import querystring from 'node:querystring';
import fs from 'node:fs/promises';
import mime from 'mime';
import { isReadable, isWritable } from 'is-type-of';
import { OSSBaseClient, } from './OSSBaseClient.js';
import { checkBucketName, signatureForURL, encodeCallback, json2xml, timestamp, checkObjectTag, computeSignature, policyToJSONString, } from './util/index.js';
export class OSSObject extends OSSBaseClient {
#bucket;
#bucketEndpoint;
constructor(options) {
if (!options.cname) {
assert.ok(options.bucket, 'bucket required');
}
if (options.bucket) {
checkBucketName(options.bucket);
}
super(options);
if (options.cname) {
// ignore bucket on cname set to true
this.#bucket = '';
this.#bucketEndpoint = this.options.endpoint;
}
else {
this.#bucket = options.bucket ?? '';
const urlObject = new URL(this.options.endpoint);
urlObject.hostname = `${this.#bucket}.${urlObject.hostname}`;
this.#bucketEndpoint = urlObject.toString();
}
}
/** public methods */
/**
* AppendObject
* @see https://help.aliyun.com/zh/oss/developer-reference/appendobject
*/
async append(name, file, options) {
const position = options?.position ?? '0';
const result = await this.#sendPutRequest(name, {
...options,
subResource: {
append: '',
position: `${position}`,
},
}, file, 'POST');
return {
...result,
nextAppendPosition: result.res.headers['x-oss-next-append-position'],
};
}
/**
* put an object from String(file path)/Buffer/Readable
* @param {String} name the object key
* @param {Mixed} file String(file path)/Buffer/Readable
* @param {Object} options options
* {Object} options.callback The callback parameter is composed of a JSON string encoded in Base64
* {String} options.callback.url the OSS sends a callback request to this URL
* {String} options.callback.host The host header value for initiating callback requests
* {String} options.callback.body The value of the request body when a callback is initiated
* {String} options.callback.contentType The Content-Type of the callback requests initiated
* {Object} options.callback.customValue Custom parameters are a map of key-values, e.g:
* customValue = {
* key1: 'value1',
* key2: 'value2'
* }
* @returns {Object} result
*/
async put(name, file, options) {
if (typeof file === 'string' || isReadable(file) || Buffer.isBuffer(file)) {
return await this.#sendPutRequest(name, options ?? {}, file);
}
throw new TypeError('Must provide String/Buffer/ReadableStream for put.');
}
/**
* put an object from ReadableStream.
*/
async putStream(name, stream, options) {
return await this.#sendPutRequest(name, options ?? {}, stream);
}
async putMeta(name, meta, options) {
return await this.copy(name, name, {
meta,
...options,
});
}
/**
* GetBucket (ListObjects)
* @see https://help.aliyun.com/zh/oss/developer-reference/listobjects
*/
async list(query, options) {
// prefix, marker, max-keys, delimiter
const params = this.#objectRequestParams('GET', '', options);
if (query) {
params.query = query;
}
params.xmlResponse = true;
params.successStatuses = [200];
const { data, res } = await this.request(params);
let contents = data.Contents;
let objects = [];
if (contents) {
if (!Array.isArray(contents)) {
contents = [contents];
}
objects = contents.map(obj => ({
name: obj.Key,
url: this.#objectUrl(obj.Key),
lastModified: obj.LastModified,
etag: obj.ETag,
type: obj.Type,
size: Number(obj.Size),
storageClass: obj.StorageClass,
owner: {
id: obj.Owner.ID,
displayName: obj.Owner.DisplayName,
},
}));
}
let commonPrefixes = data.CommonPrefixes;
let prefixes = [];
if (commonPrefixes) {
if (!Array.isArray(commonPrefixes)) {
commonPrefixes = [commonPrefixes];
}
prefixes = commonPrefixes.map(item => item.Prefix);
}
return {
res,
objects,
prefixes: prefixes || [],
nextMarker: data.NextMarker || null,
isTruncated: data.IsTruncated === 'true',
};
}
/**
* ListObjectsV2(GetBucketV2)
* @see https://help.aliyun.com/zh/oss/developer-reference/listobjectsv2
*/
async listV2(query, options) {
const params = this.#objectRequestParams('GET', '', options);
params.query = {
'list-type': '2',
};
const continuationToken = query?.['continuation-token'] ?? query?.continuationToken;
if (continuationToken) {
// should set subResource to add sign string
params.subResource = {
'continuation-token': continuationToken,
};
}
if (query?.prefix) {
params.query.prefix = query.prefix;
}
if (query?.delimiter) {
params.query.delimiter = query.delimiter;
}
if (query?.['max-keys']) {
params.query['max-keys'] = `${query['max-keys']}`;
}
if (query?.['start-after']) {
params.query['start-after'] = query['start-after'];
}
if (query?.['encoding-type']) {
params.query['encoding-type'] = query['encoding-type'];
}
if (query?.['fetch-owner']) {
params.query['fetch-owner'] = 'true';
}
params.xmlResponse = true;
params.successStatuses = [200];
const { data, res } = await this.request(params);
let contents = data.Contents;
let objects = [];
if (contents) {
if (!Array.isArray(contents)) {
contents = [contents];
}
objects = contents.map(obj => ({
name: obj.Key,
url: this.#objectUrl(obj.Key),
lastModified: obj.LastModified,
etag: obj.ETag,
type: obj.Type,
size: Number(obj.Size),
storageClass: obj.StorageClass,
owner: obj.Owner
? {
id: obj.Owner.ID,
displayName: obj.Owner.DisplayName,
}
: undefined,
}));
}
let commonPrefixes = data.CommonPrefixes;
let prefixes = [];
if (commonPrefixes) {
if (!Array.isArray(commonPrefixes)) {
commonPrefixes = [commonPrefixes];
}
prefixes = commonPrefixes.map(item => item.Prefix);
}
return {
res,
objects,
prefixes,
isTruncated: data.IsTruncated === 'true',
keyCount: Number.parseInt(data.KeyCount),
continuationToken: data.ContinuationToken,
nextContinuationToken: data.NextContinuationToken,
};
}
async get(name, file, options) {
let writeStream;
let needDestroy = false;
if (isWritable(file)) {
writeStream = file;
}
else if (typeof file === 'string') {
writeStream = createWriteStream(file);
needDestroy = true;
}
else {
// get(name, options)
options = file;
}
options = this.#formatGetOptions(options);
let result;
try {
const params = this.#objectRequestParams('GET', name, options);
params.writeStream = writeStream;
params.successStatuses = [200, 206, 304];
result = await this.request(params);
if (needDestroy && writeStream) {
writeStream.destroy();
}
}
catch (err) {
if (needDestroy && writeStream) {
writeStream.destroy();
// should delete the exists file before throw error
await fs.rm(file, { force: true });
}
throw err;
}
return {
res: result.res,
content: result.data,
};
}
async getStream(name, options) {
options = this.#formatGetOptions(options);
const params = this.#objectRequestParams('GET', name, options);
params.streaming = true;
params.successStatuses = [200, 206, 304];
const { res } = await this.request(params);
return {
stream: res,
res,
};
}
/**
* PutObjectACL
* @see https://help.aliyun.com/zh/oss/developer-reference/putobjectacl
*/
async putACL(name, acl, options) {
options = options ?? {};
if (options.subres && !options.subResource) {
options.subResource = options.subres;
}
if (!options.subResource) {
options.subResource = {};
}
options.subResource.acl = '';
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
options.headers = options.headers ?? {};
options.headers['x-oss-object-acl'] = acl;
name = this.#objectName(name);
const params = this.#objectRequestParams('PUT', name, options);
params.successStatuses = [200];
const { res } = await this.request(params);
return {
res,
};
}
/**
* GetObjectACL
* @see https://help.aliyun.com/zh/oss/developer-reference/getobjectacl
*/
async getACL(name, options) {
options = options ?? {};
if (options.subres && !options.subResource) {
options.subResource = options.subres;
delete options.subres;
}
if (!options.subResource) {
options.subResource = {};
}
options.subResource.acl = '';
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
name = this.#objectName(name);
const params = this.#objectRequestParams('GET', name, options);
params.successStatuses = [200];
params.xmlResponse = true;
const { data, res } = await this.request(params);
return {
acl: data.AccessControlList.Grant,
owner: {
id: data.Owner.ID,
displayName: data.Owner.DisplayName,
},
res,
};
}
// /**
// * Restore Object
// * @param {String} name the object key
// * @param {Object} options {type : Archive or ColdArchive}
// * @return {{res}} result
// */
// proto.restore = async function restore(name, options = { type: 'Archive' }) {
// options = options || {};
// options.subres = Object.assign({ restore: '' }, options.subres);
// if (options.versionId) {
// options.subres.versionId = options.versionId;
// }
// const params = this._objectRequestParams('POST', name, options);
// if (options.type === 'ColdArchive') {
// const paramsXMLObj = {
// RestoreRequest: {
// Days: options.Days ? options.Days : 2,
// JobParameters: {
// Tier: options.JobParameters ? options.JobParameters : 'Standard',
// },
// },
// };
// params.content = obj2xml(paramsXMLObj, {
// headers: true,
// });
// params.mime = 'xml';
// }
// params.successStatuses = [ 202 ];
// const result = await this.request(params);
// return {
// res: result.res,
// };
// };
/**
* DeleteObject
* @see https://help.aliyun.com/zh/oss/developer-reference/deleteobject
*/
async delete(name, options) {
const requestOptions = {
timeout: options?.timeout,
subResource: {},
};
if (options?.versionId) {
requestOptions.subResource.versionId = options.versionId;
}
const params = this.#objectRequestParams('DELETE', name, requestOptions);
params.successStatuses = [204];
const { res } = await this.request(params);
return {
res,
status: res.status,
headers: res.headers,
size: res.size,
rt: res.rt,
};
}
/**
* DeleteMultipleObjects
* @see https://help.aliyun.com/zh/oss/developer-reference/deletemultipleobjects
*/
async deleteMulti(namesOrObjects, options) {
const objects = [];
assert.ok(namesOrObjects.length > 0, 'namesOrObjects is empty');
for (const nameOrObject of namesOrObjects) {
if (typeof nameOrObject === 'string') {
objects.push({ Key: this.#objectName(nameOrObject) });
}
else {
assert.ok(nameOrObject.key, 'key is empty');
objects.push({
Key: this.#objectName(nameOrObject.key),
VersionId: nameOrObject.versionId,
});
}
}
const xml = json2xml({
Delete: {
Quiet: !!options?.quiet,
Object: objects,
},
}, { headers: true });
const requestOptions = {
timeout: options?.timeout,
// ?delete
subResource: { delete: '' },
};
if (options?.versionId) {
requestOptions.subResource.versionId = options.versionId;
}
const params = this.#objectRequestParams('POST', '', requestOptions);
params.mime = 'xml';
params.content = Buffer.from(xml, 'utf8');
params.xmlResponse = true;
params.successStatuses = [200];
const { data, res } = await this.request(params);
// quiet will return null
let deleted = data?.Deleted || [];
if (deleted && !Array.isArray(deleted)) {
deleted = [deleted];
}
return {
res,
deleted,
};
}
/**
* HeadObject
* @see https://help.aliyun.com/zh/oss/developer-reference/headobject
*/
async head(name, options) {
options = options ?? {};
if (options.subres && !options.subResource) {
options.subResource = options.subres;
}
if (options.versionId) {
if (!options.subResource) {
options.subResource = {};
}
options.subResource.versionId = options.versionId;
}
const params = this.#objectRequestParams('HEAD', name, options);
params.successStatuses = [200, 304];
const { res } = await this.request(params);
const meta = {};
const result = {
meta,
res,
status: res.status,
};
for (const k in res.headers) {
if (k.startsWith('x-oss-meta-')) {
const key = k.slice(11);
meta[key] = res.headers[k];
}
}
return result;
}
/**
* GetObjectMeta
* @see https://help.aliyun.com/zh/oss/developer-reference/getobjectmeta
*/
async getObjectMeta(name, options) {
options = options ?? {};
name = this.#objectName(name);
if (options.subres && !options.subResource) {
options.subResource = options.subres;
}
if (!options.subResource) {
options.subResource = {};
}
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
options.subResource.objectMeta = '';
const params = this.#objectRequestParams('HEAD', name, options);
params.successStatuses = [200];
const { res } = await this.request(params);
return {
status: res.status,
res,
};
}
/**
* PutSymlink
* @see https://help.aliyun.com/zh/oss/developer-reference/putsymlink
*/
async putSymlink(name, targetName, options) {
options = options ?? {};
if (!options.subResource) {
options.subResource = {};
}
options.subResource.symlink = '';
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
options.headers = options.headers ?? {};
this.#convertMetaToHeaders(options.meta, options.headers);
targetName = this.escape(this.#objectName(targetName));
options.headers['x-oss-symlink-target'] = targetName;
if (options.storageClass) {
options.headers['x-oss-storage-class'] = options.storageClass;
}
name = this.#objectName(name);
const params = this.#objectRequestParams('PUT', name, options);
params.successStatuses = [200];
const { res } = await this.request(params);
return {
res,
};
}
/**
* GetSymlink
* @see https://help.aliyun.com/zh/oss/developer-reference/getsymlink
*/
async getSymlink(name, options) {
options = options ?? {};
if (!options.subResource) {
options.subResource = {};
}
options.subResource.symlink = '';
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
name = this.#objectName(name);
const params = this.#objectRequestParams('GET', name, options);
params.successStatuses = [200];
const { res } = await this.request(params);
const target = res.headers['x-oss-symlink-target'];
const meta = {};
for (const k in res.headers) {
if (k.startsWith('x-oss-meta-')) {
const key = k.slice(11);
meta[key] = res.headers[k];
}
}
return {
targetName: decodeURIComponent(target),
res,
meta,
};
}
/**
* PutObjectTagging
* @see https://help.aliyun.com/zh/oss/developer-reference/putobjecttagging
*/
async putObjectTagging(name, tag, options) {
checkObjectTag(tag);
options = options ?? {};
if (!options.subResource) {
options.subResource = {};
}
options.subResource.tagging = '';
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
name = this.#objectName(name);
const params = this.#objectRequestParams('PUT', name, options);
params.successStatuses = [200];
const tags = [];
for (const key in tag) {
tags.push({ Key: key, Value: tag[key] });
}
const paramXMLObj = {
Tagging: {
TagSet: {
Tag: tags,
},
},
};
params.mime = 'xml';
params.content = Buffer.from(json2xml(paramXMLObj));
const { res } = await this.request(params);
return {
res,
status: res.status,
};
}
/**
* GetObjectTagging
* @see https://help.aliyun.com/zh/oss/developer-reference/getobjecttagging
*/
async getObjectTagging(name, options) {
options = options ?? {};
if (!options.subResource) {
options.subResource = {};
}
options.subResource.tagging = '';
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
name = this.#objectName(name);
const params = this.#objectRequestParams('GET', name, options);
params.successStatuses = [200];
params.xmlResponse = true;
const { res, data } = await this.request(params);
// console.log(data.toString());
let tags = data.TagSet?.Tag;
if (tags && !Array.isArray(tags)) {
tags = [tags];
}
const tag = {};
if (tags) {
for (const item of tags) {
tag[item.Key] = item.Value;
}
}
return {
status: res.status,
res,
tag,
};
}
/**
* DeleteObjectTagging
* @see https://help.aliyun.com/zh/oss/developer-reference/deleteobjecttagging
*/
async deleteObjectTagging(name, options) {
options = options ?? {};
if (!options.subResource) {
options.subResource = {};
}
options.subResource.tagging = '';
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
name = this.#objectName(name);
const params = this.#objectRequestParams('DELETE', name, options);
params.successStatuses = [204];
const { res } = await this.request(params);
return {
status: res.status,
res,
};
}
/**
* signatureUrl URL签名
* @see https://help.aliyun.com/zh/oss/developer-reference/signed-urls
*/
signatureUrl(name, options) {
options = options ?? {};
name = this.#objectName(name);
options.method = options.method ?? 'GET';
const expires = options.expires ?? 1800;
const expiresTimestamp = timestamp() + expires;
const params = {
bucket: this.#bucket,
object: name,
};
const resource = this.getResource(params);
const signRes = signatureForURL(this.options.accessKeySecret, options, resource, expiresTimestamp);
const url = this.getRequestURL({
object: name,
subResource: {
OSSAccessKeyId: this.options.accessKeyId,
Expires: expiresTimestamp,
Signature: signRes.Signature,
...signRes.subResource,
},
});
return url;
}
async asyncSignatureUrl(name, options) {
return this.signatureUrl(name, options);
}
/**
* Get Object url by name
* @param {string} name - object name
* @param {string} [baseUrl] - If provide `baseUrl`, will use `baseUrl` instead the default `endpoint and bucket`.
* @returns {string} object url include bucket
*/
generateObjectUrl(name, baseUrl) {
const urlObject = new URL(baseUrl ?? this.getRequestEndpoint());
urlObject.pathname = this.escape(this.#objectName(name));
return urlObject.toString();
}
/**
* @alias generateObjectUrl
*/
getObjectUrl(name, baseUrl) {
return this.generateObjectUrl(name, baseUrl);
}
/**
* @param {object | string} policy specifies the validity of the fields in the request.
* @returns {object} params.OSSAccessKeyId
* params.Signature
* params.policy JSON text encoded with UTF-8 and Base64.
*/
calculatePostSignature(policy) {
if (typeof policy !== 'object' && typeof policy !== 'string') {
throw new TypeError('policy must be JSON string or Object');
}
const policyString = Buffer.from(policyToJSONString(policy), 'utf8').toString('base64');
const Signature = computeSignature(this.options.accessKeySecret, policyString);
return {
OSSAccessKeyId: this.options.accessKeyId,
Signature,
policy: policyString,
};
}
async copy(name, sourceName, sourceBucket, options) {
if (typeof sourceBucket === 'object') {
options = sourceBucket; // 兼容旧版本,旧版本第三个参数为options
sourceBucket = undefined;
}
options = options ?? {};
options.headers = options.headers ?? {};
let hasMetadata = !!options.meta;
const REPLACE_HEADERS = new Set([
'content-type',
'content-encoding',
'content-language',
'content-disposition',
'cache-control',
'expires',
]);
for (const key in options.headers) {
const lowerCaseKey = key.toLowerCase();
options.headers[`x-oss-copy-source-${lowerCaseKey}`] =
options.headers[key];
if (REPLACE_HEADERS.has(lowerCaseKey)) {
hasMetadata = true;
}
}
if (hasMetadata) {
options.headers['x-oss-metadata-directive'] = 'REPLACE';
}
this.#convertMetaToHeaders(options.meta, options.headers);
sourceName = this.#getCopySourceName(sourceName, sourceBucket);
if (options.versionId) {
sourceName = `${sourceName}?versionId=${options.versionId}`;
}
options.headers['x-oss-copy-source'] = sourceName;
const params = this.#objectRequestParams('PUT', name, options);
params.xmlResponse = true;
params.successStatuses = [200, 304];
const { data, res } = await this.request(params);
return {
data: data
? {
etag: data.ETag ?? '',
lastModified: data.LastModified ?? '',
}
: null,
res,
};
}
/**
* 另存为
* @see https://help.aliyun.com/zh/oss/user-guide/sys-or-saveas
*/
async processObjectSave(sourceObject, targetObject, process, targetBucket) {
targetObject = this.#objectName(targetObject);
const params = this.#objectRequestParams('POST', sourceObject, {
subResource: {
'x-oss-process': '',
},
});
const bucketParam = targetBucket
? `,b_${Buffer.from(targetBucket).toString('base64')}`
: '';
targetObject = Buffer.from(targetObject).toString('base64');
const content = {
'x-oss-process': `${process}|sys/saveas,o_${targetObject}${bucketParam}`,
};
params.content = Buffer.from(querystring.stringify(content));
params.successStatuses = [200];
const result = await this.request(params);
return {
res: result.res,
status: result.res.status,
};
}
/** protected methods */
getRequestEndpoint() {
return this.#bucketEndpoint;
}
/** private methods */
#getCopySourceName(sourceName, bucketName) {
if (typeof bucketName === 'string') {
sourceName = this.#objectName(sourceName);
// eslint-disable-next-line no-negated-condition
}
else if (sourceName[0] !== '/') {
bucketName = this.#bucket;
}
else {
bucketName = sourceName.replace(/\/(.+?)(\/.*)/, '$1');
sourceName = sourceName.replace(/(\/.+?\/)(.*)/, '$2');
}
sourceName = encodeURIComponent(sourceName);
if (bucketName) {
checkBucketName(bucketName);
sourceName = `/${bucketName}/${sourceName}`;
}
return sourceName;
}
async #sendPutRequest(name, options, fileOrBufferOrStream, method = 'PUT') {
options.headers = options.headers ?? {};
if (options.headers['Content-Type'] && !options.headers['content-type']) {
options.headers['content-type'] = options.headers['Content-Type'];
delete options.headers['Content-Type'];
}
name = this.#objectName(name);
this.#convertMetaToHeaders(options.meta, options.headers);
// don't override exists headers
if (options.callback && !options.headers['x-oss-callback']) {
const callbackOptions = encodeCallback(options.callback);
options.headers['x-oss-callback'] = callbackOptions.callback;
if (callbackOptions.callbackVar) {
options.headers['x-oss-callback-var'] = callbackOptions.callbackVar;
}
}
const params = this.#objectRequestParams(method, name, options);
if (typeof fileOrBufferOrStream === 'string') {
const stats = await fs.stat(fileOrBufferOrStream);
if (!stats.isFile()) {
throw new TypeError(`${fileOrBufferOrStream} is not file`);
}
if (!options.mime) {
const mimeFromFile = mime.getType(fileOrBufferOrStream);
if (mimeFromFile) {
options.mime = mimeFromFile;
}
}
params.stream = createReadStream(fileOrBufferOrStream);
}
else if (Buffer.isBuffer(fileOrBufferOrStream)) {
params.content = fileOrBufferOrStream;
}
else {
params.stream = fileOrBufferOrStream;
}
params.mime = options.mime;
params.successStatuses = [200];
const { res, data } = await this.request(params);
const putResult = {
name,
url: this.#objectUrl(name),
res,
data: {},
};
if (params.headers?.['x-oss-callback']) {
putResult.data = JSON.parse(data.toString());
}
return putResult;
}
#objectUrl(name) {
return this.getRequestURL({ object: name });
}
#formatGetOptions(options) {
options = options ?? {};
// 兼容老的 subres 参数
if (options.subres && !options.subResource) {
options.subResource = options.subres;
}
if (!options.subResource) {
options.subResource = {};
}
if (options.versionId) {
options.subResource.versionId = options.versionId;
}
if (options.process) {
options.subResource['x-oss-process'] = options.process;
}
return options;
}
/**
* generator request params
*/
#objectRequestParams(method, name, options) {
name = this.#objectName(name);
const params = {
object: name,
bucket: this.#bucket,
method,
headers: options?.headers,
subResource: options?.subResource,
timeout: options?.timeout,
};
return params;
}
#objectName(name) {
return name.replace(/^\/+/, '');
}
#convertMetaToHeaders(meta, headers) {
if (!meta) {
return;
}
for (const key in meta) {
headers[`x-oss-meta-${key}`] = `${meta[key]}`;
}
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT1NTT2JqZWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL09TU09iamVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUQsT0FBTyxFQUFFLE1BQU0sSUFBSSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDL0MsT0FBTyxXQUFXLE1BQU0sa0JBQWtCLENBQUM7QUFDM0MsT0FBTyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFbEMsT0FBTyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQ3hCLE9BQU8sRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBeUJwRCxPQUFPLEVBRUwsYUFBYSxHQUNkLE1BQU0sb0JBQW9CLENBQUM7QUE2QjVCLE9BQU8sRUFDTCxlQUFlLEVBQ2YsZUFBZSxFQUNmLGNBQWMsRUFDZCxRQUFRLEVBQ1IsU0FBUyxFQUNULGNBQWMsRUFDZCxnQkFBZ0IsRUFDaEIsa0JBQWtCLEdBQ25CLE1BQU0saUJBQWlCLENBQUM7QUF5QnpCLE1BQU0sT0FBTyxTQUFVLFNBQVEsYUFBYTtJQUMxQyxPQUFPLENBQVM7SUFDaEIsZUFBZSxDQUFTO0lBRXhCLFlBQVksT0FBbUM7UUFDN0MsSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNuQixNQUFNLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztRQUMvQyxDQUFDO1FBQ0QsSUFBSSxPQUFPLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDbkIsZUFBZSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNsQyxDQUFDO1FBQ0QsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7WUFDbEIscUNBQXFDO1lBQ3JDLElBQUks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