@datrix/api-upload 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/dist/index.js +335 -292
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +331 -288
- package/dist/index.mjs.map +1 -1
- package/package.json +7 -8
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/schema.ts","../src/handler.ts","../src/processor.ts","../src/upload.ts","../src/providers/local.ts","../src/providers/s3.ts"],"sourcesContent":["export { Upload } from \"./upload\";\nexport { LocalStorageProvider } from \"./providers/local\";\nexport { S3StorageProvider } from \"./providers/s3\";\nexport type { UploadOptions } from \"./types\";\n","/**\n * Media Schema Factory\n */\n\nimport { defineSchema } from \"@datrix/core\";\nimport type { SchemaPermission, SchemaDefinition } from \"@datrix/core\";\nimport type { UploadOptions } from \"./types\";\n\nexport function createMediaSchema(\n\toptions: UploadOptions,\n\tpermission?: SchemaPermission,\n): SchemaDefinition {\n\tconst modelName = options.modelName ?? \"media\";\n\n\treturn defineSchema({\n\t\tname: modelName,\n\t\tfields: {\n\t\t\tfilename: { type: \"string\", required: true },\n\t\t\toriginalName: { type: \"string\", required: true },\n\t\t\tmimeType: { type: \"string\", required: true },\n\t\t\tsize: { type: \"number\", required: true, integer: true },\n\t\t\tkey: { type: \"string\", required: true },\n\t\t\tvariants: { type: \"json\" },\n\t\t},\n\t\t...(permission !== undefined && { permission }),\n\t});\n}\n","/**\n * Upload Handler\n *\n * POST /upload — multipart/form-data parse, format conversion, variant generation, DB record\n * DELETE /upload/:id — provider delete (all variants) + DB record delete\n *\n * GET /upload and GET /upload/:id fall through to normal CRUD.\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport type { UploadFile, MediaVariants } from \"@datrix/core\";\nimport type { DatrixEntry } from \"@datrix/core\";\nimport {\n\tDatrixApiError,\n\thandlerError,\n\tjsonResponse,\n\tdatrixErrorResponse,\n} from \"@datrix/api\";\nimport { DatrixError, DatrixValidationError } from \"@datrix/core\";\nimport type { UploadOptions } from \"./types\";\nimport { convertFormat, generateVariants, isImage } from \"./processor\";\n\nexport interface UploadHandlerOptions {\n\tdatrix: Datrix;\n\tuploadOptions: UploadOptions;\n\tinjectUrls?: (data: unknown) => Promise<unknown>;\n}\n\nexport async function handleUploadRequest(\n\trequest: Request,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\ttry {\n\t\tconst { method } = request;\n\t\tconst url = new URL(request.url);\n\n\t\tconst pathAfterUpload = url.pathname.replace(/.*\\/upload/, \"\");\n\t\tconst idSegment = pathAfterUpload.replace(/^\\//, \"\").split(\"/\")[0];\n\t\tconst id =\n\t\t\tidSegment !== undefined && idSegment !== \"\" ? Number(idSegment) : null;\n\n\t\tif (method === \"POST\" && id === null) {\n\t\t\treturn await handleUpload(request, options);\n\t\t}\n\n\t\tif (method === \"DELETE\" && id !== null) {\n\t\t\treturn await handleDeleteMedia(id, options);\n\t\t}\n\n\t\treturn datrixErrorResponse(handlerError.methodNotAllowed(method));\n\t} catch (error) {\n\t\tif (error instanceof DatrixValidationError || error instanceof DatrixError) {\n\t\t\treturn datrixErrorResponse(error);\n\t\t}\n\n\t\tconst message = error instanceof Error ? error.message : \"Upload failed\";\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.internalError(\n\t\t\t\tmessage,\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t),\n\t\t);\n\t}\n}\n\nasync function handleUpload(\n\trequest: Request,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\tconst { datrix, uploadOptions } = options;\n\tconst modelName = uploadOptions.modelName ?? \"media\";\n\n\tconst contentType = request.headers.get(\"content-type\") ?? \"\";\n\tif (!contentType.includes(\"multipart/form-data\")) {\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.invalidBody(\"Expected multipart/form-data\"),\n\t\t);\n\t}\n\n\tlet formData: FormData;\n\ttry {\n\t\tformData = await request.formData();\n\t} catch (error) {\n\t\tconst cause = error instanceof Error ? error : undefined;\n\t\tthrow new DatrixApiError(\"Failed to parse multipart form data\", {\n\t\t\tcode: \"MULTIPART_PARSE_ERROR\",\n\t\t\tstatus: 400,\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t}\n\n\tconst fileEntry = formData.get(\"file\");\n\tif (!(fileEntry instanceof File)) {\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.invalidBody(\"No file field in form data\"),\n\t\t);\n\t}\n\n\tconst buffer = await fileEntry.arrayBuffer();\n\tconst rawFile: UploadFile = {\n\t\tfilename: fileEntry.name,\n\t\toriginalName: fileEntry.name,\n\t\tmimetype: fileEntry.type,\n\t\tsize: fileEntry.size,\n\t\tbuffer: new Uint8Array(buffer),\n\t};\n\n\tvalidateFileLimits(rawFile, uploadOptions);\n\n\t// Format conversion (if configured and file is an image)\n\tconst quality = uploadOptions.quality ?? 80;\n\tconst fileToUpload =\n\t\tuploadOptions.format !== undefined && isImage(rawFile.mimetype)\n\t\t\t? await convertFormat(rawFile, uploadOptions.format, quality)\n\t\t\t: rawFile;\n\n\tconst uploadFile: UploadFile = {\n\t\tfilename: fileToUpload.filename,\n\t\toriginalName: rawFile.originalName,\n\t\tmimetype: fileToUpload.mimetype,\n\t\tsize: fileToUpload.buffer.length,\n\t\tbuffer: fileToUpload.buffer,\n\t};\n\n\t// Upload original (or converted) file\n\tconst result = await uploadOptions.provider.upload(uploadFile);\n\n\t// Generate resolution variants (if configured and file is an image)\n\tlet variants: MediaVariants | null = null;\n\tif (uploadOptions.resolutions !== undefined && isImage(uploadFile.mimetype)) {\n\t\tconst generated = await generateVariants(\n\t\t\tuploadFile,\n\t\t\tuploadOptions.resolutions,\n\t\t\tuploadOptions.format,\n\t\t\tquality,\n\t\t\tasync (variantFile) => {\n\t\t\t\tconst variantResult = await uploadOptions.provider.upload(variantFile);\n\t\t\t\treturn { key: variantResult.key };\n\t\t\t},\n\t\t);\n\t\tvariants = generated;\n\t}\n\n\tconst mediaRecord = await datrix.raw.create(modelName, {\n\t\tfilename: result.key,\n\t\toriginalName: uploadFile.originalName,\n\t\tmimeType: uploadFile.mimetype,\n\t\tsize: uploadFile.size,\n\t\tkey: result.key,\n\t\t...(variants !== null && { variants }),\n\t});\n\n\tconst data = options.injectUrls\n\t\t? await options.injectUrls(mediaRecord)\n\t\t: mediaRecord;\n\n\treturn jsonResponse({ data }, 201);\n}\n\n/**\n * DELETE /upload/:id\n * Deletes all variant keys from storage, then the main record.\n */\nasync function handleDeleteMedia(\n\tid: number,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\tconst { datrix, uploadOptions } = options;\n\tconst modelName = uploadOptions.modelName ?? \"media\";\n\n\ttype MediaRecord = {\n\t\tkey: string;\n\t\tvariants: Record<string, { key: string }> | null;\n\t} & DatrixEntry;\n\tconst record = await datrix.raw.findById<MediaRecord>(modelName, id);\n\n\tif (record === null) {\n\t\treturn datrixErrorResponse(handlerError.recordNotFound(modelName, id));\n\t}\n\n\t// Delete variant files from storage\n\tif (record.variants !== null && record.variants !== undefined) {\n\t\tfor (const variant of Object.values(record.variants)) {\n\t\t\tawait uploadOptions.provider.delete(variant.key);\n\t\t}\n\t}\n\n\t// Delete main file from storage\n\tawait uploadOptions.provider.delete(record.key);\n\tawait datrix.raw.delete(modelName, id);\n\n\treturn jsonResponse({ data: { id } });\n}\n\nfunction validateFileLimits(file: UploadFile, options: UploadOptions): void {\n\tif (options.maxSize !== undefined && file.size > options.maxSize) {\n\t\tthrow new DatrixApiError(\n\t\t\t`File size ${file.size} exceeds maximum allowed size ${options.maxSize}`,\n\t\t\t{ code: \"FILE_TOO_LARGE\", status: 400 },\n\t\t);\n\t}\n\n\tif (\n\t\toptions.allowedMimeTypes !== undefined &&\n\t\toptions.allowedMimeTypes.length > 0 &&\n\t\t!isMimeTypeAllowed(file.mimetype, options.allowedMimeTypes)\n\t) {\n\t\tthrow new DatrixApiError(`MIME type ${file.mimetype} is not allowed`, {\n\t\t\tcode: \"INVALID_MIME_TYPE\",\n\t\t\tstatus: 400,\n\t\t});\n\t}\n}\n\nfunction isMimeTypeAllowed(\n\tmimetype: string,\n\tallowedTypes: readonly string[],\n): boolean {\n\tfor (const allowed of allowedTypes) {\n\t\tif (allowed === mimetype) return true;\n\t\tif (allowed.endsWith(\"/*\")) {\n\t\t\tconst prefix = allowed.slice(0, -2);\n\t\t\tif (mimetype.startsWith(prefix + \"/\")) return true;\n\t\t}\n\t}\n\treturn false;\n}\n","/**\n * Image Processor\n *\n * Handles format conversion and resolution variant generation using sharp.\n * Only processes images — non-image files are passed through unchanged.\n */\n\nimport type { UploadFile, MediaVariant, MediaVariants } from \"@datrix/core\";\nimport type { ImageFormat, ResolutionConfig } from \"./types\";\nimport { DatrixError } from \"@datrix/core\";\n\nconst IMAGE_MIME_TYPES = new Set([\n\t\"image/jpeg\",\n\t\"image/png\",\n\t\"image/webp\",\n\t\"image/avif\",\n\t\"image/gif\",\n\t\"image/tiff\",\n]);\n\nfunction isImage(mimetype: string): boolean {\n\treturn IMAGE_MIME_TYPES.has(mimetype);\n}\n\nfunction getMimeType(format: ImageFormat): string {\n\tconst map: Record<ImageFormat, string> = {\n\t\twebp: \"image/webp\",\n\t\tjpeg: \"image/jpeg\",\n\t\tpng: \"image/png\",\n\t\tavif: \"image/avif\",\n\t};\n\treturn map[format];\n}\n\nfunction getExtension(format: ImageFormat): string {\n\treturn format === \"jpeg\" ? \"jpg\" : format;\n}\n\n/**\n * Apply format conversion to a file buffer.\n * Returns the converted buffer and new mimetype.\n * If file is not an image or no format set, returns original unchanged.\n */\nexport async function convertFormat(\n\tfile: UploadFile,\n\tformat: ImageFormat,\n\tquality: number,\n): Promise<{ buffer: Uint8Array; mimetype: string; filename: string }> {\n\tif (!isImage(file.mimetype)) {\n\t\treturn {\n\t\t\tbuffer: file.buffer,\n\t\t\tmimetype: file.mimetype,\n\t\t\tfilename: file.filename,\n\t\t};\n\t}\n\n\tlet sharp: typeof import(\"sharp\");\n\ttry {\n\t\tsharp = (await import(\"sharp\"))\n\t\t\t.default as unknown as typeof import(\"sharp\");\n\t} catch (error) {\n\t\tthrow new DatrixError(\"sharp is not installed\", {\n\t\t\tcode: \"SHARP_NOT_FOUND\",\n\t\t\toperation: \"upload:convertFormat\",\n\t\t});\n\t}\n\n\ttry {\n\t\tconst converted = await sharp(file.buffer)\n\t\t\t.toFormat(format, { quality })\n\t\t\t.toBuffer();\n\n\t\tconst ext = getExtension(format);\n\t\tconst baseName = file.filename.replace(/\\.[^.]+$/, \"\");\n\t\tconst newFilename = `${baseName}.${ext}`;\n\n\t\treturn {\n\t\t\tbuffer: new Uint8Array(converted),\n\t\t\tmimetype: getMimeType(format),\n\t\t\tfilename: newFilename,\n\t\t};\n\t} catch (error) {\n\t\tthrow new DatrixError(\"Failed to convert image format\", {\n\t\t\tcode: \"IMAGE_CONVERT_ERROR\",\n\t\t\toperation: \"upload:convertFormat\",\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t});\n\t}\n}\n\n/**\n * Generate resolution variants for an image.\n * Returns a map of resolution name → MediaVariant.\n * Uploads each variant via the provider.\n */\nexport async function generateVariants<TResolutions extends string>(\n\tfile: UploadFile,\n\tresolutions: Record<TResolutions, ResolutionConfig>,\n\tformat: ImageFormat | undefined,\n\tquality: number,\n\tuploadFn: (variantFile: UploadFile) => Promise<{ key: string }>,\n): Promise<MediaVariants<TResolutions>> {\n\tif (!isImage(file.mimetype)) {\n\t\treturn {};\n\t}\n\n\tlet sharp: typeof import(\"sharp\");\n\ttry {\n\t\tsharp = (await import(\"sharp\"))\n\t\t\t.default as unknown as typeof import(\"sharp\");\n\t} catch (error) {\n\t\tthrow new DatrixError(\"sharp is not installed\", {\n\t\t\tcode: \"SHARP_NOT_FOUND\",\n\t\t\toperation: \"upload:generateVariants\",\n\t\t});\n\t}\n\n\tconst targetFormat = format ?? \"jpeg\";\n\tconst outputMime = getMimeType(targetFormat);\n\tconst outputExt = getExtension(targetFormat);\n\n\tconst variants: Partial<Record<TResolutions, MediaVariant>> = {};\n\n\tfor (const [name, config] of Object.entries(resolutions) as [\n\t\tTResolutions,\n\t\tResolutionConfig,\n\t][]) {\n\t\ttry {\n\t\t\tconst resizeOptions: import(\"sharp\").ResizeOptions = {\n\t\t\t\twidth: config.width,\n\t\t\t\t...(config.height !== undefined && { height: config.height }),\n\t\t\t\t...(config.fit !== undefined &&\n\t\t\t\t\tconfig.height !== undefined && { fit: config.fit }),\n\t\t\t};\n\n\t\t\tconst variantBuffer = await sharp(file.buffer)\n\t\t\t\t.resize(resizeOptions)\n\t\t\t\t.toFormat(targetFormat, { quality })\n\t\t\t\t.toBuffer({ resolveWithObject: true });\n\n\t\t\tconst baseName = file.filename.replace(/\\.[^.]+$/, \"\");\n\t\t\tconst variantFilename = `${baseName}-${name}.${outputExt}`;\n\n\t\t\tconst variantFile: UploadFile = {\n\t\t\t\tfilename: variantFilename,\n\t\t\t\toriginalName: variantFilename,\n\t\t\t\tmimetype: outputMime,\n\t\t\t\tsize: variantBuffer.data.length,\n\t\t\t\tbuffer: new Uint8Array(variantBuffer.data),\n\t\t\t};\n\n\t\t\tconst uploaded = await uploadFn(variantFile);\n\n\t\t\tvariants[name] = {\n\t\t\t\tkey: uploaded.key,\n\t\t\t\twidth: variantBuffer.info.width,\n\t\t\t\theight: variantBuffer.info.height,\n\t\t\t\tsize: variantBuffer.data.length,\n\t\t\t\tmimeType: outputMime,\n\t\t\t\turl: undefined!,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) throw error;\n\t\t\tthrow new DatrixError(`Failed to generate variant: ${name}`, {\n\t\t\t\tcode: \"VARIANT_GENERATE_ERROR\",\n\t\t\t\toperation: \"upload:generateVariants\",\n\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn variants as MediaVariants<TResolutions>;\n}\n\nexport { isImage };\n","/**\n * Upload — main class, implements IUpload interface.\n * Pass an instance to ApiPlugin: new ApiPlugin({ upload: new Upload({ ... }) })\n *\n * @template TResolutions - Union of resolution names (e.g. \"thumbnail\" | \"small\" | \"medium\")\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport type { IUpload } from \"@datrix/core\";\nimport type { SchemaDefinition } from \"@datrix/core\";\nimport { createMediaSchema } from \"./schema\";\nimport { handleUploadRequest } from \"./handler\";\nimport type { UploadOptions } from \"./types\";\n\nexport class Upload<TResolutions extends string = string> implements IUpload {\n\tprivate readonly options: UploadOptions<TResolutions>;\n\n\treadonly provider: UploadOptions<TResolutions>[\"provider\"];\n\n\tconstructor(options: UploadOptions<TResolutions>) {\n\t\tthis.options = options;\n\t\tthis.provider = options.provider;\n\t}\n\n\tgetModelName(): string {\n\t\treturn this.options.modelName ?? \"media\";\n\t}\n\n\tgetSchemas(): SchemaDefinition[] {\n\t\tconst mediaSchema = createMediaSchema(\n\t\t\tthis.options,\n\t\t\tthis.options.permission,\n\t\t);\n\t\treturn [mediaSchema];\n\t}\n\n\tasync handleRequest(request: Request, datrix: Datrix): Promise<Response> {\n\t\treturn handleUploadRequest(request, {\n\t\t\tdatrix,\n\t\t\tuploadOptions: this.options,\n\t\t\tinjectUrls: (data) => this.injectUrls(data),\n\t\t});\n\t}\n\n\tasync injectUrls(data: unknown): Promise<unknown> {\n\t\treturn this.traverse(data);\n\t}\n\n\tgetUrl(key: string): string {\n\t\treturn this.options.provider.getUrl(key);\n\t}\n\n\tprivate async traverse(node: unknown): Promise<unknown> {\n\t\tif (Array.isArray(node)) {\n\t\t\tconst results: unknown[] = [];\n\t\t\tfor (const item of node) {\n\t\t\t\tresults.push(await this.traverse(item));\n\t\t\t}\n\t\t\treturn results;\n\t\t}\n\n\t\tif (node !== null && typeof node === \"object\") {\n\t\t\tconst obj = node as Record<string, unknown>;\n\t\t\tconst result: Record<string, unknown> = {};\n\n\t\t\tfor (const [k, v] of Object.entries(obj)) {\n\t\t\t\tresult[k] = await this.traverse(v);\n\t\t\t}\n\n\t\t\t// Inject url if this looks like a media object (has key, no url)\n\t\t\tif (typeof result[\"key\"] === \"string\" && result[\"url\"] === undefined) {\n\t\t\t\tresult[\"url\"] = this.options.provider.getUrl(result[\"key\"]);\n\t\t\t}\n\n\t\t\t// Inject urls into variants\n\t\t\tif (\n\t\t\t\tresult[\"variants\"] !== null &&\n\t\t\t\ttypeof result[\"variants\"] === \"object\"\n\t\t\t) {\n\t\t\t\tconst variants = result[\"variants\"] as Record<string, unknown>;\n\t\t\t\tfor (const [name, variant] of Object.entries(variants)) {\n\t\t\t\t\tif (variant !== null && typeof variant === \"object\") {\n\t\t\t\t\t\tconst v = variant as Record<string, unknown>;\n\t\t\t\t\t\tif (typeof v[\"key\"] === \"string\" && v[\"url\"] === undefined) {\n\t\t\t\t\t\t\tvariants[name] = {\n\t\t\t\t\t\t\t\t...v,\n\t\t\t\t\t\t\t\turl: this.options.provider.getUrl(v[\"key\"]),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t}\n\n\t\treturn node;\n\t}\n}\n","/**\n * Local Filesystem Storage Provider\n */\n\nimport type {\n\tStorageProvider,\n\tUploadFile,\n\tUploadResult,\n\tLocalProviderOptions,\n} from \"@datrix/core\";\nimport { generateUniqueFilename, sanitizeFilename } from \"@datrix/core\";\nimport { DatrixError } from \"@datrix/core\";\n\nclass UploadError extends DatrixError {\n\tconstructor(message: string, cause?: Error) {\n\t\tsuper(message, {\n\t\t\tcode: \"UPLOAD_ERROR\",\n\t\t\toperation: \"upload:local\",\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t\tthis.name = \"UploadError\";\n\t}\n}\n\nexport class LocalStorageProvider implements StorageProvider {\n\treadonly name = \"local\" as const;\n\n\tprivate readonly basePath: string;\n\tprivate readonly baseUrl: string;\n\tprivate readonly ensureDirectory: boolean;\n\n\tconstructor(options: LocalProviderOptions) {\n\t\tthis.basePath = options.basePath;\n\t\tthis.baseUrl = options.baseUrl;\n\t\tthis.ensureDirectory = options.ensureDirectory ?? true;\n\t}\n\n\tasync upload(file: UploadFile): Promise<UploadResult> {\n\t\ttry {\n\t\t\tconst fs = await import(\"fs/promises\");\n\t\t\tconst path = await import(\"path\");\n\n\t\t\tconst sanitized = sanitizeFilename(file.originalName);\n\t\t\tconst uniqueFilename = generateUniqueFilename(sanitized);\n\t\t\tconst fullPath = path.join(this.basePath, uniqueFilename);\n\n\t\t\tif (this.ensureDirectory) {\n\t\t\t\tconst dirPath = path.dirname(fullPath);\n\t\t\t\tawait fs.mkdir(dirPath, { recursive: true });\n\t\t\t}\n\n\t\t\tawait fs.writeFile(fullPath, file.buffer);\n\n\t\t\treturn {\n\t\t\t\tkey: uniqueFilename,\n\t\t\t\tsize: file.size,\n\t\t\t\tmimetype: file.mimetype,\n\t\t\t\tuploadedAt: new Date(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to upload file to local filesystem\", cause);\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<void> {\n\t\tconst fs = await import(\"fs/promises\");\n\t\tconst path = await import(\"path\");\n\n\t\tconst fullPath = path.join(this.basePath, key);\n\n\t\ttry {\n\t\t\tawait fs.access(fullPath);\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"File not found\", cause);\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.unlink(fullPath);\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\n\t\t\t\t\"Failed to delete file from local filesystem\",\n\t\t\t\tcause,\n\t\t\t);\n\t\t}\n\t}\n\n\tgetUrl(key: string): string {\n\t\treturn this.buildUrl(key);\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst fs = await import(\"fs/promises\");\n\t\t\tconst path = await import(\"path\");\n\t\t\tconst fullPath = path.join(this.basePath, key);\n\t\t\tawait fs.access(fullPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate buildUrl(key: string): string {\n\t\tconst cleanBaseUrl = this.baseUrl.replace(/\\/$/, \"\");\n\t\tconst cleanKey = key.replace(/^\\//, \"\");\n\t\treturn `${cleanBaseUrl}/${cleanKey}`;\n\t}\n}\n","/**\n * AWS S3 Storage Provider\n *\n * Implements AWS Signature V4 signing without external SDK.\n */\n\nimport type {\n\tStorageProvider,\n\tUploadFile,\n\tUploadResult,\n\tS3ProviderOptions,\n} from \"@datrix/core\";\nimport { generateUniqueFilename, sanitizeFilename } from \"@datrix/core\";\nimport { DatrixError } from \"@datrix/core\";\n\nclass UploadError extends DatrixError {\n\tconstructor(message: string, cause?: Error) {\n\t\tsuper(message, {\n\t\t\tcode: \"UPLOAD_ERROR\",\n\t\t\toperation: \"upload:s3\",\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t\tthis.name = \"UploadError\";\n\t}\n}\n\nexport class S3StorageProvider implements StorageProvider {\n\treadonly name = \"s3\" as const;\n\n\tprivate readonly bucket: string;\n\tprivate readonly region: string;\n\tprivate readonly accessKeyId: string;\n\tprivate readonly secretAccessKey: string;\n\tprivate readonly endpoint: string;\n\tprivate readonly pathPrefix: string;\n\n\tconstructor(options: S3ProviderOptions) {\n\t\tthis.bucket = options.bucket;\n\t\tthis.region = options.region;\n\t\tthis.accessKeyId = options.accessKeyId;\n\t\tthis.secretAccessKey = options.secretAccessKey;\n\t\tthis.endpoint = options.endpoint ?? `s3.${options.region}.amazonaws.com`;\n\t\tthis.pathPrefix = options.pathPrefix ?? \"uploads\";\n\t}\n\n\tasync upload(file: UploadFile): Promise<UploadResult> {\n\t\ttry {\n\t\t\tconst sanitized = sanitizeFilename(file.originalName);\n\t\t\tconst filename = generateUniqueFilename(sanitized);\n\t\t\tconst key = this.pathPrefix ? `${this.pathPrefix}/${filename}` : filename;\n\n\t\t\tawait this.putObject(key, file.buffer, file.mimetype);\n\n\t\t\treturn {\n\t\t\t\tkey,\n\t\t\t\tsize: file.size,\n\t\t\t\tmimetype: file.mimetype,\n\t\t\t\tuploadedAt: new Date(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof UploadError) throw error;\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to upload file to S3\", cause);\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.deleteObject(key);\n\t\t} catch (error) {\n\t\t\tif (error instanceof UploadError) throw error;\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to delete file from S3\", cause);\n\t\t}\n\t}\n\n\tgetUrl(key: string): string {\n\t\treturn `https://${this.bucket}.${this.endpoint}/${key}`;\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.headObject(key);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate async putObject(\n\t\tkey: string,\n\t\tbuffer: Uint8Array,\n\t\tcontentType: string,\n\t): Promise<void> {\n\t\tconst https = await import(\"https\");\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst host = `${this.bucket}.${this.endpoint}`;\n\t\tconst urlPath = `/${key}`;\n\t\tconst method = \"PUT\";\n\t\tconst date = new Date().toUTCString();\n\t\tconst contentHash = crypto\n\t\t\t.createHash(\"sha256\")\n\t\t\t.update(buffer)\n\t\t\t.digest(\"hex\");\n\t\tconst authorization = await this.signRequest(\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\thost,\n\t\t\tdate,\n\t\t\tcontentType,\n\t\t\tcontentHash,\n\t\t);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst req = https.request(\n\t\t\t\t{\n\t\t\t\t\thostname: host,\n\t\t\t\t\tport: 443,\n\t\t\t\t\tpath: urlPath,\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tHost: host,\n\t\t\t\t\t\tDate: date,\n\t\t\t\t\t\t\"Content-Type\": contentType,\n\t\t\t\t\t\t\"Content-Length\": buffer.length,\n\t\t\t\t\t\t\"x-amz-content-sha256\": contentHash,\n\t\t\t\t\t\tAuthorization: authorization,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t(res) => {\n\t\t\t\t\tconst status = res.statusCode ?? 0;\n\t\t\t\t\tif (status >= 200 && status < 300) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlet body = \"\";\n\t\t\t\t\t\tres.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\t\tbody += chunk.toString();\n\t\t\t\t\t\t});\n\t\t\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\t\t\treject(new UploadError(`S3 upload failed: ${status} ${body}`));\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\treq.on(\"error\", (error: Error) => {\n\t\t\t\treject(new UploadError(\"S3 request failed\", error));\n\t\t\t});\n\t\t\treq.write(buffer);\n\t\t\treq.end();\n\t\t});\n\t}\n\n\tprivate async deleteObject(key: string): Promise<void> {\n\t\tconst https = await import(\"https\");\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst host = `${this.bucket}.${this.endpoint}`;\n\t\tconst urlPath = `/${key}`;\n\t\tconst method = \"DELETE\";\n\t\tconst date = new Date().toUTCString();\n\t\tconst contentHash = crypto.createHash(\"sha256\").update(\"\").digest(\"hex\");\n\t\tconst authorization = await this.signRequest(\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\thost,\n\t\t\tdate,\n\t\t\t\"\",\n\t\t\tcontentHash,\n\t\t);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst req = https.request(\n\t\t\t\t{\n\t\t\t\t\thostname: host,\n\t\t\t\t\tport: 443,\n\t\t\t\t\tpath: urlPath,\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tHost: host,\n\t\t\t\t\t\tDate: date,\n\t\t\t\t\t\t\"x-amz-content-sha256\": contentHash,\n\t\t\t\t\t\tAuthorization: authorization,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t(res) => {\n\t\t\t\t\tconst status = res.statusCode ?? 0;\n\t\t\t\t\tif (status >= 200 && status < 300) {\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlet body = \"\";\n\t\t\t\t\t\tres.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\t\tbody += chunk.toString();\n\t\t\t\t\t\t});\n\t\t\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\t\t\treject(new UploadError(`S3 delete failed: ${status} ${body}`));\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\treq.on(\"error\", (error: Error) => {\n\t\t\t\treject(new UploadError(\"S3 request failed\", error));\n\t\t\t});\n\t\t\treq.end();\n\t\t});\n\t}\n\n\tprivate async headObject(key: string): Promise<void> {\n\t\tconst https = await import(\"https\");\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst host = `${this.bucket}.${this.endpoint}`;\n\t\tconst urlPath = `/${key}`;\n\t\tconst method = \"HEAD\";\n\t\tconst date = new Date().toUTCString();\n\t\tconst contentHash = crypto.createHash(\"sha256\").update(\"\").digest(\"hex\");\n\t\tconst authorization = await this.signRequest(\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\thost,\n\t\t\tdate,\n\t\t\t\"\",\n\t\t\tcontentHash,\n\t\t);\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst req = https.request(\n\t\t\t\t{\n\t\t\t\t\thostname: host,\n\t\t\t\t\tport: 443,\n\t\t\t\t\tpath: urlPath,\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tHost: host,\n\t\t\t\t\t\tDate: date,\n\t\t\t\t\t\t\"x-amz-content-sha256\": contentHash,\n\t\t\t\t\t\tAuthorization: authorization,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\t(res) => {\n\t\t\t\t\tconst status = res.statusCode ?? 0;\n\t\t\t\t\tif (status >= 200 && status < 300) resolve();\n\t\t\t\t\telse reject(new UploadError(`Object not found: ${status}`));\n\t\t\t\t},\n\t\t\t);\n\t\t\treq.on(\"error\", (error: Error) => {\n\t\t\t\treject(new UploadError(\"S3 request failed\", error));\n\t\t\t});\n\t\t\treq.end();\n\t\t});\n\t}\n\n\tprivate async signRequest(\n\t\tmethod: string,\n\t\turlPath: string,\n\t\thost: string,\n\t\tdate: string,\n\t\t_contentType: string,\n\t\tcontentHash: string,\n\t): Promise<string> {\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst canonicalHeaders = `host:${host}\\nx-amz-content-sha256:${contentHash}\\nx-amz-date:${date}\\n`;\n\t\tconst signedHeaders = \"host;x-amz-content-sha256;x-amz-date\";\n\t\tconst canonicalRequest = [\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\t\"\",\n\t\t\tcanonicalHeaders,\n\t\t\tsignedHeaders,\n\t\t\tcontentHash,\n\t\t].join(\"\\n\");\n\n\t\tconst algorithm = \"AWS4-HMAC-SHA256\";\n\t\tconst amzDate = this.getAmzDate();\n\t\tconst credentialScope = `${this.getDateStamp()}/${this.region}/s3/aws4_request`;\n\t\tconst canonicalRequestHash = crypto\n\t\t\t.createHash(\"sha256\")\n\t\t\t.update(canonicalRequest)\n\t\t\t.digest(\"hex\");\n\t\tconst stringToSign = [\n\t\t\talgorithm,\n\t\t\tamzDate,\n\t\t\tcredentialScope,\n\t\t\tcanonicalRequestHash,\n\t\t].join(\"\\n\");\n\n\t\tconst signature = this.calculateSignature(crypto, stringToSign);\n\t\treturn `${algorithm} Credential=${this.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n\t}\n\n\tprivate calculateSignature(\n\t\tcrypto: typeof import(\"crypto\"),\n\t\tstringToSign: string,\n\t): string {\n\t\tconst kDate = crypto\n\t\t\t.createHmac(\"sha256\", `AWS4${this.secretAccessKey}`)\n\t\t\t.update(this.getDateStamp())\n\t\t\t.digest();\n\t\tconst kRegion = crypto\n\t\t\t.createHmac(\"sha256\", kDate)\n\t\t\t.update(this.region)\n\t\t\t.digest();\n\t\tconst kService = crypto.createHmac(\"sha256\", kRegion).update(\"s3\").digest();\n\t\tconst kSigning = crypto\n\t\t\t.createHmac(\"sha256\", kService)\n\t\t\t.update(\"aws4_request\")\n\t\t\t.digest();\n\t\treturn crypto\n\t\t\t.createHmac(\"sha256\", kSigning)\n\t\t\t.update(stringToSign)\n\t\t\t.digest(\"hex\");\n\t}\n\n\tprivate getAmzDate(): string {\n\t\treturn new Date().toISOString().replace(/[:-]|\\.\\d{3}/g, \"\");\n\t}\n\n\tprivate getDateStamp(): string {\n\t\tconst now = new Date();\n\t\tconst year = now.getUTCFullYear();\n\t\tconst month = String(now.getUTCMonth() + 1).padStart(2, \"0\");\n\t\tconst day = String(now.getUTCDate()).padStart(2, \"0\");\n\t\treturn `${year}${month}${day}`;\n\t}\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","import_core","UploadError"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/upload.ts","../src/schema.ts","../src/handler.ts","../src/processor.ts","../src/providers/local.ts","../src/providers/s3.ts"],"sourcesContent":["export { Upload } from \"./upload\";\nexport { LocalStorageProvider } from \"./providers/local\";\nexport { S3StorageProvider } from \"./providers/s3\";\nexport type { UploadOptions } from \"./types\";\n","/**\n * Upload — main class, implements IUpload interface.\n * Pass an instance to ApiPlugin: new ApiPlugin({ upload: new Upload({ ... }) })\n *\n * @template TResolutions - Union of resolution names (e.g. \"thumbnail\" | \"small\" | \"medium\")\n */\n\nimport type { Datrix } from \"@datrix/core\";\nimport type { IUpload } from \"@datrix/core\";\nimport type { SchemaDefinition, SchemaPermission } from \"@datrix/core\";\nimport { DatrixError } from \"@datrix/core\";\nimport { createMediaSchema } from \"./schema\";\nimport { handleUploadRequest } from \"./handler\";\nimport type { UploadOptions } from \"./types\";\n\nexport class Upload<TResolutions extends string = string> implements IUpload {\n\tprivate readonly options: UploadOptions<TResolutions>;\n\n\treadonly provider: UploadOptions<TResolutions>[\"provider\"];\n\n\tconstructor(options: UploadOptions<TResolutions>) {\n\t\tconst quality = options.quality;\n\t\tif (quality !== undefined && (quality < 1 || quality > 100)) {\n\t\t\tthrow new DatrixError(\n\t\t\t\t`Upload quality must be between 1 and 100, got ${quality}`,\n\t\t\t\t{ code: \"INVALID_UPLOAD_CONFIG\", operation: \"upload:config\" },\n\t\t\t);\n\t\t}\n\n\t\tthis.options = options;\n\t\tthis.provider = options.provider;\n\t}\n\n\tgetModelName(): string {\n\t\treturn this.options.modelName ?? \"media\";\n\t}\n\n\tgetPermission(): SchemaPermission | undefined {\n\t\treturn this.options.permission;\n\t}\n\n\tgetSchemas(): SchemaDefinition[] {\n\t\treturn [createMediaSchema(this.getModelName(), this.options.permission)];\n\t}\n\n\tasync handleRequest(request: Request, datrix: Datrix): Promise<Response> {\n\t\treturn handleUploadRequest(request, {\n\t\t\tdatrix,\n\t\t\tmodelName: this.getModelName(),\n\t\t\tuploadOptions: this.options,\n\t\t\tinjectUrls: (data) => this.injectUrls(data),\n\t\t});\n\t}\n\n\tasync injectUrls(data: unknown): Promise<unknown> {\n\t\treturn this.traverse(data);\n\t}\n\n\tgetUrl(key: string): string {\n\t\treturn this.options.provider.getUrl(key);\n\t}\n\n\t/**\n\t * Recursively inject `url` into media-shaped objects (`key` + `mimeType` +\n\t * numeric `size` — the media record signature; variant entries match it\n\t * too). Non-plain objects (Date, class instances) pass through untouched,\n\t * and unchanged subtrees keep their original reference.\n\t */\n\tprivate traverse(node: unknown): unknown {\n\t\tif (Array.isArray(node)) {\n\t\t\tlet changed = false;\n\t\t\tconst results = node.map((item) => {\n\t\t\t\tconst result = this.traverse(item);\n\t\t\t\tif (result !== item) changed = true;\n\t\t\t\treturn result;\n\t\t\t});\n\t\t\treturn changed ? results : node;\n\t\t}\n\n\t\tif (!isPlainObject(node)) {\n\t\t\treturn node;\n\t\t}\n\n\t\tlet changed = false;\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [k, v] of Object.entries(node)) {\n\t\t\tconst traversed = this.traverse(v);\n\t\t\tif (traversed !== v) changed = true;\n\t\t\tresult[k] = traversed;\n\t\t}\n\n\t\tif (\n\t\t\ttypeof result[\"key\"] === \"string\" &&\n\t\t\ttypeof result[\"mimeType\"] === \"string\" &&\n\t\t\ttypeof result[\"size\"] === \"number\" &&\n\t\t\tresult[\"url\"] === undefined\n\t\t) {\n\t\t\tresult[\"url\"] = this.options.provider.getUrl(result[\"key\"]);\n\t\t\tchanged = true;\n\t\t}\n\n\t\treturn changed ? result : node;\n\t}\n}\n\nfunction isPlainObject(node: unknown): node is Record<string, unknown> {\n\tif (node === null || typeof node !== \"object\") {\n\t\treturn false;\n\t}\n\tconst proto = Object.getPrototypeOf(node);\n\treturn proto === Object.prototype || proto === null;\n}\n","/**\n * Media Schema Factory\n */\n\nimport { defineSchema } from \"@datrix/core\";\nimport type { SchemaPermission, SchemaDefinition } from \"@datrix/core\";\n\nexport function createMediaSchema(\n\tmodelName: string,\n\tpermission?: SchemaPermission,\n): SchemaDefinition {\n\t// Media rows must only be written through the upload pipeline — direct\n\t// CRUD create/update is denied unless the user explicitly overrides it.\n\t// The upload handler writes via datrix.raw.*, which bypasses this layer.\n\tconst effectivePermission: SchemaPermission = {\n\t\tcreate: false,\n\t\tupdate: false,\n\t\t...permission,\n\t};\n\n\treturn defineSchema({\n\t\tname: modelName,\n\t\tfields: {\n\t\t\tfilename: { type: \"string\", required: true },\n\t\t\toriginalName: { type: \"string\", required: true },\n\t\t\tmimeType: { type: \"string\", required: true },\n\t\t\tsize: { type: \"number\", required: true, integer: true },\n\t\t\tkey: { type: \"string\", required: true },\n\t\t\tvariants: { type: \"json\" },\n\t\t},\n\t\tpermission: effectivePermission,\n\t});\n}\n","/**\n * Upload Handler\n *\n * POST /upload — multipart/form-data parse, format conversion, variant generation, DB record\n * DELETE /upload/:id — DB record delete + provider delete (all variants, best-effort)\n *\n * GET /upload and GET /upload/:id fall through to normal CRUD.\n * Auth/permission for these endpoints is enforced by the API plugin before\n * this handler is invoked.\n */\n\nimport type { Datrix, StorageProvider } from \"@datrix/core\";\nimport type { UploadFile, MediaVariants } from \"@datrix/core\";\nimport type { DatrixEntry } from \"@datrix/core\";\nimport {\n\tDatrixApiError,\n\thandlerError,\n\tjsonResponse,\n\tdatrixErrorResponse,\n} from \"@datrix/api\";\nimport { DatrixError, DatrixValidationError } from \"@datrix/core\";\nimport type { UploadOptions } from \"./types\";\nimport {\n\tconvertFormat,\n\tdetectImageMime,\n\tgenerateVariants,\n\tisImage,\n} from \"./processor\";\n\n/**\n * Allowance on top of maxSize when pre-checking Content-Length —\n * covers multipart boundaries and non-file form fields.\n */\nconst MULTIPART_OVERHEAD = 64 * 1024;\n\nexport interface UploadHandlerOptions {\n\tdatrix: Datrix;\n\tmodelName: string;\n\tuploadOptions: UploadOptions;\n\tinjectUrls?: (data: unknown) => Promise<unknown>;\n}\n\nexport async function handleUploadRequest(\n\trequest: Request,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\ttry {\n\t\tconst { method } = request;\n\t\tconst url = new URL(request.url);\n\n\t\tconst pathAfterUpload = url.pathname.replace(/.*\\/upload/, \"\");\n\t\tconst segments = pathAfterUpload.split(\"/\").filter(Boolean);\n\t\tconst idSegment = segments[0];\n\n\t\tif (segments.length > 1) {\n\t\t\treturn datrixErrorResponse(\n\t\t\t\thandlerError.recordNotFound(options.modelName, segments.join(\"/\")),\n\t\t\t);\n\t\t}\n\n\t\tif (method === \"POST\") {\n\t\t\t// POST with an id segment is not a valid route\n\t\t\tif (idSegment !== undefined) {\n\t\t\t\treturn datrixErrorResponse(\n\t\t\t\t\thandlerError.recordNotFound(options.modelName, idSegment),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn await handleUpload(request, options);\n\t\t}\n\n\t\tif (method === \"DELETE\") {\n\t\t\tif (idSegment === undefined) {\n\t\t\t\treturn datrixErrorResponse(handlerError.missingId(\"delete\"));\n\t\t\t}\n\t\t\tif (!/^\\d+$/.test(idSegment)) {\n\t\t\t\treturn datrixErrorResponse(\n\t\t\t\t\thandlerError.recordNotFound(options.modelName, idSegment),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn await handleDeleteMedia(parseInt(idSegment, 10), options);\n\t\t}\n\n\t\treturn datrixErrorResponse(handlerError.methodNotAllowed(method));\n\t} catch (error) {\n\t\tif (\n\t\t\terror instanceof DatrixValidationError ||\n\t\t\terror instanceof DatrixError\n\t\t) {\n\t\t\treturn datrixErrorResponse(error);\n\t\t}\n\n\t\tconst message = error instanceof Error ? error.message : \"Upload failed\";\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.internalError(\n\t\t\t\tmessage,\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t),\n\t\t);\n\t}\n}\n\n/**\n * FormData.get returns string | File, but the File global only exists from\n * Node 20 — duck-type the entry instead of using instanceof.\n */\nfunction isFileEntry(entry: unknown): entry is File {\n\treturn (\n\t\tentry !== null &&\n\t\ttypeof entry === \"object\" &&\n\t\ttypeof (entry as File).arrayBuffer === \"function\" &&\n\t\ttypeof (entry as File).name === \"string\" &&\n\t\ttypeof (entry as File).size === \"number\" &&\n\t\ttypeof (entry as File).type === \"string\"\n\t);\n}\n\nasync function handleUpload(\n\trequest: Request,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\tconst { datrix, modelName, uploadOptions } = options;\n\n\tconst contentType = request.headers.get(\"content-type\") ?? \"\";\n\tif (!contentType.includes(\"multipart/form-data\")) {\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.invalidBody(\"Expected multipart/form-data\"),\n\t\t);\n\t}\n\n\t// Reject oversized requests before buffering the body. A hard streaming\n\t// limit must still be enforced at the server/proxy level — formData()\n\t// cannot stream-abort.\n\tif (uploadOptions.maxSize !== undefined) {\n\t\tconst contentLength = Number(request.headers.get(\"content-length\"));\n\t\tif (\n\t\t\tNumber.isFinite(contentLength) &&\n\t\t\tcontentLength > uploadOptions.maxSize + MULTIPART_OVERHEAD\n\t\t) {\n\t\t\tthrow new DatrixApiError(\n\t\t\t\t`Request size ${contentLength} exceeds maximum allowed size ${uploadOptions.maxSize}`,\n\t\t\t\t{ code: \"FILE_TOO_LARGE\", status: 413 },\n\t\t\t);\n\t\t}\n\t}\n\n\tlet formData: FormData;\n\ttry {\n\t\tformData = await request.formData();\n\t} catch (error) {\n\t\tconst cause = error instanceof Error ? error : undefined;\n\t\tthrow new DatrixApiError(\"Failed to parse multipart form data\", {\n\t\t\tcode: \"MULTIPART_PARSE_ERROR\",\n\t\t\tstatus: 400,\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t}\n\n\tconst fileEntries = formData.getAll(\"file\");\n\tif (fileEntries.length === 0 || !isFileEntry(fileEntries[0])) {\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.invalidBody(\"No file field in form data\"),\n\t\t);\n\t}\n\tif (fileEntries.length > 1) {\n\t\treturn datrixErrorResponse(\n\t\t\thandlerError.invalidBody(\n\t\t\t\t\"Multiple file entries — upload a single file per request\",\n\t\t\t),\n\t\t);\n\t}\n\tconst fileEntry = fileEntries[0];\n\n\t// Size and declared-type limits are checked before buffering the file\n\tvalidateFileLimits(fileEntry.size, fileEntry.type, uploadOptions);\n\n\tconst buffer = new Uint8Array(await fileEntry.arrayBuffer());\n\tconst declaredMime = fileEntry.type;\n\n\t// The declared MIME type is client-controlled: verify image claims against\n\t// the actual content so e.g. HTML cannot be stored as image/png.\n\tif (declaredMime.startsWith(\"image/\")) {\n\t\tconst actualMime = await detectImageMime(buffer);\n\t\tif (actualMime !== declaredMime) {\n\t\t\tthrow new DatrixApiError(\n\t\t\t\t`Declared MIME type ${declaredMime} does not match file content`,\n\t\t\t\t{ code: \"INVALID_MIME_TYPE\", status: 400 },\n\t\t\t);\n\t\t}\n\t}\n\n\tconst rawFile: UploadFile = {\n\t\tfilename: fileEntry.name,\n\t\toriginalName: fileEntry.name,\n\t\tmimetype: declaredMime,\n\t\tsize: fileEntry.size,\n\t\tbuffer,\n\t};\n\n\t// Format conversion (if configured and file is an image)\n\tconst quality = uploadOptions.quality ?? 80;\n\tconst fileToUpload =\n\t\tuploadOptions.format !== undefined && isImage(rawFile.mimetype)\n\t\t\t? await convertFormat(rawFile, uploadOptions.format, quality)\n\t\t\t: rawFile;\n\n\tconst uploadFile: UploadFile = {\n\t\tfilename: fileToUpload.filename,\n\t\toriginalName: rawFile.originalName,\n\t\tmimetype: fileToUpload.mimetype,\n\t\tsize: fileToUpload.buffer.length,\n\t\tbuffer: fileToUpload.buffer,\n\t};\n\n\t// Track every uploaded key so a later failure can clean up storage\n\tconst uploadedKeys: string[] = [];\n\n\t// Upload original (or converted) file\n\tconst result = await uploadOptions.provider.upload(uploadFile);\n\tuploadedKeys.push(result.key);\n\n\ttry {\n\t\t// Generate resolution variants (if configured and file is an image)\n\t\tlet variants: MediaVariants | null = null;\n\t\tif (\n\t\t\tuploadOptions.resolutions !== undefined &&\n\t\t\tisImage(uploadFile.mimetype)\n\t\t) {\n\t\t\tvariants = await generateVariants(\n\t\t\t\tuploadFile,\n\t\t\t\tuploadOptions.resolutions,\n\t\t\t\tuploadOptions.format,\n\t\t\t\tquality,\n\t\t\t\tasync (variantFile) => {\n\t\t\t\t\tconst variantResult =\n\t\t\t\t\t\tawait uploadOptions.provider.upload(variantFile);\n\t\t\t\t\tuploadedKeys.push(variantResult.key);\n\t\t\t\t\treturn { key: variantResult.key };\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst mediaRecord = await datrix.raw.create(modelName, {\n\t\t\tfilename: result.key,\n\t\t\toriginalName: uploadFile.originalName,\n\t\t\tmimeType: uploadFile.mimetype,\n\t\t\tsize: uploadFile.size,\n\t\t\tkey: result.key,\n\t\t\t...(variants !== null && { variants }),\n\t\t});\n\n\t\tconst data = options.injectUrls\n\t\t\t? await options.injectUrls(mediaRecord)\n\t\t\t: mediaRecord;\n\n\t\treturn jsonResponse({ data }, 201);\n\t} catch (error) {\n\t\t// No DB record exists — remove already-uploaded storage objects\n\t\tawait cleanupKeys(uploadOptions.provider, uploadedKeys);\n\t\tthrow error;\n\t}\n}\n\n/**\n * DELETE /upload/:id\n * Deletes the DB record first, then storage objects best-effort — an\n * orphaned file is recoverable, a record pointing at deleted storage is not.\n */\nasync function handleDeleteMedia(\n\tid: number,\n\toptions: UploadHandlerOptions,\n): Promise<Response> {\n\tconst { datrix, modelName, uploadOptions } = options;\n\n\ttype MediaRecord = {\n\t\tkey: string;\n\t\tvariants: Record<string, { key: string }> | null;\n\t} & DatrixEntry;\n\tconst record = await datrix.raw.findById<MediaRecord>(modelName, id);\n\n\tif (record === null) {\n\t\treturn datrixErrorResponse(handlerError.recordNotFound(modelName, id));\n\t}\n\n\tawait datrix.raw.delete(modelName, id);\n\n\tconst keys: string[] = [record.key];\n\tif (record.variants !== null && record.variants !== undefined) {\n\t\tfor (const variant of Object.values(record.variants)) {\n\t\t\tif (typeof variant?.key === \"string\") {\n\t\t\t\tkeys.push(variant.key);\n\t\t\t}\n\t\t}\n\t}\n\tawait cleanupKeys(uploadOptions.provider, keys);\n\n\treturn jsonResponse({ data: { id } });\n}\n\n/**\n * Best-effort storage cleanup — failures are logged, never thrown.\n */\nasync function cleanupKeys(\n\tprovider: StorageProvider,\n\tkeys: readonly string[],\n): Promise<void> {\n\tfor (const key of keys) {\n\t\ttry {\n\t\t\tawait provider.delete(key);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconsole.warn(\n\t\t\t\t`[Datrix Upload] Failed to delete storage object '${key}': ${message}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\nfunction validateFileLimits(\n\tsize: number,\n\tmimetype: string,\n\toptions: UploadOptions,\n): void {\n\tif (options.maxSize !== undefined && size > options.maxSize) {\n\t\tthrow new DatrixApiError(\n\t\t\t`File size ${size} exceeds maximum allowed size ${options.maxSize}`,\n\t\t\t{ code: \"FILE_TOO_LARGE\", status: 400 },\n\t\t);\n\t}\n\n\tif (\n\t\toptions.allowedMimeTypes !== undefined &&\n\t\toptions.allowedMimeTypes.length > 0 &&\n\t\t!isMimeTypeAllowed(mimetype, options.allowedMimeTypes)\n\t) {\n\t\tthrow new DatrixApiError(`MIME type ${mimetype} is not allowed`, {\n\t\t\tcode: \"INVALID_MIME_TYPE\",\n\t\t\tstatus: 400,\n\t\t});\n\t}\n}\n\nfunction isMimeTypeAllowed(\n\tmimetype: string,\n\tallowedTypes: readonly string[],\n): boolean {\n\tfor (const allowed of allowedTypes) {\n\t\tif (allowed === mimetype) return true;\n\t\tif (allowed.endsWith(\"/*\")) {\n\t\t\tconst prefix = allowed.slice(0, -2);\n\t\t\tif (mimetype.startsWith(prefix + \"/\")) return true;\n\t\t}\n\t}\n\treturn false;\n}\n","/**\n * Image Processor\n *\n * Handles content-type detection, format conversion and resolution variant\n * generation using sharp. Only processes images — non-image files are passed\n * through unchanged.\n */\n\nimport type { UploadFile, MediaVariant, MediaVariants } from \"@datrix/core\";\nimport type { ImageFormat, ResolutionConfig } from \"./types\";\nimport { DatrixError } from \"@datrix/core\";\n\nconst IMAGE_MIME_TYPES = new Set([\n\t\"image/jpeg\",\n\t\"image/png\",\n\t\"image/webp\",\n\t\"image/avif\",\n\t\"image/gif\",\n\t\"image/tiff\",\n]);\n\n/** sharp metadata format → MIME type, for content-based type verification */\nconst FORMAT_TO_MIME: Record<string, string> = {\n\tjpeg: \"image/jpeg\",\n\tpng: \"image/png\",\n\twebp: \"image/webp\",\n\tavif: \"image/avif\",\n\theif: \"image/avif\",\n\tgif: \"image/gif\",\n\ttiff: \"image/tiff\",\n\tsvg: \"image/svg+xml\",\n};\n\nfunction isImage(mimetype: string): boolean {\n\treturn IMAGE_MIME_TYPES.has(mimetype);\n}\n\nfunction getMimeType(format: ImageFormat): string {\n\tconst map: Record<ImageFormat, string> = {\n\t\twebp: \"image/webp\",\n\t\tjpeg: \"image/jpeg\",\n\t\tpng: \"image/png\",\n\t\tavif: \"image/avif\",\n\t};\n\treturn map[format];\n}\n\nfunction getExtension(format: ImageFormat): string {\n\treturn format === \"jpeg\" ? \"jpg\" : format;\n}\n\n// sharp is a heavy native module — load it lazily and once\ntype SharpFactory = typeof import(\"sharp\").default;\nlet sharpModule: SharpFactory | null = null;\nasync function loadSharp(): Promise<SharpFactory> {\n\tif (sharpModule === null) {\n\t\tsharpModule = (await import(\"sharp\")).default;\n\t}\n\treturn sharpModule;\n}\n\n/**\n * Detect the actual MIME type of a buffer from its content.\n * Returns undefined when the buffer is not a recognizable image.\n */\nexport async function detectImageMime(\n\tbuffer: Uint8Array,\n): Promise<string | undefined> {\n\tconst sharp = await loadSharp();\n\ttry {\n\t\tconst { format } = await sharp(buffer).metadata();\n\t\treturn format !== undefined ? FORMAT_TO_MIME[format] : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Apply format conversion to a file buffer.\n * Returns the converted buffer and new mimetype.\n * If file is not an image or no format set, returns original unchanged.\n */\nexport async function convertFormat(\n\tfile: UploadFile,\n\tformat: ImageFormat,\n\tquality: number,\n): Promise<{ buffer: Uint8Array; mimetype: string; filename: string }> {\n\tif (!isImage(file.mimetype)) {\n\t\treturn {\n\t\t\tbuffer: file.buffer,\n\t\t\tmimetype: file.mimetype,\n\t\t\tfilename: file.filename,\n\t\t};\n\t}\n\n\tconst sharp = await loadSharp();\n\n\ttry {\n\t\tconst converted = await sharp(file.buffer)\n\t\t\t.toFormat(format, { quality })\n\t\t\t.toBuffer();\n\n\t\tconst ext = getExtension(format);\n\t\tconst baseName = file.filename.replace(/\\.[^.]+$/, \"\");\n\t\tconst newFilename = `${baseName}.${ext}`;\n\n\t\treturn {\n\t\t\tbuffer: new Uint8Array(converted),\n\t\t\tmimetype: getMimeType(format),\n\t\t\tfilename: newFilename,\n\t\t};\n\t} catch (error) {\n\t\tthrow new DatrixError(\"Failed to convert image format\", {\n\t\t\tcode: \"IMAGE_CONVERT_ERROR\",\n\t\t\toperation: \"upload:convertFormat\",\n\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t});\n\t}\n}\n\n/**\n * Generate resolution variants for an image.\n * Returns a map of resolution name → MediaVariant.\n * Uploads each variant via the provider — the caller's uploadFn is\n * responsible for tracking uploaded keys for failure cleanup.\n */\nexport async function generateVariants<TResolutions extends string>(\n\tfile: UploadFile,\n\tresolutions: Record<TResolutions, ResolutionConfig>,\n\tformat: ImageFormat | undefined,\n\tquality: number,\n\tuploadFn: (variantFile: UploadFile) => Promise<{ key: string }>,\n): Promise<MediaVariants<TResolutions>> {\n\tif (!isImage(file.mimetype)) {\n\t\treturn {};\n\t}\n\n\tconst sharp = await loadSharp();\n\n\tconst targetFormat = format ?? \"jpeg\";\n\tconst outputMime = getMimeType(targetFormat);\n\tconst outputExt = getExtension(targetFormat);\n\n\tconst variants: Partial<Record<TResolutions, MediaVariant>> = {};\n\n\tfor (const [name, config] of Object.entries(resolutions) as [\n\t\tTResolutions,\n\t\tResolutionConfig,\n\t][]) {\n\t\ttry {\n\t\t\tconst resizeOptions: import(\"sharp\").ResizeOptions = {\n\t\t\t\twidth: config.width,\n\t\t\t\t...(config.height !== undefined && { height: config.height }),\n\t\t\t\t...(config.fit !== undefined &&\n\t\t\t\t\tconfig.height !== undefined && { fit: config.fit }),\n\t\t\t};\n\n\t\t\tconst variantBuffer = await sharp(file.buffer)\n\t\t\t\t.resize(resizeOptions)\n\t\t\t\t.toFormat(targetFormat, { quality })\n\t\t\t\t.toBuffer({ resolveWithObject: true });\n\n\t\t\tconst baseName = file.filename.replace(/\\.[^.]+$/, \"\");\n\t\t\tconst variantFilename = `${baseName}-${name}.${outputExt}`;\n\n\t\t\tconst variantFile: UploadFile = {\n\t\t\t\tfilename: variantFilename,\n\t\t\t\toriginalName: variantFilename,\n\t\t\t\tmimetype: outputMime,\n\t\t\t\tsize: variantBuffer.data.length,\n\t\t\t\tbuffer: new Uint8Array(variantBuffer.data),\n\t\t\t};\n\n\t\t\tconst uploaded = await uploadFn(variantFile);\n\n\t\t\tvariants[name] = {\n\t\t\t\tkey: uploaded.key,\n\t\t\t\twidth: variantBuffer.info.width,\n\t\t\t\theight: variantBuffer.info.height,\n\t\t\t\tsize: variantBuffer.data.length,\n\t\t\t\tmimeType: outputMime,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof DatrixError) throw error;\n\t\t\tthrow new DatrixError(`Failed to generate variant: ${name}`, {\n\t\t\t\tcode: \"VARIANT_GENERATE_ERROR\",\n\t\t\t\toperation: \"upload:generateVariants\",\n\t\t\t\tcause: error instanceof Error ? error : undefined,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn variants as MediaVariants<TResolutions>;\n}\n\nexport { isImage };\n","/**\n * Local Filesystem Storage Provider\n */\n\nimport type {\n\tStorageProvider,\n\tUploadFile,\n\tUploadResult,\n\tLocalProviderOptions,\n} from \"@datrix/core\";\nimport { generateUniqueFilename, sanitizeFilename } from \"@datrix/core\";\nimport { DatrixError } from \"@datrix/core\";\n\nclass UploadError extends DatrixError {\n\tconstructor(message: string, cause?: Error) {\n\t\tsuper(message, {\n\t\t\tcode: \"UPLOAD_ERROR\",\n\t\t\toperation: \"upload:local\",\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t\tthis.name = \"UploadError\";\n\t}\n}\n\nexport class LocalStorageProvider implements StorageProvider {\n\treadonly name = \"local\" as const;\n\n\tprivate readonly basePath: string;\n\tprivate readonly baseUrl: string;\n\tprivate readonly ensureDirectory: boolean;\n\n\tconstructor(options: LocalProviderOptions) {\n\t\tthis.basePath = options.basePath;\n\t\tthis.baseUrl = options.baseUrl;\n\t\tthis.ensureDirectory = options.ensureDirectory ?? true;\n\t}\n\n\tasync upload(file: UploadFile): Promise<UploadResult> {\n\t\ttry {\n\t\t\tconst fs = await import(\"fs/promises\");\n\t\t\tconst path = await import(\"path\");\n\n\t\t\tconst sanitized = sanitizeFilename(file.originalName);\n\t\t\tconst uniqueFilename = generateUniqueFilename(sanitized);\n\t\t\tconst fullPath = path.join(this.basePath, uniqueFilename);\n\n\t\t\tif (this.ensureDirectory) {\n\t\t\t\tconst dirPath = path.dirname(fullPath);\n\t\t\t\tawait fs.mkdir(dirPath, { recursive: true });\n\t\t\t}\n\n\t\t\tawait fs.writeFile(fullPath, file.buffer);\n\n\t\t\treturn {\n\t\t\t\tkey: uniqueFilename,\n\t\t\t\tsize: file.size,\n\t\t\t\tmimetype: file.mimetype,\n\t\t\t\tuploadedAt: new Date(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to upload file to local filesystem\", cause);\n\t\t}\n\t}\n\n\t/**\n\t * Delete is idempotent — a missing file is treated as already deleted.\n\t */\n\tasync delete(key: string): Promise<void> {\n\t\tconst fullPath = await this.resolveContainedPath(key);\n\t\tconst fs = await import(\"fs/promises\");\n\n\t\ttry {\n\t\t\tawait fs.unlink(fullPath);\n\t\t} catch (error) {\n\t\t\tif ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\n\t\t\t\t\"Failed to delete file from local filesystem\",\n\t\t\t\tcause,\n\t\t\t);\n\t\t}\n\t}\n\n\tgetUrl(key: string): string {\n\t\tthis.assertSafeKey(key);\n\t\treturn this.buildUrl(key);\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst fullPath = await this.resolveContainedPath(key);\n\t\t\tconst fs = await import(\"fs/promises\");\n\t\t\tawait fs.access(fullPath);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Keys must be relative paths that stay inside basePath — reject\n\t * traversal segments and absolute paths before touching the filesystem.\n\t */\n\tprivate assertSafeKey(key: string): void {\n\t\tconst isAbsolute = key.startsWith(\"/\") || /^[A-Za-z]:/.test(key);\n\t\tconst hasTraversal = key.split(/[/\\\\]/).some((segment) => segment === \"..\");\n\t\tif (key === \"\" || isAbsolute || hasTraversal) {\n\t\t\tthrow new UploadError(`Invalid storage key: ${key}`);\n\t\t}\n\t}\n\n\tprivate async resolveContainedPath(key: string): Promise<string> {\n\t\tthis.assertSafeKey(key);\n\t\tconst path = await import(\"path\");\n\t\tconst base = path.resolve(this.basePath);\n\t\tconst fullPath = path.resolve(base, key);\n\t\tif (!fullPath.startsWith(base + path.sep)) {\n\t\t\tthrow new UploadError(`Invalid storage key: ${key}`);\n\t\t}\n\t\treturn fullPath;\n\t}\n\n\tprivate buildUrl(key: string): string {\n\t\tconst cleanBaseUrl = this.baseUrl.replace(/\\/$/, \"\");\n\t\tconst cleanKey = key.replace(/^\\//, \"\");\n\t\treturn `${cleanBaseUrl}/${cleanKey}`;\n\t}\n}\n","/**\n * AWS S3 Storage Provider\n *\n * Implements AWS Signature V4 signing without external SDK.\n */\n\nimport type {\n\tStorageProvider,\n\tUploadFile,\n\tUploadResult,\n\tS3ProviderOptions,\n} from \"@datrix/core\";\nimport { generateUniqueFilename, sanitizeFilename } from \"@datrix/core\";\nimport { DatrixError } from \"@datrix/core\";\n\nclass UploadError extends DatrixError {\n\tconstructor(message: string, cause?: Error) {\n\t\tsuper(message, {\n\t\t\tcode: \"UPLOAD_ERROR\",\n\t\t\toperation: \"upload:s3\",\n\t\t\t...(cause !== undefined && { cause }),\n\t\t});\n\t\tthis.name = \"UploadError\";\n\t}\n}\n\ninterface S3RequestOptions {\n\treadonly body?: Uint8Array;\n\treadonly contentType?: string;\n\t/** HEAD responses carry no body — skip reading it on error */\n\treadonly readErrorBody?: boolean;\n}\n\nexport class S3StorageProvider implements StorageProvider {\n\treadonly name = \"s3\" as const;\n\n\tprivate readonly bucket: string;\n\tprivate readonly region: string;\n\tprivate readonly accessKeyId: string;\n\tprivate readonly secretAccessKey: string;\n\tprivate readonly endpoint: string;\n\tprivate readonly pathPrefix: string;\n\tprivate readonly sessionToken: string | undefined;\n\tprivate readonly forcePathStyle: boolean;\n\n\tconstructor(options: S3ProviderOptions) {\n\t\tthis.bucket = options.bucket;\n\t\tthis.region = options.region;\n\t\tthis.accessKeyId = options.accessKeyId;\n\t\tthis.secretAccessKey = options.secretAccessKey;\n\t\tthis.endpoint = options.endpoint ?? `s3.${options.region}.amazonaws.com`;\n\t\tthis.pathPrefix = options.pathPrefix ?? \"uploads\";\n\t\tthis.sessionToken = options.sessionToken;\n\t\tthis.forcePathStyle = options.forcePathStyle ?? false;\n\t}\n\n\tasync upload(file: UploadFile): Promise<UploadResult> {\n\t\ttry {\n\t\t\tconst sanitized = sanitizeFilename(file.originalName);\n\t\t\tconst filename = generateUniqueFilename(sanitized);\n\t\t\tconst key = this.pathPrefix ? `${this.pathPrefix}/${filename}` : filename;\n\n\t\t\tawait this.sendRequest(\"PUT\", key, {\n\t\t\t\tbody: file.buffer,\n\t\t\t\tcontentType: file.mimetype,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\tkey,\n\t\t\t\tsize: file.size,\n\t\t\t\tmimetype: file.mimetype,\n\t\t\t\tuploadedAt: new Date(),\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof UploadError) throw error;\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to upload file to S3\", cause);\n\t\t}\n\t}\n\n\tasync delete(key: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.sendRequest(\"DELETE\", key, {});\n\t\t} catch (error) {\n\t\t\tif (error instanceof UploadError) throw error;\n\t\t\tconst cause = error instanceof Error ? error : undefined;\n\t\t\tthrow new UploadError(\"Failed to delete file from S3\", cause);\n\t\t}\n\t}\n\n\tgetUrl(key: string): string {\n\t\tconst encodedKey = encodePath(key);\n\t\treturn this.forcePathStyle\n\t\t\t? `https://${this.endpoint}/${this.bucket}/${encodedKey}`\n\t\t\t: `https://${this.bucket}.${this.endpoint}/${encodedKey}`;\n\t}\n\n\tasync exists(key: string): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.sendRequest(\"HEAD\", key, { readErrorBody: false });\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\tprivate hostAndPath(key: string): { host: string; urlPath: string } {\n\t\tconst encodedKey = encodePath(key);\n\t\treturn this.forcePathStyle\n\t\t\t? { host: this.endpoint, urlPath: `/${this.bucket}/${encodedKey}` }\n\t\t\t: { host: `${this.bucket}.${this.endpoint}`, urlPath: `/${encodedKey}` };\n\t}\n\n\t/**\n\t * Send a SigV4-signed request to S3, resolving on 2xx and rejecting with\n\t * the response body otherwise.\n\t */\n\tprivate async sendRequest(\n\t\tmethod: string,\n\t\tkey: string,\n\t\toptions: S3RequestOptions,\n\t): Promise<void> {\n\t\tconst https = await import(\"https\");\n\t\tconst crypto = await import(\"crypto\");\n\n\t\tconst { host, urlPath } = this.hostAndPath(key);\n\t\tconst { body, contentType, readErrorBody = true } = options;\n\n\t\t// One timestamp for the whole signature — header, canonical request,\n\t\t// string-to-sign, and credential scope must all agree.\n\t\tconst amzDate = new Date().toISOString().replace(/[:-]|\\.\\d{3}/g, \"\");\n\t\tconst dateStamp = amzDate.slice(0, 8);\n\t\tconst contentHash = crypto\n\t\t\t.createHash(\"sha256\")\n\t\t\t.update(body ?? \"\")\n\t\t\t.digest(\"hex\");\n\n\t\t// Signed headers, alphabetically ordered\n\t\tconst signedHeaderEntries: [string, string][] = [\n\t\t\t[\"host\", host],\n\t\t\t[\"x-amz-content-sha256\", contentHash],\n\t\t\t[\"x-amz-date\", amzDate],\n\t\t];\n\t\tif (this.sessionToken !== undefined) {\n\t\t\tsignedHeaderEntries.push([\"x-amz-security-token\", this.sessionToken]);\n\t\t}\n\n\t\tconst canonicalHeaders = signedHeaderEntries\n\t\t\t.map(([name, value]) => `${name}:${value}\\n`)\n\t\t\t.join(\"\");\n\t\tconst signedHeaders = signedHeaderEntries.map(([name]) => name).join(\";\");\n\t\tconst canonicalRequest = [\n\t\t\tmethod,\n\t\t\turlPath,\n\t\t\t\"\",\n\t\t\tcanonicalHeaders,\n\t\t\tsignedHeaders,\n\t\t\tcontentHash,\n\t\t].join(\"\\n\");\n\n\t\tconst algorithm = \"AWS4-HMAC-SHA256\";\n\t\tconst credentialScope = `${dateStamp}/${this.region}/s3/aws4_request`;\n\t\tconst canonicalRequestHash = crypto\n\t\t\t.createHash(\"sha256\")\n\t\t\t.update(canonicalRequest)\n\t\t\t.digest(\"hex\");\n\t\tconst stringToSign = [\n\t\t\talgorithm,\n\t\t\tamzDate,\n\t\t\tcredentialScope,\n\t\t\tcanonicalRequestHash,\n\t\t].join(\"\\n\");\n\n\t\tconst signature = this.calculateSignature(crypto, stringToSign, dateStamp);\n\t\tconst authorization = `${algorithm} Credential=${this.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;\n\n\t\tconst headers: Record<string, string | number> = {\n\t\t\tHost: host,\n\t\t\t\"x-amz-date\": amzDate,\n\t\t\t\"x-amz-content-sha256\": contentHash,\n\t\t\tAuthorization: authorization,\n\t\t};\n\t\tif (this.sessionToken !== undefined) {\n\t\t\theaders[\"x-amz-security-token\"] = this.sessionToken;\n\t\t}\n\t\tif (body !== undefined) {\n\t\t\theaders[\"Content-Length\"] = body.length;\n\t\t\tif (contentType !== undefined) {\n\t\t\t\theaders[\"Content-Type\"] = contentType;\n\t\t\t}\n\t\t}\n\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst req = https.request(\n\t\t\t\t{ hostname: host, port: 443, path: urlPath, method, headers },\n\t\t\t\t(res) => {\n\t\t\t\t\tconst status = res.statusCode ?? 0;\n\t\t\t\t\tif (status >= 200 && status < 300) {\n\t\t\t\t\t\tres.resume();\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (!readErrorBody) {\n\t\t\t\t\t\tres.resume();\n\t\t\t\t\t\treject(new UploadError(`S3 ${method} failed: ${status}`));\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tlet responseBody = \"\";\n\t\t\t\t\tres.on(\"data\", (chunk: Buffer) => {\n\t\t\t\t\t\tresponseBody += chunk.toString();\n\t\t\t\t\t});\n\t\t\t\t\tres.on(\"end\", () => {\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew UploadError(`S3 ${method} failed: ${status} ${responseBody}`),\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t\treq.on(\"error\", (error: Error) => {\n\t\t\t\treject(new UploadError(\"S3 request failed\", error));\n\t\t\t});\n\t\t\tif (body !== undefined) {\n\t\t\t\treq.write(body);\n\t\t\t}\n\t\t\treq.end();\n\t\t});\n\t}\n\n\tprivate calculateSignature(\n\t\tcrypto: typeof import(\"crypto\"),\n\t\tstringToSign: string,\n\t\tdateStamp: string,\n\t): string {\n\t\tconst kDate = crypto\n\t\t\t.createHmac(\"sha256\", `AWS4${this.secretAccessKey}`)\n\t\t\t.update(dateStamp)\n\t\t\t.digest();\n\t\tconst kRegion = crypto\n\t\t\t.createHmac(\"sha256\", kDate)\n\t\t\t.update(this.region)\n\t\t\t.digest();\n\t\tconst kService = crypto.createHmac(\"sha256\", kRegion).update(\"s3\").digest();\n\t\tconst kSigning = crypto\n\t\t\t.createHmac(\"sha256\", kService)\n\t\t\t.update(\"aws4_request\")\n\t\t\t.digest();\n\t\treturn crypto\n\t\t\t.createHmac(\"sha256\", kSigning)\n\t\t\t.update(stringToSign)\n\t\t\t.digest(\"hex\");\n\t}\n}\n\n/**\n * RFC 3986 encoding of an object-key path, keeping `/` separators —\n * required for both the request path and the SigV4 canonical URI.\n */\nfunction encodePath(key: string): string {\n\treturn key\n\t\t.split(\"/\")\n\t\t.map((segment) =>\n\t\t\tencodeURIComponent(segment).replace(\n\t\t\t\t/[!'()*]/g,\n\t\t\t\t(char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n\t\t\t),\n\t\t)\n\t\t.join(\"/\");\n}\n"],"mappings":"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","names":["import_core","import_core","import_core","changed","result","import_core","import_core","UploadError"]}
|