UNPKG

oss-client

Version:

Aliyun OSS(Object Storage Service) Node.js Client

891 lines 66.7 kB
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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