img-upload-kit 2.0.0 → 2.0.1

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +89 -89
  2. package/MIGRATION.md +192 -192
  3. package/README.md +243 -243
  4. package/dist/bin.js +0 -0
  5. package/dist/bin.js.map +1 -1
  6. package/dist/blur.cjs +3 -3
  7. package/dist/blur.cjs.map +1 -1
  8. package/dist/blur.js +2 -2
  9. package/dist/{chunk-G7LLSKBV.js → chunk-25GYKQ6J.js} +1 -1
  10. package/dist/chunk-25GYKQ6J.js.map +1 -0
  11. package/dist/{chunk-NEDBIIE3.cjs → chunk-34L4TEE3.cjs} +1 -1
  12. package/dist/chunk-34L4TEE3.cjs.map +1 -0
  13. package/dist/{chunk-KJVYWKIG.cjs → chunk-DFCSN3X7.cjs} +1 -1
  14. package/dist/chunk-DFCSN3X7.cjs.map +1 -0
  15. package/dist/{chunk-IB3TEDLR.js → chunk-EWVIL57J.js} +1 -1
  16. package/dist/chunk-EWVIL57J.js.map +1 -0
  17. package/dist/{chunk-3ICG2SLL.cjs → chunk-FP5YIWG5.cjs} +1 -1
  18. package/dist/chunk-FP5YIWG5.cjs.map +1 -0
  19. package/dist/{chunk-TT5C7K3Q.js → chunk-HILELQGS.js} +1 -1
  20. package/dist/chunk-HILELQGS.js.map +1 -0
  21. package/dist/{chunk-AI3JJLSA.js → chunk-IDLLCVGB.js} +3 -3
  22. package/dist/chunk-IDLLCVGB.js.map +1 -0
  23. package/dist/{chunk-4ASFG3KB.js → chunk-J6A44Y3W.js} +1 -1
  24. package/dist/chunk-J6A44Y3W.js.map +1 -0
  25. package/dist/{chunk-YRXXQUDE.cjs → chunk-LOSVFO56.cjs} +1 -1
  26. package/dist/chunk-LOSVFO56.cjs.map +1 -0
  27. package/dist/{chunk-WISG74VL.cjs → chunk-MBQJ6NVJ.cjs} +1 -1
  28. package/dist/chunk-MBQJ6NVJ.cjs.map +1 -0
  29. package/dist/{chunk-CV23O4YU.js → chunk-MHGQEBPW.js} +1 -1
  30. package/dist/chunk-MHGQEBPW.js.map +1 -0
  31. package/dist/chunk-QHU7FUO4.cjs.map +1 -1
  32. package/dist/{chunk-U2TWTEBY.cjs → chunk-TA5Y64JM.cjs} +24 -24
  33. package/dist/chunk-TA5Y64JM.cjs.map +1 -0
  34. package/dist/{chunk-QQHZJKNA.js → chunk-VYVZVUNI.js} +1 -1
  35. package/dist/chunk-VYVZVUNI.js.map +1 -0
  36. package/dist/{chunk-2OVNXHTN.cjs → chunk-XKQMCY5Z.cjs} +1 -1
  37. package/dist/chunk-XKQMCY5Z.cjs.map +1 -0
  38. package/dist/cli.cjs.map +1 -1
  39. package/dist/cli.js.map +1 -1
  40. package/dist/headless.cjs +4 -4
  41. package/dist/headless.cjs.map +1 -1
  42. package/dist/headless.js +3 -3
  43. package/dist/index.cjs +5 -5
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.js +4 -4
  46. package/dist/react.cjs +138 -138
  47. package/dist/react.cjs.map +1 -1
  48. package/dist/react.js +5 -5
  49. package/dist/react.js.map +1 -1
  50. package/dist/server-handler.cjs +2 -2
  51. package/dist/server-handler.cjs.map +1 -1
  52. package/dist/server-handler.js +1 -1
  53. package/dist/server.cjs +30 -30
  54. package/dist/server.cjs.map +1 -1
  55. package/dist/server.js +2 -2
  56. package/dist/server.js.map +1 -1
  57. package/dist/storage.cjs +9 -9
  58. package/dist/storage.cjs.map +1 -1
  59. package/dist/storage.js +1 -1
  60. package/dist/storage.js.map +1 -1
  61. package/dist/styles/animated-image.css +112 -112
  62. package/dist/styles/tokens.css +123 -123
  63. package/dist/styles.css +235 -235
  64. package/package.json +3 -3
  65. package/dist/chunk-2OVNXHTN.cjs.map +0 -1
  66. package/dist/chunk-3ICG2SLL.cjs.map +0 -1
  67. package/dist/chunk-4ASFG3KB.js.map +0 -1
  68. package/dist/chunk-AI3JJLSA.js.map +0 -1
  69. package/dist/chunk-CV23O4YU.js.map +0 -1
  70. package/dist/chunk-G7LLSKBV.js.map +0 -1
  71. package/dist/chunk-IB3TEDLR.js.map +0 -1
  72. package/dist/chunk-KJVYWKIG.cjs.map +0 -1
  73. package/dist/chunk-NEDBIIE3.cjs.map +0 -1
  74. package/dist/chunk-QQHZJKNA.js.map +0 -1
  75. package/dist/chunk-TT5C7K3Q.js.map +0 -1
  76. package/dist/chunk-U2TWTEBY.cjs.map +0 -1
  77. package/dist/chunk-WISG74VL.cjs.map +0 -1
  78. package/dist/chunk-YRXXQUDE.cjs.map +0 -1
package/dist/storage.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
 
5
5
 
6
- var _chunkYRXXQUDEcjs = require('./chunk-YRXXQUDE.cjs');
6
+ var _chunkLOSVFO56cjs = require('./chunk-LOSVFO56.cjs');
7
7
 
8
8
 
9
9
 
@@ -185,10 +185,10 @@ function createLocalFsStorageAdapter(options) {
185
185
  const limit = _nullishCoalesce(_optionalChain([listOptions, 'optionalAccess', _2 => _2.limit]), () => ( results.length));
186
186
  const sliced = _optionalChain([listOptions, 'optionalAccess', _3 => _3.limit]) !== void 0 || offset > 0 ? results.slice(offset, offset + limit) : results;
187
187
  const nextCursor = _optionalChain([listOptions, 'optionalAccess', _4 => _4.limit]) !== void 0 && offset + sliced.length < results.length ? String(offset + sliced.length) : void 0;
188
- return _chunkYRXXQUDEcjs.toPaginatedListResult.call(void 0, sliced, nextCursor);
188
+ return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, sliced, nextCursor);
189
189
  } catch (err) {
190
190
  if (err.code === "ENOENT") {
191
- return _chunkYRXXQUDEcjs.toPaginatedListResult.call(void 0, []);
191
+ return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, []);
192
192
  }
193
193
  throw err;
194
194
  }
@@ -227,7 +227,7 @@ function createS3StorageAdapter(options) {
227
227
  return {
228
228
  async upload(params) {
229
229
  const key = normalizeS3Key(params.path);
230
- _chunkYRXXQUDEcjs.validateStorageMetadata.call(void 0, params.metadata);
230
+ _chunkLOSVFO56cjs.validateStorageMetadata.call(void 0, params.metadata);
231
231
  const isImmutable = params.upsert === false && params.contentType.includes("image/avif");
232
232
  const cacheControl = _nullishCoalesce(_nullishCoalesce(params.cacheControl, () => ( options.cacheControl)), () => ( (isImmutable ? "public, max-age=31536000, immutable" : "public, max-age=2592000, must-revalidate")));
233
233
  const metadata = params.metadata || params.contentHash ? {
@@ -301,7 +301,7 @@ function createS3StorageAdapter(options) {
301
301
  ...item.Size !== void 0 ? { size: item.Size } : {}
302
302
  };
303
303
  }).filter((entry) => entry.name.length > 0);
304
- return _chunkYRXXQUDEcjs.toPaginatedListResult.call(void 0, entries, _optionalChain([output, 'optionalAccess', _10 => _10.NextContinuationToken]));
304
+ return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, entries, _optionalChain([output, 'optionalAccess', _10 => _10.NextContinuationToken]));
305
305
  },
306
306
  parseUrl(url) {
307
307
  if (typeof options.publicBaseUrl !== "string" || !url) return null;
@@ -346,7 +346,7 @@ function createSupabaseStorageAdapter(client, options = {}) {
346
346
  const publicPrefix = options.publicUrlPrefix || _chunkQHU7FUO4cjs.DEFAULT_PUBLIC_STORAGE_PREFIX;
347
347
  return {
348
348
  async upload(params) {
349
- _chunkYRXXQUDEcjs.validateStorageMetadata.call(void 0, params.metadata);
349
+ _chunkLOSVFO56cjs.validateStorageMetadata.call(void 0, params.metadata);
350
350
  const isImmutable = params.upsert === false && params.contentType.includes("image/avif");
351
351
  const cacheControl = params.cacheControl || (isImmutable ? "public, max-age=31536000, immutable" : "public, max-age=2592000, must-revalidate");
352
352
  const { data, error } = await client.storage.from(params.bucket).upload(params.path, params.data, {
@@ -396,7 +396,7 @@ function createSupabaseStorageAdapter(client, options = {}) {
396
396
  async list(bucket, folder, listOptions) {
397
397
  const bucketClient = client.storage.from(bucket);
398
398
  if (!bucketClient.list) {
399
- return _chunkYRXXQUDEcjs.toPaginatedListResult.call(void 0, []);
399
+ return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, []);
400
400
  }
401
401
  const limit = _nullishCoalesce(_optionalChain([listOptions, 'optionalAccess', _14 => _14.limit]), () => ( 1e3));
402
402
  const offset = _optionalChain([listOptions, 'optionalAccess', _15 => _15.cursor]) ? Number(listOptions.cursor) || 0 : 0;
@@ -414,7 +414,7 @@ function createSupabaseStorageAdapter(client, options = {}) {
414
414
  createdAt: _nullishCoalesce(f.created_at, () => ( void 0))
415
415
  }));
416
416
  const nextCursor = _optionalChain([listOptions, 'optionalAccess', _16 => _16.limit]) !== void 0 && entries.length >= limit ? String(offset + entries.length) : void 0;
417
- return _chunkYRXXQUDEcjs.toPaginatedListResult.call(void 0, entries, nextCursor);
417
+ return _chunkLOSVFO56cjs.toPaginatedListResult.call(void 0, entries, nextCursor);
418
418
  },
419
419
  parseUrl(url) {
420
420
  const info = _chunkQHU7FUO4cjs.parsePublicStorageUrl.call(void 0, url, publicPrefix);
@@ -443,5 +443,5 @@ function createSupabaseStorageAdapter(client, options = {}) {
443
443
 
444
444
 
445
445
 
446
- exports.DEFAULT_PUBLIC_STORAGE_PREFIX = _chunkQHU7FUO4cjs.DEFAULT_PUBLIC_STORAGE_PREFIX; exports.createLocalFsStorageAdapter = createLocalFsStorageAdapter; exports.createMemoryStorageAdapter = _chunkYRXXQUDEcjs.createMemoryStorageAdapter; exports.createS3StorageAdapter = createS3StorageAdapter; exports.createStorageAdapter = _chunkYRXXQUDEcjs.createStorageAdapter; exports.createSupabaseStorageAdapter = createSupabaseStorageAdapter; exports.parsePublicStorageUrl = _chunkQHU7FUO4cjs.parsePublicStorageUrl; exports.toPaginatedListResult = _chunkYRXXQUDEcjs.toPaginatedListResult; exports.validateStorageMetadata = _chunkYRXXQUDEcjs.validateStorageMetadata;
446
+ exports.DEFAULT_PUBLIC_STORAGE_PREFIX = _chunkQHU7FUO4cjs.DEFAULT_PUBLIC_STORAGE_PREFIX; exports.createLocalFsStorageAdapter = createLocalFsStorageAdapter; exports.createMemoryStorageAdapter = _chunkLOSVFO56cjs.createMemoryStorageAdapter; exports.createS3StorageAdapter = createS3StorageAdapter; exports.createStorageAdapter = _chunkLOSVFO56cjs.createStorageAdapter; exports.createSupabaseStorageAdapter = createSupabaseStorageAdapter; exports.parsePublicStorageUrl = _chunkQHU7FUO4cjs.parsePublicStorageUrl; exports.toPaginatedListResult = _chunkLOSVFO56cjs.toPaginatedListResult; exports.validateStorageMetadata = _chunkLOSVFO56cjs.validateStorageMetadata;
447
447
  //# sourceMappingURL=storage.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/image-upload-kit/image-upload-kit/dist/storage.cjs","../src/storage/localFsAdapter.ts","../src/storage/s3Adapter.ts","../src/storage/supabaseAdapter.ts"],"names":["path"],"mappings":"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","file":"/home/runner/work/image-upload-kit/image-upload-kit/dist/storage.cjs","sourcesContent":[null,"import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n type StorageAdapter,\n type StorageFileEntry,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n} from './types';\n\nexport interface LocalFsStorageOptions {\n /** Absolute or relative root directory on disk (e.g. `./public/uploads`) */\n rootDir: string;\n /** Public URL prefix matching `rootDir` (e.g. `/uploads` or `https://cdn.example.com/uploads`) */\n baseUrl?: string;\n /** Include the bucket segment as a subdirectory under `rootDir`. Defaults to `true`. */\n includeBucketInPath?: boolean;\n}\n\nfunction sanitizeRelativePath(inputPath: string): string {\n if (!inputPath || typeof inputPath !== 'string') {\n throw new Error('Invalid storage path');\n }\n if (inputPath.includes(':') || inputPath.includes('\\0')) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n\n const normalized = inputPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n const rawSegments = normalized.split('/').filter(Boolean);\n\n if (rawSegments.length === 0) {\n throw new Error('Invalid storage path');\n }\n\n for (const segment of rawSegments) {\n if (segment === '.' || segment === '..') {\n throw new Error('Invalid storage path: path traversal blocked');\n }\n }\n\n return rawSegments.join('/');\n}\n\nfunction assertContainedInRoot(resolvedRoot: string, targetPath: string): void {\n const relative = path.relative(resolvedRoot, targetPath);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n}\n\nexport interface LocalFsStorageAdapter extends StorageAdapter {\n exists(bucket: string, relativePath: string): Promise<boolean>;\n getPublicUrl(bucket: string, relativePath: string): string;\n list(\n bucket: string,\n folder?: string,\n options?: StorageListOptions,\n ): Promise<StoragePaginatedListResult>;\n deleteByUrl(url: string): Promise<boolean>;\n parseUrl(url: string): { bucket?: string; path: string } | null;\n}\n\nexport function createLocalFsStorageAdapter(\n options: LocalFsStorageOptions,\n): LocalFsStorageAdapter {\n const resolvedRoot = path.resolve(options.rootDir);\n const baseUrl = (options.baseUrl ?? '/uploads').replace(/\\/+$/, '');\n const includeBucket = options.includeBucketInPath !== false;\n\n const resolveDiskTarget = (bucket: string, relativePath: string) => {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanRel = sanitizeRelativePath(relativePath);\n const subPath = includeBucket ? `${cleanBucket}/${cleanRel}` : cleanRel;\n const diskPath = path.resolve(resolvedRoot, subPath);\n\n assertContainedInRoot(resolvedRoot, diskPath);\n\n return { diskPath, subPath, cleanRel };\n };\n\n const getPublicUrl = (bucket: string, relativePath: string): string => {\n const { subPath } = resolveDiskTarget(bucket, relativePath);\n return `${baseUrl}/${subPath}`;\n };\n\n const parseUrl = (url: string): { bucket?: string; path: string } | null => {\n if (!url || typeof url !== 'string') return null;\n\n try {\n const cleanUrl = url.split(/[?#]/)[0];\n const prefix = `${baseUrl}/`;\n let subPath: string | null = null;\n\n if (cleanUrl.startsWith(prefix)) {\n subPath = cleanUrl.slice(prefix.length);\n } else if (baseUrl.startsWith('/')) {\n const parsed = new URL(cleanUrl, 'http://localhost');\n const isLocal =\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n const isRelative =\n !cleanUrl.startsWith('http://') && !cleanUrl.startsWith('https://');\n if ((isLocal || isRelative) && parsed.pathname.startsWith(prefix)) {\n subPath = parsed.pathname.slice(prefix.length);\n }\n }\n\n if (!subPath) return null;\n\n const decoded = decodeURIComponent(subPath)\n .replace(/\\\\/g, '/')\n .replace(/^\\/+/, '');\n if (!decoded || decoded.includes(':') || decoded.includes('\\0')) {\n return null;\n }\n\n const segments = decoded.split('/').filter(Boolean);\n if (\n segments.length === 0 ||\n segments.includes('..') ||\n segments.includes('.')\n ) {\n return null;\n }\n\n const diskPath = path.resolve(resolvedRoot, ...segments);\n const relative = path.relative(resolvedRoot, diskPath);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n return null;\n }\n\n if (includeBucket) {\n if (segments.length < 2) return null;\n return {\n bucket: segments[0],\n path: segments.slice(1).join('/'),\n };\n }\n\n return { path: segments.join('/') };\n } catch {\n return null;\n }\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const { diskPath, cleanRel } = resolveDiskTarget(\n params.bucket,\n params.path,\n );\n\n await fs.mkdir(path.dirname(diskPath), { recursive: true });\n\n try {\n await fs.writeFile(\n diskPath,\n params.data,\n params.upsert === false ? { flag: 'wx' } : undefined,\n );\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n const existsError = new Error(\n `File already exists at ${params.bucket}/${cleanRel}`,\n ) as NodeJS.ErrnoException;\n existsError.code = 'EEXIST';\n throw existsError;\n }\n throw err;\n }\n\n return {\n bucket: params.bucket,\n path: cleanRel,\n url: getPublicUrl(params.bucket, cleanRel),\n };\n },\n\n async delete(bucket: string, relativePath: string): Promise<void> {\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\n try {\n await fs.unlink(diskPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err;\n }\n }\n },\n\n async exists(bucket: string, relativePath: string): Promise<boolean> {\n try {\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\n const stat = await fs.stat(diskPath);\n return stat.isFile();\n } catch {\n return false;\n }\n },\n\n getPublicUrl,\n parseUrl,\n\n async deleteByUrl(url: string): Promise<boolean> {\n const parsed = parseUrl(url);\n if (!parsed) return false;\n\n try {\n const subSegments =\n includeBucket && parsed.bucket\n ? [parsed.bucket, ...parsed.path.split('/')]\n : parsed.path.split('/');\n const diskPath = path.resolve(resolvedRoot, ...subSegments);\n assertContainedInRoot(resolvedRoot, diskPath);\n await fs.unlink(diskPath);\n return true;\n } catch {\n return false;\n }\n },\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanFolder = folder ? sanitizeRelativePath(folder) : '';\n const targetDir = path.resolve(\n resolvedRoot,\n includeBucket ? cleanBucket : '',\n cleanFolder,\n );\n\n assertContainedInRoot(resolvedRoot, targetDir);\n\n try {\n const dirEntries = await fs.readdir(targetDir, { withFileTypes: true });\n const results: StorageFileEntry[] = [];\n\n for (const entry of dirEntries) {\n if (!entry.isFile()) continue;\n const full = path.join(targetDir, entry.name);\n const stat = await fs.stat(full);\n results.push({\n name: entry.name,\n id: entry.name,\n createdAt: stat.birthtime.toISOString(),\n size: stat.size,\n });\n }\n\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\n const limit = listOptions?.limit ?? results.length;\n const sliced =\n listOptions?.limit !== undefined || offset > 0\n ? results.slice(offset, offset + limit)\n : results;\n const nextCursor =\n listOptions?.limit !== undefined && offset + sliced.length < results.length\n ? String(offset + sliced.length)\n : undefined;\n\n return toPaginatedListResult(sliced, nextCursor);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return toPaginatedListResult([]);\n }\n throw err;\n }\n },\n };\n}\n","import {\n type StorageAdapter,\n type StorageFileEntry,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n validateStorageMetadata,\n} from './types';\n\nexport interface S3PutObjectInput {\n Bucket: string;\n Key: string;\n Body: Buffer | Uint8Array;\n ContentType: string;\n CacheControl?: string;\n Metadata?: Record<string, string>;\n IfNoneMatch?: string;\n}\n\nexport interface S3DeleteObjectInput {\n Bucket: string;\n Key: string;\n}\n\nexport interface S3HeadObjectInput {\n Bucket: string;\n Key: string;\n}\n\nexport interface S3ListObjectsInput {\n Bucket: string;\n Prefix?: string;\n MaxKeys?: number;\n ContinuationToken?: string;\n}\n\nexport interface S3ListedObject {\n Key?: string;\n Size?: number;\n LastModified?: Date | string;\n}\n\nexport interface S3ListObjectsOutput {\n Contents?: S3ListedObject[];\n NextContinuationToken?: string;\n IsTruncated?: boolean;\n}\n\n/** Structural S3 / Cloudflare R2 / MinIO transport with zero runtime SDK dependency. */\nexport interface S3TransportLike {\n putObject(input: S3PutObjectInput): Promise<unknown>;\n deleteObject(input: S3DeleteObjectInput): Promise<unknown>;\n headObject?(input: S3HeadObjectInput): Promise<unknown>;\n listObjects?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n listObjectsV2?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n}\n\nexport type S3PublicUrlBuilder = (bucket: string, path: string) => string;\n\nexport interface S3StorageBaseOptions {\n /** Public base URL prefix (e.g. `'https://cdn.example.com'`) or `(bucket, path) => string` builder. */\n publicBaseUrl: string | S3PublicUrlBuilder;\n /** Default `Cache-Control` header applied when `StorageUploadParams.cacheControl` is omitted. */\n cacheControl?: string;\n /** Include `bucket` segment in string-prefix URLs (`${publicBaseUrl}/${bucket}/${path}`). Defaults to `true`. */\n includeBucketInUrl?: boolean;\n /** Fallback bucket used by `deleteByUrl` when `includeBucketInUrl` is `false`. */\n defaultBucket?: string;\n}\n\nexport type S3StorageAdapterOptions = S3StorageBaseOptions &\n (\n | (S3TransportLike & { client?: never; transport?: never })\n | { client: S3TransportLike; transport?: never; putObject?: never }\n | { transport: S3TransportLike; client?: never; putObject?: never }\n );\n\nfunction normalizeS3Key(rawPath: string): string {\n const normalized = rawPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (!normalized) throw new Error('Invalid S3 object key');\n return normalized;\n}\n\nfunction resolveTransport(options: S3StorageAdapterOptions): S3TransportLike {\n if ('client' in options && options.client) return options.client;\n if ('transport' in options && options.transport) return options.transport;\n if ('putObject' in options && typeof options.putObject === 'function') return options;\n throw new Error('S3StorageAdapter requires putObject/deleteObject or client/transport');\n}\n\nfunction isS3PreconditionError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as {\n code?: unknown;\n name?: unknown;\n statusCode?: unknown;\n $metadata?: { httpStatusCode?: number };\n };\n const status = e.$metadata?.httpStatusCode ?? Number(e.statusCode);\n const code = String(e.code ?? e.name ?? '').toLowerCase();\n return status === 412 || code === 'preconditionfailed' || code === 'eexist';\n}\n\n/** Zero-dependency S3 / Cloudflare R2 / MinIO `StorageAdapter`. */\nexport function createS3StorageAdapter(options: S3StorageAdapterOptions): StorageAdapter {\n const transport = resolveTransport(options);\n const includeBucket = options.includeBucketInUrl !== false;\n\n const getPublicUrl = (bucket: string, path: string): string => {\n const key = normalizeS3Key(path);\n if (typeof options.publicBaseUrl === 'function') return options.publicBaseUrl(bucket, key);\n const base = options.publicBaseUrl.replace(/\\/+$/, '');\n return includeBucket ? `${base}/${bucket}/${key}` : `${base}/${key}`;\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const key = normalizeS3Key(params.path);\n validateStorageMetadata(params.metadata);\n\n const isImmutable =\n params.upsert === false && params.contentType.includes('image/avif');\n const cacheControl =\n params.cacheControl ??\n options.cacheControl ??\n (isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const metadata =\n params.metadata || params.contentHash\n ? {\n ...params.metadata,\n ...(params.contentHash ? { contentHash: params.contentHash } : {}),\n }\n : undefined;\n\n try {\n await transport.putObject({\n Bucket: params.bucket,\n Key: key,\n Body: params.data,\n ContentType: params.contentType,\n ...(cacheControl ? { CacheControl: cacheControl } : {}),\n ...(metadata ? { Metadata: metadata } : {}),\n ...(params.upsert === false ? { IfNoneMatch: '*' } : {}),\n });\n } catch (err) {\n if (isS3PreconditionError(err)) {\n const existsErr = new Error(\n `Object already exists at ${params.bucket}/${key}`,\n ) as Error & { code?: string; statusCode?: number };\n existsErr.code = 'EEXIST';\n existsErr.statusCode = 412;\n throw existsErr;\n }\n throw err;\n }\n\n return {\n bucket: params.bucket,\n path: key,\n url: getPublicUrl(params.bucket, key),\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n await transport.deleteObject({ Bucket: bucket, Key: normalizeS3Key(path) });\n },\n\n async exists(bucket: string, path: string): Promise<boolean> {\n const key = normalizeS3Key(path);\n if (transport.headObject) {\n try {\n await transport.headObject({ Bucket: bucket, Key: key });\n return true;\n } catch {\n return false;\n }\n }\n const lastSlash = key.lastIndexOf('/');\n const folder = lastSlash > 0 ? key.slice(0, lastSlash) : undefined;\n const targetName = lastSlash > 0 ? key.slice(lastSlash + 1) : key;\n const listed = await this.list!(bucket, folder);\n return listed.some((item) => item.name === targetName);\n },\n\n getPublicUrl,\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const cleanFolder = folder ? normalizeS3Key(folder).replace(/\\/+$/, '') : '';\n const prefix = cleanFolder ? `${cleanFolder}/` : undefined;\n const input: S3ListObjectsInput = {\n Bucket: bucket,\n ...(prefix ? { Prefix: prefix } : {}),\n MaxKeys: listOptions?.limit ?? 1000,\n ...(listOptions?.cursor ? { ContinuationToken: listOptions.cursor } : {}),\n };\n\n const output = transport.listObjects\n ? await transport.listObjects(input)\n : transport.listObjectsV2\n ? await transport.listObjectsV2(input)\n : undefined;\n\n const entries: StorageFileEntry[] = (output?.Contents ?? [])\n .filter((item): item is S3ListedObject & { Key: string } => Boolean(item.Key))\n .map((item) => {\n const name =\n prefix && item.Key.startsWith(prefix)\n ? item.Key.slice(prefix.length)\n : item.Key;\n const createdAt =\n item.LastModified instanceof Date\n ? item.LastModified.toISOString()\n : item.LastModified;\n return {\n name,\n id: `${bucket}/${item.Key}`,\n ...(createdAt !== undefined ? { createdAt } : {}),\n ...(item.Size !== undefined ? { size: item.Size } : {}),\n };\n })\n .filter((entry) => entry.name.length > 0);\n\n return toPaginatedListResult(entries, output?.NextContinuationToken);\n },\n\n parseUrl(url: string): { bucket?: string; path: string } | null {\n if (typeof options.publicBaseUrl !== 'string' || !url) return null;\n const prefix = `${options.publicBaseUrl.replace(/\\/+$/, '')}/`;\n const cleanUrl = url.split(/[?#]/)[0];\n if (!cleanUrl.startsWith(prefix)) return null;\n\n try {\n const rest = decodeURIComponent(cleanUrl.slice(prefix.length)).replace(/^\\/+/, '');\n if (!rest) return null;\n\n if (includeBucket) {\n const slashIdx = rest.indexOf('/');\n if (slashIdx <= 0 || slashIdx === rest.length - 1) return null;\n return {\n bucket: rest.slice(0, slashIdx),\n path: rest.slice(slashIdx + 1),\n };\n }\n\n if (!options.defaultBucket) return null;\n return { bucket: options.defaultBucket, path: rest };\n } catch {\n return null;\n }\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const parsed = this.parseUrl?.(url);\n if (!parsed || !parsed.bucket) return false;\n\n try {\n await transport.deleteObject({\n Bucket: parsed.bucket,\n Key: parsed.path,\n });\n return true;\n } catch {\n return false;\n }\n },\n };\n}\n","import {\n type StorageAdapter,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n validateStorageMetadata,\n} from './types';\nimport {\n DEFAULT_PUBLIC_STORAGE_PREFIX,\n parsePublicStorageUrl,\n} from './urlParser';\n\n/**\n * Minimal structural interface matching `@supabase/supabase-js` Storage client,\n * avoiding a mandatory `@supabase/supabase-js` runtime dependency.\n */\nexport interface SupabaseBucketClientLike {\n upload(\n path: string,\n fileBody: Buffer | Uint8Array,\n fileOptions?: {\n contentType?: string;\n upsert?: boolean;\n cacheControl?: string;\n metadata?: Record<string, string>;\n },\n ): Promise<{\n data: { path: string } | null;\n error: { message: string } | null;\n }>;\n remove(paths: string[]): Promise<{\n data?: unknown;\n error: { message: string } | null;\n }>;\n getPublicUrl(path: string): {\n data: { publicUrl: string };\n };\n list?(\n path?: string,\n options?: {\n limit?: number;\n offset?: number;\n sortBy?: { column: string; order: string };\n },\n ): Promise<{\n data: Array<{\n name: string;\n id: string | null;\n created_at: string | null;\n }> | null;\n error: { message: string } | null;\n }>;\n}\n\nexport interface SupabaseStorageClientLike {\n storage: {\n from(bucket: string): SupabaseBucketClientLike;\n };\n}\n\nexport interface SupabaseStorageAdapterOptions {\n publicUrlPrefix?: string;\n}\n\n/**\n * Plug in any `@supabase/supabase-js` client instance (e.g., service-role client)\n * as a `StorageAdapter` with zero extra wrapper boilerplate.\n */\nexport function createSupabaseStorageAdapter(\n client: SupabaseStorageClientLike,\n options: SupabaseStorageAdapterOptions = {},\n): StorageAdapter {\n const publicPrefix =\n options.publicUrlPrefix || DEFAULT_PUBLIC_STORAGE_PREFIX;\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n validateStorageMetadata(params.metadata);\n\n const isImmutable =\n params.upsert === false && params.contentType.includes('image/avif');\n const cacheControl =\n params.cacheControl ||\n (isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const { data, error } = await client.storage\n .from(params.bucket)\n .upload(params.path, params.data, {\n contentType: params.contentType,\n upsert: params.upsert === undefined ? true : params.upsert,\n cacheControl,\n metadata: {\n ...params.metadata,\n uploadedAt: new Date().toISOString(),\n contentHash: params.contentHash || 'unknown',\n },\n });\n\n if (error || !data) {\n throw new Error(\n `Failed to upload to Supabase storage (bucket: ${params.bucket}, path: ${params.path}): ${error?.message || 'Unknown error'}`,\n );\n }\n\n const {\n data: { publicUrl },\n } = client.storage.from(params.bucket).getPublicUrl(data.path);\n\n return {\n bucket: params.bucket,\n path: data.path,\n url: publicUrl,\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n const { error } = await client.storage.from(bucket).remove([path]);\n if (error) {\n throw new Error(`Failed to delete from Supabase storage: ${error.message}`);\n }\n },\n\n async exists(bucket: string, path: string): Promise<boolean> {\n const cleanPath = path.replace(/^\\/+/, '');\n const lastSlash = cleanPath.lastIndexOf('/');\n const folder = lastSlash > 0 ? cleanPath.slice(0, lastSlash) : undefined;\n const targetName = lastSlash > 0 ? cleanPath.slice(lastSlash + 1) : cleanPath;\n const entries = await this.list!(bucket, folder);\n return entries.some((entry) => entry.name === targetName);\n },\n\n getPublicUrl(bucket: string, path: string): string {\n const {\n data: { publicUrl },\n } = client.storage.from(bucket).getPublicUrl(path);\n return publicUrl;\n },\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const bucketClient = client.storage.from(bucket);\n if (!bucketClient.list) {\n return toPaginatedListResult([]);\n }\n\n const limit = listOptions?.limit ?? 1000;\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\n\n const { data, error } = await bucketClient.list(folder, {\n limit,\n ...(offset > 0 ? { offset } : {}),\n sortBy: { column: 'created_at', order: 'desc' },\n });\n\n if (error) {\n throw new Error(`Failed to list Supabase bucket files: ${error.message}`);\n }\n\n const entries = (data || [])\n .filter((f) => f.id !== null)\n .map((f) => ({\n name: f.name,\n id: f.id ?? undefined,\n createdAt: f.created_at ?? undefined,\n }));\n\n const nextCursor =\n listOptions?.limit !== undefined && entries.length >= limit\n ? String(offset + entries.length)\n : undefined;\n\n return toPaginatedListResult(entries, nextCursor);\n },\n\n parseUrl(url: string): { bucket?: string; path: string } | null {\n const info = parsePublicStorageUrl(url, publicPrefix);\n if (!info) return null;\n return { bucket: info.bucket, path: info.path };\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const info = parsePublicStorageUrl(url, publicPrefix);\n if (!info) return false;\n\n try {\n const { error } = await client.storage\n .from(info.bucket)\n .remove([info.path]);\n return !error;\n } catch {\n return false;\n }\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["e:\\Personal-Projects\\image-upload-kit\\dist\\storage.cjs"],"names":[],"mappings":"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,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,YAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,YAAA;AACA,YAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"E:\\Personal-Projects\\image-upload-kit\\dist\\storage.cjs","sourcesContent":[null]}
package/dist/storage.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createStorageAdapter,
4
4
  toPaginatedListResult,
5
5
  validateStorageMetadata
6
- } from "./chunk-CV23O4YU.js";
6
+ } from "./chunk-MHGQEBPW.js";
7
7
  import {
8
8
  DEFAULT_PUBLIC_STORAGE_PREFIX,
9
9
  parsePublicStorageUrl
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storage/localFsAdapter.ts","../src/storage/s3Adapter.ts","../src/storage/supabaseAdapter.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n type StorageAdapter,\n type StorageFileEntry,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n} from './types';\n\nexport interface LocalFsStorageOptions {\n /** Absolute or relative root directory on disk (e.g. `./public/uploads`) */\n rootDir: string;\n /** Public URL prefix matching `rootDir` (e.g. `/uploads` or `https://cdn.example.com/uploads`) */\n baseUrl?: string;\n /** Include the bucket segment as a subdirectory under `rootDir`. Defaults to `true`. */\n includeBucketInPath?: boolean;\n}\n\nfunction sanitizeRelativePath(inputPath: string): string {\n if (!inputPath || typeof inputPath !== 'string') {\n throw new Error('Invalid storage path');\n }\n if (inputPath.includes(':') || inputPath.includes('\\0')) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n\n const normalized = inputPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n const rawSegments = normalized.split('/').filter(Boolean);\n\n if (rawSegments.length === 0) {\n throw new Error('Invalid storage path');\n }\n\n for (const segment of rawSegments) {\n if (segment === '.' || segment === '..') {\n throw new Error('Invalid storage path: path traversal blocked');\n }\n }\n\n return rawSegments.join('/');\n}\n\nfunction assertContainedInRoot(resolvedRoot: string, targetPath: string): void {\n const relative = path.relative(resolvedRoot, targetPath);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\n }\n}\n\nexport interface LocalFsStorageAdapter extends StorageAdapter {\n exists(bucket: string, relativePath: string): Promise<boolean>;\n getPublicUrl(bucket: string, relativePath: string): string;\n list(\n bucket: string,\n folder?: string,\n options?: StorageListOptions,\n ): Promise<StoragePaginatedListResult>;\n deleteByUrl(url: string): Promise<boolean>;\n parseUrl(url: string): { bucket?: string; path: string } | null;\n}\n\nexport function createLocalFsStorageAdapter(\n options: LocalFsStorageOptions,\n): LocalFsStorageAdapter {\n const resolvedRoot = path.resolve(options.rootDir);\n const baseUrl = (options.baseUrl ?? '/uploads').replace(/\\/+$/, '');\n const includeBucket = options.includeBucketInPath !== false;\n\n const resolveDiskTarget = (bucket: string, relativePath: string) => {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanRel = sanitizeRelativePath(relativePath);\n const subPath = includeBucket ? `${cleanBucket}/${cleanRel}` : cleanRel;\n const diskPath = path.resolve(resolvedRoot, subPath);\n\n assertContainedInRoot(resolvedRoot, diskPath);\n\n return { diskPath, subPath, cleanRel };\n };\n\n const getPublicUrl = (bucket: string, relativePath: string): string => {\n const { subPath } = resolveDiskTarget(bucket, relativePath);\n return `${baseUrl}/${subPath}`;\n };\n\n const parseUrl = (url: string): { bucket?: string; path: string } | null => {\n if (!url || typeof url !== 'string') return null;\n\n try {\n const cleanUrl = url.split(/[?#]/)[0];\n const prefix = `${baseUrl}/`;\n let subPath: string | null = null;\n\n if (cleanUrl.startsWith(prefix)) {\n subPath = cleanUrl.slice(prefix.length);\n } else if (baseUrl.startsWith('/')) {\n const parsed = new URL(cleanUrl, 'http://localhost');\n const isLocal =\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\n const isRelative =\n !cleanUrl.startsWith('http://') && !cleanUrl.startsWith('https://');\n if ((isLocal || isRelative) && parsed.pathname.startsWith(prefix)) {\n subPath = parsed.pathname.slice(prefix.length);\n }\n }\n\n if (!subPath) return null;\n\n const decoded = decodeURIComponent(subPath)\n .replace(/\\\\/g, '/')\n .replace(/^\\/+/, '');\n if (!decoded || decoded.includes(':') || decoded.includes('\\0')) {\n return null;\n }\n\n const segments = decoded.split('/').filter(Boolean);\n if (\n segments.length === 0 ||\n segments.includes('..') ||\n segments.includes('.')\n ) {\n return null;\n }\n\n const diskPath = path.resolve(resolvedRoot, ...segments);\n const relative = path.relative(resolvedRoot, diskPath);\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\n return null;\n }\n\n if (includeBucket) {\n if (segments.length < 2) return null;\n return {\n bucket: segments[0],\n path: segments.slice(1).join('/'),\n };\n }\n\n return { path: segments.join('/') };\n } catch {\n return null;\n }\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const { diskPath, cleanRel } = resolveDiskTarget(\n params.bucket,\n params.path,\n );\n\n await fs.mkdir(path.dirname(diskPath), { recursive: true });\n\n try {\n await fs.writeFile(\n diskPath,\n params.data,\n params.upsert === false ? { flag: 'wx' } : undefined,\n );\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n const existsError = new Error(\n `File already exists at ${params.bucket}/${cleanRel}`,\n ) as NodeJS.ErrnoException;\n existsError.code = 'EEXIST';\n throw existsError;\n }\n throw err;\n }\n\n return {\n bucket: params.bucket,\n path: cleanRel,\n url: getPublicUrl(params.bucket, cleanRel),\n };\n },\n\n async delete(bucket: string, relativePath: string): Promise<void> {\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\n try {\n await fs.unlink(diskPath);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw err;\n }\n }\n },\n\n async exists(bucket: string, relativePath: string): Promise<boolean> {\n try {\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\n const stat = await fs.stat(diskPath);\n return stat.isFile();\n } catch {\n return false;\n }\n },\n\n getPublicUrl,\n parseUrl,\n\n async deleteByUrl(url: string): Promise<boolean> {\n const parsed = parseUrl(url);\n if (!parsed) return false;\n\n try {\n const subSegments =\n includeBucket && parsed.bucket\n ? [parsed.bucket, ...parsed.path.split('/')]\n : parsed.path.split('/');\n const diskPath = path.resolve(resolvedRoot, ...subSegments);\n assertContainedInRoot(resolvedRoot, diskPath);\n await fs.unlink(diskPath);\n return true;\n } catch {\n return false;\n }\n },\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const cleanBucket = sanitizeRelativePath(bucket);\n const cleanFolder = folder ? sanitizeRelativePath(folder) : '';\n const targetDir = path.resolve(\n resolvedRoot,\n includeBucket ? cleanBucket : '',\n cleanFolder,\n );\n\n assertContainedInRoot(resolvedRoot, targetDir);\n\n try {\n const dirEntries = await fs.readdir(targetDir, { withFileTypes: true });\n const results: StorageFileEntry[] = [];\n\n for (const entry of dirEntries) {\n if (!entry.isFile()) continue;\n const full = path.join(targetDir, entry.name);\n const stat = await fs.stat(full);\n results.push({\n name: entry.name,\n id: entry.name,\n createdAt: stat.birthtime.toISOString(),\n size: stat.size,\n });\n }\n\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\n const limit = listOptions?.limit ?? results.length;\n const sliced =\n listOptions?.limit !== undefined || offset > 0\n ? results.slice(offset, offset + limit)\n : results;\n const nextCursor =\n listOptions?.limit !== undefined && offset + sliced.length < results.length\n ? String(offset + sliced.length)\n : undefined;\n\n return toPaginatedListResult(sliced, nextCursor);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return toPaginatedListResult([]);\n }\n throw err;\n }\n },\n };\n}\n","import {\n type StorageAdapter,\n type StorageFileEntry,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n validateStorageMetadata,\n} from './types';\n\nexport interface S3PutObjectInput {\n Bucket: string;\n Key: string;\n Body: Buffer | Uint8Array;\n ContentType: string;\n CacheControl?: string;\n Metadata?: Record<string, string>;\n IfNoneMatch?: string;\n}\n\nexport interface S3DeleteObjectInput {\n Bucket: string;\n Key: string;\n}\n\nexport interface S3HeadObjectInput {\n Bucket: string;\n Key: string;\n}\n\nexport interface S3ListObjectsInput {\n Bucket: string;\n Prefix?: string;\n MaxKeys?: number;\n ContinuationToken?: string;\n}\n\nexport interface S3ListedObject {\n Key?: string;\n Size?: number;\n LastModified?: Date | string;\n}\n\nexport interface S3ListObjectsOutput {\n Contents?: S3ListedObject[];\n NextContinuationToken?: string;\n IsTruncated?: boolean;\n}\n\n/** Structural S3 / Cloudflare R2 / MinIO transport with zero runtime SDK dependency. */\nexport interface S3TransportLike {\n putObject(input: S3PutObjectInput): Promise<unknown>;\n deleteObject(input: S3DeleteObjectInput): Promise<unknown>;\n headObject?(input: S3HeadObjectInput): Promise<unknown>;\n listObjects?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n listObjectsV2?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\n}\n\nexport type S3PublicUrlBuilder = (bucket: string, path: string) => string;\n\nexport interface S3StorageBaseOptions {\n /** Public base URL prefix (e.g. `'https://cdn.example.com'`) or `(bucket, path) => string` builder. */\n publicBaseUrl: string | S3PublicUrlBuilder;\n /** Default `Cache-Control` header applied when `StorageUploadParams.cacheControl` is omitted. */\n cacheControl?: string;\n /** Include `bucket` segment in string-prefix URLs (`${publicBaseUrl}/${bucket}/${path}`). Defaults to `true`. */\n includeBucketInUrl?: boolean;\n /** Fallback bucket used by `deleteByUrl` when `includeBucketInUrl` is `false`. */\n defaultBucket?: string;\n}\n\nexport type S3StorageAdapterOptions = S3StorageBaseOptions &\n (\n | (S3TransportLike & { client?: never; transport?: never })\n | { client: S3TransportLike; transport?: never; putObject?: never }\n | { transport: S3TransportLike; client?: never; putObject?: never }\n );\n\nfunction normalizeS3Key(rawPath: string): string {\n const normalized = rawPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\n if (!normalized) throw new Error('Invalid S3 object key');\n return normalized;\n}\n\nfunction resolveTransport(options: S3StorageAdapterOptions): S3TransportLike {\n if ('client' in options && options.client) return options.client;\n if ('transport' in options && options.transport) return options.transport;\n if ('putObject' in options && typeof options.putObject === 'function') return options;\n throw new Error('S3StorageAdapter requires putObject/deleteObject or client/transport');\n}\n\nfunction isS3PreconditionError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const e = err as {\n code?: unknown;\n name?: unknown;\n statusCode?: unknown;\n $metadata?: { httpStatusCode?: number };\n };\n const status = e.$metadata?.httpStatusCode ?? Number(e.statusCode);\n const code = String(e.code ?? e.name ?? '').toLowerCase();\n return status === 412 || code === 'preconditionfailed' || code === 'eexist';\n}\n\n/** Zero-dependency S3 / Cloudflare R2 / MinIO `StorageAdapter`. */\nexport function createS3StorageAdapter(options: S3StorageAdapterOptions): StorageAdapter {\n const transport = resolveTransport(options);\n const includeBucket = options.includeBucketInUrl !== false;\n\n const getPublicUrl = (bucket: string, path: string): string => {\n const key = normalizeS3Key(path);\n if (typeof options.publicBaseUrl === 'function') return options.publicBaseUrl(bucket, key);\n const base = options.publicBaseUrl.replace(/\\/+$/, '');\n return includeBucket ? `${base}/${bucket}/${key}` : `${base}/${key}`;\n };\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n const key = normalizeS3Key(params.path);\n validateStorageMetadata(params.metadata);\n\n const isImmutable =\n params.upsert === false && params.contentType.includes('image/avif');\n const cacheControl =\n params.cacheControl ??\n options.cacheControl ??\n (isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const metadata =\n params.metadata || params.contentHash\n ? {\n ...params.metadata,\n ...(params.contentHash ? { contentHash: params.contentHash } : {}),\n }\n : undefined;\n\n try {\n await transport.putObject({\n Bucket: params.bucket,\n Key: key,\n Body: params.data,\n ContentType: params.contentType,\n ...(cacheControl ? { CacheControl: cacheControl } : {}),\n ...(metadata ? { Metadata: metadata } : {}),\n ...(params.upsert === false ? { IfNoneMatch: '*' } : {}),\n });\n } catch (err) {\n if (isS3PreconditionError(err)) {\n const existsErr = new Error(\n `Object already exists at ${params.bucket}/${key}`,\n ) as Error & { code?: string; statusCode?: number };\n existsErr.code = 'EEXIST';\n existsErr.statusCode = 412;\n throw existsErr;\n }\n throw err;\n }\n\n return {\n bucket: params.bucket,\n path: key,\n url: getPublicUrl(params.bucket, key),\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n await transport.deleteObject({ Bucket: bucket, Key: normalizeS3Key(path) });\n },\n\n async exists(bucket: string, path: string): Promise<boolean> {\n const key = normalizeS3Key(path);\n if (transport.headObject) {\n try {\n await transport.headObject({ Bucket: bucket, Key: key });\n return true;\n } catch {\n return false;\n }\n }\n const lastSlash = key.lastIndexOf('/');\n const folder = lastSlash > 0 ? key.slice(0, lastSlash) : undefined;\n const targetName = lastSlash > 0 ? key.slice(lastSlash + 1) : key;\n const listed = await this.list!(bucket, folder);\n return listed.some((item) => item.name === targetName);\n },\n\n getPublicUrl,\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const cleanFolder = folder ? normalizeS3Key(folder).replace(/\\/+$/, '') : '';\n const prefix = cleanFolder ? `${cleanFolder}/` : undefined;\n const input: S3ListObjectsInput = {\n Bucket: bucket,\n ...(prefix ? { Prefix: prefix } : {}),\n MaxKeys: listOptions?.limit ?? 1000,\n ...(listOptions?.cursor ? { ContinuationToken: listOptions.cursor } : {}),\n };\n\n const output = transport.listObjects\n ? await transport.listObjects(input)\n : transport.listObjectsV2\n ? await transport.listObjectsV2(input)\n : undefined;\n\n const entries: StorageFileEntry[] = (output?.Contents ?? [])\n .filter((item): item is S3ListedObject & { Key: string } => Boolean(item.Key))\n .map((item) => {\n const name =\n prefix && item.Key.startsWith(prefix)\n ? item.Key.slice(prefix.length)\n : item.Key;\n const createdAt =\n item.LastModified instanceof Date\n ? item.LastModified.toISOString()\n : item.LastModified;\n return {\n name,\n id: `${bucket}/${item.Key}`,\n ...(createdAt !== undefined ? { createdAt } : {}),\n ...(item.Size !== undefined ? { size: item.Size } : {}),\n };\n })\n .filter((entry) => entry.name.length > 0);\n\n return toPaginatedListResult(entries, output?.NextContinuationToken);\n },\n\n parseUrl(url: string): { bucket?: string; path: string } | null {\n if (typeof options.publicBaseUrl !== 'string' || !url) return null;\n const prefix = `${options.publicBaseUrl.replace(/\\/+$/, '')}/`;\n const cleanUrl = url.split(/[?#]/)[0];\n if (!cleanUrl.startsWith(prefix)) return null;\n\n try {\n const rest = decodeURIComponent(cleanUrl.slice(prefix.length)).replace(/^\\/+/, '');\n if (!rest) return null;\n\n if (includeBucket) {\n const slashIdx = rest.indexOf('/');\n if (slashIdx <= 0 || slashIdx === rest.length - 1) return null;\n return {\n bucket: rest.slice(0, slashIdx),\n path: rest.slice(slashIdx + 1),\n };\n }\n\n if (!options.defaultBucket) return null;\n return { bucket: options.defaultBucket, path: rest };\n } catch {\n return null;\n }\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const parsed = this.parseUrl?.(url);\n if (!parsed || !parsed.bucket) return false;\n\n try {\n await transport.deleteObject({\n Bucket: parsed.bucket,\n Key: parsed.path,\n });\n return true;\n } catch {\n return false;\n }\n },\n };\n}\n","import {\n type StorageAdapter,\n type StorageListOptions,\n type StoragePaginatedListResult,\n type StorageUploadParams,\n type StorageUploadResult,\n toPaginatedListResult,\n validateStorageMetadata,\n} from './types';\nimport {\n DEFAULT_PUBLIC_STORAGE_PREFIX,\n parsePublicStorageUrl,\n} from './urlParser';\n\n/**\n * Minimal structural interface matching `@supabase/supabase-js` Storage client,\n * avoiding a mandatory `@supabase/supabase-js` runtime dependency.\n */\nexport interface SupabaseBucketClientLike {\n upload(\n path: string,\n fileBody: Buffer | Uint8Array,\n fileOptions?: {\n contentType?: string;\n upsert?: boolean;\n cacheControl?: string;\n metadata?: Record<string, string>;\n },\n ): Promise<{\n data: { path: string } | null;\n error: { message: string } | null;\n }>;\n remove(paths: string[]): Promise<{\n data?: unknown;\n error: { message: string } | null;\n }>;\n getPublicUrl(path: string): {\n data: { publicUrl: string };\n };\n list?(\n path?: string,\n options?: {\n limit?: number;\n offset?: number;\n sortBy?: { column: string; order: string };\n },\n ): Promise<{\n data: Array<{\n name: string;\n id: string | null;\n created_at: string | null;\n }> | null;\n error: { message: string } | null;\n }>;\n}\n\nexport interface SupabaseStorageClientLike {\n storage: {\n from(bucket: string): SupabaseBucketClientLike;\n };\n}\n\nexport interface SupabaseStorageAdapterOptions {\n publicUrlPrefix?: string;\n}\n\n/**\n * Plug in any `@supabase/supabase-js` client instance (e.g., service-role client)\n * as a `StorageAdapter` with zero extra wrapper boilerplate.\n */\nexport function createSupabaseStorageAdapter(\n client: SupabaseStorageClientLike,\n options: SupabaseStorageAdapterOptions = {},\n): StorageAdapter {\n const publicPrefix =\n options.publicUrlPrefix || DEFAULT_PUBLIC_STORAGE_PREFIX;\n\n return {\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\n validateStorageMetadata(params.metadata);\n\n const isImmutable =\n params.upsert === false && params.contentType.includes('image/avif');\n const cacheControl =\n params.cacheControl ||\n (isImmutable\n ? 'public, max-age=31536000, immutable'\n : 'public, max-age=2592000, must-revalidate');\n\n const { data, error } = await client.storage\n .from(params.bucket)\n .upload(params.path, params.data, {\n contentType: params.contentType,\n upsert: params.upsert === undefined ? true : params.upsert,\n cacheControl,\n metadata: {\n ...params.metadata,\n uploadedAt: new Date().toISOString(),\n contentHash: params.contentHash || 'unknown',\n },\n });\n\n if (error || !data) {\n throw new Error(\n `Failed to upload to Supabase storage (bucket: ${params.bucket}, path: ${params.path}): ${error?.message || 'Unknown error'}`,\n );\n }\n\n const {\n data: { publicUrl },\n } = client.storage.from(params.bucket).getPublicUrl(data.path);\n\n return {\n bucket: params.bucket,\n path: data.path,\n url: publicUrl,\n };\n },\n\n async delete(bucket: string, path: string): Promise<void> {\n const { error } = await client.storage.from(bucket).remove([path]);\n if (error) {\n throw new Error(`Failed to delete from Supabase storage: ${error.message}`);\n }\n },\n\n async exists(bucket: string, path: string): Promise<boolean> {\n const cleanPath = path.replace(/^\\/+/, '');\n const lastSlash = cleanPath.lastIndexOf('/');\n const folder = lastSlash > 0 ? cleanPath.slice(0, lastSlash) : undefined;\n const targetName = lastSlash > 0 ? cleanPath.slice(lastSlash + 1) : cleanPath;\n const entries = await this.list!(bucket, folder);\n return entries.some((entry) => entry.name === targetName);\n },\n\n getPublicUrl(bucket: string, path: string): string {\n const {\n data: { publicUrl },\n } = client.storage.from(bucket).getPublicUrl(path);\n return publicUrl;\n },\n\n async list(\n bucket: string,\n folder?: string,\n listOptions?: StorageListOptions,\n ): Promise<StoragePaginatedListResult> {\n const bucketClient = client.storage.from(bucket);\n if (!bucketClient.list) {\n return toPaginatedListResult([]);\n }\n\n const limit = listOptions?.limit ?? 1000;\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\n\n const { data, error } = await bucketClient.list(folder, {\n limit,\n ...(offset > 0 ? { offset } : {}),\n sortBy: { column: 'created_at', order: 'desc' },\n });\n\n if (error) {\n throw new Error(`Failed to list Supabase bucket files: ${error.message}`);\n }\n\n const entries = (data || [])\n .filter((f) => f.id !== null)\n .map((f) => ({\n name: f.name,\n id: f.id ?? undefined,\n createdAt: f.created_at ?? undefined,\n }));\n\n const nextCursor =\n listOptions?.limit !== undefined && entries.length >= limit\n ? String(offset + entries.length)\n : undefined;\n\n return toPaginatedListResult(entries, nextCursor);\n },\n\n parseUrl(url: string): { bucket?: string; path: string } | null {\n const info = parsePublicStorageUrl(url, publicPrefix);\n if (!info) return null;\n return { bucket: info.bucket, path: info.path };\n },\n\n async deleteByUrl(url: string): Promise<boolean> {\n const info = parsePublicStorageUrl(url, publicPrefix);\n if (!info) return false;\n\n try {\n const { error } = await client.storage\n .from(info.bucket)\n .remove([info.path]);\n return !error;\n } catch {\n return false;\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AAoBjB,SAAS,qBAAqB,WAA2B;AACvD,MAAI,CAAC,aAAa,OAAO,cAAc,UAAU;AAC/C,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACxC;AACA,MAAI,UAAU,SAAS,GAAG,KAAK,UAAU,SAAS,IAAI,GAAG;AACvD,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,QAAM,aAAa,UAAU,QAAQ,OAAO,GAAG,EAAE,QAAQ,QAAQ,EAAE;AACnE,QAAM,cAAc,WAAW,MAAM,GAAG,EAAE,OAAO,OAAO;AAExD,MAAI,YAAY,WAAW,GAAG;AAC5B,UAAM,IAAI,MAAM,sBAAsB;AAAA,EACxC;AAEA,aAAW,WAAW,aAAa;AACjC,QAAI,YAAY,OAAO,YAAY,MAAM;AACvC,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAAA,EACF;AAEA,SAAO,YAAY,KAAK,GAAG;AAC7B;AAEA,SAAS,sBAAsB,cAAsB,YAA0B;AAC7E,QAAM,WAAW,KAAK,SAAS,cAAc,UAAU;AACvD,MAAI,SAAS,WAAW,IAAI,KAAK,KAAK,WAAW,QAAQ,GAAG;AAC1D,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AACF;AAcO,SAAS,4BACd,SACuB;AACvB,QAAM,eAAe,KAAK,QAAQ,QAAQ,OAAO;AACjD,QAAM,WAAW,QAAQ,WAAW,YAAY,QAAQ,QAAQ,EAAE;AAClE,QAAM,gBAAgB,QAAQ,wBAAwB;AAEtD,QAAM,oBAAoB,CAAC,QAAgB,iBAAyB;AAClE,UAAM,cAAc,qBAAqB,MAAM;AAC/C,UAAM,WAAW,qBAAqB,YAAY;AAClD,UAAM,UAAU,gBAAgB,GAAG,WAAW,IAAI,QAAQ,KAAK;AAC/D,UAAM,WAAW,KAAK,QAAQ,cAAc,OAAO;AAEnD,0BAAsB,cAAc,QAAQ;AAE5C,WAAO,EAAE,UAAU,SAAS,SAAS;AAAA,EACvC;AAEA,QAAM,eAAe,CAAC,QAAgB,iBAAiC;AACrE,UAAM,EAAE,QAAQ,IAAI,kBAAkB,QAAQ,YAAY;AAC1D,WAAO,GAAG,OAAO,IAAI,OAAO;AAAA,EAC9B;AAEA,QAAM,WAAW,CAAC,QAA0D;AAC1E,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAE5C,QAAI;AACF,YAAM,WAAW,IAAI,MAAM,MAAM,EAAE,CAAC;AACpC,YAAM,SAAS,GAAG,OAAO;AACzB,UAAI,UAAyB;AAE7B,UAAI,SAAS,WAAW,MAAM,GAAG;AAC/B,kBAAU,SAAS,MAAM,OAAO,MAAM;AAAA,MACxC,WAAW,QAAQ,WAAW,GAAG,GAAG;AAClC,cAAM,SAAS,IAAI,IAAI,UAAU,kBAAkB;AACnD,cAAM,UACJ,OAAO,aAAa,eAAe,OAAO,aAAa;AACzD,cAAM,aACJ,CAAC,SAAS,WAAW,SAAS,KAAK,CAAC,SAAS,WAAW,UAAU;AACpE,aAAK,WAAW,eAAe,OAAO,SAAS,WAAW,MAAM,GAAG;AACjE,oBAAU,OAAO,SAAS,MAAM,OAAO,MAAM;AAAA,QAC/C;AAAA,MACF;AAEA,UAAI,CAAC,QAAS,QAAO;AAErB,YAAM,UAAU,mBAAmB,OAAO,EACvC,QAAQ,OAAO,GAAG,EAClB,QAAQ,QAAQ,EAAE;AACrB,UAAI,CAAC,WAAW,QAAQ,SAAS,GAAG,KAAK,QAAQ,SAAS,IAAI,GAAG;AAC/D,eAAO;AAAA,MACT;AAEA,YAAM,WAAW,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO;AAClD,UACE,SAAS,WAAW,KACpB,SAAS,SAAS,IAAI,KACtB,SAAS,SAAS,GAAG,GACrB;AACA,eAAO;AAAA,MACT;AAEA,YAAM,WAAW,KAAK,QAAQ,cAAc,GAAG,QAAQ;AACvD,YAAM,WAAW,KAAK,SAAS,cAAc,QAAQ;AACrD,UAAI,SAAS,WAAW,IAAI,KAAK,KAAK,WAAW,QAAQ,GAAG;AAC1D,eAAO;AAAA,MACT;AAEA,UAAI,eAAe;AACjB,YAAI,SAAS,SAAS,EAAG,QAAO;AAChC,eAAO;AAAA,UACL,QAAQ,SAAS,CAAC;AAAA,UAClB,MAAM,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;AAAA,QAClC;AAAA,MACF;AAEA,aAAO,EAAE,MAAM,SAAS,KAAK,GAAG,EAAE;AAAA,IACpC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,OAAO,QAA2D;AACtE,YAAM,EAAE,UAAU,SAAS,IAAI;AAAA,QAC7B,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAEA,YAAM,GAAG,MAAM,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAE1D,UAAI;AACF,cAAM,GAAG;AAAA,UACP;AAAA,UACA,OAAO;AAAA,UACP,OAAO,WAAW,QAAQ,EAAE,MAAM,KAAK,IAAI;AAAA,QAC7C;AAAA,MACF,SAAS,KAAK;AACZ,YAAK,IAA8B,SAAS,UAAU;AACpD,gBAAM,cAAc,IAAI;AAAA,YACtB,0BAA0B,OAAO,MAAM,IAAI,QAAQ;AAAA,UACrD;AACA,sBAAY,OAAO;AACnB,gBAAM;AAAA,QACR;AACA,cAAM;AAAA,MACR;AAEA,aAAO;AAAA,QACL,QAAQ,OAAO;AAAA,QACf,MAAM;AAAA,QACN,KAAK,aAAa,OAAO,QAAQ,QAAQ;AAAA,MAC3C;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,QAAgB,cAAqC;AAChE,YAAM,EAAE,SAAS,IAAI,kBAAkB,QAAQ,YAAY;AAC3D,UAAI;AACF,cAAM,GAAG,OAAO,QAAQ;AAAA,MAC1B,SAAS,KAAK;AACZ,YAAK,IAA8B,SAAS,UAAU;AACpD,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,QAAgB,cAAwC;AACnE,UAAI;AACF,cAAM,EAAE,SAAS,IAAI,kBAAkB,QAAQ,YAAY;AAC3D,cAAM,OAAO,MAAM,GAAG,KAAK,QAAQ;AACnC,eAAO,KAAK,OAAO;AAAA,MACrB,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA;AAAA,IACA;AAAA,IAEA,MAAM,YAAY,KAA+B;AAC/C,YAAM,SAAS,SAAS,GAAG;AAC3B,UAAI,CAAC,OAAQ,QAAO;AAEpB,UAAI;AACF,cAAM,cACJ,iBAAiB,OAAO,SACpB,CAAC,OAAO,QAAQ,GAAG,OAAO,KAAK,MAAM,GAAG,CAAC,IACzC,OAAO,KAAK,MAAM,GAAG;AAC3B,cAAM,WAAW,KAAK,QAAQ,cAAc,GAAG,WAAW;AAC1D,8BAAsB,cAAc,QAAQ;AAC5C,cAAM,GAAG,OAAO,QAAQ;AACxB,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,MAAM,KACJ,QACA,QACA,aACqC;AACrC,YAAM,cAAc,qBAAqB,MAAM;AAC/C,YAAM,cAAc,SAAS,qBAAqB,MAAM,IAAI;AAC5D,YAAM,YAAY,KAAK;AAAA,QACrB;AAAA,QACA,gBAAgB,cAAc;AAAA,QAC9B;AAAA,MACF;AAEA,4BAAsB,cAAc,SAAS;AAE7C,UAAI;AACF,cAAM,aAAa,MAAM,GAAG,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;AACtE,cAAM,UAA8B,CAAC;AAErC,mBAAW,SAAS,YAAY;AAC9B,cAAI,CAAC,MAAM,OAAO,EAAG;AACrB,gBAAM,OAAO,KAAK,KAAK,WAAW,MAAM,IAAI;AAC5C,gBAAM,OAAO,MAAM,GAAG,KAAK,IAAI;AAC/B,kBAAQ,KAAK;AAAA,YACX,MAAM,MAAM;AAAA,YACZ,IAAI,MAAM;AAAA,YACV,WAAW,KAAK,UAAU,YAAY;AAAA,YACtC,MAAM,KAAK;AAAA,UACb,CAAC;AAAA,QACH;AAEA,cAAM,SAAS,aAAa,SAAS,OAAO,YAAY,MAAM,KAAK,IAAI;AACvE,cAAM,QAAQ,aAAa,SAAS,QAAQ;AAC5C,cAAM,SACJ,aAAa,UAAU,UAAa,SAAS,IACzC,QAAQ,MAAM,QAAQ,SAAS,KAAK,IACpC;AACN,cAAM,aACJ,aAAa,UAAU,UAAa,SAAS,OAAO,SAAS,QAAQ,SACjE,OAAO,SAAS,OAAO,MAAM,IAC7B;AAEN,eAAO,sBAAsB,QAAQ,UAAU;AAAA,MACjD,SAAS,KAAK;AACZ,YAAK,IAA8B,SAAS,UAAU;AACpD,iBAAO,sBAAsB,CAAC,CAAC;AAAA,QACjC;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;;;ACjMA,SAAS,eAAe,SAAyB;AAC/C,QAAM,aAAa,QAAQ,QAAQ,OAAO,GAAG,EAAE,QAAQ,QAAQ,EAAE;AACjE,MAAI,CAAC,WAAY,OAAM,IAAI,MAAM,uBAAuB;AACxD,SAAO;AACT;AAEA,SAAS,iBAAiB,SAAmD;AAC3E,MAAI,YAAY,WAAW,QAAQ,OAAQ,QAAO,QAAQ;AAC1D,MAAI,eAAe,WAAW,QAAQ,UAAW,QAAO,QAAQ;AAChE,MAAI,eAAe,WAAW,OAAO,QAAQ,cAAc,WAAY,QAAO;AAC9E,QAAM,IAAI,MAAM,sEAAsE;AACxF;AAEA,SAAS,sBAAsB,KAAuB;AACpD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,IAAI;AAMV,QAAM,SAAS,EAAE,WAAW,kBAAkB,OAAO,EAAE,UAAU;AACjE,QAAM,OAAO,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,YAAY;AACxD,SAAO,WAAW,OAAO,SAAS,wBAAwB,SAAS;AACrE;AAGO,SAAS,uBAAuB,SAAkD;AACvF,QAAM,YAAY,iBAAiB,OAAO;AAC1C,QAAM,gBAAgB,QAAQ,uBAAuB;AAErD,QAAM,eAAe,CAAC,QAAgBA,UAAyB;AAC7D,UAAM,MAAM,eAAeA,KAAI;AAC/B,QAAI,OAAO,QAAQ,kBAAkB,WAAY,QAAO,QAAQ,cAAc,QAAQ,GAAG;AACzF,UAAM,OAAO,QAAQ,cAAc,QAAQ,QAAQ,EAAE;AACrD,WAAO,gBAAgB,GAAG,IAAI,IAAI,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,IAAI,GAAG;AAAA,EACpE;AAEA,SAAO;AAAA,IACL,MAAM,OAAO,QAA2D;AACtE,YAAM,MAAM,eAAe,OAAO,IAAI;AACtC,8BAAwB,OAAO,QAAQ;AAEvC,YAAM,cACJ,OAAO,WAAW,SAAS,OAAO,YAAY,SAAS,YAAY;AACrE,YAAM,eACJ,OAAO,gBACP,QAAQ,iBACP,cACG,wCACA;AAEN,YAAM,WACJ,OAAO,YAAY,OAAO,cACtB;AAAA,QACE,GAAG,OAAO;AAAA,QACV,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,MAClE,IACA;AAEN,UAAI;AACF,cAAM,UAAU,UAAU;AAAA,UACxB,QAAQ,OAAO;AAAA,UACf,KAAK;AAAA,UACL,MAAM,OAAO;AAAA,UACb,aAAa,OAAO;AAAA,UACpB,GAAI,eAAe,EAAE,cAAc,aAAa,IAAI,CAAC;AAAA,UACrD,GAAI,WAAW,EAAE,UAAU,SAAS,IAAI,CAAC;AAAA,UACzC,GAAI,OAAO,WAAW,QAAQ,EAAE,aAAa,IAAI,IAAI,CAAC;AAAA,QACxD,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,YAAI,sBAAsB,GAAG,GAAG;AAC9B,gBAAM,YAAY,IAAI;AAAA,YACpB,4BAA4B,OAAO,MAAM,IAAI,GAAG;AAAA,UAClD;AACA,oBAAU,OAAO;AACjB,oBAAU,aAAa;AACvB,gBAAM;AAAA,QACR;AACA,cAAM;AAAA,MACR;AAEA,aAAO;AAAA,QACL,QAAQ,OAAO;AAAA,QACf,MAAM;AAAA,QACN,KAAK,aAAa,OAAO,QAAQ,GAAG;AAAA,MACtC;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,QAAgBA,OAA6B;AACxD,YAAM,UAAU,aAAa,EAAE,QAAQ,QAAQ,KAAK,eAAeA,KAAI,EAAE,CAAC;AAAA,IAC5E;AAAA,IAEA,MAAM,OAAO,QAAgBA,OAAgC;AAC3D,YAAM,MAAM,eAAeA,KAAI;AAC/B,UAAI,UAAU,YAAY;AACxB,YAAI;AACF,gBAAM,UAAU,WAAW,EAAE,QAAQ,QAAQ,KAAK,IAAI,CAAC;AACvD,iBAAO;AAAA,QACT,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,YAAY,IAAI,YAAY,GAAG;AACrC,YAAM,SAAS,YAAY,IAAI,IAAI,MAAM,GAAG,SAAS,IAAI;AACzD,YAAM,aAAa,YAAY,IAAI,IAAI,MAAM,YAAY,CAAC,IAAI;AAC9D,YAAM,SAAS,MAAM,KAAK,KAAM,QAAQ,MAAM;AAC9C,aAAO,OAAO,KAAK,CAAC,SAAS,KAAK,SAAS,UAAU;AAAA,IACvD;AAAA,IAEA;AAAA,IAEA,MAAM,KACJ,QACA,QACA,aACqC;AACrC,YAAM,cAAc,SAAS,eAAe,MAAM,EAAE,QAAQ,QAAQ,EAAE,IAAI;AAC1E,YAAM,SAAS,cAAc,GAAG,WAAW,MAAM;AACjD,YAAM,QAA4B;AAAA,QAChC,QAAQ;AAAA,QACR,GAAI,SAAS,EAAE,QAAQ,OAAO,IAAI,CAAC;AAAA,QACnC,SAAS,aAAa,SAAS;AAAA,QAC/B,GAAI,aAAa,SAAS,EAAE,mBAAmB,YAAY,OAAO,IAAI,CAAC;AAAA,MACzE;AAEA,YAAM,SAAS,UAAU,cACrB,MAAM,UAAU,YAAY,KAAK,IACjC,UAAU,gBACR,MAAM,UAAU,cAAc,KAAK,IACnC;AAEN,YAAM,WAA+B,QAAQ,YAAY,CAAC,GACvD,OAAO,CAAC,SAAmD,QAAQ,KAAK,GAAG,CAAC,EAC5E,IAAI,CAAC,SAAS;AACb,cAAM,OACJ,UAAU,KAAK,IAAI,WAAW,MAAM,IAChC,KAAK,IAAI,MAAM,OAAO,MAAM,IAC5B,KAAK;AACX,cAAM,YACJ,KAAK,wBAAwB,OACzB,KAAK,aAAa,YAAY,IAC9B,KAAK;AACX,eAAO;AAAA,UACL;AAAA,UACA,IAAI,GAAG,MAAM,IAAI,KAAK,GAAG;AAAA,UACzB,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,UAC/C,GAAI,KAAK,SAAS,SAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,QACvD;AAAA,MACF,CAAC,EACA,OAAO,CAAC,UAAU,MAAM,KAAK,SAAS,CAAC;AAE1C,aAAO,sBAAsB,SAAS,QAAQ,qBAAqB;AAAA,IACrE;AAAA,IAEA,SAAS,KAAuD;AAC9D,UAAI,OAAO,QAAQ,kBAAkB,YAAY,CAAC,IAAK,QAAO;AAC9D,YAAM,SAAS,GAAG,QAAQ,cAAc,QAAQ,QAAQ,EAAE,CAAC;AAC3D,YAAM,WAAW,IAAI,MAAM,MAAM,EAAE,CAAC;AACpC,UAAI,CAAC,SAAS,WAAW,MAAM,EAAG,QAAO;AAEzC,UAAI;AACF,cAAM,OAAO,mBAAmB,SAAS,MAAM,OAAO,MAAM,CAAC,EAAE,QAAQ,QAAQ,EAAE;AACjF,YAAI,CAAC,KAAM,QAAO;AAElB,YAAI,eAAe;AACjB,gBAAM,WAAW,KAAK,QAAQ,GAAG;AACjC,cAAI,YAAY,KAAK,aAAa,KAAK,SAAS,EAAG,QAAO;AAC1D,iBAAO;AAAA,YACL,QAAQ,KAAK,MAAM,GAAG,QAAQ;AAAA,YAC9B,MAAM,KAAK,MAAM,WAAW,CAAC;AAAA,UAC/B;AAAA,QACF;AAEA,YAAI,CAAC,QAAQ,cAAe,QAAO;AACnC,eAAO,EAAE,QAAQ,QAAQ,eAAe,MAAM,KAAK;AAAA,MACrD,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IAEA,MAAM,YAAY,KAA+B;AAC/C,YAAM,SAAS,KAAK,WAAW,GAAG;AAClC,UAAI,CAAC,UAAU,CAAC,OAAO,OAAQ,QAAO;AAEtC,UAAI;AACF,cAAM,UAAU,aAAa;AAAA,UAC3B,QAAQ,OAAO;AAAA,UACf,KAAK,OAAO;AAAA,QACd,CAAC;AACD,eAAO;AAAA,MACT,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;;;AC7MO,SAAS,6BACd,QACA,UAAyC,CAAC,GAC1B;AAChB,QAAM,eACJ,QAAQ,mBAAmB;AAE7B,SAAO;AAAA,IACL,MAAM,OAAO,QAA2D;AACtE,8BAAwB,OAAO,QAAQ;AAEvC,YAAM,cACJ,OAAO,WAAW,SAAS,OAAO,YAAY,SAAS,YAAY;AACrE,YAAM,eACJ,OAAO,iBACN,cACG,wCACA;AAEN,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,QAClC,KAAK,OAAO,MAAM,EAClB,OAAO,OAAO,MAAM,OAAO,MAAM;AAAA,QAChC,aAAa,OAAO;AAAA,QACpB,QAAQ,OAAO,WAAW,SAAY,OAAO,OAAO;AAAA,QACpD;AAAA,QACA,UAAU;AAAA,UACR,GAAG,OAAO;AAAA,UACV,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,UACnC,aAAa,OAAO,eAAe;AAAA,QACrC;AAAA,MACF,CAAC;AAEH,UAAI,SAAS,CAAC,MAAM;AAClB,cAAM,IAAI;AAAA,UACR,iDAAiD,OAAO,MAAM,WAAW,OAAO,IAAI,MAAM,OAAO,WAAW,eAAe;AAAA,QAC7H;AAAA,MACF;AAEA,YAAM;AAAA,QACJ,MAAM,EAAE,UAAU;AAAA,MACpB,IAAI,OAAO,QAAQ,KAAK,OAAO,MAAM,EAAE,aAAa,KAAK,IAAI;AAE7D,aAAO;AAAA,QACL,QAAQ,OAAO;AAAA,QACf,MAAM,KAAK;AAAA,QACX,KAAK;AAAA,MACP;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,QAAgBC,OAA6B;AACxD,YAAM,EAAE,MAAM,IAAI,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE,OAAO,CAACA,KAAI,CAAC;AACjE,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,2CAA2C,MAAM,OAAO,EAAE;AAAA,MAC5E;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,QAAgBA,OAAgC;AAC3D,YAAM,YAAYA,MAAK,QAAQ,QAAQ,EAAE;AACzC,YAAM,YAAY,UAAU,YAAY,GAAG;AAC3C,YAAM,SAAS,YAAY,IAAI,UAAU,MAAM,GAAG,SAAS,IAAI;AAC/D,YAAM,aAAa,YAAY,IAAI,UAAU,MAAM,YAAY,CAAC,IAAI;AACpE,YAAM,UAAU,MAAM,KAAK,KAAM,QAAQ,MAAM;AAC/C,aAAO,QAAQ,KAAK,CAAC,UAAU,MAAM,SAAS,UAAU;AAAA,IAC1D;AAAA,IAEA,aAAa,QAAgBA,OAAsB;AACjD,YAAM;AAAA,QACJ,MAAM,EAAE,UAAU;AAAA,MACpB,IAAI,OAAO,QAAQ,KAAK,MAAM,EAAE,aAAaA,KAAI;AACjD,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,KACJ,QACA,QACA,aACqC;AACrC,YAAM,eAAe,OAAO,QAAQ,KAAK,MAAM;AAC/C,UAAI,CAAC,aAAa,MAAM;AACtB,eAAO,sBAAsB,CAAC,CAAC;AAAA,MACjC;AAEA,YAAM,QAAQ,aAAa,SAAS;AACpC,YAAM,SAAS,aAAa,SAAS,OAAO,YAAY,MAAM,KAAK,IAAI;AAEvE,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,aAAa,KAAK,QAAQ;AAAA,QACtD;AAAA,QACA,GAAI,SAAS,IAAI,EAAE,OAAO,IAAI,CAAC;AAAA,QAC/B,QAAQ,EAAE,QAAQ,cAAc,OAAO,OAAO;AAAA,MAChD,CAAC;AAED,UAAI,OAAO;AACT,cAAM,IAAI,MAAM,yCAAyC,MAAM,OAAO,EAAE;AAAA,MAC1E;AAEA,YAAM,WAAW,QAAQ,CAAC,GACvB,OAAO,CAAC,MAAM,EAAE,OAAO,IAAI,EAC3B,IAAI,CAAC,OAAO;AAAA,QACX,MAAM,EAAE;AAAA,QACR,IAAI,EAAE,MAAM;AAAA,QACZ,WAAW,EAAE,cAAc;AAAA,MAC7B,EAAE;AAEJ,YAAM,aACJ,aAAa,UAAU,UAAa,QAAQ,UAAU,QAClD,OAAO,SAAS,QAAQ,MAAM,IAC9B;AAEN,aAAO,sBAAsB,SAAS,UAAU;AAAA,IAClD;AAAA,IAEA,SAAS,KAAuD;AAC9D,YAAM,OAAO,sBAAsB,KAAK,YAAY;AACpD,UAAI,CAAC,KAAM,QAAO;AAClB,aAAO,EAAE,QAAQ,KAAK,QAAQ,MAAM,KAAK,KAAK;AAAA,IAChD;AAAA,IAEA,MAAM,YAAY,KAA+B;AAC/C,YAAM,OAAO,sBAAsB,KAAK,YAAY;AACpD,UAAI,CAAC,KAAM,QAAO;AAElB,UAAI;AACF,cAAM,EAAE,MAAM,IAAI,MAAM,OAAO,QAC5B,KAAK,KAAK,MAAM,EAChB,OAAO,CAAC,KAAK,IAAI,CAAC;AACrB,eAAO,CAAC;AAAA,MACV,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;","names":["path","path"]}
1
+ {"version":3,"sources":["../src/storage/localFsAdapter.ts","../src/storage/s3Adapter.ts","../src/storage/supabaseAdapter.ts"],"sourcesContent":["import fs from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport {\r\n type StorageAdapter,\r\n type StorageFileEntry,\r\n type StorageListOptions,\r\n type StoragePaginatedListResult,\r\n type StorageUploadParams,\r\n type StorageUploadResult,\r\n toPaginatedListResult,\r\n} from './types';\r\n\r\nexport interface LocalFsStorageOptions {\r\n /** Absolute or relative root directory on disk (e.g. `./public/uploads`) */\r\n rootDir: string;\r\n /** Public URL prefix matching `rootDir` (e.g. `/uploads` or `https://cdn.example.com/uploads`) */\r\n baseUrl?: string;\r\n /** Include the bucket segment as a subdirectory under `rootDir`. Defaults to `true`. */\r\n includeBucketInPath?: boolean;\r\n}\r\n\r\nfunction sanitizeRelativePath(inputPath: string): string {\r\n if (!inputPath || typeof inputPath !== 'string') {\r\n throw new Error('Invalid storage path');\r\n }\r\n if (inputPath.includes(':') || inputPath.includes('\\0')) {\r\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\r\n }\r\n\r\n const normalized = inputPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\r\n const rawSegments = normalized.split('/').filter(Boolean);\r\n\r\n if (rawSegments.length === 0) {\r\n throw new Error('Invalid storage path');\r\n }\r\n\r\n for (const segment of rawSegments) {\r\n if (segment === '.' || segment === '..') {\r\n throw new Error('Invalid storage path: path traversal blocked');\r\n }\r\n }\r\n\r\n return rawSegments.join('/');\r\n}\r\n\r\nfunction assertContainedInRoot(resolvedRoot: string, targetPath: string): void {\r\n const relative = path.relative(resolvedRoot, targetPath);\r\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\r\n throw new Error('Path traversal blocked in LocalFsStorageAdapter');\r\n }\r\n}\r\n\r\nexport interface LocalFsStorageAdapter extends StorageAdapter {\r\n exists(bucket: string, relativePath: string): Promise<boolean>;\r\n getPublicUrl(bucket: string, relativePath: string): string;\r\n list(\r\n bucket: string,\r\n folder?: string,\r\n options?: StorageListOptions,\r\n ): Promise<StoragePaginatedListResult>;\r\n deleteByUrl(url: string): Promise<boolean>;\r\n parseUrl(url: string): { bucket?: string; path: string } | null;\r\n}\r\n\r\nexport function createLocalFsStorageAdapter(\r\n options: LocalFsStorageOptions,\r\n): LocalFsStorageAdapter {\r\n const resolvedRoot = path.resolve(options.rootDir);\r\n const baseUrl = (options.baseUrl ?? '/uploads').replace(/\\/+$/, '');\r\n const includeBucket = options.includeBucketInPath !== false;\r\n\r\n const resolveDiskTarget = (bucket: string, relativePath: string) => {\r\n const cleanBucket = sanitizeRelativePath(bucket);\r\n const cleanRel = sanitizeRelativePath(relativePath);\r\n const subPath = includeBucket ? `${cleanBucket}/${cleanRel}` : cleanRel;\r\n const diskPath = path.resolve(resolvedRoot, subPath);\r\n\r\n assertContainedInRoot(resolvedRoot, diskPath);\r\n\r\n return { diskPath, subPath, cleanRel };\r\n };\r\n\r\n const getPublicUrl = (bucket: string, relativePath: string): string => {\r\n const { subPath } = resolveDiskTarget(bucket, relativePath);\r\n return `${baseUrl}/${subPath}`;\r\n };\r\n\r\n const parseUrl = (url: string): { bucket?: string; path: string } | null => {\r\n if (!url || typeof url !== 'string') return null;\r\n\r\n try {\r\n const cleanUrl = url.split(/[?#]/)[0];\r\n const prefix = `${baseUrl}/`;\r\n let subPath: string | null = null;\r\n\r\n if (cleanUrl.startsWith(prefix)) {\r\n subPath = cleanUrl.slice(prefix.length);\r\n } else if (baseUrl.startsWith('/')) {\r\n const parsed = new URL(cleanUrl, 'http://localhost');\r\n const isLocal =\r\n parsed.hostname === 'localhost' || parsed.hostname === '127.0.0.1';\r\n const isRelative =\r\n !cleanUrl.startsWith('http://') && !cleanUrl.startsWith('https://');\r\n if ((isLocal || isRelative) && parsed.pathname.startsWith(prefix)) {\r\n subPath = parsed.pathname.slice(prefix.length);\r\n }\r\n }\r\n\r\n if (!subPath) return null;\r\n\r\n const decoded = decodeURIComponent(subPath)\r\n .replace(/\\\\/g, '/')\r\n .replace(/^\\/+/, '');\r\n if (!decoded || decoded.includes(':') || decoded.includes('\\0')) {\r\n return null;\r\n }\r\n\r\n const segments = decoded.split('/').filter(Boolean);\r\n if (\r\n segments.length === 0 ||\r\n segments.includes('..') ||\r\n segments.includes('.')\r\n ) {\r\n return null;\r\n }\r\n\r\n const diskPath = path.resolve(resolvedRoot, ...segments);\r\n const relative = path.relative(resolvedRoot, diskPath);\r\n if (relative.startsWith('..') || path.isAbsolute(relative)) {\r\n return null;\r\n }\r\n\r\n if (includeBucket) {\r\n if (segments.length < 2) return null;\r\n return {\r\n bucket: segments[0],\r\n path: segments.slice(1).join('/'),\r\n };\r\n }\r\n\r\n return { path: segments.join('/') };\r\n } catch {\r\n return null;\r\n }\r\n };\r\n\r\n return {\r\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\r\n const { diskPath, cleanRel } = resolveDiskTarget(\r\n params.bucket,\r\n params.path,\r\n );\r\n\r\n await fs.mkdir(path.dirname(diskPath), { recursive: true });\r\n\r\n try {\r\n await fs.writeFile(\r\n diskPath,\r\n params.data,\r\n params.upsert === false ? { flag: 'wx' } : undefined,\r\n );\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\r\n const existsError = new Error(\r\n `File already exists at ${params.bucket}/${cleanRel}`,\r\n ) as NodeJS.ErrnoException;\r\n existsError.code = 'EEXIST';\r\n throw existsError;\r\n }\r\n throw err;\r\n }\r\n\r\n return {\r\n bucket: params.bucket,\r\n path: cleanRel,\r\n url: getPublicUrl(params.bucket, cleanRel),\r\n };\r\n },\r\n\r\n async delete(bucket: string, relativePath: string): Promise<void> {\r\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\r\n try {\r\n await fs.unlink(diskPath);\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {\r\n throw err;\r\n }\r\n }\r\n },\r\n\r\n async exists(bucket: string, relativePath: string): Promise<boolean> {\r\n try {\r\n const { diskPath } = resolveDiskTarget(bucket, relativePath);\r\n const stat = await fs.stat(diskPath);\r\n return stat.isFile();\r\n } catch {\r\n return false;\r\n }\r\n },\r\n\r\n getPublicUrl,\r\n parseUrl,\r\n\r\n async deleteByUrl(url: string): Promise<boolean> {\r\n const parsed = parseUrl(url);\r\n if (!parsed) return false;\r\n\r\n try {\r\n const subSegments =\r\n includeBucket && parsed.bucket\r\n ? [parsed.bucket, ...parsed.path.split('/')]\r\n : parsed.path.split('/');\r\n const diskPath = path.resolve(resolvedRoot, ...subSegments);\r\n assertContainedInRoot(resolvedRoot, diskPath);\r\n await fs.unlink(diskPath);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n },\r\n\r\n async list(\r\n bucket: string,\r\n folder?: string,\r\n listOptions?: StorageListOptions,\r\n ): Promise<StoragePaginatedListResult> {\r\n const cleanBucket = sanitizeRelativePath(bucket);\r\n const cleanFolder = folder ? sanitizeRelativePath(folder) : '';\r\n const targetDir = path.resolve(\r\n resolvedRoot,\r\n includeBucket ? cleanBucket : '',\r\n cleanFolder,\r\n );\r\n\r\n assertContainedInRoot(resolvedRoot, targetDir);\r\n\r\n try {\r\n const dirEntries = await fs.readdir(targetDir, { withFileTypes: true });\r\n const results: StorageFileEntry[] = [];\r\n\r\n for (const entry of dirEntries) {\r\n if (!entry.isFile()) continue;\r\n const full = path.join(targetDir, entry.name);\r\n const stat = await fs.stat(full);\r\n results.push({\r\n name: entry.name,\r\n id: entry.name,\r\n createdAt: stat.birthtime.toISOString(),\r\n size: stat.size,\r\n });\r\n }\r\n\r\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\r\n const limit = listOptions?.limit ?? results.length;\r\n const sliced =\r\n listOptions?.limit !== undefined || offset > 0\r\n ? results.slice(offset, offset + limit)\r\n : results;\r\n const nextCursor =\r\n listOptions?.limit !== undefined && offset + sliced.length < results.length\r\n ? String(offset + sliced.length)\r\n : undefined;\r\n\r\n return toPaginatedListResult(sliced, nextCursor);\r\n } catch (err) {\r\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return toPaginatedListResult([]);\r\n }\r\n throw err;\r\n }\r\n },\r\n };\r\n}\r\n","import {\r\n type StorageAdapter,\r\n type StorageFileEntry,\r\n type StorageListOptions,\r\n type StoragePaginatedListResult,\r\n type StorageUploadParams,\r\n type StorageUploadResult,\r\n toPaginatedListResult,\r\n validateStorageMetadata,\r\n} from './types';\r\n\r\nexport interface S3PutObjectInput {\r\n Bucket: string;\r\n Key: string;\r\n Body: Buffer | Uint8Array;\r\n ContentType: string;\r\n CacheControl?: string;\r\n Metadata?: Record<string, string>;\r\n IfNoneMatch?: string;\r\n}\r\n\r\nexport interface S3DeleteObjectInput {\r\n Bucket: string;\r\n Key: string;\r\n}\r\n\r\nexport interface S3HeadObjectInput {\r\n Bucket: string;\r\n Key: string;\r\n}\r\n\r\nexport interface S3ListObjectsInput {\r\n Bucket: string;\r\n Prefix?: string;\r\n MaxKeys?: number;\r\n ContinuationToken?: string;\r\n}\r\n\r\nexport interface S3ListedObject {\r\n Key?: string;\r\n Size?: number;\r\n LastModified?: Date | string;\r\n}\r\n\r\nexport interface S3ListObjectsOutput {\r\n Contents?: S3ListedObject[];\r\n NextContinuationToken?: string;\r\n IsTruncated?: boolean;\r\n}\r\n\r\n/** Structural S3 / Cloudflare R2 / MinIO transport with zero runtime SDK dependency. */\r\nexport interface S3TransportLike {\r\n putObject(input: S3PutObjectInput): Promise<unknown>;\r\n deleteObject(input: S3DeleteObjectInput): Promise<unknown>;\r\n headObject?(input: S3HeadObjectInput): Promise<unknown>;\r\n listObjects?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\r\n listObjectsV2?(input: S3ListObjectsInput): Promise<S3ListObjectsOutput>;\r\n}\r\n\r\nexport type S3PublicUrlBuilder = (bucket: string, path: string) => string;\r\n\r\nexport interface S3StorageBaseOptions {\r\n /** Public base URL prefix (e.g. `'https://cdn.example.com'`) or `(bucket, path) => string` builder. */\r\n publicBaseUrl: string | S3PublicUrlBuilder;\r\n /** Default `Cache-Control` header applied when `StorageUploadParams.cacheControl` is omitted. */\r\n cacheControl?: string;\r\n /** Include `bucket` segment in string-prefix URLs (`${publicBaseUrl}/${bucket}/${path}`). Defaults to `true`. */\r\n includeBucketInUrl?: boolean;\r\n /** Fallback bucket used by `deleteByUrl` when `includeBucketInUrl` is `false`. */\r\n defaultBucket?: string;\r\n}\r\n\r\nexport type S3StorageAdapterOptions = S3StorageBaseOptions &\r\n (\r\n | (S3TransportLike & { client?: never; transport?: never })\r\n | { client: S3TransportLike; transport?: never; putObject?: never }\r\n | { transport: S3TransportLike; client?: never; putObject?: never }\r\n );\r\n\r\nfunction normalizeS3Key(rawPath: string): string {\r\n const normalized = rawPath.replace(/\\\\/g, '/').replace(/^\\/+/, '');\r\n if (!normalized) throw new Error('Invalid S3 object key');\r\n return normalized;\r\n}\r\n\r\nfunction resolveTransport(options: S3StorageAdapterOptions): S3TransportLike {\r\n if ('client' in options && options.client) return options.client;\r\n if ('transport' in options && options.transport) return options.transport;\r\n if ('putObject' in options && typeof options.putObject === 'function') return options;\r\n throw new Error('S3StorageAdapter requires putObject/deleteObject or client/transport');\r\n}\r\n\r\nfunction isS3PreconditionError(err: unknown): boolean {\r\n if (!err || typeof err !== 'object') return false;\r\n const e = err as {\r\n code?: unknown;\r\n name?: unknown;\r\n statusCode?: unknown;\r\n $metadata?: { httpStatusCode?: number };\r\n };\r\n const status = e.$metadata?.httpStatusCode ?? Number(e.statusCode);\r\n const code = String(e.code ?? e.name ?? '').toLowerCase();\r\n return status === 412 || code === 'preconditionfailed' || code === 'eexist';\r\n}\r\n\r\n/** Zero-dependency S3 / Cloudflare R2 / MinIO `StorageAdapter`. */\r\nexport function createS3StorageAdapter(options: S3StorageAdapterOptions): StorageAdapter {\r\n const transport = resolveTransport(options);\r\n const includeBucket = options.includeBucketInUrl !== false;\r\n\r\n const getPublicUrl = (bucket: string, path: string): string => {\r\n const key = normalizeS3Key(path);\r\n if (typeof options.publicBaseUrl === 'function') return options.publicBaseUrl(bucket, key);\r\n const base = options.publicBaseUrl.replace(/\\/+$/, '');\r\n return includeBucket ? `${base}/${bucket}/${key}` : `${base}/${key}`;\r\n };\r\n\r\n return {\r\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\r\n const key = normalizeS3Key(params.path);\r\n validateStorageMetadata(params.metadata);\r\n\r\n const isImmutable =\r\n params.upsert === false && params.contentType.includes('image/avif');\r\n const cacheControl =\r\n params.cacheControl ??\r\n options.cacheControl ??\r\n (isImmutable\r\n ? 'public, max-age=31536000, immutable'\r\n : 'public, max-age=2592000, must-revalidate');\r\n\r\n const metadata =\r\n params.metadata || params.contentHash\r\n ? {\r\n ...params.metadata,\r\n ...(params.contentHash ? { contentHash: params.contentHash } : {}),\r\n }\r\n : undefined;\r\n\r\n try {\r\n await transport.putObject({\r\n Bucket: params.bucket,\r\n Key: key,\r\n Body: params.data,\r\n ContentType: params.contentType,\r\n ...(cacheControl ? { CacheControl: cacheControl } : {}),\r\n ...(metadata ? { Metadata: metadata } : {}),\r\n ...(params.upsert === false ? { IfNoneMatch: '*' } : {}),\r\n });\r\n } catch (err) {\r\n if (isS3PreconditionError(err)) {\r\n const existsErr = new Error(\r\n `Object already exists at ${params.bucket}/${key}`,\r\n ) as Error & { code?: string; statusCode?: number };\r\n existsErr.code = 'EEXIST';\r\n existsErr.statusCode = 412;\r\n throw existsErr;\r\n }\r\n throw err;\r\n }\r\n\r\n return {\r\n bucket: params.bucket,\r\n path: key,\r\n url: getPublicUrl(params.bucket, key),\r\n };\r\n },\r\n\r\n async delete(bucket: string, path: string): Promise<void> {\r\n await transport.deleteObject({ Bucket: bucket, Key: normalizeS3Key(path) });\r\n },\r\n\r\n async exists(bucket: string, path: string): Promise<boolean> {\r\n const key = normalizeS3Key(path);\r\n if (transport.headObject) {\r\n try {\r\n await transport.headObject({ Bucket: bucket, Key: key });\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n const lastSlash = key.lastIndexOf('/');\r\n const folder = lastSlash > 0 ? key.slice(0, lastSlash) : undefined;\r\n const targetName = lastSlash > 0 ? key.slice(lastSlash + 1) : key;\r\n const listed = await this.list!(bucket, folder);\r\n return listed.some((item) => item.name === targetName);\r\n },\r\n\r\n getPublicUrl,\r\n\r\n async list(\r\n bucket: string,\r\n folder?: string,\r\n listOptions?: StorageListOptions,\r\n ): Promise<StoragePaginatedListResult> {\r\n const cleanFolder = folder ? normalizeS3Key(folder).replace(/\\/+$/, '') : '';\r\n const prefix = cleanFolder ? `${cleanFolder}/` : undefined;\r\n const input: S3ListObjectsInput = {\r\n Bucket: bucket,\r\n ...(prefix ? { Prefix: prefix } : {}),\r\n MaxKeys: listOptions?.limit ?? 1000,\r\n ...(listOptions?.cursor ? { ContinuationToken: listOptions.cursor } : {}),\r\n };\r\n\r\n const output = transport.listObjects\r\n ? await transport.listObjects(input)\r\n : transport.listObjectsV2\r\n ? await transport.listObjectsV2(input)\r\n : undefined;\r\n\r\n const entries: StorageFileEntry[] = (output?.Contents ?? [])\r\n .filter((item): item is S3ListedObject & { Key: string } => Boolean(item.Key))\r\n .map((item) => {\r\n const name =\r\n prefix && item.Key.startsWith(prefix)\r\n ? item.Key.slice(prefix.length)\r\n : item.Key;\r\n const createdAt =\r\n item.LastModified instanceof Date\r\n ? item.LastModified.toISOString()\r\n : item.LastModified;\r\n return {\r\n name,\r\n id: `${bucket}/${item.Key}`,\r\n ...(createdAt !== undefined ? { createdAt } : {}),\r\n ...(item.Size !== undefined ? { size: item.Size } : {}),\r\n };\r\n })\r\n .filter((entry) => entry.name.length > 0);\r\n\r\n return toPaginatedListResult(entries, output?.NextContinuationToken);\r\n },\r\n\r\n parseUrl(url: string): { bucket?: string; path: string } | null {\r\n if (typeof options.publicBaseUrl !== 'string' || !url) return null;\r\n const prefix = `${options.publicBaseUrl.replace(/\\/+$/, '')}/`;\r\n const cleanUrl = url.split(/[?#]/)[0];\r\n if (!cleanUrl.startsWith(prefix)) return null;\r\n\r\n try {\r\n const rest = decodeURIComponent(cleanUrl.slice(prefix.length)).replace(/^\\/+/, '');\r\n if (!rest) return null;\r\n\r\n if (includeBucket) {\r\n const slashIdx = rest.indexOf('/');\r\n if (slashIdx <= 0 || slashIdx === rest.length - 1) return null;\r\n return {\r\n bucket: rest.slice(0, slashIdx),\r\n path: rest.slice(slashIdx + 1),\r\n };\r\n }\r\n\r\n if (!options.defaultBucket) return null;\r\n return { bucket: options.defaultBucket, path: rest };\r\n } catch {\r\n return null;\r\n }\r\n },\r\n\r\n async deleteByUrl(url: string): Promise<boolean> {\r\n const parsed = this.parseUrl?.(url);\r\n if (!parsed || !parsed.bucket) return false;\r\n\r\n try {\r\n await transport.deleteObject({\r\n Bucket: parsed.bucket,\r\n Key: parsed.path,\r\n });\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n },\r\n };\r\n}\r\n","import {\r\n type StorageAdapter,\r\n type StorageListOptions,\r\n type StoragePaginatedListResult,\r\n type StorageUploadParams,\r\n type StorageUploadResult,\r\n toPaginatedListResult,\r\n validateStorageMetadata,\r\n} from './types';\r\nimport {\r\n DEFAULT_PUBLIC_STORAGE_PREFIX,\r\n parsePublicStorageUrl,\r\n} from './urlParser';\r\n\r\n/**\r\n * Minimal structural interface matching `@supabase/supabase-js` Storage client,\r\n * avoiding a mandatory `@supabase/supabase-js` runtime dependency.\r\n */\r\nexport interface SupabaseBucketClientLike {\r\n upload(\r\n path: string,\r\n fileBody: Buffer | Uint8Array,\r\n fileOptions?: {\r\n contentType?: string;\r\n upsert?: boolean;\r\n cacheControl?: string;\r\n metadata?: Record<string, string>;\r\n },\r\n ): Promise<{\r\n data: { path: string } | null;\r\n error: { message: string } | null;\r\n }>;\r\n remove(paths: string[]): Promise<{\r\n data?: unknown;\r\n error: { message: string } | null;\r\n }>;\r\n getPublicUrl(path: string): {\r\n data: { publicUrl: string };\r\n };\r\n list?(\r\n path?: string,\r\n options?: {\r\n limit?: number;\r\n offset?: number;\r\n sortBy?: { column: string; order: string };\r\n },\r\n ): Promise<{\r\n data: Array<{\r\n name: string;\r\n id: string | null;\r\n created_at: string | null;\r\n }> | null;\r\n error: { message: string } | null;\r\n }>;\r\n}\r\n\r\nexport interface SupabaseStorageClientLike {\r\n storage: {\r\n from(bucket: string): SupabaseBucketClientLike;\r\n };\r\n}\r\n\r\nexport interface SupabaseStorageAdapterOptions {\r\n publicUrlPrefix?: string;\r\n}\r\n\r\n/**\r\n * Plug in any `@supabase/supabase-js` client instance (e.g., service-role client)\r\n * as a `StorageAdapter` with zero extra wrapper boilerplate.\r\n */\r\nexport function createSupabaseStorageAdapter(\r\n client: SupabaseStorageClientLike,\r\n options: SupabaseStorageAdapterOptions = {},\r\n): StorageAdapter {\r\n const publicPrefix =\r\n options.publicUrlPrefix || DEFAULT_PUBLIC_STORAGE_PREFIX;\r\n\r\n return {\r\n async upload(params: StorageUploadParams): Promise<StorageUploadResult> {\r\n validateStorageMetadata(params.metadata);\r\n\r\n const isImmutable =\r\n params.upsert === false && params.contentType.includes('image/avif');\r\n const cacheControl =\r\n params.cacheControl ||\r\n (isImmutable\r\n ? 'public, max-age=31536000, immutable'\r\n : 'public, max-age=2592000, must-revalidate');\r\n\r\n const { data, error } = await client.storage\r\n .from(params.bucket)\r\n .upload(params.path, params.data, {\r\n contentType: params.contentType,\r\n upsert: params.upsert === undefined ? true : params.upsert,\r\n cacheControl,\r\n metadata: {\r\n ...params.metadata,\r\n uploadedAt: new Date().toISOString(),\r\n contentHash: params.contentHash || 'unknown',\r\n },\r\n });\r\n\r\n if (error || !data) {\r\n throw new Error(\r\n `Failed to upload to Supabase storage (bucket: ${params.bucket}, path: ${params.path}): ${error?.message || 'Unknown error'}`,\r\n );\r\n }\r\n\r\n const {\r\n data: { publicUrl },\r\n } = client.storage.from(params.bucket).getPublicUrl(data.path);\r\n\r\n return {\r\n bucket: params.bucket,\r\n path: data.path,\r\n url: publicUrl,\r\n };\r\n },\r\n\r\n async delete(bucket: string, path: string): Promise<void> {\r\n const { error } = await client.storage.from(bucket).remove([path]);\r\n if (error) {\r\n throw new Error(`Failed to delete from Supabase storage: ${error.message}`);\r\n }\r\n },\r\n\r\n async exists(bucket: string, path: string): Promise<boolean> {\r\n const cleanPath = path.replace(/^\\/+/, '');\r\n const lastSlash = cleanPath.lastIndexOf('/');\r\n const folder = lastSlash > 0 ? cleanPath.slice(0, lastSlash) : undefined;\r\n const targetName = lastSlash > 0 ? cleanPath.slice(lastSlash + 1) : cleanPath;\r\n const entries = await this.list!(bucket, folder);\r\n return entries.some((entry) => entry.name === targetName);\r\n },\r\n\r\n getPublicUrl(bucket: string, path: string): string {\r\n const {\r\n data: { publicUrl },\r\n } = client.storage.from(bucket).getPublicUrl(path);\r\n return publicUrl;\r\n },\r\n\r\n async list(\r\n bucket: string,\r\n folder?: string,\r\n listOptions?: StorageListOptions,\r\n ): Promise<StoragePaginatedListResult> {\r\n const bucketClient = client.storage.from(bucket);\r\n if (!bucketClient.list) {\r\n return toPaginatedListResult([]);\r\n }\r\n\r\n const limit = listOptions?.limit ?? 1000;\r\n const offset = listOptions?.cursor ? Number(listOptions.cursor) || 0 : 0;\r\n\r\n const { data, error } = await bucketClient.list(folder, {\r\n limit,\r\n ...(offset > 0 ? { offset } : {}),\r\n sortBy: { column: 'created_at', order: 'desc' },\r\n });\r\n\r\n if (error) {\r\n throw new Error(`Failed to list Supabase bucket files: ${error.message}`);\r\n }\r\n\r\n const entries = (data || [])\r\n .filter((f) => f.id !== null)\r\n .map((f) => ({\r\n name: f.name,\r\n id: f.id ?? undefined,\r\n createdAt: f.created_at ?? undefined,\r\n }));\r\n\r\n const nextCursor =\r\n listOptions?.limit !== undefined && entries.length >= limit\r\n ? String(offset + entries.length)\r\n : undefined;\r\n\r\n return toPaginatedListResult(entries, nextCursor);\r\n },\r\n\r\n parseUrl(url: string): { bucket?: string; path: string } | null {\r\n const info = parsePublicStorageUrl(url, publicPrefix);\r\n if (!info) return null;\r\n return { bucket: info.bucket, path: info.path };\r\n },\r\n\r\n async deleteByUrl(url: string): Promise<boolean> {\r\n const info = parsePublicStorageUrl(url, publicPrefix);\r\n if (!info) return false;\r\n\r\n try {\r\n const { error } = await client.storage\r\n .from(info.bucket)\r\n .remove([info.path]);\r\n return !error;\r\n } catch {\r\n return false;\r\n }\r\n },\r\n };\r\n}\r\n"],"mappings":"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","names":["path","path"]}
@@ -1,112 +1,112 @@
1
- /* ── AnimatedImage: blur-up crossfade image (Imagine-Global parity) ─────── */
2
- .iuk-animated-image {
3
- position: relative;
4
- width: 100%;
5
- height: 100%;
6
- overflow: hidden;
7
- overflow: clip;
8
- border-radius: inherit;
9
- clip-path: inset(0 round inherit);
10
- contain: paint;
11
- transition: background-color var(--iuk-animated-image-color-transition, 300ms ease);
12
- }
13
-
14
- .iuk-animated-image--fill {
15
- position: absolute;
16
- inset: 0;
17
- }
18
-
19
- .iuk-animated-image__placeholder {
20
- position: absolute;
21
- inset: 0;
22
- width: 100%;
23
- height: 100%;
24
- object-position: center;
25
- /* Tiny placeholder stretched to full size is blurred and scaled slightly so
26
- blurred edges stay clipped inside the overflow: hidden wrapper. */
27
- filter: blur(var(--iuk-animated-image-blur, 20px));
28
- transform: scale(var(--iuk-animated-image-placeholder-scale, 1.1));
29
- transform-origin: center;
30
- z-index: 1;
31
- opacity: 1;
32
- transition: opacity var(--iuk-ai-ph-dur, 500ms) ease-in-out;
33
- pointer-events: none;
34
- }
35
-
36
- .iuk-animated-image__placeholder--hidden {
37
- opacity: 0;
38
- }
39
-
40
- .iuk-animated-image__img {
41
- position: relative;
42
- display: block;
43
- z-index: 2;
44
- width: 100%;
45
- height: auto;
46
- opacity: 0;
47
- transform: scale(var(--iuk-animated-image-reveal-scale, 1.02));
48
- border-radius: inherit;
49
- user-select: none;
50
- -webkit-user-select: none;
51
- -webkit-user-drag: none;
52
- transition:
53
- opacity var(--iuk-animated-image-duration, 600ms) ease-in-out,
54
- transform var(--iuk-animated-image-duration, 600ms) ease-in-out;
55
- }
56
-
57
- .iuk-animated-image__img[data-state='loaded'] {
58
- opacity: 1;
59
- transform: scale(1);
60
- }
61
-
62
- .iuk-animated-image__img[data-state='error'] {
63
- opacity: 0;
64
- visibility: hidden;
65
- }
66
-
67
- .iuk-animated-image--fill .iuk-animated-image__img {
68
- position: absolute;
69
- inset: 0;
70
- height: 100%;
71
- }
72
-
73
- /* In contain mode, the generated blur placeholder stays visible with object-fit: cover
74
- and scale(1.1) behind the contained image so letterbox/pillarbox bars show the blur. */
75
- .iuk-animated-image--contain .iuk-animated-image__img {
76
- transform: none;
77
- }
78
-
79
- .iuk-animated-image__shimmer {
80
- position: absolute;
81
- inset: 0;
82
- z-index: 3;
83
- background: linear-gradient(
84
- 90deg,
85
- transparent 0%,
86
- var(--iuk-animated-image-shimmer, rgba(255, 255, 255, 0.1)) 50%,
87
- transparent 100%
88
- );
89
- animation: iuk-animated-image-shimmer 2s infinite;
90
- pointer-events: none;
91
- }
92
-
93
- .iuk-animated-image__shimmer--hidden {
94
- opacity: 0;
95
- }
96
-
97
- @keyframes iuk-animated-image-shimmer {
98
- from { transform: translateX(-100%); }
99
- to { transform: translateX(100%); }
100
- }
101
-
102
- @media (prefers-reduced-motion: reduce) {
103
- .iuk-animated-image__img,
104
- .iuk-animated-image__placeholder {
105
- transform: none !important;
106
- transition-duration: 0ms !important;
107
- transition-delay: 0ms !important;
108
- }
109
- .iuk-animated-image__shimmer {
110
- animation: none;
111
- }
112
- }
1
+ /* ── AnimatedImage: blur-up crossfade image (Imagine-Global parity) ─────── */
2
+ .iuk-animated-image {
3
+ position: relative;
4
+ width: 100%;
5
+ height: 100%;
6
+ overflow: hidden;
7
+ overflow: clip;
8
+ border-radius: inherit;
9
+ clip-path: inset(0 round inherit);
10
+ contain: paint;
11
+ transition: background-color var(--iuk-animated-image-color-transition, 300ms ease);
12
+ }
13
+
14
+ .iuk-animated-image--fill {
15
+ position: absolute;
16
+ inset: 0;
17
+ }
18
+
19
+ .iuk-animated-image__placeholder {
20
+ position: absolute;
21
+ inset: 0;
22
+ width: 100%;
23
+ height: 100%;
24
+ object-position: center;
25
+ /* Tiny placeholder stretched to full size is blurred and scaled slightly so
26
+ blurred edges stay clipped inside the overflow: hidden wrapper. */
27
+ filter: blur(var(--iuk-animated-image-blur, 20px));
28
+ transform: scale(var(--iuk-animated-image-placeholder-scale, 1.1));
29
+ transform-origin: center;
30
+ z-index: 1;
31
+ opacity: 1;
32
+ transition: opacity var(--iuk-ai-ph-dur, 500ms) ease-in-out;
33
+ pointer-events: none;
34
+ }
35
+
36
+ .iuk-animated-image__placeholder--hidden {
37
+ opacity: 0;
38
+ }
39
+
40
+ .iuk-animated-image__img {
41
+ position: relative;
42
+ display: block;
43
+ z-index: 2;
44
+ width: 100%;
45
+ height: auto;
46
+ opacity: 0;
47
+ transform: scale(var(--iuk-animated-image-reveal-scale, 1.02));
48
+ border-radius: inherit;
49
+ user-select: none;
50
+ -webkit-user-select: none;
51
+ -webkit-user-drag: none;
52
+ transition:
53
+ opacity var(--iuk-animated-image-duration, 600ms) ease-in-out,
54
+ transform var(--iuk-animated-image-duration, 600ms) ease-in-out;
55
+ }
56
+
57
+ .iuk-animated-image__img[data-state='loaded'] {
58
+ opacity: 1;
59
+ transform: scale(1);
60
+ }
61
+
62
+ .iuk-animated-image__img[data-state='error'] {
63
+ opacity: 0;
64
+ visibility: hidden;
65
+ }
66
+
67
+ .iuk-animated-image--fill .iuk-animated-image__img {
68
+ position: absolute;
69
+ inset: 0;
70
+ height: 100%;
71
+ }
72
+
73
+ /* In contain mode, the generated blur placeholder stays visible with object-fit: cover
74
+ and scale(1.1) behind the contained image so letterbox/pillarbox bars show the blur. */
75
+ .iuk-animated-image--contain .iuk-animated-image__img {
76
+ transform: none;
77
+ }
78
+
79
+ .iuk-animated-image__shimmer {
80
+ position: absolute;
81
+ inset: 0;
82
+ z-index: 3;
83
+ background: linear-gradient(
84
+ 90deg,
85
+ transparent 0%,
86
+ var(--iuk-animated-image-shimmer, rgba(255, 255, 255, 0.1)) 50%,
87
+ transparent 100%
88
+ );
89
+ animation: iuk-animated-image-shimmer 2s infinite;
90
+ pointer-events: none;
91
+ }
92
+
93
+ .iuk-animated-image__shimmer--hidden {
94
+ opacity: 0;
95
+ }
96
+
97
+ @keyframes iuk-animated-image-shimmer {
98
+ from { transform: translateX(-100%); }
99
+ to { transform: translateX(100%); }
100
+ }
101
+
102
+ @media (prefers-reduced-motion: reduce) {
103
+ .iuk-animated-image__img,
104
+ .iuk-animated-image__placeholder {
105
+ transform: none !important;
106
+ transition-duration: 0ms !important;
107
+ transition-delay: 0ms !important;
108
+ }
109
+ .iuk-animated-image__shimmer {
110
+ animation: none;
111
+ }
112
+ }