diff --git a/src/server/package.json b/src/server/package.json index 029943484..8cc08cae6 100644 --- a/src/server/package.json +++ b/src/server/package.json @@ -58,6 +58,7 @@ "sentiment": "^5.0.2", "sequelize": "^6.29.0", "sequelize-typescript": "^2.1.5", + "sharp": "^0.32.6", "swagger-ui-express": "^4.6.2", "tsoa": "^5.1.1", "user-agents": "^1.0.1307", diff --git a/src/server/src/api/v1/schema/user/Membership.model.ts b/src/server/src/api/v1/schema/user/Membership.model.ts deleted file mode 100644 index 11e934dc9..000000000 --- a/src/server/src/api/v1/schema/user/Membership.model.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { - Column, - DataType, - Table, -} from 'sequelize-typescript'; - -import { MembershipAttributes, MembershipCreationAttributes } from './Membership.types'; -import { BaseModel } from '../base'; - -@Table({ - modelName: 'membership', - paranoid: true, - timestamps: true, -}) -export class Membership - extends BaseModel - implements MembershipAttributes { - - @Column({ - allowNull: false, - type: DataType.NUMBER, - }) - declare userId: number; - - @Column({ - allowNull: false, - type: DataType.DATE, - }) - declare renewsOn: Date; - - @Column({ - allowNull: false, - type: DataType.NUMBER, - }) - declare tier: number; - - @Column({ - allowNull: false, - type: DataType.STRING, - }) - declare platform: string; - - @Column({ - allowNull: false, - type: DataType.STRING, - }) - declare platformUUID: string; - -} diff --git a/src/server/src/api/v1/schema/user/Membership.types.ts b/src/server/src/api/v1/schema/user/Membership.types.ts deleted file mode 100644 index 121cd698c..000000000 --- a/src/server/src/api/v1/schema/user/Membership.types.ts +++ /dev/null @@ -1,18 +0,0 @@ - -import { DatedAttributes } from '../types'; - -export type MembershipAttributes = DatedAttributes & { - userId: number; - renewsOn: Date; - tier: number; - platform: string; - platformUUID: string; -}; - -export type MembershipCreationAttributes = { - userId: number; - renewsOn: Date; - tier: number; - platform: string; - platformUUID: string; -}; \ No newline at end of file diff --git a/src/server/src/services/aws/s3/S3Service.ts b/src/server/src/services/aws/s3/S3Service.ts index 537e2d079..c3bf42753 100644 --- a/src/server/src/services/aws/s3/S3Service.ts +++ b/src/server/src/services/aws/s3/S3Service.ts @@ -3,10 +3,16 @@ import p from 'path'; import { Readable } from 'stream'; import { + DeleteObjectCommand, + DeleteObjectCommandInput, GetObjectCommand, GetObjectCommandInput, + ListObjectsV2Command, + ListObjectsV2CommandInput, PutObjectCommand, PutObjectCommandInput, + RestoreObjectCommand, + RestoreObjectCommandInput, S3Client, } from '@aws-sdk/client-s3'; import axios from 'axios'; @@ -28,6 +34,11 @@ export type GetOptions = Omit & { Provider?: string; }; +export type ListOptions = Omit & { + Bucket?: string; + Provider?: string; +}; + export type PutOptions = Omit & { Bucket?: string; Body?: string; @@ -40,6 +51,17 @@ export type PutOptions = Omit & { + Bucket?: string; + Provider?: string; + Folder?: string; +}; + +export type RestoreOptions = Omit & { + Bucket?: string; + Provider?: string; +}; + export class S3Service extends BaseService { public static s3Client = new S3Client({ @@ -104,6 +126,7 @@ export class S3Service extends BaseService { if (!filepath) { filepath = `/tmp/${filename}`; } + console.log('Downloaded', filepath); return new Promise((resolve, reject) => { response.data.pipe(fs.createWriteStream(filepath)) .on('error', reject) @@ -124,6 +147,7 @@ export class S3Service extends BaseService { throw new Error('Malformed key'); } const response = await this.s3Client.send(new GetObjectCommand(params)); + filepath = `${filepath}.${mime.extension(response.ContentType) || mime.extension(mime.lookup(response.ContentType) || '')}`; const stream = response.Body as Readable; return new Promise((resolve, reject) => { stream.pipe(fs.createWriteStream(filepath)) @@ -131,6 +155,30 @@ export class S3Service extends BaseService { .once('close', () => resolve(filepath)); }); } + + public static async listObjects(options: ListOptions = {}) { + const params = { + ...options, + Bucket: options.Bucket || process.env.S3_BUCKET, + MaxKeys: options.MaxKeys, + Provider: options.Provider || process.env.S3_PROVIDER || 'nyc3.digitaloceanspaces.com', + }; + if (!params.Bucket) { + throw new Error('Malformed bucket'); + } + let isTruncated = true; + const items: string[] = []; + const command = new ListObjectsV2Command(params); + while (isTruncated) { + const { + Contents, IsTruncated, NextContinuationToken, + } = await this.s3Client.send(command); + items.push(...Contents.map((c) => c.Key)); + isTruncated = IsTruncated; + command.input.ContinuationToken = NextContinuationToken; + } + return items; + } public static async putObject(options: PutOptions) { const params = { @@ -159,6 +207,30 @@ export class S3Service extends BaseService { }; } + public static async deleteObject(options: DeleteOptions) { + const params = { + ...options, + Bucket: options.Bucket || process.env.S3_BUCKET, + Key: options.Folder ? [options.Folder, options.Key].join('/') : options.Key, + Provider: options.Provider || process.env.S3_PROVIDER || 'nyc3.digitaloceanspaces.com', + }; + const data = await this.s3Client.send(new DeleteObjectCommand(params)); + return data; + } + + public static async restoreObject(options: RestoreOptions) { + const params = { + ...options, + Bucket: options.Bucket || process.env.S3_BUCKET, + Provider: options.Provider || process.env.S3_PROVIDER || 'nyc3.digitaloceanspaces.com', + }; + if (!params.Bucket) { + throw new Error('Malformed bucket'); + } + const data = await this.s3Client.send(new RestoreObjectCommand(params)); + return data; + } + public static async mirror(url: string, options: PutOptions) { try { const file = await this.download(url, { diff --git a/src/server/src/worker/MediaWorker.ts b/src/server/src/worker/MediaWorker.ts new file mode 100644 index 000000000..61013712b --- /dev/null +++ b/src/server/src/worker/MediaWorker.ts @@ -0,0 +1,137 @@ +import fs from 'fs'; + +import sharp from 'sharp'; + +import { SummaryMedia } from '../api/v1/schema/models'; +import { SummaryMediaAttributes } from '../api/v1/schema/types'; +import { DBService, S3Service } from '../services'; + +export async function main() { + await DBService.prepare(); + doWork(); +} + +class Size { + + name: string; + value: number; + + static xs = new Size('xs', 60); + static sm = new Size('sm', 120); + static md = new Size('md', 240); + static lg = new Size('lg', 360); + static xl = new Size('xl', 480); + static xxl = new Size('xxl', 720); + static xxxl = new Size('xxxl', 1920); + + constructor(name: string, value: number) { + this.name = name; + this.value = value; + } + +} + +type DownsampleOptions = Pick & { + sizes?: Size[]; +}; + +async function downsampleImage({ + key, + parentId, + path, + sizes = [Size.xs, Size.sm, Size.md, Size.lg], +}: DownsampleOptions, folder: string) { + // eslint-disable-next-line no-async-promise-executor + return new Promise(async (resolve, reject) => { + const file = await S3Service.getObject({ Key: path }); + if (!file) { + reject('Missing file'); + return; + } + const results: SummaryMedia[] = []; + for (const [index, size] of sizes.entries()) { + const subkey = `${key}@${size.name}`; + const media = await SummaryMedia.findOne({ + where: { + key: subkey, + parentId, + }, + }); + if (media) { + console.log('media already exists'); + results.push(media); + if (index + 1 === sizes.length) { + resolve(results); + return; + } + continue; + } + const target = file.replace(/(\.\w+)$/, (_, $1) => `@${size.name}${$1}`); + sharp(file) + .resize(size.value) + .jpeg() + .toFile(target, async (err) => { + if (err) { + reject(err); + return; + } + const response = await S3Service.putObject({ + ACL: 'public-read', + Accept: 'image', + File: target, + Folder: folder, + }); + const media = await SummaryMedia.create({ + key: subkey, + parentId, + path: response.key, + type: 'image', + url: response.url, + }); + try { + fs.unlinkSync(file); + } catch (e) { + console.error(e); + } + try { + fs.unlinkSync(target); + } catch (e) { + console.error(e); + } + results.push(media); + if (index + 1 === sizes.length) { + resolve(results); + } + }); + } + }); +} + +export async function doWork() { + console.log('fetching objects'); + try { + const items = await S3Service.listObjects(); + console.log(items.length); + for (const item of items) { + if (/^img\/s/.test(item)) { + const media = await SummaryMedia.findOne({ where: { path: item } }); + if (!media) { + console.log('Unlinking dangling media', item); + await S3Service.deleteObject({ Key: item }); + } + if (!/@(?:xs|sm|md|lg|x+l)\.\w+$/.test(item)) { + console.log('Downsampling', item); + const folders = item.split('/'); + folders.pop(); + await downsampleImage(media, folders.join('/')); + } + } + } + } catch (e) { + console.error(e); + } finally { + setTimeout(doWork, 3_000); + } +} + +main(); \ No newline at end of file diff --git a/src/server/src/worker/index.ts b/src/server/src/worker/index.ts index c05767ee8..1f5a6c925 100644 --- a/src/server/src/worker/index.ts +++ b/src/server/src/worker/index.ts @@ -6,6 +6,9 @@ export async function main() { case 'topics': await import('./TopicWorker'); break; + case 'media': + await import('./MediaWorker'); + break; case 'notifs': await import('./NotificationsWorker'); break; diff --git a/src/server/tests/s3.test.ts b/src/server/tests/s3.test.ts index 3cf7b8090..50cc6b002 100644 --- a/src/server/tests/s3.test.ts +++ b/src/server/tests/s3.test.ts @@ -45,5 +45,11 @@ describe('uploads to the s3 bucket', () => { console.log(response); expect(response).toBeDefined(); }); + + test('list files', async () => { + const response = await S3Service.listObjects(); + console.log(response); + expect(response).toBeDefined(); + }); }); \ No newline at end of file diff --git a/src/server/yarn.lock b/src/server/yarn.lock index 8a51cfe23..23f61c5a9 100644 --- a/src/server/yarn.lock +++ b/src/server/yarn.lock @@ -3008,6 +3008,11 @@ axios@^1.3.4: form-data "^4.0.0" proxy-from-env "^1.1.0" +b4a@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" + integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== + babel-jest@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5" @@ -3648,7 +3653,7 @@ color-name@^1.0.0, color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.6.0: +color-string@^1.6.0, color-string@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== @@ -3664,6 +3669,14 @@ color@^3.1.3: color-convert "^1.9.3" color-string "^1.6.0" +color@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" + colors@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -3928,6 +3941,11 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3993,6 +4011,11 @@ detect-indent@~6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.0.0.tgz#0abd0f549f69fc6659a254fe96786186b6f528fd" integrity sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA== +detect-libc@^2.0.0, detect-libc@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d" + integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw== + detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -4544,6 +4567,11 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expand-template@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" + integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== + expect@^29.0.0, expect@^29.5.0: version "29.5.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7" @@ -4675,6 +4703,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-fifo@^1.1.0, fast-fifo@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" + integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== + fast-glob@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" @@ -5045,6 +5078,11 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +github-from-package@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" + integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw== + glob-parent@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" @@ -5597,6 +5635,11 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + internal-slot@^1.0.4, internal-slot@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -6932,7 +6975,7 @@ minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -6965,7 +7008,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp-classic@^0.5.2: +mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== @@ -7102,6 +7145,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +napi-build-utils@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" + integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -7130,11 +7178,23 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-abi@^3.3.0: + version "3.50.0" + resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.50.0.tgz#bbee6943c8812d20e241539854d7b8003404d917" + integrity sha512-2Gxu7Eq7vnBIRfYSmqPruEllMM14FjOQFJSoqdGWthVn+tmwEXzmdPpya6cvvwf0uZA3F5N1fMFr9mijZBplFA== + dependencies: + semver "^7.3.5" + node-addon-api@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -7641,6 +7701,24 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +prebuild-install@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" + integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -7863,6 +7941,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== +queue-tick@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142" + integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -7900,6 +7983,16 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" @@ -8173,6 +8266,13 @@ semver@^7.1.2: dependencies: lru-cache "^6.0.0" +semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + semver@~7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" @@ -8294,6 +8394,20 @@ sha.js@^2.4.0, sha.js@^2.4.8: inherits "^2.0.1" safe-buffer "^5.0.1" +sharp@^0.32.6: + version "0.32.6" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.32.6.tgz#6ad30c0b7cd910df65d5f355f774aa4fce45732a" + integrity sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w== + dependencies: + color "^4.2.3" + detect-libc "^2.0.2" + node-addon-api "^6.1.0" + prebuild-install "^7.1.1" + semver "^7.5.4" + simple-get "^4.0.1" + tar-fs "^3.0.4" + tunnel-agent "^0.6.0" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -8334,6 +8448,15 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" +simple-get@^4.0.0, simple-get@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543" + integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA== + dependencies: + decompress-response "^6.0.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-swizzle@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" @@ -8520,6 +8643,14 @@ streamroller@^3.1.3: debug "^4.3.4" fs-extra "^8.1.0" +streamx@^2.15.0: + version "2.15.1" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.1.tgz#396ad286d8bc3eeef8f5cea3f029e81237c024c6" + integrity sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA== + dependencies: + fast-fifo "^1.1.0" + queue-tick "^1.0.1" + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -8610,6 +8741,11 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + strnum@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" @@ -8675,7 +8811,7 @@ swarm-js@^0.1.40: tar "^4.0.2" xhr-request "^1.0.1" -tar-fs@2.1.1: +tar-fs@2.1.1, tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== @@ -8685,6 +8821,15 @@ tar-fs@2.1.1: pump "^3.0.0" tar-stream "^2.1.4" +tar-fs@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" + integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== + dependencies: + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^3.1.5" + tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -8696,6 +8841,15 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" +tar-stream@^3.1.5: + version "3.1.6" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.6.tgz#6520607b55a06f4a2e2e04db360ba7d338cc5bab" + integrity sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg== + dependencies: + b4a "^1.6.4" + fast-fifo "^1.2.0" + streamx "^2.15.0" + tar@^4.0.2: version "4.4.19" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3"