diff --git a/apps/server/package.json b/apps/server/package.json index b8c2cdb..566bfb2 100755 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -32,8 +32,6 @@ "@nice/common": "workspace:*", "@nice/tus": "workspace:*", "@trpc/server": "11.0.0-rc.456", - "@tus/file-store": "^1.5.1", - "@tus/s3-store": "^1.6.2", "argon2": "^0.41.1", "axios": "^1.7.2", "bullmq": "^5.12.0", @@ -49,9 +47,13 @@ "mime-types": "^2.1.35", "minio": "^8.0.1", "mitt": "^3.0.1", + "nanoid": "^5.0.9", + "nanoid-cjs": "^0.0.7", + "pinyin-pro": "^3.26.0", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "sharp": "^0.33.5", + "slugify": "^1.6.6", "socket.io": "^4.7.5", "superjson-cjs": "^2.2.3", "transliteration": "^2.3.5", diff --git a/apps/server/src/auth/auth.controller.ts b/apps/server/src/auth/auth.controller.ts index e35c9a5..e396a51 100755 --- a/apps/server/src/auth/auth.controller.ts +++ b/apps/server/src/auth/auth.controller.ts @@ -18,7 +18,7 @@ export class AuthController { @Headers('host') host: string, @Headers('authorization') authorization: string, ) { - + try { const fileRequest = { originalUri, @@ -28,6 +28,7 @@ export class AuthController { host, authorization }; + const authResult = await this.authService.validateFileRequest(fileRequest); if (!authResult.isValid) { // 使用枚举类型进行错误处理 diff --git a/apps/server/src/auth/auth.module.ts b/apps/server/src/auth/auth.module.ts index 7d119d7..ade8298 100755 --- a/apps/server/src/auth/auth.module.ts +++ b/apps/server/src/auth/auth.module.ts @@ -6,10 +6,13 @@ import { TrpcService } from '@server/trpc/trpc.service'; import { DepartmentService } from '@server/models/department/department.service'; import { SessionService } from './session.service'; import { RoleMapModule } from '@server/models/rbac/rbac.module'; - @Module({ imports: [StaffModule, RoleMapModule], - providers: [AuthService, TrpcService, DepartmentService, SessionService], + providers: [ + AuthService, + TrpcService, + DepartmentService, + SessionService], exports: [AuthService], controllers: [AuthController], }) diff --git a/apps/server/src/auth/auth.service.ts b/apps/server/src/auth/auth.service.ts index cd793ef..82a65b9 100755 --- a/apps/server/src/auth/auth.service.ts +++ b/apps/server/src/auth/auth.service.ts @@ -20,30 +20,35 @@ import { SessionInfo, SessionService } from './session.service'; import { tokenConfig } from './config'; import { z } from 'zod'; import { FileAuthResult, FileRequest, FileValidationErrorType } from './types'; -import { extractFilePathFromUri } from '@server/utils/file'; +import { TusService } from '@server/upload/tus.service'; +import { extractFileIdFromNginxUrl } from '@server/upload/utils'; @Injectable() export class AuthService { private logger = new Logger(AuthService.name) constructor( private readonly staffService: StaffService, private readonly jwtService: JwtService, - private readonly sessionService: SessionService, - ) { } + private readonly sessionService: SessionService + ) { + } async validateFileRequest(params: FileRequest): Promise { try { // 基础参数验证 if (!params?.originalUri) { return { isValid: false, error: FileValidationErrorType.INVALID_URI }; } - const fileId = extractFilePathFromUri(params.originalUri); + const fileId = extractFileIdFromNginxUrl(params.originalUri); + console.log(params.originalUri, fileId) const resource = await db.resource.findFirst({ where: { fileId } }); + // 资源验证 if (!resource) { return { isValid: false, error: FileValidationErrorType.RESOURCE_NOT_FOUND }; } // 处理公开资源 if (resource.isPublic) { + return { isValid: true, resourceType: resource.type || 'unknown' @@ -58,6 +63,7 @@ export class AuthService { if (!payload.sub) { return { isValid: false, error: FileValidationErrorType.INVALID_TOKEN }; } + return { isValid: true, userId: payload.sub, diff --git a/apps/server/src/models/resource/processor/BaseProcessor.ts b/apps/server/src/models/resource/processor/BaseProcessor.ts new file mode 100644 index 0000000..21907f5 --- /dev/null +++ b/apps/server/src/models/resource/processor/BaseProcessor.ts @@ -0,0 +1,23 @@ +import path, { dirname } from "path"; +import { FileMetadata, VideoMetadata, ResourceProcessor } from "../types"; +import { Resource, ResourceStatus, db } from "@nice/common"; +import { Logger } from "@nestjs/common"; +import fs from 'fs/promises'; + +export abstract class BaseProcessor implements ResourceProcessor { + constructor() { } + protected logger = new Logger(BaseProcessor.name) + + abstract process(resource: Resource): Promise + protected createOutputDir(filepath: string, subdirectory: string = 'assets'): string { + const outputDir = path.join( + path.dirname(filepath), + subdirectory, + ); + fs.mkdir(outputDir, { recursive: true }).catch(err => this.logger.error(`Failed to create directory: ${err.message}`)); + + return outputDir; + + } +} +// \ No newline at end of file diff --git a/apps/server/src/models/resource/processor/ImageProcessor.ts b/apps/server/src/models/resource/processor/ImageProcessor.ts index 05b9041..71cc56e 100644 --- a/apps/server/src/models/resource/processor/ImageProcessor.ts +++ b/apps/server/src/models/resource/processor/ImageProcessor.ts @@ -3,33 +3,28 @@ import sharp from 'sharp'; import { FileMetadata, ImageMetadata, ResourceProcessor } from "../types"; import { Resource, ResourceStatus, db } from "@nice/common"; import { getUploadFilePath } from "@server/utils/file"; -import { Logger } from "@nestjs/common"; +import { BaseProcessor } from "./BaseProcessor"; -export class ImageProcessor implements ResourceProcessor { - private logger = new Logger(ImageProcessor.name) - constructor() { } +export class ImageProcessor extends BaseProcessor { + constructor() { super() } async process(resource: Resource): Promise { - const { fileId } = resource; - const filepath = getUploadFilePath(fileId); + const { url } = resource; + const filepath = getUploadFilePath(url); const originMeta = resource.metadata as unknown as FileMetadata; - if (!originMeta.mimeType?.startsWith('image/')) { this.logger.log(`Skipping non-image resource: ${resource.id}`); return resource; } - try { const image = sharp(filepath); const metadata = await image.metadata(); if (!metadata) { - throw new Error(`Failed to get metadata for image: ${fileId}`); + throw new Error(`Failed to get metadata for image: ${url}`); } // Create WebP compressed version - const compressedPath = path.join( - path.dirname(filepath), - `${path.basename(filepath, path.extname(filepath))}_compressed.webp` - ); + const compressedDir = this.createOutputDir(filepath, "compressed") + const compressedPath = path.join(compressedDir, `${path.basename(filepath, path.extname(filepath))}.webp`); await image .webp({ quality: 80, @@ -40,12 +35,10 @@ export class ImageProcessor implements ResourceProcessor { const imageMeta: ImageMetadata = { width: metadata.width || 0, height: metadata.height || 0, - compressedUrl: path.basename(compressedPath), orientation: metadata.orientation, space: metadata.space, hasAlpha: metadata.hasAlpha, } - console.log(imageMeta) const updatedResource = await db.resource.update({ where: { id: resource.id }, data: { diff --git a/apps/server/src/models/resource/processor/VideoProcessor.ts b/apps/server/src/models/resource/processor/VideoProcessor.ts index 5ab678d..38d0ae2 100644 --- a/apps/server/src/models/resource/processor/VideoProcessor.ts +++ b/apps/server/src/models/resource/processor/VideoProcessor.ts @@ -1,9 +1,167 @@ -// import ffmpeg from 'fluent-ffmpeg'; -// import { ResourceProcessor } from '../types'; -// import { Resource } from '@nice/common'; +import path, { dirname } from "path"; +import ffmpeg from 'fluent-ffmpeg'; +import { FileMetadata, VideoMetadata, ResourceProcessor } from "../types"; +import { Resource, ResourceStatus, db } from "@nice/common"; +import { getUploadFilePath } from "@server/utils/file"; +import fs from 'fs/promises'; +import sharp from 'sharp'; +import { BaseProcessor } from "./BaseProcessor"; -// export class VideoProcessor implements ResourceProcessor { -// async process(resource: Resource): Promise { - -// } -// } \ No newline at end of file +export class VideoProcessor extends BaseProcessor { + constructor() { super() } + async process(resource: Resource): Promise { + const { url} = resource; + const filepath = getUploadFilePath(url); + this.logger.log(`Processing video for resource ID: ${resource.id}, File ID: ${url}`); + + const originMeta = resource.metadata as unknown as FileMetadata; + if (!originMeta.mimeType?.startsWith('video/')) { + this.logger.log(`Skipping non-video resource: ${resource.id}`); + return resource; + } + + try { + const streamDir = this.createOutputDir(filepath, 'stream'); + const [m3u8Path, videoMetadata, coverUrl] = await Promise.all([ + this.generateM3U8Stream(filepath, streamDir), + this.getVideoMetadata(filepath), + this.generateVideoCover(filepath, dirname(filepath)) + ]); + + const videoMeta: VideoMetadata = { + ...videoMetadata, + coverUrl: coverUrl, + }; + + const updatedResource = await db.resource.update({ + where: { id: resource.id }, + data: { + metadata: { + ...originMeta, + ...videoMeta, + }, + }, + }); + + this.logger.log(`Successfully processed video for resource ID: ${resource.id}`); + return updatedResource; + } catch (error: any) { + this.logger.error(`Failed to process video for resource ID: ${resource.id}, Error: ${error.message}`); + throw new Error(`Failed to process video: ${error.message}`); + } + } + private async generateVideoCover(filepath: string, outputDir: string): Promise { + this.logger.log(`Generating video cover for: ${filepath}`); + const jpgCoverPath = path.join(outputDir, 'cover.jpg'); + const webpCoverPath = path.join(outputDir, 'cover.webp'); + return new Promise((resolve, reject) => { + ffmpeg(filepath) + .on('end', async () => { + try { + // 使用 Sharp 将 JPG 转换为 WebP + await sharp(jpgCoverPath) + .webp({ quality: 80 }) // 设置 WebP 压缩质量 + .toFile(webpCoverPath); + + // 删除临时 JPG 文件 + await fs.unlink(jpgCoverPath); + + this.logger.log(`Video cover generated at: ${webpCoverPath}`); + resolve(path.basename(webpCoverPath)); + } catch (error: any) { + this.logger.error(`Error converting cover to WebP: ${error.message}`); + reject(error); + } + }) + .on('error', (err) => { + this.logger.error(`Error generating video cover: ${err.message}`); + reject(err); + }) + .screenshots({ + count: 1, + folder: outputDir, + filename: 'cover.jpg', + size: '640x360' + }); + }); + } + private async getVideoDuration(filepath: string): Promise { + this.logger.log(`Getting video duration for file: ${filepath}`); + return new Promise((resolve, reject) => { + ffmpeg.ffprobe(filepath, (err, metadata) => { + if (err) { + this.logger.error(`Error getting video duration: ${err.message}`); + reject(err); + return; + } + const duration = metadata.format.duration || 0; + this.logger.log(`Video duration: ${duration} seconds`); + resolve(duration); + }); + }); + } + private async generateM3U8Stream(filepath: string, outputDir: string): Promise { + const m3u8Path = path.join(outputDir, 'index.m3u8'); + this.logger.log(`Generating M3U8 stream for video: ${filepath}, Output Dir: ${outputDir}`); + return new Promise((resolve, reject) => { + ffmpeg(filepath) + .outputOptions([ + // Improved video encoding settings + '-c:v libx264', + '-preset medium', // Balance between encoding speed and compression + '-crf 23', // Constant Rate Factor for quality + '-profile:v high', // Higher profile for better compression + '-level:v 4.1', // Updated level for better compatibility + // Parallel processing and performance + '-threads 0', // Auto-detect optimal thread count + '-x264-params keyint=48:min-keyint=48', // More precise GOP control + // HLS specific optimizations + '-hls_time 4', // Shorter segment duration for better adaptive streaming + '-hls_list_size 0', // Keep all segments in playlist + '-hls_flags independent_segments+delete_segments', // Allow segment cleanup + // Additional encoding optimizations + '-sc_threshold 0', // Disable scene change detection for more consistent segments + '-max_muxing_queue_size 1024', // Increase muxing queue size + // Output format + '-f hls', + ]) + .output(m3u8Path) + .on('start', (commandLine) => { + this.logger.log(`Starting ffmpeg with command: ${commandLine}`); + }) + .on('end', () => { + this.logger.log(`Successfully generated M3U8 stream at: ${m3u8Path}`); + resolve(m3u8Path); + }) + .on('error', (err) => { + const errorMessage = `Error generating M3U8 stream for ${filepath}: ${err.message}`; + this.logger.error(errorMessage); + reject(new Error(errorMessage)); + }) + .run(); + }); + } + private async getVideoMetadata(filepath: string): Promise> { + this.logger.log(`Getting video metadata for file: ${filepath}`); + return new Promise((resolve, reject) => { + ffmpeg.ffprobe(filepath, (err, metadata) => { + if (err) { + this.logger.error(`Error getting video metadata: ${err.message}`); + reject(err); + return; + } + const videoStream = metadata.streams.find(stream => stream.codec_type === 'video'); + const audioStream = metadata.streams.find(stream => stream.codec_type === 'audio'); + const videoMetadata: Partial = { + width: videoStream?.width || 0, + height: videoStream?.height || 0, + duration: metadata.format.duration || 0, + videoCodec: videoStream?.codec_name || '', + audioCodec: audioStream?.codec_name || '' + }; + this.logger.log(`Extracted video metadata: ${JSON.stringify(videoMetadata)}`); + resolve(videoMetadata); + }); + }); + } +} \ No newline at end of file diff --git a/apps/server/src/models/resource/resource.service.ts b/apps/server/src/models/resource/resource.service.ts index d7a2df4..3a35a7e 100644 --- a/apps/server/src/models/resource/resource.service.ts +++ b/apps/server/src/models/resource/resource.service.ts @@ -19,18 +19,10 @@ export class ResourceService extends BaseService { params?: { staff?: UserProfile }, ): Promise { if (params?.staff) { - args.data.ownerId = params?.staff?.id + args.data.ownerId = params?.staff?.id; } return super.create(args); } - async checkFileExists(hash: string): Promise { - return this.findFirst({ - where: { - // hash, - deletedAt: null, - }, - }); - } async softDeleteByFileId(fileId: string) { return this.update({ where: { @@ -41,4 +33,4 @@ export class ResourceService extends BaseService { }, }); } -} \ No newline at end of file +} diff --git a/apps/server/src/models/resource/types.ts b/apps/server/src/models/resource/types.ts index b766681..7e39e42 100644 --- a/apps/server/src/models/resource/types.ts +++ b/apps/server/src/models/resource/types.ts @@ -2,7 +2,7 @@ import { Resource } from "@nice/common"; export interface ResourceProcessor { process(resource: Resource): Promise -}// 处理结果 +} export interface ProcessResult { success: boolean resource: Resource @@ -22,7 +22,7 @@ export interface BaseMetadata { export interface ImageMetadata { width: number; // 图片宽度(px) height: number; // 图片高度(px) - compressedUrl?:string; + compressedUrl?: string; orientation?: number; // EXIF方向信息 space?: string; // 色彩空间 (如: RGB, CMYK) hasAlpha?: boolean; // 是否包含透明通道 @@ -32,13 +32,12 @@ export interface ImageMetadata { * 视频特有元数据接口 */ export interface VideoMetadata { - width: number; // 视频宽度(px) - height: number; // 视频高度(px) - duration: number; // 视频时长(秒) - thumbnail?: string; // 视频封面图URL - codec?: string; // 视频编码格式 - frameRate?: number; // 帧率(fps) - bitrate?: number; // 比特率(bps) + width?: number; + height?: number; + duration?: number; + videoCodec?: string; + audioCodec?: string; + coverUrl?: string } /** diff --git a/apps/server/src/queue/postprocess/postprocess.service.ts b/apps/server/src/queue/postprocess/postprocess.service.ts new file mode 100644 index 0000000..ddbc821 --- /dev/null +++ b/apps/server/src/queue/postprocess/postprocess.service.ts @@ -0,0 +1,28 @@ +import { InjectQueue } from "@nestjs/bullmq"; +import { Injectable } from "@nestjs/common"; +import EventBus from "@server/utils/event-bus"; +import { Queue } from "bullmq"; +import { ObjectType } from "@nice/common"; +import { QueueJobType } from "../types"; +@Injectable() +export class PostProcessService { + constructor( + @InjectQueue('general') private generalQueue: Queue + ) { + + } + + private generateJobId(type: ObjectType, data: any): string { + // 根据类型和相关ID生成唯一的job标识 + switch (type) { + case ObjectType.ENROLLMENT: + return `stats_${type}_${data.courseId}`; + case ObjectType.LECTURE: + return `stats_${type}_${data.courseId}_${data.sectionId}`; + case ObjectType.POST: + return `stats_${type}_${data.courseId}`; + default: + return `stats_${type}_${Date.now()}`; + } + } +} \ No newline at end of file diff --git a/apps/server/src/queue/worker/file.processor.ts b/apps/server/src/queue/worker/file.processor.ts index fe4398b..de3d836 100644 --- a/apps/server/src/queue/worker/file.processor.ts +++ b/apps/server/src/queue/worker/file.processor.ts @@ -3,9 +3,11 @@ import { Logger } from '@nestjs/common'; import { QueueJobType } from '../types'; import { ResourceProcessingPipeline } from '@server/models/resource/pipe/resource.pipeline'; import { ImageProcessor } from '@server/models/resource/processor/ImageProcessor'; +import { VideoProcessor } from '@server/models/resource/processor/VideoProcessor'; const logger = new Logger('FileProcessorWorker'); const pipeline = new ResourceProcessingPipeline() .addProcessor(new ImageProcessor()) + .addProcessor(new VideoProcessor()) export default async function processJob(job: Job) { if (job.name === QueueJobType.FILE_PROCESS) { console.log(job) diff --git a/apps/server/src/upload/tus.service.ts b/apps/server/src/upload/tus.service.ts index d3e0702..4589b61 100644 --- a/apps/server/src/upload/tus.service.ts +++ b/apps/server/src/upload/tus.service.ts @@ -1,6 +1,6 @@ import { Injectable, OnModuleInit, Logger } from '@nestjs/common'; -import { Server, Upload } from "@nice/tus" -import { FileStore } from '@tus/file-store'; +import { Server, Uid, Upload } from "@nice/tus" +import { FileStore } from '@nice/tus'; import { Request, Response } from "express" import { db, ResourceStatus } from '@nice/common'; import { getFilenameWithoutExt } from '@server/utils/file'; @@ -9,30 +9,36 @@ import { Cron, CronExpression } from '@nestjs/schedule'; import { InjectQueue } from '@nestjs/bullmq'; import { Queue } from 'bullmq'; import { QueueJobType } from '@server/queue/types'; - -// Centralized configuration for file storage +import { nanoid } from 'nanoid-cjs'; +import { slugify } from 'transliteration'; +import path from 'path'; const FILE_UPLOAD_CONFIG = { - directory: "./uploads", + directory: process.env.UPLOAD_DIR, maxSizeBytes: 20_000_000_000, // 20GB expirationPeriod: 24 * 60 * 60 * 1000 // 24 hours }; - @Injectable() export class TusService implements OnModuleInit { private readonly logger = new Logger(TusService.name); private tusServer: Server; - constructor(private readonly resourceService: ResourceService, @InjectQueue("file-queue") private fileQueue: Queue ) { } - onModuleInit() { this.initializeTusServer(); this.setupTusEventHandlers(); } - private initializeTusServer() { this.tusServer = new Server({ + namingFunction(req, metadata) { + const safeFilename = slugify(metadata.filename); + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + const day = String(now.getDate()).padStart(2, '0'); + const uniqueId = nanoid(10); + return `${year}/${month}/${day}/${uniqueId}/${safeFilename}`; + }, path: '/upload', datastore: new FileStore({ directory: FILE_UPLOAD_CONFIG.directory, @@ -40,7 +46,10 @@ export class TusService implements OnModuleInit { }), maxSize: FILE_UPLOAD_CONFIG.maxSizeBytes, postReceiveInterval: 1000, - getFileIdFromRequest: (_, lastPath) => lastPath + getFileIdFromRequest: (req, lastPath) => { + const match = req.url.match(/\/upload\/(.+)/); + return match ? match[1] : lastPath; + } }); } @@ -48,15 +57,20 @@ export class TusService implements OnModuleInit { this.tusServer.on("POST_CREATE", this.handleUploadCreate.bind(this)); this.tusServer.on("POST_FINISH", this.handleUploadFinish.bind(this)); } - + private getFileId(uploadId: string) { + return uploadId.replace(/\/[^/]+$/, '') + } private async handleUploadCreate(req: Request, res: Response, upload: Upload, url: string) { try { + + const fileId = this.getFileId(upload.id) + const filename = upload.metadata.filename await this.resourceService.create({ data: { title: getFilenameWithoutExt(upload.metadata.filename), - filename: upload.metadata.filename, - fileId: upload.id, - url, + filename, + fileId, // 移除最后的文件名 + url: upload.id, metadata: upload.metadata, status: ResourceStatus.UPLOADING } @@ -69,11 +83,11 @@ export class TusService implements OnModuleInit { private async handleUploadFinish(req: Request, res: Response, upload: Upload) { try { const resource = await this.resourceService.update({ - where: { fileId: upload.id }, + where: { fileId: this.getFileId(upload.id) }, data: { status: ResourceStatus.UPLOADED } }); this.fileQueue.add(QueueJobType.FILE_PROCESS, { resource }, { jobId: resource.id }) - this.logger.log('Upload finished', { resourceId: resource.id }); + this.logger.log(`Upload finished ${resource.url}`); } catch (error) { this.logger.error('Failed to update resource after upload', error); } @@ -99,6 +113,7 @@ export class TusService implements OnModuleInit { } async handleTus(req: Request, res: Response) { + return this.tusServer.handle(req, res); } } \ No newline at end of file diff --git a/apps/server/src/upload/upload.controller.ts b/apps/server/src/upload/upload.controller.ts index 84e993c..ff3e38b 100644 --- a/apps/server/src/upload/upload.controller.ts +++ b/apps/server/src/upload/upload.controller.ts @@ -8,6 +8,8 @@ import { Patch, Param, Delete, + Head, + Options, } from '@nestjs/common'; import { Request, Response } from "express" import { TusService } from './tus.service'; @@ -15,97 +17,39 @@ import { TusService } from './tus.service'; @Controller('upload') export class UploadController { constructor(private readonly tusService: TusService) { } + // @Post() + // async handlePost(@Req() req: Request, @Res() res: Response) { + // return this.tusService.handleTus(req, res); + // } + + + @Options() + async handleOptions(@Req() req: Request, @Res() res: Response) { + return this.tusService.handleTus(req, res); + } + + @Head() + async handleHead(@Req() req: Request, @Res() res: Response) { + return this.tusService.handleTus(req, res); + } + @Post() async handlePost(@Req() req: Request, @Res() res: Response) { return this.tusService.handleTus(req, res); } - @Patch(':fileId') // 添加文件ID参数 - async handlePatch( - @Req() req: Request, - @Res() res: Response, - @Param('fileId') fileId: string // 添加文件ID参数 - ) { - try { - // 添加错误处理和日志 - const result = await this.tusService.handleTus(req, res); - return result; - } catch (error: any) { - console.error('Upload PATCH error:', error); - res.status(500).json({ - message: 'Upload failed', - error: error.message - }); - } - } - @Delete(':fileId') - async handleDelete( - @Req() req: Request, - @Res() res: Response, - @Param('fileId') fileId: string - ) { - try { - const result = await this.tusService.handleTus(req, res); - return result; - } catch (error: any) { - console.error('Upload DELETE error:', error); - res.status(500).json({ - message: 'Delete failed', - error: error.message - }); - } + @Get("/*") + async handleGet(@Req() req: Request, @Res() res: Response) { + return this.tusService.handleTus(req, res); } - @Get(':fileId') - async handleGet( - @Req() req: Request, - @Res() res: Response, - @Param('fileId') fileId: string - ) { - try { - const result = await this.tusService.handleTus(req, res); - return result; - } catch (error: any) { - console.error('Upload GET error:', error); - res.status(500).json({ - message: 'Retrieve failed', - error: error.message - }); - } + @Patch("/*") + async handlePatch(@Req() req: Request, @Res() res: Response) { + return this.tusService.handleTus(req, res); } - // @Post('chunk') - // @UseInterceptors(FileInterceptor('file')) - // async uploadChunk( - // @Body('chunk') chunkString: string, // 改为接收字符串 - // @UploadedFile() file: Express.Multer.File, - // @Body('clientId') clientId: string - // ) { - // const chunk = JSON.parse(chunkString); // 解析字符串为对象 - // await this.uploadService.uploadChunk(chunk, file, clientId); - // return { message: 'Chunk uploaded successfully' }; - // } - // @Get('status/:identifier') - // checkUploadStatusInfo(@Param('identifier') identifier: string) { - // const status = this.uploadService.checkUploadStatusInfo(identifier); - // return status || { message: 'No upload status found' }; - // } - // @Post('pause/:identifier') - // pauseUpload( - // @Param('identifier') identifier: string, - // @Body('clientId') clientId: string - // ) { - // this.uploadService.pauseUpload(identifier, clientId); - // return { message: 'Upload paused successfully' }; - // } - // @Post('resume/:identifier') - // async resumeUpload( - // @Param('identifier') identifier: string, - // @Body('clientId') clientId: string - // ) { - // const resumed = this.uploadService.resumeUpload(identifier, clientId); - // if (!resumed) { - // throw new Error('Unable to resume upload'); - // } - // return { message: 'Upload resumed successfully' }; - // } + // Keeping the catch-all method as a fallback + @All() + async handleUpload(@Req() req: Request, @Res() res: Response) { + return this.tusService.handleTus(req, res); + } } \ No newline at end of file diff --git a/apps/server/src/upload/utils.ts b/apps/server/src/upload/utils.ts new file mode 100644 index 0000000..a171d7f --- /dev/null +++ b/apps/server/src/upload/utils.ts @@ -0,0 +1,4 @@ +export function extractFileIdFromNginxUrl(url: string) { + const match = url.match(/uploads\/(\d{4}\/\d{2}\/\d{2}\/[^/]+)/); + return match ? match[1] : ''; +} \ No newline at end of file diff --git a/apps/server/src/utils/file.ts b/apps/server/src/utils/file.ts index a9e24fc..776d2ec 100644 --- a/apps/server/src/utils/file.ts +++ b/apps/server/src/utils/file.ts @@ -7,10 +7,6 @@ dotenv.config(); export function getFilenameWithoutExt(filename: string) { return filename ? filename.replace(/\.[^/.]+$/, '') : filename; } -export function extractFilePathFromUri(uri: string): string { - // 从 /uploads/ 路径中提取文件路径 - return uri.replace('/uploads/', ''); -} /** * 计算文件的 SHA-256 哈希值 * @param filePath 文件路径 diff --git a/config/nginx/conf.d/web.conf b/config/nginx/conf.d/web.conf index 4c078e4..bf90259 100644 --- a/config/nginx/conf.d/web.conf +++ b/config/nginx/conf.d/web.conf @@ -1,20 +1,32 @@ server { + # 监听80端口 listen 80; - server_name 192.168.12.77; + # 服务器域名/IP地址,使用环境变量 + server_name host.docker.internal; - # 基础优化配置 + # 基础性能优化配置 + # 启用tcp_nopush以优化数据发送 tcp_nopush on; + # 启用tcp_nodelay减少网络延迟 tcp_nodelay on; + # 设置哈希表最大大小 types_hash_max_size 2048; - # Gzip 压缩配置 + # Gzip压缩配置,提高传输效率 gzip on; + # 对IE6禁用Gzip gzip_disable "msie6"; + # 启用Vary头,支持缓存变体 gzip_vary on; + # 对所有代理请求启用压缩 gzip_proxied any; + # 压缩级别(1-9),6为推荐值 gzip_comp_level 6; + # 设置压缩缓冲区 gzip_buffers 16 8k; + # 压缩HTTP版本 gzip_http_version 1.1; + # 压缩的文件类型 gzip_types text/plain text/css @@ -25,47 +37,57 @@ server { application/xml+rss text/javascript; - # 默认首页配置 + # 默认站点位置配置 location / { + # 网站根目录 root /usr/share/nginx/html; + # 默认首页文件 index index.html index.htm; - - # 文件缓存配置 + + # 文件缓存优化 + # 最大缓存1000个文件,非活跃文件20秒后失效 open_file_cache max=1000 inactive=20s; + # 缓存验证时间 open_file_cache_valid 30s; + # 至少被访问2次的文件才缓存 open_file_cache_min_uses 2; + # 缓存文件错误信息 open_file_cache_errors on; - + # 尝试查找文件,不存在则重定向到index.html(适用于单页应用) try_files $uri $uri/ /index.html; } - # 文件上传处理配置 + # 文件上传处理位置 location /uploads/ { + # 文件实际存储路径 alias /data/uploads/; - - # 文件传输优化 + # 文件传输性能优化 sendfile on; tcp_nopush on; + # 异步IO aio on; + # 直接IO,提高大文件传输效率 directio 512; - - # 认证配置 + + # 文件访问认证 + # 通过内部认证服务验证 auth_request /auth-file; + # 存储认证状态和用户信息 auth_request_set $auth_status $upstream_status; auth_request_set $auth_user_id $upstream_http_x_user_id; auth_request_set $auth_resource_type $upstream_http_x_resource_type; - - # 缓存控制 + # 不缓存 expires 0; + # 私有缓存,禁止转换 add_header Cache-Control "private, no-transform"; + # 添加用户和资源类型头 add_header X-User-Id $auth_user_id; add_header X-Resource-Type $auth_resource_type; - # 带宽控制 + # 超过100MB后限制速率为102400KB/s limit_rate 102400k; limit_rate_after 100m; - - # CORS 配置 + # 跨域资源共享(CORS)配置 add_header 'Access-Control-Allow-Origin' '$http_origin' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always; @@ -73,21 +95,20 @@ server { 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always; } - - # 认证服务配置 + # 内部认证服务位置 location = /auth-file { + # 仅供内部使用 internal; - proxy_pass http://192.168.12.77:3000/auth/file; - - # 请求优化 + # 代理到认证服务 + proxy_pass http://host.docker.internal:3000/auth/file; + # 请求优化:不传递请求体 proxy_pass_request_body off; proxy_set_header Content-Length ""; - - # 请求信息传递 + # 传递原始请求信息 proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Original-Method $request_method; proxy_set_header Host $host; proxy_set_header X-Query-Params $query_string; } -} \ No newline at end of file +} diff --git a/config/nginx/conf.d/web.template b/config/nginx/conf.d/web.template index bce5ac0..35c1d2b 100644 --- a/config/nginx/conf.d/web.template +++ b/config/nginx/conf.d/web.template @@ -1,20 +1,32 @@ server { + # 监听80端口 listen 80; + # 服务器域名/IP地址,使用环境变量 server_name ${SERVER_IP}; - # 基础优化配置 + # 基础性能优化配置 + # 启用tcp_nopush以优化数据发送 tcp_nopush on; + # 启用tcp_nodelay减少网络延迟 tcp_nodelay on; + # 设置哈希表最大大小 types_hash_max_size 2048; - # Gzip 压缩配置 + # Gzip压缩配置,提高传输效率 gzip on; + # 对IE6禁用Gzip gzip_disable "msie6"; + # 启用Vary头,支持缓存变体 gzip_vary on; + # 对所有代理请求启用压缩 gzip_proxied any; + # 压缩级别(1-9),6为推荐值 gzip_comp_level 6; + # 设置压缩缓冲区 gzip_buffers 16 8k; + # 压缩HTTP版本 gzip_http_version 1.1; + # 压缩的文件类型 gzip_types text/plain text/css @@ -25,47 +37,57 @@ server { application/xml+rss text/javascript; - # 默认首页配置 + # 默认站点位置配置 location / { + # 网站根目录 root /usr/share/nginx/html; + # 默认首页文件 index index.html index.htm; - - # 文件缓存配置 + + # 文件缓存优化 + # 最大缓存1000个文件,非活跃文件20秒后失效 open_file_cache max=1000 inactive=20s; + # 缓存验证时间 open_file_cache_valid 30s; + # 至少被访问2次的文件才缓存 open_file_cache_min_uses 2; + # 缓存文件错误信息 open_file_cache_errors on; - + # 尝试查找文件,不存在则重定向到index.html(适用于单页应用) try_files $uri $uri/ /index.html; } - # 文件上传处理配置 + # 文件上传处理位置 location /uploads/ { + # 文件实际存储路径 alias /data/uploads/; - - # 文件传输优化 + # 文件传输性能优化 sendfile on; tcp_nopush on; + # 异步IO aio on; + # 直接IO,提高大文件传输效率 directio 512; - - # 认证配置 + + # 文件访问认证 + # 通过内部认证服务验证 auth_request /auth-file; + # 存储认证状态和用户信息 auth_request_set $auth_status $upstream_status; auth_request_set $auth_user_id $upstream_http_x_user_id; auth_request_set $auth_resource_type $upstream_http_x_resource_type; - - # 缓存控制 + # 不缓存 expires 0; + # 私有缓存,禁止转换 add_header Cache-Control "private, no-transform"; + # 添加用户和资源类型头 add_header X-User-Id $auth_user_id; add_header X-Resource-Type $auth_resource_type; - # 带宽控制 + # 超过100MB后限制速率为102400KB/s limit_rate 102400k; limit_rate_after 100m; - - # CORS 配置 + # 跨域资源共享(CORS)配置 add_header 'Access-Control-Allow-Origin' '$http_origin' always; add_header 'Access-Control-Allow-Credentials' 'true' always; add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always; @@ -73,21 +95,20 @@ server { 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always; } - - # 认证服务配置 + # 内部认证服务位置 location = /auth-file { + # 仅供内部使用 internal; + # 代理到认证服务 proxy_pass http://${SERVER_IP}:3000/auth/file; - - # 请求优化 + # 请求优化:不传递请求体 proxy_pass_request_body off; proxy_set_header Content-Length ""; - - # 请求信息传递 + # 传递原始请求信息 proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Original-Method $request_method; proxy_set_header Host $host; proxy_set_header X-Query-Params $query_string; } -} \ No newline at end of file +} diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 81c00e9..64e3bd9 100644 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -5,16 +5,6 @@ generator client { provider = "prisma-client-js" } -// generator zod { -// provider = "zod-prisma-types" -// output = "../src/generated" // (default) the directory where generated zod schemas will be saved -// createModelTypes = true -// createRelationValuesTypes = true -// writeNullishInModelTypes = true -// createPartialTypes = false -// useMultipleFiles = false -// useTypeAssertions = true -// } datasource db { provider = "postgresql" @@ -442,21 +432,18 @@ model Resource { metadata Json? @map("metadata") // 处理状态控制 status String? - // 审计字段 createdAt DateTime? @default(now()) @map("created_at") updatedAt DateTime? @updatedAt @map("updated_at") createdBy String? @map("created_by") updatedBy String? @map("updated_by") deletedAt DateTime? @map("deleted_at") isPublic Boolean? @default(true) @map("is_public") - owner Staff? @relation(fields: [ownerId], references: [id]) ownerId String? @map("owner_id") post Post? @relation(fields: [postId], references: [id]) postId String? @map("post_id") lecture Lecture? @relation(fields: [lectureId], references: [id]) lectureId String? @map("lecture_id") - // 索引 @@index([type]) @@index([createdAt]) diff --git a/packages/tus/package.json b/packages/tus/package.json index e26dc04..3600307 100644 --- a/packages/tus/package.json +++ b/packages/tus/package.json @@ -12,17 +12,24 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@aws-sdk/client-s3": "^3.723.0", + "@shopify/semaphore": "^3.1.0", "debug": "^4.4.0", - "lodash.throttle": "^4.1.1" + "lodash.throttle": "^4.1.1", + "multistream": "^4.1.0" }, "devDependencies": { "@types/debug": "^4.1.12", "@types/lodash.throttle": "^4.1.9", + "@types/multistream": "^4.1.3", "@types/node": "^20.3.1", "concurrently": "^8.0.0", + "ioredis": "^5.4.1", "rimraf": "^6.0.1", + "should": "^13.2.3", "ts-node": "^10.9.1", "tsup": "^8.3.5", - "typescript": "^5.5.4" + "typescript": "^5.5.4", + "@redis/client": "^1.6.0" } } \ No newline at end of file diff --git a/packages/tus/src/handlers/PatchHandler.ts b/packages/tus/src/handlers/PatchHandler.ts index b6b4aed..4197007 100644 --- a/packages/tus/src/handlers/PatchHandler.ts +++ b/packages/tus/src/handlers/PatchHandler.ts @@ -62,6 +62,7 @@ export class PatchHandler extends BaseHandler { try { // 从请求中获取文件ID const id = this.getFileIdFromRequest(req) + console.log('id', id) if (!id) { throw ERRORS.FILE_NOT_FOUND } diff --git a/packages/tus/src/index.ts b/packages/tus/src/index.ts index e9ec3b0..f403e0e 100644 --- a/packages/tus/src/index.ts +++ b/packages/tus/src/index.ts @@ -1,4 +1,5 @@ export { Server } from './server' export * from './types' export * from './lockers' -export * from './utils' \ No newline at end of file +export * from './utils' +export * from "./store" \ No newline at end of file diff --git a/packages/tus/src/server.ts b/packages/tus/src/server.ts index c0009cb..77b524a 100644 --- a/packages/tus/src/server.ts +++ b/packages/tus/src/server.ts @@ -12,6 +12,7 @@ import type stream from 'node:stream' import type { ServerOptions, RouteHandler, WithOptional } from './types' import { MemoryLocker } from './lockers' import { EVENTS, Upload, DataStore, REQUEST_METHODS, ERRORS, TUS_RESUMABLE, EXPOSED_HEADERS, CancellationContext } from './utils' +import { message } from 'antd'; /** * 处理器类型映射 @@ -225,6 +226,7 @@ export class Server extends EventEmitter { res: http.ServerResponse // biome-ignore lint/suspicious/noConfusingVoidType: it's fine ): Promise { + const context = this.createContext(req) log(`[TusServer] handle: ${req.method} ${req.url}`) // 允许覆盖 HTTP 方法。这样做的原因是某些库/环境不支持 PATCH 和 DELETE 请求,例如浏览器中的 Flash 和 Java 部分环境 @@ -236,6 +238,7 @@ export class Server extends EventEmitter { body?: string message: string }) => { + let status_code = error.status_code || ERRORS.UNKNOWN_ERROR.status_code let body = error.body || `${ERRORS.UNKNOWN_ERROR.body}${error.message || ''}\n` if (this.options.onResponseError) { @@ -251,6 +254,7 @@ export class Server extends EventEmitter { const handler = this.handlers.GET return handler.send(req, res).catch(onError) } + // Tus-Resumable 头部必须包含在每个请求和响应中,除了 OPTIONS 请求。其值必须是客户端或服务器使用的协议版本。 res.setHeader('Tus-Resumable', TUS_RESUMABLE) if (req.method !== 'OPTIONS' && req.headers['tus-resumable'] === undefined) { @@ -283,9 +287,11 @@ export class Server extends EventEmitter { if (this.options.allowedCredentials === true) { res.setHeader('Access-Control-Allow-Credentials', 'true') } + // 调用请求方法的处理器 const handler = this.handlers[req.method as keyof Handlers] if (handler) { + return handler.send(req, res, context).catch(onError) } diff --git a/packages/tus/src/store/file-store/index.ts b/packages/tus/src/store/file-store/index.ts new file mode 100644 index 0000000..b9e11d7 --- /dev/null +++ b/packages/tus/src/store/file-store/index.ts @@ -0,0 +1,230 @@ +// TODO: use /promises versions +import fs from 'node:fs' +import fsProm from 'node:fs/promises' +import path from 'node:path' +import stream from 'node:stream' +import type http from 'node:http' + +import debug from 'debug' +import { DataStore, Upload, ERRORS } from '../../utils' +import { + FileKvStore as FileConfigstore, + MemoryKvStore as MemoryConfigstore, + RedisKvStore as RedisConfigstore, + KvStore as Configstore, +} from '../../utils' + +type Options = { + directory: string + configstore?: Configstore + expirationPeriodInMilliseconds?: number +} + +const MASK = '0777' +const IGNORED_MKDIR_ERROR = 'EEXIST' +const FILE_DOESNT_EXIST = 'ENOENT' +const log = debug('tus-node-server:stores:filestore') + +export class FileStore extends DataStore { + directory: string + configstore: Configstore + expirationPeriodInMilliseconds: number + + constructor({ directory, configstore, expirationPeriodInMilliseconds }: Options) { + super() + this.directory = directory + this.configstore = configstore ?? new FileConfigstore(directory) + this.expirationPeriodInMilliseconds = expirationPeriodInMilliseconds ?? 0 + this.extensions = [ + 'creation', + 'creation-with-upload', + 'creation-defer-length', + 'termination', + 'expiration', + ] + // TODO: this async call can not happen in the constructor + this.checkOrCreateDirectory() + } + + /** + * Ensure the directory exists. + */ + private checkOrCreateDirectory() { + fs.mkdir(this.directory, { mode: MASK, recursive: true }, (error) => { + if (error && error.code !== IGNORED_MKDIR_ERROR) { + throw error + } + }) + } + + /** + * Create an empty file. + */ + async create(file: Upload): Promise { + const dirs = file.id.split('/').slice(0, -1) + const filePath = path.join(this.directory, file.id) + + await fsProm.mkdir(path.join(this.directory, ...dirs), { recursive: true }) + await fsProm.writeFile(filePath, '') + await this.configstore.set(file.id, file) + + file.storage = { type: 'file', path: filePath } + + return file + } + + read(file_id: string) { + return fs.createReadStream(path.join(this.directory, file_id)) + } + + remove(file_id: string): Promise { + return new Promise((resolve, reject) => { + fs.unlink(`${this.directory}/${file_id}`, (err) => { + if (err) { + log('[FileStore] delete: Error', err) + reject(ERRORS.FILE_NOT_FOUND) + return + } + + try { + resolve(this.configstore.delete(file_id)) + } catch (error) { + reject(error) + } + }) + }) + } + + write( + readable: http.IncomingMessage | stream.Readable, + file_id: string, + offset: number + ): Promise { + const file_path = path.join(this.directory, file_id) + const writeable = fs.createWriteStream(file_path, { + flags: 'r+', + start: offset, + }) + + let bytes_received = 0 + const transform = new stream.Transform({ + transform(chunk, _, callback) { + bytes_received += chunk.length + callback(null, chunk) + }, + }) + + return new Promise((resolve, reject) => { + stream.pipeline(readable, transform, writeable, (err) => { + if (err) { + log('[FileStore] write: Error', err) + return reject(ERRORS.FILE_WRITE_ERROR) + } + + log(`[FileStore] write: ${bytes_received} bytes written to ${file_path}`) + offset += bytes_received + log(`[FileStore] write: File is now ${offset} bytes`) + + return resolve(offset) + }) + }) + } + + async getUpload(id: string): Promise { + const file = await this.configstore.get(id) + + if (!file) { + throw ERRORS.FILE_NOT_FOUND + } + + return new Promise((resolve, reject) => { + const file_path = `${this.directory}/${id}` + fs.stat(file_path, (error, stats) => { + if (error && error.code === FILE_DOESNT_EXIST && file) { + log( + `[FileStore] getUpload: No file found at ${file_path} but db record exists`, + file + ) + return reject(ERRORS.FILE_NO_LONGER_EXISTS) + } + + if (error && error.code === FILE_DOESNT_EXIST) { + log(`[FileStore] getUpload: No file found at ${file_path}`) + return reject(ERRORS.FILE_NOT_FOUND) + } + + if (error) { + return reject(error) + } + + if (stats.isDirectory()) { + log(`[FileStore] getUpload: ${file_path} is a directory`) + return reject(ERRORS.FILE_NOT_FOUND) + } + + return resolve( + new Upload({ + id, + size: file.size, + offset: stats.size, + metadata: file.metadata, + creation_date: file.creation_date, + storage: { type: 'file', path: file_path }, + }) + ) + }) + }) + } + + async declareUploadLength(id: string, upload_length: number) { + const file = await this.configstore.get(id) + + if (!file) { + throw ERRORS.FILE_NOT_FOUND + } + + file.size = upload_length + + await this.configstore.set(id, file) + } + + async deleteExpired(): Promise { + const now = new Date() + const toDelete: Promise[] = [] + + if (!this.configstore.list) { + throw ERRORS.UNSUPPORTED_EXPIRATION_EXTENSION + } + + const uploadKeys = await this.configstore.list() + for (const file_id of uploadKeys) { + try { + const info = await this.configstore.get(file_id) + if ( + info && + 'creation_date' in info && + this.getExpiration() > 0 && + info.size !== info.offset && + info.creation_date + ) { + const creation = new Date(info.creation_date) + const expires = new Date(creation.getTime() + this.getExpiration()) + if (now > expires) { + toDelete.push(this.remove(file_id)) + } + } + } catch (error) { + if (error !== ERRORS.FILE_NO_LONGER_EXISTS) { + throw error + } + } + } + + await Promise.all(toDelete) + return toDelete.length + } + + getExpiration(): number { + return this.expirationPeriodInMilliseconds + } +} \ No newline at end of file diff --git a/packages/tus/src/store/index.ts b/packages/tus/src/store/index.ts new file mode 100644 index 0000000..bf02d53 --- /dev/null +++ b/packages/tus/src/store/index.ts @@ -0,0 +1,2 @@ +export * from "./file-store" +export * from "./s3-store" \ No newline at end of file diff --git a/packages/tus/src/store/s3-store/index.ts b/packages/tus/src/store/s3-store/index.ts new file mode 100644 index 0000000..2b58506 --- /dev/null +++ b/packages/tus/src/store/s3-store/index.ts @@ -0,0 +1,803 @@ +import os from 'node:os' +import fs, { promises as fsProm } from 'node:fs' +import stream, { promises as streamProm } from 'node:stream' +import type { Readable } from 'node:stream' + +import type AWS from '@aws-sdk/client-s3' +import { NoSuchKey, NotFound, S3, type S3ClientConfig } from '@aws-sdk/client-s3' +import debug from 'debug' + +import { + DataStore, + StreamSplitter, + Upload, + ERRORS, + TUS_RESUMABLE, + type KvStore, + MemoryKvStore, +} from '../../utils' + +import { Semaphore, type Permit } from '@shopify/semaphore' +import MultiStream from 'multistream' +import crypto from 'node:crypto' +import path from 'node:path' + +const log = debug('tus-node-server:stores:s3store') + +type Options = { + // The preferred part size for parts send to S3. Can not be lower than 5MiB or more than 5GiB. + // The server calculates the optimal part size, which takes this size into account, + // but may increase it to not exceed the S3 10K parts limit. + partSize?: number + useTags?: boolean + maxConcurrentPartUploads?: number + cache?: KvStore + expirationPeriodInMilliseconds?: number + // Options to pass to the AWS S3 SDK. + s3ClientConfig: S3ClientConfig & { bucket: string } +} + +export type MetadataValue = { + file: Upload + 'upload-id': string + 'tus-version': string +} + +function calcOffsetFromParts(parts?: Array) { + // @ts-expect-error not undefined + return parts && parts.length > 0 ? parts.reduce((a, b) => a + b.Size, 0) : 0 +} + +// Implementation (based on https://github.com/tus/tusd/blob/master/s3store/s3store.go) +// +// Once a new tus upload is initiated, multiple objects in S3 are created: +// +// First of all, a new info object is stored which contains (as Metadata) a JSON-encoded +// blob of general information about the upload including its size and meta data. +// This kind of objects have the suffix ".info" in their key. +// +// In addition a new multipart upload +// (http://docs.aws.amazon.com/AmazonS3/latest/dev/uploadobjusingmpu.html) is +// created. Whenever a new chunk is uploaded to tus-node-server using a PATCH request, a +// new part is pushed to the multipart upload on S3. +// +// If meta data is associated with the upload during creation, it will be added +// to the multipart upload and after finishing it, the meta data will be passed +// to the final object. However, the metadata which will be attached to the +// final object can only contain ASCII characters and every non-ASCII character +// will be replaced by a question mark (for example, "Menü" will be "Men?"). +// However, this does not apply for the metadata returned by the `_getMetadata` +// function since it relies on the info object for reading the metadata. +// Therefore, HEAD responses will always contain the unchanged metadata, Base64- +// encoded, even if it contains non-ASCII characters. +// +// Once the upload is finished, the multipart upload is completed, resulting in +// the entire file being stored in the bucket. The info object, containing +// meta data is not deleted. +// +// Considerations +// +// In order to support tus' principle of resumable upload, S3's Multipart-Uploads +// are internally used. +// For each incoming PATCH request (a call to `write`), a new part is uploaded +// to S3. +export class S3Store extends DataStore { + private bucket: string + private cache: KvStore + private client: S3 + private preferredPartSize: number + private expirationPeriodInMilliseconds = 0 + private useTags = true + private partUploadSemaphore: Semaphore + public maxMultipartParts = 10_000 as const + public minPartSize = 5_242_880 as const // 5MiB + public maxUploadSize = 5_497_558_138_880 as const // 5TiB + + constructor(options: Options) { + super() + const { partSize, s3ClientConfig } = options + const { bucket, ...restS3ClientConfig } = s3ClientConfig + this.extensions = [ + 'creation', + 'creation-with-upload', + 'creation-defer-length', + 'termination', + 'expiration', + ] + this.bucket = bucket + this.preferredPartSize = partSize || 8 * 1024 * 1024 + this.expirationPeriodInMilliseconds = options.expirationPeriodInMilliseconds ?? 0 + this.useTags = options.useTags ?? true + this.cache = options.cache ?? new MemoryKvStore() + this.client = new S3(restS3ClientConfig) + this.partUploadSemaphore = new Semaphore(options.maxConcurrentPartUploads ?? 60) + } + + protected shouldUseExpirationTags() { + return this.expirationPeriodInMilliseconds !== 0 && this.useTags + } + + protected useCompleteTag(value: 'true' | 'false') { + if (!this.shouldUseExpirationTags()) { + return undefined + } + + return `Tus-Completed=${value}` + } + + /** + * Saves upload metadata to a `${file_id}.info` file on S3. + * Please note that the file is empty and the metadata is saved + * on the S3 object's `Metadata` field, so that only a `headObject` + * is necessary to retrieve the data. + */ + private async saveMetadata(upload: Upload, uploadId: string) { + log(`[${upload.id}] saving metadata`) + await this.client.putObject({ + Bucket: this.bucket, + Key: this.infoKey(upload.id), + Body: JSON.stringify(upload), + Tagging: this.useCompleteTag('false'), + Metadata: { + 'upload-id': uploadId, + 'tus-version': TUS_RESUMABLE, + }, + }) + log(`[${upload.id}] metadata file saved`) + } + + private async completeMetadata(upload: Upload) { + if (!this.shouldUseExpirationTags()) { + return + } + + const { 'upload-id': uploadId } = await this.getMetadata(upload.id) + await this.client.putObject({ + Bucket: this.bucket, + Key: this.infoKey(upload.id), + Body: JSON.stringify(upload), + Tagging: this.useCompleteTag('true'), + Metadata: { + 'upload-id': uploadId, + 'tus-version': TUS_RESUMABLE, + }, + }) + } + + /** + * Retrieves upload metadata previously saved in `${file_id}.info`. + * There's a small and simple caching mechanism to avoid multiple + * HTTP calls to S3. + */ + private async getMetadata(id: string): Promise { + const cached = await this.cache.get(id) + if (cached) { + return cached + } + + const { Metadata, Body } = await this.client.getObject({ + Bucket: this.bucket, + Key: this.infoKey(id), + }) + const file = JSON.parse((await Body?.transformToString()) as string) + const metadata: MetadataValue = { + 'tus-version': Metadata?.['tus-version'] as string, + 'upload-id': Metadata?.['upload-id'] as string, + file: new Upload({ + id, + size: file.size ? Number.parseInt(file.size, 10) : undefined, + offset: Number.parseInt(file.offset, 10), + metadata: file.metadata, + creation_date: file.creation_date, + storage: file.storage, + }), + } + await this.cache.set(id, metadata) + return metadata + } + + private infoKey(id: string) { + return `${id}.info` + } + + private partKey(id: string, isIncomplete = false) { + if (isIncomplete) { + id += '.part' + } + + // TODO: introduce ObjectPrefixing for parts and incomplete parts. + // ObjectPrefix is prepended to the name of each S3 object that is created + // to store uploaded files. It can be used to create a pseudo-directory + // structure in the bucket, e.g. "path/to/my/uploads". + return id + } + + private async uploadPart( + metadata: MetadataValue, + readStream: fs.ReadStream | Readable, + partNumber: number + ): Promise { + const data = await this.client.uploadPart({ + Bucket: this.bucket, + Key: metadata.file.id, + UploadId: metadata['upload-id'], + PartNumber: partNumber, + Body: readStream, + }) + log(`[${metadata.file.id}] finished uploading part #${partNumber}`) + return data.ETag as string + } + + private async uploadIncompletePart( + id: string, + readStream: fs.ReadStream | Readable + ): Promise { + const data = await this.client.putObject({ + Bucket: this.bucket, + Key: this.partKey(id, true), + Body: readStream, + Tagging: this.useCompleteTag('false'), + }) + log(`[${id}] finished uploading incomplete part`) + return data.ETag as string + } + + private async downloadIncompletePart(id: string) { + const incompletePart = await this.getIncompletePart(id) + + if (!incompletePart) { + return + } + const filePath = await this.uniqueTmpFileName('tus-s3-incomplete-part-') + + try { + let incompletePartSize = 0 + + const byteCounterTransform = new stream.Transform({ + transform(chunk, _, callback) { + incompletePartSize += chunk.length + callback(null, chunk) + }, + }) + + // write to temporary file + await streamProm.pipeline( + incompletePart, + byteCounterTransform, + fs.createWriteStream(filePath) + ) + + const createReadStream = (options: { cleanUpOnEnd: boolean }) => { + const fileReader = fs.createReadStream(filePath) + + if (options.cleanUpOnEnd) { + fileReader.on('end', () => { + fs.unlink(filePath, () => { + // ignore + }) + }) + + fileReader.on('error', (err) => { + fileReader.destroy(err) + fs.unlink(filePath, () => { + // ignore + }) + }) + } + + return fileReader + } + + return { + size: incompletePartSize, + path: filePath, + createReader: createReadStream, + } + } catch (err) { + fsProm.rm(filePath).catch(() => { + /* ignore */ + }) + throw err + } + } + + private async getIncompletePart(id: string): Promise { + try { + const data = await this.client.getObject({ + Bucket: this.bucket, + Key: this.partKey(id, true), + }) + return data.Body as Readable + } catch (error) { + if (error instanceof NoSuchKey) { + return undefined + } + + throw error + } + } + + private async getIncompletePartSize(id: string): Promise { + try { + const data = await this.client.headObject({ + Bucket: this.bucket, + Key: this.partKey(id, true), + }) + return data.ContentLength + } catch (error) { + if (error instanceof NotFound) { + return undefined + } + throw error + } + } + + private async deleteIncompletePart(id: string): Promise { + await this.client.deleteObject({ + Bucket: this.bucket, + Key: this.partKey(id, true), + }) + } + + /** + * Uploads a stream to s3 using multiple parts + */ + private async uploadParts( + metadata: MetadataValue, + readStream: stream.Readable, + currentPartNumber: number, + offset: number + ): Promise { + const size = metadata.file.size + const promises: Promise[] = [] + let pendingChunkFilepath: string | null = null + let bytesUploaded = 0 + let permit: Permit | undefined = undefined + + const splitterStream = new StreamSplitter({ + chunkSize: this.calcOptimalPartSize(size), + directory: os.tmpdir(), + }) + .on('beforeChunkStarted', async () => { + permit = await this.partUploadSemaphore.acquire() + }) + .on('chunkStarted', (filepath) => { + pendingChunkFilepath = filepath + }) + .on('chunkFinished', ({ path, size: partSize }) => { + pendingChunkFilepath = null + + const acquiredPermit = permit + const partNumber = currentPartNumber++ + + offset += partSize + + const isFinalPart = size === offset + + // biome-ignore lint/suspicious/noAsyncPromiseExecutor: it's fine + const deferred = new Promise(async (resolve, reject) => { + try { + // Only the first chunk of each PATCH request can prepend + // an incomplete part (last chunk) from the previous request. + const readable = fs.createReadStream(path) + readable.on('error', reject) + + if (partSize >= this.minPartSize || isFinalPart) { + await this.uploadPart(metadata, readable, partNumber) + } else { + await this.uploadIncompletePart(metadata.file.id, readable) + } + + bytesUploaded += partSize + resolve() + } catch (error) { + reject(error) + } finally { + fsProm.rm(path).catch(() => { + /* ignore */ + }) + acquiredPermit?.release() + } + }) + + promises.push(deferred) + }) + .on('chunkError', () => { + permit?.release() + }) + + try { + await streamProm.pipeline(readStream, splitterStream) + } catch (error) { + if (pendingChunkFilepath !== null) { + try { + await fsProm.rm(pendingChunkFilepath) + } catch { + log(`[${metadata.file.id}] failed to remove chunk ${pendingChunkFilepath}`) + } + } + + promises.push(Promise.reject(error)) + } finally { + await Promise.all(promises) + } + + return bytesUploaded + } + + /** + * Completes a multipart upload on S3. + * This is where S3 concatenates all the uploaded parts. + */ + private async finishMultipartUpload(metadata: MetadataValue, parts: Array) { + const response = await this.client.completeMultipartUpload({ + Bucket: this.bucket, + Key: metadata.file.id, + UploadId: metadata['upload-id'], + MultipartUpload: { + Parts: parts.map((part) => { + return { + ETag: part.ETag, + PartNumber: part.PartNumber, + } + }), + }, + }) + return response.Location + } + + /** + * Gets the number of complete parts/chunks already uploaded to S3. + * Retrieves only consecutive parts. + */ + private async retrieveParts( + id: string, + partNumberMarker?: string + ): Promise> { + const metadata = await this.getMetadata(id) + + const params: AWS.ListPartsCommandInput = { + Bucket: this.bucket, + Key: id, + UploadId: metadata['upload-id'], + PartNumberMarker: partNumberMarker, + } + + const data = await this.client.listParts(params) + + let parts = data.Parts ?? [] + + if (data.IsTruncated) { + const rest = await this.retrieveParts(id, data.NextPartNumberMarker) + parts = [...parts, ...rest] + } + + if (!partNumberMarker) { + // biome-ignore lint/style/noNonNullAssertion: it's fine + parts.sort((a, b) => a.PartNumber! - b.PartNumber!) + } + + return parts + } + + /** + * Removes cached data for a given file. + */ + private async clearCache(id: string) { + log(`[${id}] removing cached data`) + await this.cache.delete(id) + } + + private calcOptimalPartSize(size?: number): number { + // When upload size is not know we assume largest possible value (`maxUploadSize`) + if (size === undefined) { + size = this.maxUploadSize + } + + let optimalPartSize: number + + // When upload is smaller or equal to PreferredPartSize, we upload in just one part. + if (size <= this.preferredPartSize) { + optimalPartSize = size + } + // Does the upload fit in MaxMultipartParts parts or less with PreferredPartSize. + else if (size <= this.preferredPartSize * this.maxMultipartParts) { + optimalPartSize = this.preferredPartSize + // The upload is too big for the preferred size. + // We devide the size with the max amount of parts and round it up. + } else { + optimalPartSize = Math.ceil(size / this.maxMultipartParts) + } + + return optimalPartSize + } + + /** + * Creates a multipart upload on S3 attaching any metadata to it. + * Also, a `${file_id}.info` file is created which holds some information + * about the upload itself like: `upload-id`, `upload-length`, etc. + */ + public async create(upload: Upload) { + log(`[${upload.id}] initializing multipart upload`) + const request: AWS.CreateMultipartUploadCommandInput = { + Bucket: this.bucket, + Key: upload.id, + Metadata: { 'tus-version': TUS_RESUMABLE }, + } + + if (upload.metadata?.contentType) { + request.ContentType = upload.metadata.contentType + } + + if (upload.metadata?.cacheControl) { + request.CacheControl = upload.metadata.cacheControl + } + + upload.creation_date = new Date().toISOString() + + const res = await this.client.createMultipartUpload(request) + upload.storage = { + type: 's3', + path: res.Key as string, + bucket: this.bucket, + } + await this.saveMetadata(upload, res.UploadId as string) + log(`[${upload.id}] multipart upload created (${res.UploadId})`) + + return upload + } + + async read(id: string) { + const data = await this.client.getObject({ + Bucket: this.bucket, + Key: id, + }) + return data.Body as Readable + } + + /** + * Write to the file, starting at the provided offset + */ + public async write(src: stream.Readable, id: string, offset: number): Promise { + // Metadata request needs to happen first + const metadata = await this.getMetadata(id) + const parts = await this.retrieveParts(id) + // biome-ignore lint/style/noNonNullAssertion: it's fine + const partNumber: number = parts.length > 0 ? parts[parts.length - 1].PartNumber! : 0 + const nextPartNumber = partNumber + 1 + + const incompletePart = await this.downloadIncompletePart(id) + const requestedOffset = offset + + if (incompletePart) { + // once the file is on disk, we delete the incomplete part + await this.deleteIncompletePart(id) + + offset = requestedOffset - incompletePart.size + src = new MultiStream([incompletePart.createReader({ cleanUpOnEnd: true }), src]) + } + + const bytesUploaded = await this.uploadParts(metadata, src, nextPartNumber, offset) + + // The size of the incomplete part should not be counted, because the + // process of the incomplete part should be fully transparent to the user. + const newOffset = requestedOffset + bytesUploaded - (incompletePart?.size ?? 0) + + if (metadata.file.size === newOffset) { + try { + const parts = await this.retrieveParts(id) + await this.finishMultipartUpload(metadata, parts) + await this.completeMetadata(metadata.file) + await this.clearCache(id) + } catch (error) { + log(`[${id}] failed to finish upload`, error) + throw error + } + } + + return newOffset + } + + public async getUpload(id: string): Promise { + let metadata: MetadataValue + try { + metadata = await this.getMetadata(id) + } catch (error) { + log('getUpload: No file found.', error) + throw ERRORS.FILE_NOT_FOUND + } + + let offset = 0 + + try { + const parts = await this.retrieveParts(id) + offset = calcOffsetFromParts(parts) + } catch (error: any) { + // Check if the error is caused by the upload not being found. This happens + // when the multipart upload has already been completed or aborted. Since + // we already found the info object, we know that the upload has been + // completed and therefore can ensure the the offset is the size. + // AWS S3 returns NoSuchUpload, but other implementations, such as DigitalOcean + // Spaces, can also return NoSuchKey. + if (error.Code === 'NoSuchUpload' || error.Code === 'NoSuchKey') { + return new Upload({ + ...metadata.file, + offset: metadata.file.size as number, + size: metadata.file.size, + metadata: metadata.file.metadata, + storage: metadata.file.storage, + }) + } + + log(error) + throw error + } + + const incompletePartSize = await this.getIncompletePartSize(id) + + return new Upload({ + ...metadata.file, + offset: offset + (incompletePartSize ?? 0), + size: metadata.file.size, + storage: metadata.file.storage, + }) + } + + public async declareUploadLength(file_id: string, upload_length: number) { + const { file, 'upload-id': uploadId } = await this.getMetadata(file_id) + if (!file) { + throw ERRORS.FILE_NOT_FOUND + } + + file.size = upload_length + + await this.saveMetadata(file, uploadId) + } + + public async remove(id: string): Promise { + try { + const { 'upload-id': uploadId } = await this.getMetadata(id) + if (uploadId) { + await this.client.abortMultipartUpload({ + Bucket: this.bucket, + Key: id, + UploadId: uploadId, + }) + } + } catch (error: any) { + if (error?.code && ['NotFound', 'NoSuchKey', 'NoSuchUpload'].includes(error.Code)) { + log('remove: No file found.', error) + throw ERRORS.FILE_NOT_FOUND + } + throw error + } + + await this.client.deleteObjects({ + Bucket: this.bucket, + Delete: { + Objects: [{ Key: id }, { Key: this.infoKey(id) }], + }, + }) + + this.clearCache(id) + } + + protected getExpirationDate(created_at: string) { + const date = new Date(created_at) + + return new Date(date.getTime() + this.getExpiration()) + } + + getExpiration(): number { + return this.expirationPeriodInMilliseconds + } + + async deleteExpired(): Promise { + if (this.getExpiration() === 0) { + return 0 + } + + let keyMarker: string | undefined = undefined + let uploadIdMarker: string | undefined = undefined + let isTruncated = true + let deleted = 0 + + while (isTruncated) { + const listResponse: AWS.ListMultipartUploadsCommandOutput = + await this.client.listMultipartUploads({ + Bucket: this.bucket, + KeyMarker: keyMarker, + UploadIdMarker: uploadIdMarker, + }) + + const expiredUploads = + listResponse.Uploads?.filter((multiPartUpload) => { + const initiatedDate = multiPartUpload.Initiated + return ( + initiatedDate && + new Date().getTime() > + this.getExpirationDate(initiatedDate.toISOString()).getTime() + ) + }) || [] + + const objectsToDelete = expiredUploads.reduce( + (all, expiredUpload) => { + all.push( + { + key: this.infoKey(expiredUpload.Key as string), + }, + { + key: this.partKey(expiredUpload.Key as string, true), + } + ) + return all + }, + [] as { key: string }[] + ) + + const deletions: Promise[] = [] + + // Batch delete 1000 items at a time + while (objectsToDelete.length > 0) { + const objects = objectsToDelete.splice(0, 1000) + deletions.push( + this.client.deleteObjects({ + Bucket: this.bucket, + Delete: { + Objects: objects.map((object) => ({ + Key: object.key, + })), + }, + }) + ) + } + + const [objectsDeleted] = await Promise.all([ + Promise.all(deletions), + ...expiredUploads.map((expiredUpload) => { + return this.client.abortMultipartUpload({ + Bucket: this.bucket, + Key: expiredUpload.Key, + UploadId: expiredUpload.UploadId, + }) + }), + ]) + + deleted += objectsDeleted.reduce((all, acc) => all + (acc.Deleted?.length ?? 0), 0) + + isTruncated = Boolean(listResponse.IsTruncated) + + if (isTruncated) { + keyMarker = listResponse.NextKeyMarker + uploadIdMarker = listResponse.NextUploadIdMarker + } + } + + return deleted + } + + private async uniqueTmpFileName(template: string): Promise { + let tries = 0 + const maxTries = 10 + + while (tries < maxTries) { + const fileName = + template + crypto.randomBytes(10).toString('base64url').slice(0, 10) + const filePath = path.join(os.tmpdir(), fileName) + + try { + await fsProm.lstat(filePath) + // If no error, file exists, so try again + tries++ + } catch (e: any) { + if (e.code === 'ENOENT') { + // File does not exist, return the path + return filePath + } + throw e // For other errors, rethrow + } + } + + throw new Error(`Could not find a unique file name after ${maxTries} tries`) + } +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e59d876..d183884 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,12 +46,6 @@ importers: '@trpc/server': specifier: 11.0.0-rc.456 version: 11.0.0-rc.456 - '@tus/file-store': - specifier: ^1.5.1 - version: 1.5.1 - '@tus/s3-store': - specifier: ^1.6.2 - version: 1.6.2 argon2: specifier: ^0.41.1 version: 0.41.1 @@ -97,6 +91,15 @@ importers: mitt: specifier: ^3.0.1 version: 3.0.1 + nanoid: + specifier: ^5.0.9 + version: 5.0.9 + nanoid-cjs: + specifier: ^0.0.7 + version: 0.0.7 + pinyin-pro: + specifier: ^3.26.0 + version: 3.26.0 reflect-metadata: specifier: ^0.2.0 version: 0.2.2 @@ -106,6 +109,9 @@ importers: sharp: specifier: ^0.33.5 version: 0.33.5 + slugify: + specifier: ^1.6.6 + version: 1.6.6 socket.io: specifier: ^4.7.5 version: 4.7.5 @@ -581,28 +587,49 @@ importers: packages/tus: dependencies: + '@aws-sdk/client-s3': + specifier: ^3.723.0 + version: 3.723.0 + '@shopify/semaphore': + specifier: ^3.1.0 + version: 3.1.0 debug: specifier: ^4.4.0 version: 4.4.0 lodash.throttle: specifier: ^4.1.1 version: 4.1.1 + multistream: + specifier: ^4.1.0 + version: 4.1.0 devDependencies: + '@redis/client': + specifier: ^1.6.0 + version: 1.6.0 '@types/debug': specifier: ^4.1.12 version: 4.1.12 '@types/lodash.throttle': specifier: ^4.1.9 version: 4.1.9 + '@types/multistream': + specifier: ^4.1.3 + version: 4.1.3 '@types/node': specifier: ^20.3.1 version: 20.14.10 concurrently: specifier: ^8.0.0 version: 8.2.2 + ioredis: + specifier: ^5.4.1 + version: 5.4.1 rimraf: specifier: ^6.0.1 version: 6.0.1 + should: + specifier: ^13.2.3 + version: 13.2.3 ts-node: specifier: ^10.9.1 version: 10.9.2(@swc/core@1.6.13)(@types/node@20.14.10)(typescript@5.7.2) @@ -750,145 +777,145 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.717.0': - resolution: {integrity: sha512-jzaH8IskAXVnqlZ3/H/ROwrB2HCnq/atlN7Hi7FIfjWvMPf5nfcJKfzJ1MXFX0EQR5qO6X4TbK7rgi7Bjw9NjQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-s3@3.723.0': + resolution: {integrity: sha512-uJkSBWeAbEORApCSc8ZlD8nmmJVZnklauSR+GLnG19ZiHQl3ib6IzT4zdnMHrrIXqVttwkyC8eT703ZUDVaacw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso-oidc@3.716.0': - resolution: {integrity: sha512-lA4IB9FzR2KjH7EVCo+mHGFKqdViVyeBQEIX9oVratL/l7P0bMS1fMwgfHOc3ACazqNxBxDES7x08ZCp32y6Lw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso-oidc@3.723.0': + resolution: {integrity: sha512-9IH90m4bnHogBctVna2FnXaIGVORncfdxcqeEIovOxjIJJyHDmEAtA7B91dAM4sruddTbVzOYnqfPVst3odCbA==} + engines: {node: '>=18.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.716.0 + '@aws-sdk/client-sts': ^3.723.0 - '@aws-sdk/client-sso@3.716.0': - resolution: {integrity: sha512-5Nb0jJXce2TclbjG7WVPufwhgV1TRydz1QnsuBtKU0AdViEpr787YrZhPpGnNIM1Dx+R1H/tmAHZnOoohS6D8g==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sso@3.723.0': + resolution: {integrity: sha512-r1ddZDb8yPmdofX1gQ4m8oqKozgkgVONLlAuSprGObbyMy8bYt1Psxu+GjnwMmgVu3vlF069PHyW1ndrBiL1zA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/client-sts@3.716.0': - resolution: {integrity: sha512-i4SVNsrdXudp8T4bkm7Fi3YWlRnvXCSwvNDqf6nLqSJxqr4CN3VlBELueDyjBK7TAt453/qSif+eNx+bHmwo4Q==} - engines: {node: '>=16.0.0'} + '@aws-sdk/client-sts@3.723.0': + resolution: {integrity: sha512-YyN8x4MI/jMb4LpHsLf+VYqvbColMK8aZeGWVk2fTFsmt8lpTYGaGC1yybSwGX42mZ4W8ucu8SAYSbUraJZEjA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.716.0': - resolution: {integrity: sha512-5DkUiTrbyzO8/W4g7UFEqRFpuhgizayHI/Zbh0wtFMcot8801nJV+MP/YMhdjimlvAr/OqYB08FbGsPyWppMTw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/core@3.723.0': + resolution: {integrity: sha512-UraXNmvqj3vScSsTkjMwQkhei30BhXlW5WxX6JacMKVtl95c7z0qOXquTWeTalYkFfulfdirUhvSZrl+hcyqTw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.716.0': - resolution: {integrity: sha512-JI2KQUnn2arICwP9F3CnqP1W3nAbm4+meQg/yOhp9X0DMzQiHrHRd4HIrK2vyVgi2/6hGhONY5uLF26yRTA7nQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-env@3.723.0': + resolution: {integrity: sha512-OuH2yULYUHTVDUotBoP/9AEUIJPn81GQ/YBtZLoo2QyezRJ2QiO/1epVtbJlhNZRwXrToLEDmQGA2QfC8c7pbA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.716.0': - resolution: {integrity: sha512-CZ04pl2z7igQPysQyH2xKZHM3fLwkemxQbKOlje3TmiS1NwXvcKvERhp9PE/H23kOL7beTM19NMRog/Fka/rlw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-http@3.723.0': + resolution: {integrity: sha512-DTsKC6xo/kz/ZSs1IcdbQMTgiYbpGTGEd83kngFc1bzmw7AmK92DBZKNZpumf8R/UfSpTcj9zzUUmrWz1kD0eQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.716.0': - resolution: {integrity: sha512-P37We2GtZvdROxiwP0zrpEL81/HuYK1qlYxp5VCj3uV+G4mG8UQN2gMIU/baYrpOQqa0h81RfyQGRFUjVaDVqw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-ini@3.723.0': + resolution: {integrity: sha512-fWRLksuSG851e7Iu+ltMrQTM7C/5iI9OkxAmCYblcCetAzjTRmMB2arku0Z83D8edIZEQtOJMt5oQ9KNg43pzg==} + engines: {node: '>=18.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.716.0 + '@aws-sdk/client-sts': ^3.723.0 - '@aws-sdk/credential-provider-node@3.716.0': - resolution: {integrity: sha512-FGQPK2uKfS53dVvoskN/s/t6m0Po24BGd1PzJdzHBFCOjxbZLM6+8mDMXeyi2hCLVVQOUcuW41kOgmJ0+zMbww==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-node@3.723.0': + resolution: {integrity: sha512-OyLHt+aY+rkuRejigcxviS5RLUBcqbxhDTSNfP8dp9I+1SP610qRLpTIROvtKwXZssFcATpPfgikFtVYRrihXQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.716.0': - resolution: {integrity: sha512-0spcu2MWVVHSTHH3WE2E//ttUJPwXRM3BCp+WyI41xLzpNu1Fd8zjOrDpEo0SnGUzsSiRTIJWgkuu/tqv9NJ2A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-process@3.723.0': + resolution: {integrity: sha512-fgupvUjz1+jeoCBA7GMv0L6xEk92IN6VdF4YcFhsgRHlHvNgm7ayaoKQg7pz2JAAhG/3jPX6fp0ASNy+xOhmPA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.716.0': - resolution: {integrity: sha512-J2IA3WuCpRGGoZm6VHZVFCnrxXP+41iUWb9Ct/1spljegTa1XjiaZ5Jf3+Ubj7WKiyvP9/dgz1L0bu2bYEjliw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-sso@3.723.0': + resolution: {integrity: sha512-laCnxrk0pgUegU+ib6rj1/Uv51wei+cH8crvBJddybc8EDn7Qht61tCvBwf3o33qUDC+ZWZZewlpSebf+J+tBw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.716.0': - resolution: {integrity: sha512-vzgpWKs2gGXZGdbMKRFrMW4PqEFWkGvwWH2T7ZwQv9m+8lQ7P4Dk2uimqu0f37HZAbpn8HFMqRh4CaySjU354A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/credential-provider-web-identity@3.723.0': + resolution: {integrity: sha512-tl7pojbFbr3qLcOE6xWaNCf1zEfZrIdSJtOPeSXfV/thFMMAvIjgf3YN6Zo1a6cxGee8zrV/C8PgOH33n+Ev/A==} + engines: {node: '>=18.0.0'} peerDependencies: - '@aws-sdk/client-sts': ^3.716.0 + '@aws-sdk/client-sts': ^3.723.0 - '@aws-sdk/middleware-bucket-endpoint@3.714.0': - resolution: {integrity: sha512-I/xSOskiseJJ8i183Z522BgqbgYzLKP7jGcg2Qeib/IWoG2IP+9DH8pwqagKaPAycyswtnoKBJiiFXY43n0CkA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.723.0': + resolution: {integrity: sha512-OmKSXwSlXyW+zg+xq4hUf7V4VF5/fa4LHu1JzeBlomrKX3/NnqhnJn7760GXoDr16AT+dP7nvv35Ofp91umEAg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-expect-continue@3.714.0': - resolution: {integrity: sha512-rlzsXdG8Lzo4Qpl35ZnpOBAWlzvDHpP9++0AXoUwAJA0QmMm7auIRmgxJuNj91VwT9h15ZU6xjU4S7fJl4W0+w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-expect-continue@3.723.0': + resolution: {integrity: sha512-w/O0EkIzkiqvGu7U8Ke7tue0V0HYM5dZQrz6nVU+R8T2LddWJ+njEIHU4Wh8aHPLQXdZA5NQumv0xLPdEutykw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.717.0': - resolution: {integrity: sha512-a5kY5r7/7bDZZlOQQGWOR1ulQewdtNexdW1Ex5DD0FLKlFY7RD0va24hxQ6BP7mWHol+Dx4pj6UQ8ahk0ap1tw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.723.0': + resolution: {integrity: sha512-JY76mrUCLa0FHeMZp8X9+KK6uEuZaRZaQrlgq6zkXX/3udukH0T3YdFC+Y9uw5ddbiwZ5+KwgmlhnPpiXKfP4g==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.714.0': - resolution: {integrity: sha512-6l68kjNrh5QC8FGX3I3geBDavWN5Tg1RLHJ2HLA8ByGBtJyCwnz3hEkKfaxn0bBx0hF9DzbfjEOUF6cDqy2Kjg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-host-header@3.723.0': + resolution: {integrity: sha512-LLVzLvk299pd7v4jN9yOSaWDZDfH0SnBPb6q+FDPaOCMGBY8kuwQso7e/ozIKSmZHRMGO3IZrflasHM+rI+2YQ==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-location-constraint@3.714.0': - resolution: {integrity: sha512-MX7M+V+FblujKck3fyuzePVIAy9530gY719IiSxV6uN1qLHl7VDJxNblpF/KpXakD6rOg8OpvtmqsXj9aBMftw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-location-constraint@3.723.0': + resolution: {integrity: sha512-inp9tyrdRWjGOMu1rzli8i2gTo0P4X6L7nNRXNTKfyPNZcBimZ4H0H1B671JofSI5isaklVy5r4pvv2VjjLSHw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.714.0': - resolution: {integrity: sha512-RkqHlMvQWUaRklU1bMfUuBvdWwxgUtEqpADaHXlGVj3vtEY2UgBjy+57CveC4MByqKIunNvVHBBbjrGVtwY7Lg==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-logger@3.723.0': + resolution: {integrity: sha512-chASQfDG5NJ8s5smydOEnNK7N0gDMyuPbx7dYYcm1t/PKtnVfvWF+DHCTrRC2Ej76gLJVCVizlAJKM8v8Kg3cg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.714.0': - resolution: {integrity: sha512-AVU5ixnh93nqtsfgNc284oXsXaadyHGPHpql/jwgaaqQfEXjS/1/j3j9E/vpacfTTz2Vzo7hAOjnvrOXSEVDaA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-recursion-detection@3.723.0': + resolution: {integrity: sha512-7usZMtoynT9/jxL/rkuDOFQ0C2mhXl4yCm67Rg7GNTstl67u7w5WN1aIRImMeztaKlw8ExjoTyo6WTs1Kceh7A==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.716.0': - resolution: {integrity: sha512-Qzz5OfRA/5brqfvq+JHTInwS1EuJ1+tC6qMtwKWJN3czMnVJVdnnsPTf+G5IM/1yYaGEIjY8rC1ExQLcc8ApFQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-sdk-s3@3.723.0': + resolution: {integrity: sha512-wfjOvNJVp8LDWhq4wO5jtSMb8Vgf4tNlR7QTEQfoYc6AGU3WlK5xyUQcpfcpwytEhQTN9u0cJLQpSyXDO+qSCw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-ssec@3.714.0': - resolution: {integrity: sha512-RkK8REAVwNUQmYbIDRw8eYbMJ8F1Rw4C9mlME4BBMhFlelGcD3ErU2ce24moQbDxBjNwHNESmIqgmdQk93CDCQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-ssec@3.723.0': + resolution: {integrity: sha512-Bs+8RAeSMik6ZYCGSDJzJieGsDDh2fRbh1HQG94T8kpwBXVxMYihm6e9Xp2cyl+w9fyyCnh0IdCKChP/DvrdhA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.716.0': - resolution: {integrity: sha512-FpAtT6nNKrYdkDZndutEraiRMf+TgDzAGvniqRtZ/YTPA+gIsWrsn+TwMKINR81lFC3nQfb9deS5CFtxd021Ew==} - engines: {node: '>=16.0.0'} + '@aws-sdk/middleware-user-agent@3.723.0': + resolution: {integrity: sha512-AY5H2vD3IRElplBO4DCyRMNnOG/4/cb0tsHyLe1HJy0hdUF6eY5z/VVjKJoKbbDk7ui9euyOBWslXxDyLmyPWg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.714.0': - resolution: {integrity: sha512-HJzsQxgMOAzZrbf/YIqEx30or4tZK1oNAk6Wm6xecUQx+23JXIaePRu1YFUOLBBERQ4QBPpISFurZWBMZ5ibAw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/region-config-resolver@3.723.0': + resolution: {integrity: sha512-tGF/Cvch3uQjZIj34LY2mg8M2Dr4kYG8VU8Yd0dFnB1ybOEOveIK/9ypUo9ycZpB9oO6q01KRe5ijBaxNueUQg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.716.0': - resolution: {integrity: sha512-k0goWotZKKz+kV6Ln0qeAMSeSVi4NipuIIz5R8A0uCF2zBK4CXWdZR7KeaIoLBhJwQnHj1UU7E+2MK74KIUBzA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/signature-v4-multi-region@3.723.0': + resolution: {integrity: sha512-lJlVAa5Sl589qO8lwMLVUtnlF1Q7I+6k1Iomv2goY9d1bRl4q2N5Pit2qJVr2AMW0sceQXeh23i2a/CKOqVAdg==} + engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.714.0': - resolution: {integrity: sha512-vKN064aLE3kl+Zl16Ony3jltHnMddMBT7JRkP1L+lLywhA0PcAKxpdvComul/sTBWnbnwLnaS5NsDUhcWySH8A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/token-providers@3.723.0': + resolution: {integrity: sha512-hniWi1x4JHVwKElANh9afKIMUhAutHVBRD8zo6usr0PAoj+Waf220+1ULS74GXtLXAPCiNXl5Og+PHA7xT8ElQ==} + engines: {node: '>=18.0.0'} peerDependencies: - '@aws-sdk/client-sso-oidc': ^3.714.0 + '@aws-sdk/client-sso-oidc': ^3.723.0 - '@aws-sdk/types@3.714.0': - resolution: {integrity: sha512-ZjpP2gYbSFlxxaUDa1Il5AVvfggvUPbjzzB/l3q0gIE5Thd6xKW+yzEpt2mLZ5s5UaYSABZbF94g8NUOF4CVGA==} - engines: {node: '>=16.0.0'} + '@aws-sdk/types@3.723.0': + resolution: {integrity: sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA==} + engines: {node: '>=18.0.0'} - '@aws-sdk/util-arn-parser@3.693.0': - resolution: {integrity: sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-arn-parser@3.723.0': + resolution: {integrity: sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==} + engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.714.0': - resolution: {integrity: sha512-Xv+Z2lhe7w7ZZRsgBwBMZgGTVmS+dkkj2S13uNHAx9lhB5ovM8PhK5G/j28xYf6vIibeuHkRAbb7/ozdZIGR+A==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-endpoints@3.723.0': + resolution: {integrity: sha512-vR1ZfAUvrTtdA1Q78QxgR8TFgi2gzk+N4EmNjbyR5hHmeOXuaKRdhbNQAzLPYVe1aNUpoiy9cl8mWkg9SrNHBw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/util-locate-window@3.693.0': - resolution: {integrity: sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-locate-window@3.723.0': + resolution: {integrity: sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==} + engines: {node: '>=18.0.0'} - '@aws-sdk/util-user-agent-browser@3.714.0': - resolution: {integrity: sha512-OdJJ03cP9/MgIVToPJPCPUImbpZzTcwdIgbXC0tUQPJhbD7b7cB4LdnkhNHko+MptpOrCq4CPY/33EpOjRdofw==} + '@aws-sdk/util-user-agent-browser@3.723.0': + resolution: {integrity: sha512-Wh9I6j2jLhNFq6fmXydIpqD1WyQLyTfSxjW9B+PXSnPyk3jtQW8AKQur7p97rO8LAUzVI0bv8kb3ZzDEVbquIg==} - '@aws-sdk/util-user-agent-node@3.716.0': - resolution: {integrity: sha512-3PqaXmQbxrtHKAsPCdp7kn5FrQktj8j3YyuNsqFZ8rWZeEQ88GWlsvE61PTsr2peYCKzpFqYVddef2x1axHU0w==} - engines: {node: '>=16.0.0'} + '@aws-sdk/util-user-agent-node@3.723.0': + resolution: {integrity: sha512-uCtW5sGq8jCwA9w57TvVRIwNnPbSDD1lJaTIgotf7Jit2bTrYR64thgMy/drL5yU5aHOdFIQljqn/5aDXLtTJw==} + engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' peerDependenciesMeta: aws-crt: optional: true - '@aws-sdk/xml-builder@3.709.0': - resolution: {integrity: sha512-2GPCwlNxeHspoK/Mc8nbk9cBOkSpp3j2SJUQmFnyQK6V/pR6II2oPRyZkMomug1Rc10hqlBHByMecq4zhV2uUw==} - engines: {node: '>=16.0.0'} + '@aws-sdk/xml-builder@3.723.0': + resolution: {integrity: sha512-5xK2SqGU1mzzsOeemy7cy3fGKxR1sEpUs4pEiIjaT0OIvU+fZaDVUEYWOqsgns6wI90XZEQJlXtI8uAHX/do5Q==} + engines: {node: '>=18.0.0'} '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} @@ -2202,208 +2229,217 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@smithy/abort-controller@3.1.9': - resolution: {integrity: sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw==} - engines: {node: '>=16.0.0'} + '@smithy/abort-controller@4.0.0': + resolution: {integrity: sha512-xFNL1ZfluscKiVI0qlPEnu7pL1UgNNIzQdjTPkaO7JCJtIkbArPYNtqbxohuNaQdksJ01Tn1wLbDA5oIp62P8w==} + engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader-native@3.0.1': - resolution: {integrity: sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ==} + '@smithy/chunked-blob-reader-native@4.0.0': + resolution: {integrity: sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==} + engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@4.0.0': - resolution: {integrity: sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ==} + '@smithy/chunked-blob-reader@5.0.0': + resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==} + engines: {node: '>=18.0.0'} - '@smithy/config-resolver@3.0.13': - resolution: {integrity: sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg==} - engines: {node: '>=16.0.0'} + '@smithy/config-resolver@4.0.0': + resolution: {integrity: sha512-29pIDlUY/a9+ChJPAarPiD9cU8fBtBh0wFnmnhj7j5AhgMzc+uyXdfzmziH6xx2jzw54waSP3HfnFkTANZuPYA==} + engines: {node: '>=18.0.0'} - '@smithy/core@2.5.6': - resolution: {integrity: sha512-w494xO+CPwG/5B/N2l0obHv2Fi9U4DAY+sTi1GWT3BVvGpZetJjJXAynIO9IHp4zS1PinGhXtRSZydUXbJO4ag==} - engines: {node: '>=16.0.0'} + '@smithy/core@3.0.0': + resolution: {integrity: sha512-pKaas7RWvPljJ8uByCeBa10rtbVJCy4N/Fr7OSPxFezcyG0SQuXWnESZqzXj7m2+A+kPzG6fKyP4wrKidl2Ikg==} + engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@3.2.8': - resolution: {integrity: sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw==} - engines: {node: '>=16.0.0'} + '@smithy/credential-provider-imds@4.0.0': + resolution: {integrity: sha512-+hTShyZHiq2AVFOxJja3k6O17DKU6TaZbwr2y1OH5HQtUw2a+7O3mMR+10LVmc39ef72SAj+uFX0IW9rJGaLQQ==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-codec@3.1.10': - resolution: {integrity: sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ==} + '@smithy/eventstream-codec@4.0.0': + resolution: {integrity: sha512-YvKUUOo3qehqOxNrkax3YKXF1v0ff475FhDgbBmF8Bo0oOOpsXZyltjQnwBzIeTYo446ZPV85KM3kY4YoxUNOg==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@3.0.14': - resolution: {integrity: sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-browser@4.0.0': + resolution: {integrity: sha512-YRwsVPJU/DN1VshH8tKs4CxY66HLhmDSw6oZDM2LVIgHODsqpJBcRdEfcnb97ULmgyFrWxTjL9UXpyKPuJXQRA==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@3.0.11': - resolution: {integrity: sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-config-resolver@4.0.0': + resolution: {integrity: sha512-OZ/aK9LHsZch0VZ6bnf+dPD80kJripnZnkc36QNymnej49VkHJLSNJxsM0pwt53FA6+fUYYMMT0DVDTH1Msq2g==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@3.0.13': - resolution: {integrity: sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-node@4.0.0': + resolution: {integrity: sha512-10b4F+zXbzxZHKuP+m2st/C+rEGK7FUut1dNSRw6DQCCfaTUecJGCoHPCmk2CRvuMTzunVpS1BKLMk839318VQ==} + engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@3.0.13': - resolution: {integrity: sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw==} - engines: {node: '>=16.0.0'} + '@smithy/eventstream-serde-universal@4.0.0': + resolution: {integrity: sha512-HEhZpf731J3oFYJtaKO3dnV6stIjA+lJwXuXGu/WbSgicDWGAOITUwTt9ynldEFsnFkNu9b/C4ebXnJA16xSCA==} + engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@4.1.2': - resolution: {integrity: sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA==} + '@smithy/fetch-http-handler@5.0.0': + resolution: {integrity: sha512-jUEq+4056uqsDLRqQb1fm48rrSMBYcBxVvODfiP37ORcV5n9xWJQsINWcIffyYxWTM5K0Y/GOfhSQGDtWpAPpQ==} + engines: {node: '>=18.0.0'} - '@smithy/hash-blob-browser@3.1.10': - resolution: {integrity: sha512-elwslXOoNunmfS0fh55jHggyhccobFkexLYC1ZeZ1xP2BTSrcIBaHV2b4xUQOdctrSNOpMqOZH1r2XzWTEhyfA==} + '@smithy/hash-blob-browser@4.0.0': + resolution: {integrity: sha512-JBXNC2YCDlm9uqP/eQJbK6auahAaq4HndJC2PURxWPRUDjbXDRJS5Npfi+7zSxKOSOWxXCG/3dLE5D8znI9l/w==} + engines: {node: '>=18.0.0'} - '@smithy/hash-node@3.0.11': - resolution: {integrity: sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA==} - engines: {node: '>=16.0.0'} + '@smithy/hash-node@4.0.0': + resolution: {integrity: sha512-25OxGYGnG3JPEOTk4iFE03bfmoC6GXUQ4L13z4cNdsS3mkncH22AGSDRfKwwEqutNUxXQZWVy9f72Fm59C9qlg==} + engines: {node: '>=18.0.0'} - '@smithy/hash-stream-node@3.1.10': - resolution: {integrity: sha512-olomK/jZQ93OMayW1zfTHwcbwBdhcZOHsyWyiZ9h9IXvc1mCD/VuvzbLb3Gy/qNJwI4MANPLctTp2BucV2oU/Q==} - engines: {node: '>=16.0.0'} + '@smithy/hash-stream-node@4.0.0': + resolution: {integrity: sha512-MRgYnr9atik1c02mdgjjJkNK5A8IvRRlpa/zOdA8PxmQtBCwjODKzobyI166uamxrL20wg7vuKoVSAjQU4IXfw==} + engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@3.0.11': - resolution: {integrity: sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ==} + '@smithy/invalid-dependency@4.0.0': + resolution: {integrity: sha512-0GTyet02HX/sPctEhOExY+3HI7hwkVwOoJg0XnItTJ+Xw7JMuL9FOxALTmKVIV6+wg0kF6veLeg72hVSbD9UCw==} + engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@3.0.0': - resolution: {integrity: sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==} - engines: {node: '>=16.0.0'} + '@smithy/is-array-buffer@4.0.0': + resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} + engines: {node: '>=18.0.0'} - '@smithy/md5-js@3.0.11': - resolution: {integrity: sha512-3NM0L3i2Zm4bbgG6Ymi9NBcxXhryi3uE8fIfHJZIOfZVxOkGdjdgjR9A06SFIZCfnEIWKXZdm6Yq5/aPXFFhsQ==} + '@smithy/md5-js@4.0.0': + resolution: {integrity: sha512-NUjbK+M1RNd0J/mM3eh4Yw5SfUrJBsIAea/H5dvc8tirxWFHFDUHJ/CK40/vtY3niiYnygWjZZ+ISydray6Raw==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@3.0.13': - resolution: {integrity: sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-content-length@4.0.0': + resolution: {integrity: sha512-nM1RJqLwkSCidumGK8WwNEZ0a0D/4LkwqdPna+QmHrdPoAK6WGLyZFosdMpsAW1OIbDLWGa+r37Mo4Vth4S4kQ==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@3.2.7': - resolution: {integrity: sha512-GTxSKf280aJBANGN97MomUQhW1VNxZ6w7HAj/pvZM5MUHbMPOGnWOp1PRYKi4czMaHNj9bdiA+ZarmT3Wkdqiw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-endpoint@4.0.0': + resolution: {integrity: sha512-/f6z5SqUurmqemhBZNhM0c+C7QW0AY/zJpic//sbdu26q98HSPAI/xvzStjYq+UhtWeAe/jaX6gamdL/2r3W1g==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@3.0.32': - resolution: {integrity: sha512-v8gVA9HqibuZkFuFpfkC/EcHE8no/3Mv3JvRUGly63Axt4yyas1WDVOasFSdiqm2hZVpY7/k8mRT1Wd5k7r3Yw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-retry@4.0.0': + resolution: {integrity: sha512-K6tsFp3Ik44H3694a+LWoXLV8mqy8zn6/vTw2feU72MaIzi51EHMVNNxxpL6e2GI6oxw8FFRGWgGn8+wQRrHZQ==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@3.0.11': - resolution: {integrity: sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-serde@4.0.0': + resolution: {integrity: sha512-aW4Zo8Cm988RCvhysErzqrQ4YPKgZFhajvgPoZnsWIDaZfT419J17Ahr13Lul3kqGad2dCz7YOrXd7r+UAEj/w==} + engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@3.0.11': - resolution: {integrity: sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA==} - engines: {node: '>=16.0.0'} + '@smithy/middleware-stack@4.0.0': + resolution: {integrity: sha512-4NFaX88RmgVrCyJv/3RsSdqMwxzI/EQa8nvhUDVxmLUMRS2JUdHnliD6IwKuqIwIzz+E1aZK3EhSHUM4HXp3ww==} + engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@3.1.12': - resolution: {integrity: sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ==} - engines: {node: '>=16.0.0'} + '@smithy/node-config-provider@4.0.0': + resolution: {integrity: sha512-Crp9rg1ewjqgM2i7pWSpNhfbBa0usyKGDVQLEXTOpu6trFqq3BFLLCgbCE1S18h6mxqKnOqUONq3nWOxUk75XA==} + engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@3.3.3': - resolution: {integrity: sha512-BrpZOaZ4RCbcJ2igiSNG16S+kgAc65l/2hmxWdmhyoGWHTLlzQzr06PXavJp9OBlPEG/sHlqdxjWmjzV66+BSQ==} - engines: {node: '>=16.0.0'} + '@smithy/node-http-handler@4.0.0': + resolution: {integrity: sha512-WvumtEaFyxaI95zmj6eYlF/vCFCKNyru3P/UUHCUS9BjvajUtNckH2cY3bBfi+qqMPX5gha4g26lcOlE/wPz/Q==} + engines: {node: '>=18.0.0'} - '@smithy/property-provider@3.1.11': - resolution: {integrity: sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A==} - engines: {node: '>=16.0.0'} + '@smithy/property-provider@4.0.0': + resolution: {integrity: sha512-AJSvY1k3SdM0stGrIjL8/FIjXO7X9I7KkznXDmr76RGz+yvaDHLsLm2hSHyzAlmwEQnHaafSU2dwaV0JcnR/4w==} + engines: {node: '>=18.0.0'} - '@smithy/protocol-http@4.1.8': - resolution: {integrity: sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw==} - engines: {node: '>=16.0.0'} + '@smithy/protocol-http@5.0.0': + resolution: {integrity: sha512-laAcIHWq9GQ5VdAS71DUrCj5HUHZ/89Ee+HRTLhFR5/E3toBlnZfPG+kqBajwfEB5aSdRuKslfzl5Dzrn3pr8A==} + engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@3.0.11': - resolution: {integrity: sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-builder@4.0.0': + resolution: {integrity: sha512-kMqPDRf+/hwm+Dmk8AQCaYTJxNWWpNdJJteeMm0jwDbmRDqSqHQ7oLEVzvOnbWJu1poVtOhv6v7jsbyx9JASsw==} + engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@3.0.11': - resolution: {integrity: sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw==} - engines: {node: '>=16.0.0'} + '@smithy/querystring-parser@4.0.0': + resolution: {integrity: sha512-SbogL1PNEmm28ya0eK2S0EZEbYwe0qpaqSGrODm+uYS6dQ7pekPLVNXjBRuuLIAT26ZF2wTsp6X7AVRBNZd8qw==} + engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@3.0.11': - resolution: {integrity: sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog==} - engines: {node: '>=16.0.0'} + '@smithy/service-error-classification@4.0.0': + resolution: {integrity: sha512-hIZreT6aXSG0PK/psT1S+kfeGTnYnRRlf7rU3yDmH/crSVjTbS/5h5w2J7eO2ODrQb3xfhJcYxQBREdwsZk6TA==} + engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@3.1.12': - resolution: {integrity: sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q==} - engines: {node: '>=16.0.0'} + '@smithy/shared-ini-file-loader@4.0.0': + resolution: {integrity: sha512-Ktupe8msp2GPaKKVfiz3NNUNnslJiGGRoVh3BDpm/RChkQ5INQpqmTc2taE0XChNYumNynLfb3keekIPaiaZeg==} + engines: {node: '>=18.0.0'} - '@smithy/signature-v4@4.2.4': - resolution: {integrity: sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA==} - engines: {node: '>=16.0.0'} + '@smithy/signature-v4@5.0.0': + resolution: {integrity: sha512-zqcOR1sZTuoA6K3PBNwzu4YgT1pmIwz47tYpgaJjBTfGUIMtcjUaXKtuSKEScdv+0wx45/PbXz0//hk80fky3w==} + engines: {node: '>=18.0.0'} - '@smithy/smithy-client@3.5.2': - resolution: {integrity: sha512-h7xn+1wlpbXyLrtvo/teHR1SFGIIrQ3imzG0nz43zVLAJgvfC1Mtdwa1pFhoIOYrt/TiNjt4pD0gSYQEdZSBtg==} - engines: {node: '>=16.0.0'} + '@smithy/smithy-client@4.0.0': + resolution: {integrity: sha512-AgcZ6B+JuqArYioAbaYrCpTCjYsD3/1hPSXntbN2ipsfc4hE+72RFZevUPYgsKxpy3G+QxuLfqm11i3+oX4oSA==} + engines: {node: '>=18.0.0'} - '@smithy/types@3.7.2': - resolution: {integrity: sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg==} - engines: {node: '>=16.0.0'} + '@smithy/types@4.0.0': + resolution: {integrity: sha512-aNwIGSOgDOhtTRY/rrn2aeuQeKw/IFrQ998yK5l6Ah853WeWIEmFPs/EO4OpfADEdcK+igWnZytm/oUgkLgUYg==} + engines: {node: '>=18.0.0'} - '@smithy/url-parser@3.0.11': - resolution: {integrity: sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw==} + '@smithy/url-parser@4.0.0': + resolution: {integrity: sha512-2iPpuLoH0hCKpLtqVgilHtpPKsmHihbkwBm3h3RPuEctdmuiOlFRZ2ZI8IHSwl0o4ff5IdyyJ0yu/2tS9KpUug==} + engines: {node: '>=18.0.0'} - '@smithy/util-base64@3.0.0': - resolution: {integrity: sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-base64@4.0.0': + resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} + engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@3.0.0': - resolution: {integrity: sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==} + '@smithy/util-body-length-browser@4.0.0': + resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} + engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@3.0.0': - resolution: {integrity: sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==} - engines: {node: '>=16.0.0'} + '@smithy/util-body-length-node@4.0.0': + resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} + engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@3.0.0': - resolution: {integrity: sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==} - engines: {node: '>=16.0.0'} + '@smithy/util-buffer-from@4.0.0': + resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} + engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@3.0.0': - resolution: {integrity: sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-config-provider@4.0.0': + resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} + engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@3.0.32': - resolution: {integrity: sha512-FAGsnm/xJ19SZeoqGyo9CosqjUlm+XJTmygDMktebvDKw3bKiIiZ40O1MA6Z52KLmekYU2GO7BEK7u6e7ZORKw==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-browser@4.0.0': + resolution: {integrity: sha512-7wqsXkzaJkpSqV+Ca95pN9yQutXvhaKeCxGGmjWnRGXY1fW/yR7wr1ouNnUYCJuTS8MvmB61xp5Qdj8YMgIA2Q==} + engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@3.0.32': - resolution: {integrity: sha512-2CzKhkPFCVdd15f3+0D1rldNlvJME8pVRBtVVsea2hy7lcOn0bGB0dTVUwzgfM4LW/aU4IOg3jWf25ZWaxbOiw==} - engines: {node: '>= 10.0.0'} + '@smithy/util-defaults-mode-node@4.0.0': + resolution: {integrity: sha512-P8VK885kiRT6TEtvcQvz+L/+xIhrDhCmM664ToUtrshFSBhwGYaJWlQNAH9fXlMhwnNvR+tmh1KngKJIgQP6bw==} + engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@2.1.7': - resolution: {integrity: sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw==} - engines: {node: '>=16.0.0'} + '@smithy/util-endpoints@3.0.0': + resolution: {integrity: sha512-kyOKbkg77lsIVN2jC08uEWm3s16eK1YdVDyi/nKeBDbUnjR30dmTEga79E5tiu5OEgTAdngNswA9V+L6xa65sA==} + engines: {node: '>=18.0.0'} - '@smithy/util-hex-encoding@3.0.0': - resolution: {integrity: sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-hex-encoding@4.0.0': + resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} + engines: {node: '>=18.0.0'} - '@smithy/util-middleware@3.0.11': - resolution: {integrity: sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow==} - engines: {node: '>=16.0.0'} + '@smithy/util-middleware@4.0.0': + resolution: {integrity: sha512-ncuvK6ekpDqtASHg7jx3d3nrkD2BsTzUmeVgvtepuHGxtySY8qUlb4SiNRdxHYcv3pL2SwdXs70RwKBU0edW5w==} + engines: {node: '>=18.0.0'} - '@smithy/util-retry@3.0.11': - resolution: {integrity: sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ==} - engines: {node: '>=16.0.0'} + '@smithy/util-retry@4.0.0': + resolution: {integrity: sha512-64WFoC19NVuHh3HQO2QbGw+n6GzQ6VH/drxwXLOU3GDLKxUUzIR9XNm9aTVqh8/7R+y+DgITiv5LpX5XdOy73A==} + engines: {node: '>=18.0.0'} - '@smithy/util-stream@3.3.3': - resolution: {integrity: sha512-bOm0YMMxRjbI3X6QkWwADPFkh2AH2xBMQIB1IQgCsCRqXXpSJatgjUR3oxHthpYwFkw3WPkOt8VgMpJxC0rFqg==} - engines: {node: '>=16.0.0'} + '@smithy/util-stream@4.0.0': + resolution: {integrity: sha512-ctcLq8Ogi2FQuGy2RxJXGGrozhFEb4p9FawB5SpTNAkNQWbNHcwrGcVSVI3FtdQtkNAINLiEdMnrx+UN/mafvw==} + engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@3.0.0': - resolution: {integrity: sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==} - engines: {node: '>=16.0.0'} + '@smithy/util-uri-escape@4.0.0': + resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} + engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@3.0.0': - resolution: {integrity: sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==} - engines: {node: '>=16.0.0'} + '@smithy/util-utf8@4.0.0': + resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} + engines: {node: '>=18.0.0'} - '@smithy/util-waiter@3.2.0': - resolution: {integrity: sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg==} - engines: {node: '>=16.0.0'} + '@smithy/util-waiter@4.0.0': + resolution: {integrity: sha512-C8dSfGmZH0ecrmnJOw4aDXJ47krihnUtee8eDZ2fA+28wTjD4TVM3L/bBQYnBBlDVWcYzldLV7loPRsPc1kERg==} + engines: {node: '>=18.0.0'} '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -2607,18 +2643,6 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tus/file-store@1.5.1': - resolution: {integrity: sha512-bEaZ9zPCezpJW0Cr7rfles+gylXZ+0rSGfQvoTHXoTAbwyy4iIytC25njf1PdWkh3JQU0dKIhZuspnAvVOMntA==} - engines: {node: '>=16'} - - '@tus/s3-store@1.6.2': - resolution: {integrity: sha512-u8+CxH8Q0E1Bf3PrmaxOWki826wHrOci2GLEyQ5Lj5UI+CzFOWzhNuq213PI9y2RDY53uizt5qMuXcrAO13bYw==} - engines: {node: '>=16'} - - '@tus/utils@0.5.0': - resolution: {integrity: sha512-SFJC9db7hJ5O9HbvpN9EKJYdjn8cx8tGgwFDDHPRXh+qBNYdb/MK6kh/vN6Rh82QH07D4k4nwGh4jlpugo6F7g==} - engines: {node: '>=16'} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -2725,6 +2749,9 @@ packages: '@types/multer@1.4.12': resolution: {integrity: sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==} + '@types/multistream@4.1.3': + resolution: {integrity: sha512-t57vmDEJOZuC0M3IrZYfCd9wolTcr3ZTCGk1iwHNosvgBX+7/SMvCGcR8wP9lidpelBZQ12crSuINOxkk0azPA==} + '@types/node@14.18.63': resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} @@ -5120,11 +5147,19 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + nanoid-cjs@0.0.7: + resolution: {integrity: sha512-z72crZ0JcTb5s40Pm9Vk99qfEw9Oe1qyVjK/kpelCKyZDH8YTX4HejSfp54PMJT8F5rmsiBpG6wfVAGAhLEFhA==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@5.0.9: + resolution: {integrity: sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q==} + engines: {node: ^18 || >=20} + hasBin: true + napi-macros@2.0.0: resolution: {integrity: sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==} @@ -5329,6 +5364,9 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + pinyin-pro@3.26.0: + resolution: {integrity: sha512-HcBZZb0pvm0/JkPhZHWA5Hqp2cWHXrrW/WrV+OtaYYM+kf35ffvZppIUuGmyuQ7gDr1JDJKMkbEE+GN0wfMoGg==} + pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -6037,6 +6075,24 @@ packages: shell-quote@1.8.1: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + should-equal@2.0.0: + resolution: {integrity: sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==} + + should-format@3.0.3: + resolution: {integrity: sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==} + + should-type-adaptors@1.1.0: + resolution: {integrity: sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==} + + should-type@1.4.0: + resolution: {integrity: sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==} + + should-util@1.0.1: + resolution: {integrity: sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==} + + should@13.2.3: + resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} + side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} @@ -6058,6 +6114,10 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slugify@1.6.6: + resolution: {integrity: sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==} + engines: {node: '>=8.0.0'} + snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} @@ -6996,21 +7056,21 @@ snapshots: '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.714.0 + '@aws-sdk/types': 3.723.0 tslib: 2.6.3 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.714.0 + '@aws-sdk/types': 3.723.0 tslib: 2.6.3 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-locate-window': 3.693.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-locate-window': 3.723.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 @@ -7019,15 +7079,15 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-locate-window': 3.693.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-locate-window': 3.723.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.714.0 + '@aws-sdk/types': 3.723.0 tslib: 2.6.3 '@aws-crypto/supports-web-crypto@5.2.0': @@ -7036,468 +7096,468 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.714.0 + '@aws-sdk/types': 3.723.0 '@smithy/util-utf8': 2.3.0 tslib: 2.6.3 - '@aws-sdk/client-s3@3.717.0': + '@aws-sdk/client-s3@3.723.0': dependencies: '@aws-crypto/sha1-browser': 5.2.0 '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/client-sts': 3.716.0 - '@aws-sdk/core': 3.716.0 - '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/middleware-bucket-endpoint': 3.714.0 - '@aws-sdk/middleware-expect-continue': 3.714.0 - '@aws-sdk/middleware-flexible-checksums': 3.717.0 - '@aws-sdk/middleware-host-header': 3.714.0 - '@aws-sdk/middleware-location-constraint': 3.714.0 - '@aws-sdk/middleware-logger': 3.714.0 - '@aws-sdk/middleware-recursion-detection': 3.714.0 - '@aws-sdk/middleware-sdk-s3': 3.716.0 - '@aws-sdk/middleware-ssec': 3.714.0 - '@aws-sdk/middleware-user-agent': 3.716.0 - '@aws-sdk/region-config-resolver': 3.714.0 - '@aws-sdk/signature-v4-multi-region': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-endpoints': 3.714.0 - '@aws-sdk/util-user-agent-browser': 3.714.0 - '@aws-sdk/util-user-agent-node': 3.716.0 - '@aws-sdk/xml-builder': 3.709.0 - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.6 - '@smithy/eventstream-serde-browser': 3.0.14 - '@smithy/eventstream-serde-config-resolver': 3.0.11 - '@smithy/eventstream-serde-node': 3.0.13 - '@smithy/fetch-http-handler': 4.1.2 - '@smithy/hash-blob-browser': 3.1.10 - '@smithy/hash-node': 3.0.11 - '@smithy/hash-stream-node': 3.1.10 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/md5-js': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.7 - '@smithy/middleware-retry': 3.0.32 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.32 - '@smithy/util-defaults-mode-node': 3.0.32 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-stream': 3.3.3 - '@smithy/util-utf8': 3.0.0 - '@smithy/util-waiter': 3.2.0 + '@aws-sdk/client-sso-oidc': 3.723.0(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/client-sts': 3.723.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/credential-provider-node': 3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/middleware-bucket-endpoint': 3.723.0 + '@aws-sdk/middleware-expect-continue': 3.723.0 + '@aws-sdk/middleware-flexible-checksums': 3.723.0 + '@aws-sdk/middleware-host-header': 3.723.0 + '@aws-sdk/middleware-location-constraint': 3.723.0 + '@aws-sdk/middleware-logger': 3.723.0 + '@aws-sdk/middleware-recursion-detection': 3.723.0 + '@aws-sdk/middleware-sdk-s3': 3.723.0 + '@aws-sdk/middleware-ssec': 3.723.0 + '@aws-sdk/middleware-user-agent': 3.723.0 + '@aws-sdk/region-config-resolver': 3.723.0 + '@aws-sdk/signature-v4-multi-region': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-endpoints': 3.723.0 + '@aws-sdk/util-user-agent-browser': 3.723.0 + '@aws-sdk/util-user-agent-node': 3.723.0 + '@aws-sdk/xml-builder': 3.723.0 + '@smithy/config-resolver': 4.0.0 + '@smithy/core': 3.0.0 + '@smithy/eventstream-serde-browser': 4.0.0 + '@smithy/eventstream-serde-config-resolver': 4.0.0 + '@smithy/eventstream-serde-node': 4.0.0 + '@smithy/fetch-http-handler': 5.0.0 + '@smithy/hash-blob-browser': 4.0.0 + '@smithy/hash-node': 4.0.0 + '@smithy/hash-stream-node': 4.0.0 + '@smithy/invalid-dependency': 4.0.0 + '@smithy/md5-js': 4.0.0 + '@smithy/middleware-content-length': 4.0.0 + '@smithy/middleware-endpoint': 4.0.0 + '@smithy/middleware-retry': 4.0.0 + '@smithy/middleware-serde': 4.0.0 + '@smithy/middleware-stack': 4.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/node-http-handler': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/url-parser': 4.0.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.0 + '@smithy/util-defaults-mode-node': 4.0.0 + '@smithy/util-endpoints': 3.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-retry': 4.0.0 + '@smithy/util-stream': 4.0.0 + '@smithy/util-utf8': 4.0.0 + '@smithy/util-waiter': 4.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)': + '@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0)': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sts': 3.716.0 - '@aws-sdk/core': 3.716.0 - '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/middleware-host-header': 3.714.0 - '@aws-sdk/middleware-logger': 3.714.0 - '@aws-sdk/middleware-recursion-detection': 3.714.0 - '@aws-sdk/middleware-user-agent': 3.716.0 - '@aws-sdk/region-config-resolver': 3.714.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-endpoints': 3.714.0 - '@aws-sdk/util-user-agent-browser': 3.714.0 - '@aws-sdk/util-user-agent-node': 3.716.0 - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.6 - '@smithy/fetch-http-handler': 4.1.2 - '@smithy/hash-node': 3.0.11 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.7 - '@smithy/middleware-retry': 3.0.32 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.32 - '@smithy/util-defaults-mode-node': 3.0.32 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/client-sts': 3.723.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/credential-provider-node': 3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/middleware-host-header': 3.723.0 + '@aws-sdk/middleware-logger': 3.723.0 + '@aws-sdk/middleware-recursion-detection': 3.723.0 + '@aws-sdk/middleware-user-agent': 3.723.0 + '@aws-sdk/region-config-resolver': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-endpoints': 3.723.0 + '@aws-sdk/util-user-agent-browser': 3.723.0 + '@aws-sdk/util-user-agent-node': 3.723.0 + '@smithy/config-resolver': 4.0.0 + '@smithy/core': 3.0.0 + '@smithy/fetch-http-handler': 5.0.0 + '@smithy/hash-node': 4.0.0 + '@smithy/invalid-dependency': 4.0.0 + '@smithy/middleware-content-length': 4.0.0 + '@smithy/middleware-endpoint': 4.0.0 + '@smithy/middleware-retry': 4.0.0 + '@smithy/middleware-serde': 4.0.0 + '@smithy/middleware-stack': 4.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/node-http-handler': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/url-parser': 4.0.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.0 + '@smithy/util-defaults-mode-node': 4.0.0 + '@smithy/util-endpoints': 3.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-retry': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.716.0': + '@aws-sdk/client-sso@3.723.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.716.0 - '@aws-sdk/middleware-host-header': 3.714.0 - '@aws-sdk/middleware-logger': 3.714.0 - '@aws-sdk/middleware-recursion-detection': 3.714.0 - '@aws-sdk/middleware-user-agent': 3.716.0 - '@aws-sdk/region-config-resolver': 3.714.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-endpoints': 3.714.0 - '@aws-sdk/util-user-agent-browser': 3.714.0 - '@aws-sdk/util-user-agent-node': 3.716.0 - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.6 - '@smithy/fetch-http-handler': 4.1.2 - '@smithy/hash-node': 3.0.11 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.7 - '@smithy/middleware-retry': 3.0.32 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.32 - '@smithy/util-defaults-mode-node': 3.0.32 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/middleware-host-header': 3.723.0 + '@aws-sdk/middleware-logger': 3.723.0 + '@aws-sdk/middleware-recursion-detection': 3.723.0 + '@aws-sdk/middleware-user-agent': 3.723.0 + '@aws-sdk/region-config-resolver': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-endpoints': 3.723.0 + '@aws-sdk/util-user-agent-browser': 3.723.0 + '@aws-sdk/util-user-agent-node': 3.723.0 + '@smithy/config-resolver': 4.0.0 + '@smithy/core': 3.0.0 + '@smithy/fetch-http-handler': 5.0.0 + '@smithy/hash-node': 4.0.0 + '@smithy/invalid-dependency': 4.0.0 + '@smithy/middleware-content-length': 4.0.0 + '@smithy/middleware-endpoint': 4.0.0 + '@smithy/middleware-retry': 4.0.0 + '@smithy/middleware-serde': 4.0.0 + '@smithy/middleware-stack': 4.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/node-http-handler': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/url-parser': 4.0.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.0 + '@smithy/util-defaults-mode-node': 4.0.0 + '@smithy/util-endpoints': 3.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-retry': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sts@3.716.0': + '@aws-sdk/client-sts@3.723.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/core': 3.716.0 - '@aws-sdk/credential-provider-node': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/middleware-host-header': 3.714.0 - '@aws-sdk/middleware-logger': 3.714.0 - '@aws-sdk/middleware-recursion-detection': 3.714.0 - '@aws-sdk/middleware-user-agent': 3.716.0 - '@aws-sdk/region-config-resolver': 3.714.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-endpoints': 3.714.0 - '@aws-sdk/util-user-agent-browser': 3.714.0 - '@aws-sdk/util-user-agent-node': 3.716.0 - '@smithy/config-resolver': 3.0.13 - '@smithy/core': 2.5.6 - '@smithy/fetch-http-handler': 4.1.2 - '@smithy/hash-node': 3.0.11 - '@smithy/invalid-dependency': 3.0.11 - '@smithy/middleware-content-length': 3.0.13 - '@smithy/middleware-endpoint': 3.2.7 - '@smithy/middleware-retry': 3.0.32 - '@smithy/middleware-serde': 3.0.11 - '@smithy/middleware-stack': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/node-http-handler': 3.3.3 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.32 - '@smithy/util-defaults-mode-node': 3.0.32 - '@smithy/util-endpoints': 2.1.7 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/client-sso-oidc': 3.723.0(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/core': 3.723.0 + '@aws-sdk/credential-provider-node': 3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/middleware-host-header': 3.723.0 + '@aws-sdk/middleware-logger': 3.723.0 + '@aws-sdk/middleware-recursion-detection': 3.723.0 + '@aws-sdk/middleware-user-agent': 3.723.0 + '@aws-sdk/region-config-resolver': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-endpoints': 3.723.0 + '@aws-sdk/util-user-agent-browser': 3.723.0 + '@aws-sdk/util-user-agent-node': 3.723.0 + '@smithy/config-resolver': 4.0.0 + '@smithy/core': 3.0.0 + '@smithy/fetch-http-handler': 5.0.0 + '@smithy/hash-node': 4.0.0 + '@smithy/invalid-dependency': 4.0.0 + '@smithy/middleware-content-length': 4.0.0 + '@smithy/middleware-endpoint': 4.0.0 + '@smithy/middleware-retry': 4.0.0 + '@smithy/middleware-serde': 4.0.0 + '@smithy/middleware-stack': 4.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/node-http-handler': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/url-parser': 4.0.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.0 + '@smithy/util-defaults-mode-node': 4.0.0 + '@smithy/util-endpoints': 3.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-retry': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.716.0': + '@aws-sdk/core@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/core': 2.5.6 - '@smithy/node-config-provider': 3.1.12 - '@smithy/property-provider': 3.1.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/signature-v4': 4.2.4 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/util-middleware': 3.0.11 + '@aws-sdk/types': 3.723.0 + '@smithy/core': 3.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/property-provider': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/signature-v4': 5.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-middleware': 4.0.0 fast-xml-parser: 4.4.1 tslib: 2.6.3 - '@aws-sdk/credential-provider-env@3.716.0': + '@aws-sdk/credential-provider-env@3.723.0': dependencies: - '@aws-sdk/core': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@smithy/property-provider': 3.1.11 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@smithy/property-provider': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-http@3.716.0': + '@aws-sdk/credential-provider-http@3.723.0': dependencies: - '@aws-sdk/core': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@smithy/fetch-http-handler': 4.1.2 - '@smithy/node-http-handler': 3.3.3 - '@smithy/property-provider': 3.1.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/util-stream': 3.3.3 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@smithy/fetch-http-handler': 5.0.0 + '@smithy/node-http-handler': 4.0.0 + '@smithy/property-provider': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-stream': 4.0.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-ini@3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0)': + '@aws-sdk/credential-provider-ini@3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))(@aws-sdk/client-sts@3.723.0)': dependencies: - '@aws-sdk/client-sts': 3.716.0 - '@aws-sdk/core': 3.716.0 - '@aws-sdk/credential-provider-env': 3.716.0 - '@aws-sdk/credential-provider-http': 3.716.0 - '@aws-sdk/credential-provider-process': 3.716.0 - '@aws-sdk/credential-provider-sso': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)) - '@aws-sdk/credential-provider-web-identity': 3.716.0(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/types': 3.714.0 - '@smithy/credential-provider-imds': 3.2.8 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/client-sts': 3.723.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/credential-provider-env': 3.723.0 + '@aws-sdk/credential-provider-http': 3.723.0 + '@aws-sdk/credential-provider-process': 3.723.0 + '@aws-sdk/credential-provider-sso': 3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0)) + '@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/types': 3.723.0 + '@smithy/credential-provider-imds': 4.0.0 + '@smithy/property-provider': 4.0.0 + '@smithy/shared-ini-file-loader': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-node@3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0)': + '@aws-sdk/credential-provider-node@3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))(@aws-sdk/client-sts@3.723.0)': dependencies: - '@aws-sdk/credential-provider-env': 3.716.0 - '@aws-sdk/credential-provider-http': 3.716.0 - '@aws-sdk/credential-provider-ini': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/credential-provider-process': 3.716.0 - '@aws-sdk/credential-provider-sso': 3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)) - '@aws-sdk/credential-provider-web-identity': 3.716.0(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/types': 3.714.0 - '@smithy/credential-provider-imds': 3.2.8 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/credential-provider-env': 3.723.0 + '@aws-sdk/credential-provider-http': 3.723.0 + '@aws-sdk/credential-provider-ini': 3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/credential-provider-process': 3.723.0 + '@aws-sdk/credential-provider-sso': 3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0)) + '@aws-sdk/credential-provider-web-identity': 3.723.0(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/types': 3.723.0 + '@smithy/credential-provider-imds': 4.0.0 + '@smithy/property-provider': 4.0.0 + '@smithy/shared-ini-file-loader': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - '@aws-sdk/client-sts' - aws-crt - '@aws-sdk/credential-provider-process@3.716.0': + '@aws-sdk/credential-provider-process@3.723.0': dependencies: - '@aws-sdk/core': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@smithy/property-provider': 4.0.0 + '@smithy/shared-ini-file-loader': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/credential-provider-sso@3.716.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))': + '@aws-sdk/credential-provider-sso@3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))': dependencies: - '@aws-sdk/client-sso': 3.716.0 - '@aws-sdk/core': 3.716.0 - '@aws-sdk/token-providers': 3.714.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0)) - '@aws-sdk/types': 3.714.0 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/client-sso': 3.723.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/token-providers': 3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0)) + '@aws-sdk/types': 3.723.0 + '@smithy/property-provider': 4.0.0 + '@smithy/shared-ini-file-loader': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 transitivePeerDependencies: - '@aws-sdk/client-sso-oidc' - aws-crt - '@aws-sdk/credential-provider-web-identity@3.716.0(@aws-sdk/client-sts@3.716.0)': + '@aws-sdk/credential-provider-web-identity@3.723.0(@aws-sdk/client-sts@3.723.0)': dependencies: - '@aws-sdk/client-sts': 3.716.0 - '@aws-sdk/core': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@smithy/property-provider': 3.1.11 - '@smithy/types': 3.7.2 + '@aws-sdk/client-sts': 3.723.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@smithy/property-provider': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-bucket-endpoint@3.714.0': + '@aws-sdk/middleware-bucket-endpoint@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-arn-parser': 3.693.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-config-provider': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-expect-continue@3.714.0': + '@aws-sdk/middleware-expect-continue@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.723.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-flexible-checksums@3.717.0': + '@aws-sdk/middleware-flexible-checksums@3.723.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@smithy/is-array-buffer': 3.0.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-stream': 3.3.3 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@smithy/is-array-buffer': 4.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-stream': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-host-header@3.714.0': + '@aws-sdk/middleware-host-header@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.723.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-location-constraint@3.714.0': + '@aws-sdk/middleware-location-constraint@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.723.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-logger@3.714.0': + '@aws-sdk/middleware-logger@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.723.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-recursion-detection@3.714.0': + '@aws-sdk/middleware-recursion-detection@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.723.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-sdk-s3@3.716.0': + '@aws-sdk/middleware-sdk-s3@3.723.0': dependencies: - '@aws-sdk/core': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-arn-parser': 3.693.0 - '@smithy/core': 2.5.6 - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/signature-v4': 4.2.4 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-stream': 3.3.3 - '@smithy/util-utf8': 3.0.0 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/core': 3.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/signature-v4': 5.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-stream': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-ssec@3.714.0': + '@aws-sdk/middleware-ssec@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.723.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/middleware-user-agent@3.716.0': + '@aws-sdk/middleware-user-agent@3.723.0': dependencies: - '@aws-sdk/core': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@aws-sdk/util-endpoints': 3.714.0 - '@smithy/core': 2.5.6 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@aws-sdk/core': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@aws-sdk/util-endpoints': 3.723.0 + '@smithy/core': 3.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/region-config-resolver@3.714.0': + '@aws-sdk/region-config-resolver@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.11 + '@aws-sdk/types': 3.723.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.0 tslib: 2.6.3 - '@aws-sdk/signature-v4-multi-region@3.716.0': + '@aws-sdk/signature-v4-multi-region@3.723.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/signature-v4': 4.2.4 - '@smithy/types': 3.7.2 + '@aws-sdk/middleware-sdk-s3': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/signature-v4': 5.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/token-providers@3.714.0(@aws-sdk/client-sso-oidc@3.716.0(@aws-sdk/client-sts@3.716.0))': + '@aws-sdk/token-providers@3.723.0(@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0))': dependencies: - '@aws-sdk/client-sso-oidc': 3.716.0(@aws-sdk/client-sts@3.716.0) - '@aws-sdk/types': 3.714.0 - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/client-sso-oidc': 3.723.0(@aws-sdk/client-sts@3.723.0) + '@aws-sdk/types': 3.723.0 + '@smithy/property-provider': 4.0.0 + '@smithy/shared-ini-file-loader': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/types@3.714.0': + '@aws-sdk/types@3.723.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/util-arn-parser@3.693.0': + '@aws-sdk/util-arn-parser@3.723.0': dependencies: tslib: 2.6.3 - '@aws-sdk/util-endpoints@3.714.0': + '@aws-sdk/util-endpoints@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/types': 3.7.2 - '@smithy/util-endpoints': 2.1.7 + '@aws-sdk/types': 3.723.0 + '@smithy/types': 4.0.0 + '@smithy/util-endpoints': 3.0.0 tslib: 2.6.3 - '@aws-sdk/util-locate-window@3.693.0': + '@aws-sdk/util-locate-window@3.723.0': dependencies: tslib: 2.6.3 - '@aws-sdk/util-user-agent-browser@3.714.0': + '@aws-sdk/util-user-agent-browser@3.723.0': dependencies: - '@aws-sdk/types': 3.714.0 - '@smithy/types': 3.7.2 + '@aws-sdk/types': 3.723.0 + '@smithy/types': 4.0.0 bowser: 2.11.0 tslib: 2.6.3 - '@aws-sdk/util-user-agent-node@3.716.0': + '@aws-sdk/util-user-agent-node@3.723.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.716.0 - '@aws-sdk/types': 3.714.0 - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 + '@aws-sdk/middleware-user-agent': 3.723.0 + '@aws-sdk/types': 3.723.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@aws-sdk/xml-builder@3.709.0': + '@aws-sdk/xml-builder@3.723.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 '@babel/code-frame@7.24.7': @@ -8611,7 +8671,6 @@ snapshots: cluster-key-slot: 1.1.2 generic-pool: 3.9.0 yallist: 4.0.0 - optional: true '@remix-run/router@1.17.1': {} @@ -8740,250 +8799,250 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@smithy/abort-controller@3.1.9': + '@smithy/abort-controller@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/chunked-blob-reader-native@3.0.1': + '@smithy/chunked-blob-reader-native@4.0.0': dependencies: - '@smithy/util-base64': 3.0.0 + '@smithy/util-base64': 4.0.0 tslib: 2.6.3 - '@smithy/chunked-blob-reader@4.0.0': + '@smithy/chunked-blob-reader@5.0.0': dependencies: tslib: 2.6.3 - '@smithy/config-resolver@3.0.13': + '@smithy/config-resolver@4.0.0': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 - '@smithy/util-config-provider': 3.0.0 - '@smithy/util-middleware': 3.0.11 + '@smithy/node-config-provider': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.0 tslib: 2.6.3 - '@smithy/core@2.5.6': + '@smithy/core@3.0.0': dependencies: - '@smithy/middleware-serde': 3.0.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-stream': 3.3.3 - '@smithy/util-utf8': 3.0.0 + '@smithy/middleware-serde': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-stream': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@smithy/credential-provider-imds@3.2.8': + '@smithy/credential-provider-imds@4.0.0': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/property-provider': 3.1.11 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 + '@smithy/node-config-provider': 4.0.0 + '@smithy/property-provider': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/url-parser': 4.0.0 tslib: 2.6.3 - '@smithy/eventstream-codec@3.1.10': + '@smithy/eventstream-codec@4.0.0': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 3.7.2 - '@smithy/util-hex-encoding': 3.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 tslib: 2.6.3 - '@smithy/eventstream-serde-browser@3.0.14': + '@smithy/eventstream-serde-browser@4.0.0': dependencies: - '@smithy/eventstream-serde-universal': 3.0.13 - '@smithy/types': 3.7.2 + '@smithy/eventstream-serde-universal': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/eventstream-serde-config-resolver@3.0.11': + '@smithy/eventstream-serde-config-resolver@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/eventstream-serde-node@3.0.13': + '@smithy/eventstream-serde-node@4.0.0': dependencies: - '@smithy/eventstream-serde-universal': 3.0.13 - '@smithy/types': 3.7.2 + '@smithy/eventstream-serde-universal': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/eventstream-serde-universal@3.0.13': + '@smithy/eventstream-serde-universal@4.0.0': dependencies: - '@smithy/eventstream-codec': 3.1.10 - '@smithy/types': 3.7.2 + '@smithy/eventstream-codec': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/fetch-http-handler@4.1.2': + '@smithy/fetch-http-handler@5.0.0': dependencies: - '@smithy/protocol-http': 4.1.8 - '@smithy/querystring-builder': 3.0.11 - '@smithy/types': 3.7.2 - '@smithy/util-base64': 3.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/querystring-builder': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-base64': 4.0.0 tslib: 2.6.3 - '@smithy/hash-blob-browser@3.1.10': + '@smithy/hash-blob-browser@4.0.0': dependencies: - '@smithy/chunked-blob-reader': 4.0.0 - '@smithy/chunked-blob-reader-native': 3.0.1 - '@smithy/types': 3.7.2 + '@smithy/chunked-blob-reader': 5.0.0 + '@smithy/chunked-blob-reader-native': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/hash-node@3.0.11': + '@smithy/hash-node@4.0.0': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@smithy/hash-stream-node@3.1.10': + '@smithy/hash-stream-node@4.0.0': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-utf8': 3.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@smithy/invalid-dependency@3.0.11': + '@smithy/invalid-dependency@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.6.3 - '@smithy/is-array-buffer@3.0.0': + '@smithy/is-array-buffer@4.0.0': dependencies: tslib: 2.6.3 - '@smithy/md5-js@3.0.11': + '@smithy/md5-js@4.0.0': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-utf8': 3.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@smithy/middleware-content-length@3.0.13': + '@smithy/middleware-content-length@4.0.0': dependencies: - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/middleware-endpoint@3.2.7': + '@smithy/middleware-endpoint@4.0.0': dependencies: - '@smithy/core': 2.5.6 - '@smithy/middleware-serde': 3.0.11 - '@smithy/node-config-provider': 3.1.12 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 - '@smithy/url-parser': 3.0.11 - '@smithy/util-middleware': 3.0.11 + '@smithy/core': 3.0.0 + '@smithy/middleware-serde': 4.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/shared-ini-file-loader': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/url-parser': 4.0.0 + '@smithy/util-middleware': 4.0.0 tslib: 2.6.3 - '@smithy/middleware-retry@3.0.32': + '@smithy/middleware-retry@4.0.0': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/protocol-http': 4.1.8 - '@smithy/service-error-classification': 3.0.11 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-retry': 3.0.11 + '@smithy/node-config-provider': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/service-error-classification': 4.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-retry': 4.0.0 tslib: 2.6.3 uuid: 9.0.1 - '@smithy/middleware-serde@3.0.11': + '@smithy/middleware-serde@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/middleware-stack@3.0.11': + '@smithy/middleware-stack@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/node-config-provider@3.1.12': + '@smithy/node-config-provider@4.0.0': dependencies: - '@smithy/property-provider': 3.1.11 - '@smithy/shared-ini-file-loader': 3.1.12 - '@smithy/types': 3.7.2 + '@smithy/property-provider': 4.0.0 + '@smithy/shared-ini-file-loader': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/node-http-handler@3.3.3': + '@smithy/node-http-handler@4.0.0': dependencies: - '@smithy/abort-controller': 3.1.9 - '@smithy/protocol-http': 4.1.8 - '@smithy/querystring-builder': 3.0.11 - '@smithy/types': 3.7.2 + '@smithy/abort-controller': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/querystring-builder': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/property-provider@3.1.11': + '@smithy/property-provider@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/protocol-http@4.1.8': + '@smithy/protocol-http@5.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/querystring-builder@3.0.11': + '@smithy/querystring-builder@4.0.0': dependencies: - '@smithy/types': 3.7.2 - '@smithy/util-uri-escape': 3.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-uri-escape': 4.0.0 tslib: 2.6.3 - '@smithy/querystring-parser@3.0.11': + '@smithy/querystring-parser@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/service-error-classification@3.0.11': + '@smithy/service-error-classification@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 - '@smithy/shared-ini-file-loader@3.1.12': + '@smithy/shared-ini-file-loader@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/signature-v4@4.2.4': + '@smithy/signature-v4@5.0.0': dependencies: - '@smithy/is-array-buffer': 3.0.0 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-middleware': 3.0.11 - '@smithy/util-uri-escape': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/is-array-buffer': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-middleware': 4.0.0 + '@smithy/util-uri-escape': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@smithy/smithy-client@3.5.2': + '@smithy/smithy-client@4.0.0': dependencies: - '@smithy/core': 2.5.6 - '@smithy/middleware-endpoint': 3.2.7 - '@smithy/middleware-stack': 3.0.11 - '@smithy/protocol-http': 4.1.8 - '@smithy/types': 3.7.2 - '@smithy/util-stream': 3.3.3 + '@smithy/core': 3.0.0 + '@smithy/middleware-endpoint': 4.0.0 + '@smithy/middleware-stack': 4.0.0 + '@smithy/protocol-http': 5.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-stream': 4.0.0 tslib: 2.6.3 - '@smithy/types@3.7.2': + '@smithy/types@4.0.0': dependencies: tslib: 2.6.3 - '@smithy/url-parser@3.0.11': + '@smithy/url-parser@4.0.0': dependencies: - '@smithy/querystring-parser': 3.0.11 - '@smithy/types': 3.7.2 + '@smithy/querystring-parser': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/util-base64@3.0.0': + '@smithy/util-base64@4.0.0': dependencies: - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@smithy/util-body-length-browser@3.0.0': + '@smithy/util-body-length-browser@4.0.0': dependencies: tslib: 2.6.3 - '@smithy/util-body-length-node@3.0.0': + '@smithy/util-body-length-node@4.0.0': dependencies: tslib: 2.6.3 @@ -8992,66 +9051,66 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.6.3 - '@smithy/util-buffer-from@3.0.0': + '@smithy/util-buffer-from@4.0.0': dependencies: - '@smithy/is-array-buffer': 3.0.0 + '@smithy/is-array-buffer': 4.0.0 tslib: 2.6.3 - '@smithy/util-config-provider@3.0.0': + '@smithy/util-config-provider@4.0.0': dependencies: tslib: 2.6.3 - '@smithy/util-defaults-mode-browser@3.0.32': + '@smithy/util-defaults-mode-browser@4.0.0': dependencies: - '@smithy/property-provider': 3.1.11 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 + '@smithy/property-provider': 4.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 bowser: 2.11.0 tslib: 2.6.3 - '@smithy/util-defaults-mode-node@3.0.32': + '@smithy/util-defaults-mode-node@4.0.0': dependencies: - '@smithy/config-resolver': 3.0.13 - '@smithy/credential-provider-imds': 3.2.8 - '@smithy/node-config-provider': 3.1.12 - '@smithy/property-provider': 3.1.11 - '@smithy/smithy-client': 3.5.2 - '@smithy/types': 3.7.2 + '@smithy/config-resolver': 4.0.0 + '@smithy/credential-provider-imds': 4.0.0 + '@smithy/node-config-provider': 4.0.0 + '@smithy/property-provider': 4.0.0 + '@smithy/smithy-client': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/util-endpoints@2.1.7': + '@smithy/util-endpoints@3.0.0': dependencies: - '@smithy/node-config-provider': 3.1.12 - '@smithy/types': 3.7.2 + '@smithy/node-config-provider': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/util-hex-encoding@3.0.0': + '@smithy/util-hex-encoding@4.0.0': dependencies: tslib: 2.6.3 - '@smithy/util-middleware@3.0.11': + '@smithy/util-middleware@4.0.0': dependencies: - '@smithy/types': 3.7.2 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/util-retry@3.0.11': + '@smithy/util-retry@4.0.0': dependencies: - '@smithy/service-error-classification': 3.0.11 - '@smithy/types': 3.7.2 + '@smithy/service-error-classification': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 - '@smithy/util-stream@3.3.3': + '@smithy/util-stream@4.0.0': dependencies: - '@smithy/fetch-http-handler': 4.1.2 - '@smithy/node-http-handler': 3.3.3 - '@smithy/types': 3.7.2 - '@smithy/util-base64': 3.0.0 - '@smithy/util-buffer-from': 3.0.0 - '@smithy/util-hex-encoding': 3.0.0 - '@smithy/util-utf8': 3.0.0 + '@smithy/fetch-http-handler': 5.0.0 + '@smithy/node-http-handler': 4.0.0 + '@smithy/types': 4.0.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-utf8': 4.0.0 tslib: 2.6.3 - '@smithy/util-uri-escape@3.0.0': + '@smithy/util-uri-escape@4.0.0': dependencies: tslib: 2.6.3 @@ -9060,15 +9119,15 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.6.3 - '@smithy/util-utf8@3.0.0': + '@smithy/util-utf8@4.0.0': dependencies: - '@smithy/util-buffer-from': 3.0.0 + '@smithy/util-buffer-from': 4.0.0 tslib: 2.6.3 - '@smithy/util-waiter@3.2.0': + '@smithy/util-waiter@4.0.0': dependencies: - '@smithy/abort-controller': 3.1.9 - '@smithy/types': 3.7.2 + '@smithy/abort-controller': 4.0.0 + '@smithy/types': 4.0.0 tslib: 2.6.3 '@socket.io/component-emitter@3.1.2': {} @@ -9248,28 +9307,6 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@tus/file-store@1.5.1': - dependencies: - '@tus/utils': 0.5.0 - debug: 4.4.0 - optionalDependencies: - '@redis/client': 1.6.0 - transitivePeerDependencies: - - supports-color - - '@tus/s3-store@1.6.2': - dependencies: - '@aws-sdk/client-s3': 3.717.0 - '@shopify/semaphore': 3.1.0 - '@tus/utils': 0.5.0 - debug: 4.4.0 - multistream: 4.1.0 - transitivePeerDependencies: - - aws-crt - - supports-color - - '@tus/utils@0.5.0': {} - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.24.7 @@ -9400,6 +9437,10 @@ snapshots: dependencies: '@types/express': 4.17.21 + '@types/multistream@4.1.3': + dependencies: + '@types/node': 20.14.10 + '@types/node@14.18.63': {} '@types/node@20.14.10': @@ -11236,8 +11277,7 @@ snapshots: function-bind@1.1.2: {} - generic-pool@3.9.0: - optional: true + generic-pool@3.9.0: {} gensync@1.0.0-beta.2: {} @@ -12322,8 +12362,14 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + nanoid-cjs@0.0.7: + dependencies: + nanoid: 5.0.9 + nanoid@3.3.7: {} + nanoid@5.0.9: {} + napi-macros@2.0.0: {} natural-compare@1.4.0: {} @@ -12490,6 +12536,8 @@ snapshots: pify@2.3.0: {} + pinyin-pro@3.26.0: {} + pirates@4.0.6: {} pkg-dir@4.2.0: @@ -13336,6 +13384,32 @@ snapshots: shell-quote@1.8.1: {} + should-equal@2.0.0: + dependencies: + should-type: 1.4.0 + + should-format@3.0.3: + dependencies: + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + + should-type-adaptors@1.1.0: + dependencies: + should-type: 1.4.0 + should-util: 1.0.1 + + should-type@1.4.0: {} + + should-util@1.0.1: {} + + should@13.2.3: + dependencies: + should-equal: 2.0.0 + should-format: 3.0.3 + should-type: 1.4.0 + should-type-adaptors: 1.1.0 + should-util: 1.0.1 + side-channel@1.0.6: dependencies: call-bind: 1.0.7 @@ -13355,6 +13429,8 @@ snapshots: slash@3.0.0: {} + slugify@1.6.6: {} + snake-case@3.0.4: dependencies: dot-case: 3.0.4 @@ -14082,8 +14158,7 @@ snapshots: yallist@3.1.1: {} - yallist@4.0.0: - optional: true + yallist@4.0.0: {} yaml@2.4.5: {}