@visulima/storage 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (224) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +33 -14
  3. package/dist/adapter/nuxt/module.d.ts +39 -0
  4. package/dist/adapter/nuxt/module.js +93 -0
  5. package/dist/adapter/nuxt/package.json +20 -0
  6. package/dist/handler/base/base-handler-core.d.ts +91 -0
  7. package/dist/handler/base/base-handler-fetch.d.ts +76 -0
  8. package/dist/handler/base/base-handler-node.d.ts +137 -0
  9. package/dist/handler/http/fetch/index.d.ts +4 -0
  10. package/dist/handler/http/fetch/index.js +4 -0
  11. package/dist/handler/http/hono/index.d.ts +67 -0
  12. package/dist/handler/http/hono/index.js +65 -0
  13. package/dist/handler/http/nextjs/index.d.ts +61 -0
  14. package/dist/handler/http/nextjs/index.js +37 -0
  15. package/dist/handler/http/node/index.d.ts +4 -0
  16. package/dist/handler/http/node/index.js +4 -0
  17. package/dist/handler/http/solid-start/index.d.ts +60 -0
  18. package/dist/handler/http/solid-start/index.js +44 -0
  19. package/dist/handler/multipart/multipart-base.d.ts +84 -0
  20. package/dist/handler/multipart/multipart-fetch.d.ts +64 -0
  21. package/dist/handler/multipart/multipart.d.ts +49 -0
  22. package/dist/handler/rest/rest-base.d.ts +106 -0
  23. package/dist/handler/rest/rest-fetch.d.ts +88 -0
  24. package/dist/handler/rest/rest.d.ts +93 -0
  25. package/dist/handler/tus/tus-base.d.ts +152 -0
  26. package/dist/handler/tus/tus-fetch.d.ts +72 -0
  27. package/dist/handler/tus/tus.d.ts +78 -0
  28. package/dist/handler/types.d.ts +53 -0
  29. package/dist/handler/utils/request-parser.d.ts +72 -0
  30. package/dist/handler/utils/response-builder.d.ts +83 -0
  31. package/dist/handler/utils/storage-utils.d.ts +10 -0
  32. package/dist/handler/utils/stream-utils.d.ts +29 -0
  33. package/dist/handler/utils/upload-handlers.d.ts +76 -0
  34. package/dist/index.d.ts +17 -0
  35. package/dist/index.js +13 -0
  36. package/dist/metrics/index.d.ts +2 -0
  37. package/dist/metrics/no-op-metrics.d.ts +15 -0
  38. package/dist/metrics/opentelemetry-metrics.d.ts +55 -0
  39. package/dist/openapi/index.d.ts +5 -0
  40. package/dist/openapi/index.js +5 -0
  41. package/dist/openapi/rest.d.ts +7 -0
  42. package/dist/openapi/shared.d.ts +13 -0
  43. package/dist/openapi/transform.d.ts +3 -0
  44. package/dist/openapi/tus.d.ts +7 -0
  45. package/dist/openapi/xhr.d.ts +7 -0
  46. package/dist/packem_shared/AwsLightFile-tTneXZgG.js +11 -0
  47. package/dist/packem_shared/AwsLightMetaStorage-BWSOtVaN.js +4 -0
  48. package/dist/packem_shared/AwsLightStorage-Blv-GP4v.js +133 -0
  49. package/dist/packem_shared/AzureFile-CesgFzps.js +8 -0
  50. package/dist/packem_shared/AzureSMetaStorage-CFs-OYJT.js +88 -0
  51. package/dist/packem_shared/AzureStorage-B0v048hG.js +357 -0
  52. package/dist/packem_shared/BaseTransformer-C2gLib6v.js +82 -0
  53. package/dist/packem_shared/DiskStorage-BlltBst6.js +12 -0
  54. package/dist/packem_shared/DiskStorageWithChecksum-BJTmMklc.js +238 -0
  55. package/dist/packem_shared/ERRORS-D0apMqnc.js +97 -0
  56. package/dist/packem_shared/File-Bb3P23dr.js +69 -0
  57. package/dist/packem_shared/GCSConfig-vPP22kN6.js +11 -0
  58. package/dist/packem_shared/GCSFile-BIEunhAN.js +8 -0
  59. package/dist/packem_shared/GCSMetaStorage-BMFEdzJ7.js +7 -0
  60. package/dist/packem_shared/GCStorage-DnhiwaS9.js +406 -0
  61. package/dist/packem_shared/LocalMetaStorage-CZHhKkMd.js +7 -0
  62. package/dist/packem_shared/MediaTransformer-D6658DL1.js +1197 -0
  63. package/dist/packem_shared/MetaStorage-pECeFOad.js +46 -0
  64. package/dist/packem_shared/Metadata-DRLXeJ0F.js +89 -0
  65. package/dist/packem_shared/Multipart-CNFK_Okz.js +163 -0
  66. package/dist/packem_shared/Multipart-CtUL7BQw.js +128 -0
  67. package/dist/packem_shared/NetlifyBlobFile-CXzyjqrD.js +14 -0
  68. package/dist/packem_shared/NetlifyBlobMetaStorage-DxZ5aDvD.js +9 -0
  69. package/dist/packem_shared/NetlifyBlobStorage-CLPtjHqd.js +376 -0
  70. package/dist/packem_shared/NoOpMetrics-DhAk5rXc.js +10 -0
  71. package/dist/packem_shared/OpenTelemetryMetrics-BnxhqIaH.js +67 -0
  72. package/dist/packem_shared/Rest-CAOAEkOj.js +267 -0
  73. package/dist/packem_shared/Rest-DNuLwBrK.js +228 -0
  74. package/dist/packem_shared/S3File-DZiyk9Qt.js +11 -0
  75. package/dist/packem_shared/S3MetaStorage-Dz9aDabA.js +76 -0
  76. package/dist/packem_shared/S3Storage-DHmTk-hU.js +316 -0
  77. package/dist/packem_shared/TUS_RESUMABLE-GJzZ9R-f.js +434 -0
  78. package/dist/packem_shared/Tus-B8PmlMgR.js +200 -0
  79. package/dist/packem_shared/Tus-C4F1aYOl.js +195 -0
  80. package/dist/packem_shared/ValidationError-BfF1aE4h.js +26 -0
  81. package/dist/packem_shared/VercelBlobFile-BCg4aTEq.js +18 -0
  82. package/dist/packem_shared/VercelBlobMetaStorage-Bd9F-VFm.js +9 -0
  83. package/dist/packem_shared/VercelBlobStorage-DD9LMOS6.js +261 -0
  84. package/dist/packem_shared/_commonjsHelpers-B85MJLTf.js +5 -0
  85. package/dist/packem_shared/aws-light-meta-storage-DKJBgbR6.js +446 -0
  86. package/dist/packem_shared/backblaze-BlMnIcBC.js +20 -0
  87. package/dist/packem_shared/base-handler-core-BKuf4YLT.js +303 -0
  88. package/dist/packem_shared/base-handler-fetch-Cr0hLiqg.js +291 -0
  89. package/dist/packem_shared/base-handler-node-gk5aN9Cx.js +730 -0
  90. package/dist/packem_shared/cache-B88MXQ_2.js +18 -0
  91. package/dist/packem_shared/cloudflare-Bi1q8wXE.js +21 -0
  92. package/dist/packem_shared/defaultCloudStorageFileNameValidation-DUuacjWv.js +11 -0
  93. package/dist/packem_shared/digitalOcean-CWQRJM3L.js +21 -0
  94. package/dist/packem_shared/disk-storage-Bh2wy3YW.js +810 -0
  95. package/dist/packem_shared/gcs-meta-storage-0tgMNnPO.js +158 -0
  96. package/dist/packem_shared/has-content-CY66ehMK.js +3 -0
  97. package/dist/packem_shared/headers-DoS5nwM-.js +3220 -0
  98. package/dist/packem_shared/is-expired-CTThU1q5.js +8 -0
  99. package/dist/packem_shared/isRetryableError-Dycp7127.js +82 -0
  100. package/dist/packem_shared/isValidMediaType-BeDgiObq.js +35 -0
  101. package/dist/packem_shared/local-meta-storage-By-3SNBI.js +649 -0
  102. package/dist/packem_shared/minio-C2YBZQOw.js +22 -0
  103. package/dist/packem_shared/multipart-base-B3i67SSX.js +102 -0
  104. package/dist/packem_shared/part-match-BMNqHDYD.js +96 -0
  105. package/dist/packem_shared/path-CR6YkPXX-7R1-9CMk.js +161 -0
  106. package/dist/packem_shared/response-builder-BtnRiBUI.js +45 -0
  107. package/dist/packem_shared/rest-base-GRCcnan7.js +457 -0
  108. package/dist/packem_shared/restOpenApiSpec-CwcHHY2C.js +800 -0
  109. package/dist/packem_shared/s3-base-storage-C1DIHTYY.js +510 -0
  110. package/dist/packem_shared/sharedGet-Cpo7QUyu.js +1019 -0
  111. package/dist/packem_shared/storage-C6NSXb8h.js +1175 -0
  112. package/dist/packem_shared/tigris-Hac8TKnX.js +21 -0
  113. package/dist/packem_shared/transformOpenApiSpec-DWM5WtnN.js +975 -0
  114. package/dist/packem_shared/tusOpenApiSpec-6ohiToBy.js +740 -0
  115. package/dist/packem_shared/update-size-CCGm6i1J.js +8 -0
  116. package/dist/packem_shared/validator-BeX_lJet.js +78 -0
  117. package/dist/packem_shared/waitForStorage-Cscw85sx.js +18 -0
  118. package/dist/packem_shared/wasabi-DIyflHSd.js +21 -0
  119. package/dist/packem_shared/xhrOpenApiSpec-DS17brnx.js +264 -0
  120. package/dist/storage/aws/clients/backblaze.d.ts +12 -0
  121. package/dist/storage/aws/clients/cloudflare.d.ts +13 -0
  122. package/dist/storage/aws/clients/digital-ocean.d.ts +12 -0
  123. package/dist/storage/aws/clients/index.d.ts +6 -0
  124. package/dist/storage/aws/clients/index.js +6 -0
  125. package/dist/storage/aws/clients/minio.d.ts +13 -0
  126. package/dist/storage/aws/clients/tigris.d.ts +12 -0
  127. package/dist/storage/aws/clients/types.d.ts +95 -0
  128. package/dist/storage/aws/clients/wasabi.d.ts +12 -0
  129. package/dist/storage/aws/index.d.ts +4 -0
  130. package/dist/storage/aws/index.js +3 -0
  131. package/dist/storage/aws/s3-base-storage.d.ts +248 -0
  132. package/dist/storage/aws/s3-client-adapter.d.ts +110 -0
  133. package/dist/storage/aws/s3-file.d.ts +10 -0
  134. package/dist/storage/aws/s3-meta-storage.d.ts +15 -0
  135. package/dist/storage/aws/s3-storage.d.ts +70 -0
  136. package/dist/storage/aws/types.d.ts +119 -0
  137. package/dist/storage/aws-light/aws-light-api-adapter.d.ts +130 -0
  138. package/dist/storage/aws-light/aws-light-file.d.ts +10 -0
  139. package/dist/storage/aws-light/aws-light-meta-storage.d.ts +19 -0
  140. package/dist/storage/aws-light/aws-light-storage.d.ts +65 -0
  141. package/dist/storage/aws-light/index.d.ts +4 -0
  142. package/dist/storage/aws-light/index.js +3 -0
  143. package/dist/storage/aws-light/types.d.ts +37 -0
  144. package/dist/storage/azure/azure-file.d.ts +6 -0
  145. package/dist/storage/azure/azure-meta-storage.d.ts +15 -0
  146. package/dist/storage/azure/azure-storage.d.ts +69 -0
  147. package/dist/storage/azure/index.d.ts +4 -0
  148. package/dist/storage/azure/index.js +3 -0
  149. package/dist/storage/azure/types.d.ts +62 -0
  150. package/dist/storage/gcs/fetch-error.d.ts +11 -0
  151. package/dist/storage/gcs/gcs-config.d.ts +2 -0
  152. package/dist/storage/gcs/gcs-file.d.ts +6 -0
  153. package/dist/storage/gcs/gcs-meta-storage.d.ts +27 -0
  154. package/dist/storage/gcs/gcs-storage.d.ts +91 -0
  155. package/dist/storage/gcs/index.d.ts +5 -0
  156. package/dist/storage/gcs/index.js +4 -0
  157. package/dist/storage/gcs/types.d.ts +55 -0
  158. package/dist/storage/gcs/utils.d.ts +7 -0
  159. package/dist/storage/local/disk-storage-with-checksum.d.ts +15 -0
  160. package/dist/storage/local/disk-storage.d.ts +135 -0
  161. package/dist/storage/local/index.d.ts +4 -0
  162. package/dist/storage/local/index.js +3 -0
  163. package/dist/storage/local/local-meta-storage.d.ts +32 -0
  164. package/dist/storage/meta-storage.d.ts +30 -0
  165. package/dist/storage/netlify-blob/index.d.ts +4 -0
  166. package/dist/storage/netlify-blob/index.js +3 -0
  167. package/dist/storage/netlify-blob/netlify-blob-file.d.ts +12 -0
  168. package/dist/storage/netlify-blob/netlify-blob-meta-storage.d.ts +7 -0
  169. package/dist/storage/netlify-blob/netlify-blob-storage.d.ts +93 -0
  170. package/dist/storage/netlify-blob/types.d.ts +30 -0
  171. package/dist/storage/storage.d.ts +353 -0
  172. package/dist/storage/types.d.ts +221 -0
  173. package/dist/storage/utils/file/file.d.ts +25 -0
  174. package/dist/storage/utils/file/get-file-status.d.ts +9 -0
  175. package/dist/storage/utils/file/has-content.d.ts +9 -0
  176. package/dist/storage/utils/file/index.d.ts +10 -0
  177. package/dist/storage/utils/file/is-expired.d.ts +8 -0
  178. package/dist/storage/utils/file/metadata.d.ts +18 -0
  179. package/dist/storage/utils/file/part-match.d.ts +11 -0
  180. package/dist/storage/utils/file/types.d.ts +37 -0
  181. package/dist/storage/utils/file/update-metadata.d.ts +10 -0
  182. package/dist/storage/utils/file/update-size.d.ts +10 -0
  183. package/dist/storage/vercel-blob/index.d.ts +4 -0
  184. package/dist/storage/vercel-blob/index.js +3 -0
  185. package/dist/storage/vercel-blob/types.d.ts +32 -0
  186. package/dist/storage/vercel-blob/vercel-blob-file.d.ts +16 -0
  187. package/dist/storage/vercel-blob/vercel-blob-meta-storage.d.ts +7 -0
  188. package/dist/storage/vercel-blob/vercel-blob-storage.d.ts +76 -0
  189. package/dist/transformer/audio-transformer.d.ts +128 -0
  190. package/dist/transformer/audio-transformer.js +278 -0
  191. package/dist/transformer/base-transformer.d.ts +63 -0
  192. package/dist/transformer/image-transformer.d.ts +571 -0
  193. package/dist/transformer/image-transformer.js +1100 -0
  194. package/dist/transformer/index.d.ts +5 -0
  195. package/dist/transformer/index.js +4 -0
  196. package/dist/transformer/media-transformer.d.ts +332 -0
  197. package/dist/transformer/types.d.ts +652 -0
  198. package/dist/transformer/utils.d.ts +33 -0
  199. package/dist/transformer/validation-error.d.ts +23 -0
  200. package/dist/transformer/video-transformer.d.ts +145 -0
  201. package/dist/transformer/video-transformer.js +310 -0
  202. package/dist/utils/cache.d.ts +32 -0
  203. package/dist/utils/chunked-upload.d.ts +65 -0
  204. package/dist/utils/detect-file-type.d.ts +28 -0
  205. package/dist/utils/errors.d.ts +74 -0
  206. package/dist/utils/file-path-url-matcher.d.ts +18 -0
  207. package/dist/utils/headers.d.ts +111 -0
  208. package/dist/utils/http.d.ts +92 -0
  209. package/dist/utils/locker.d.ts +26 -0
  210. package/dist/utils/pipes/stream-checksum.d.ts +47 -0
  211. package/dist/utils/pipes/stream-length.d.ts +22 -0
  212. package/dist/utils/primitives/get-last-one.d.ts +3 -0
  213. package/dist/utils/primitives/is-record.d.ts +2 -0
  214. package/dist/utils/primitives/map-values.d.ts +9 -0
  215. package/dist/utils/primitives/pick.d.ts +2 -0
  216. package/dist/utils/primitives/to-milliseconds.d.ts +12 -0
  217. package/dist/utils/primitives/to-seconds.d.ts +12 -0
  218. package/dist/utils/range-checksum.d.ts +33 -0
  219. package/dist/utils/range-hasher.d.ts +46 -0
  220. package/dist/utils/retry.d.ts +64 -0
  221. package/dist/utils/types.d.ts +108 -0
  222. package/dist/utils/validation-error.d.ts +22 -0
  223. package/dist/utils/validator.d.ts +36 -0
  224. package/package.json +5 -5
@@ -0,0 +1,353 @@
1
+ import { Readable } from "node:stream";
2
+ import type { Cache } from "../utils/cache.d.ts";
3
+ import type { ErrorResponses } from "../utils/errors.d.ts";
4
+ import Locker from "../utils/locker.d.ts";
5
+ import type { HttpError, Metrics } from "../utils/types.d.ts";
6
+ import { Validator } from "../utils/validator.d.ts";
7
+ import type MetaStorage from "./meta-storage.d.ts";
8
+ import type { BaseStorageOptions, BatchOperationResponse, PurgeList } from "./types.d.ts";
9
+ import type { File, FileInit, FilePart, FileQuery } from "./utils/index.d.ts";
10
+ import type { FileReturn } from "./utils/file/types.d.ts";
11
+ /**
12
+ * Default filename validation for cloud storage platforms.
13
+ * Permissive validation that only blocks dangerous patterns (path traversal, null bytes).
14
+ * Cloud storage platforms (S3, Azure, GCS) accept most special characters and handle URL encoding automatically.
15
+ */
16
+ export declare const defaultCloudStorageFileNameValidation: (name: string) => boolean;
17
+ /**
18
+ * Default filename validation for local filesystems.
19
+ * Stricter validation that blocks filesystem-incompatible characters.
20
+ */
21
+ export declare const defaultFilesystemFileNameValidation: (name: string) => boolean;
22
+ /**
23
+ * Abstract base class for all storage backends.
24
+ * @template TFile The file type used by this storage backend.
25
+ * @template TFileReturn The return type for file retrieval operations.
26
+ * @remarks
27
+ * ## Error Handling
28
+ *
29
+ * All storage operations follow consistent error handling patterns:
30
+ * - Operations throw `UploadError` with specific error codes (see ERRORS enum)
31
+ * - Common error codes: FILE_NOT_FOUND, GONE (expired), FILE_LOCKED, STORAGE_BUSY
32
+ * - Errors are normalized with storage class context via `normalizeError()`
33
+ * - Batch operations capture individual failures without stopping the batch
34
+ *
35
+ * ## Retry Behavior
36
+ *
37
+ * Storage implementations handle retries differently:
38
+ *
39
+ * ### Cloud Storage (S3, GCS, Azure, Netlify Blob)
40
+ * - Use configurable retry wrappers via `retryConfig` option
41
+ * - Default retryable status codes: 408, 429, 500, 502, 503, 504
42
+ * - Retry logic handles transient network errors and rate limiting
43
+ * - Custom `shouldRetry` functions can be provided for advanced retry logic
44
+ *
45
+ * ### Local Storage (DiskStorage)
46
+ * - No automatic retries (filesystem operations are typically immediate)
47
+ * - Errors are thrown directly for immediate feedback
48
+ *
49
+ * ## Operation Instrumentation
50
+ *
51
+ * All public operations are automatically instrumented via `instrumentOperation()`:
52
+ * - Metrics are recorded for operation count, duration, and errors
53
+ * - File sizes are tracked for operations that return file objects
54
+ * - Error metrics include error messages for debugging
55
+ *
56
+ * ## Metadata Caching
57
+ *
58
+ * File metadata is automatically cached to reduce storage API calls:
59
+ * - Cache is updated on save, delete, and get operations
60
+ * - Cache is invalidated when metadata is deleted
61
+ * - Implementations can override caching behavior if needed
62
+ */
63
+ export declare abstract class BaseStorage<TFile extends File = File, TFileReturn extends FileReturn = FileReturn> {
64
+ /**
65
+ * Hook called when a new file is created.
66
+ * @param file The newly created file object.
67
+ * @remarks This hook is called after file metadata is saved but before returning the file.
68
+ * Can be used for side effects like logging, notifications, or custom processing.
69
+ */
70
+ onCreate: (file: TFile) => Promise<void> | void;
71
+ /**
72
+ * Hook called when file metadata is updated.
73
+ * @param file The updated file object.
74
+ * @remarks This hook is called after metadata is updated and saved.
75
+ * Can be used for side effects like logging or custom processing.
76
+ */
77
+ onUpdate: (file: TFile) => Promise<void> | void;
78
+ /**
79
+ * Hook called when a file upload is completed.
80
+ * @param file The completed file object.
81
+ * @param response The response object that can be modified in place (headers, statusCode, body).
82
+ * @param request Optional request object for additional context.
83
+ * @remarks This hook is called when file status becomes "completed".
84
+ * The response object can be modified directly to add headers or change the status code.
85
+ */
86
+ onComplete: (file: TFile, response: unknown, request?: unknown) => Promise<void> | void;
87
+ /**
88
+ * Hook called when a file is deleted.
89
+ * @param file The deleted file object.
90
+ * @remarks This hook is called after the file is deleted but before returning.
91
+ * Can be used for side effects like cleanup or logging.
92
+ */
93
+ onDelete: (file: TFile) => Promise<void> | void;
94
+ /**
95
+ * Hook called when an error occurs during storage operations.
96
+ * @param error The HTTP error object that can be modified in place.
97
+ * @remarks This hook allows customizing error responses by modifying the error object.
98
+ * The error object can be modified to change headers, statusCode, or body properties.
99
+ * Error formatting happens in handlers after this hook is called.
100
+ */
101
+ onError: (error: HttpError) => Promise<void> | void;
102
+ isReady: boolean;
103
+ errorResponses: ErrorResponses;
104
+ cache: Cache<string, TFile>;
105
+ readonly logger?: Console;
106
+ readonly metrics: Metrics;
107
+ readonly genericConfig: BaseStorageOptions<TFile>;
108
+ maxMetadataSize: number;
109
+ checksumTypes: string[];
110
+ maxUploadSize: number;
111
+ protected expiration?: {
112
+ maxAge?: string | number;
113
+ purgeInterval?: string | number;
114
+ rolling?: boolean;
115
+ };
116
+ protected locker: Locker;
117
+ protected namingFunction: (file: TFile) => string;
118
+ protected validation: Validator<TFile>;
119
+ protected abstract meta: MetaStorage<TFile>;
120
+ protected assetFolder: string | undefined;
121
+ /**
122
+ * Limits the number of concurrent upload requests
123
+ */
124
+ protected concurrency?: number;
125
+ protected constructor(config: BaseStorageOptions<TFile>);
126
+ get tusExtension(): string[];
127
+ /**
128
+ * Validates a file against configured validation rules.
129
+ * @param file File object to validate.
130
+ * @returns Promise resolving to undefined if file is valid, throws ValidationError otherwise.
131
+ * @throws {ValidationError} If validation fails
132
+ */
133
+ validate(file: TFile): Promise<void>;
134
+ /**
135
+ * Checks if a file exists by querying its metadata.
136
+ * @param query File query containing the file ID to check.
137
+ * @param query.id File ID to check.
138
+ * @returns Promise resolving to true if file exists, false otherwise.
139
+ * @remarks This method does not throw errors - it returns false if the file is not found.
140
+ */
141
+ exists(query: FileQuery): Promise<boolean>;
142
+ /**
143
+ * Normalizes errors with storage-specific context.
144
+ * @param error The error to normalize.
145
+ * @returns Normalized HTTP error with storage class context added to the message.
146
+ * @remarks Errors are enhanced with the storage class name for better debugging.
147
+ */
148
+ normalizeError(error: Error): HttpError;
149
+ /**
150
+ * Gets the storage configuration.
151
+ * @returns The current storage configuration options.
152
+ */
153
+ get config(): BaseStorageOptions<TFile>;
154
+ /**
155
+ * Saves upload metadata to the metadata storage.
156
+ * @param file File object containing metadata to save.
157
+ * @returns Promise resolving to the saved file object.
158
+ * @remarks Updates timestamps and caches the file metadata.
159
+ */
160
+ saveMeta(file: TFile): Promise<TFile>;
161
+ /**
162
+ * Deletes upload metadata from the metadata storage.
163
+ * @param id File ID whose metadata should be deleted.
164
+ * @returns Promise resolving when metadata is deleted.
165
+ * @remarks Also removes the file from the cache.
166
+ */
167
+ deleteMeta(id: string): Promise<void>;
168
+ /**
169
+ * Retrieves upload metadata by file ID.
170
+ * @param id File ID to retrieve metadata for.
171
+ * @returns Promise resolving to the file metadata object.
172
+ * @throws {UploadError} If the file metadata cannot be found (ERRORS.FILE_NOT_FOUND).
173
+ * @remarks Caches the retrieved metadata for faster subsequent access.
174
+ */
175
+ getMeta(id: string): Promise<TFile>;
176
+ /**
177
+ * Checks if a file has expired and deletes it if so.
178
+ * @param file File object to check for expiration.
179
+ * @returns Promise resolving to the file object if not expired.
180
+ * @throws {UploadError} If the file has expired (ERRORS.GONE).
181
+ * @remarks If the file is expired, it is automatically deleted and the metadata is removed.
182
+ */
183
+ checkIfExpired(file: TFile): Promise<TFile>;
184
+ /**
185
+ * Searches for and purges expired uploads.
186
+ * @param maxAge Maximum age of files to keep (files older than this will be purged).
187
+ * Can be a number (milliseconds) or string (e.g., "1h", "30m", "7d").
188
+ * If not provided, uses the expiration.maxAge from configuration.
189
+ * @returns Promise resolving to a list of purged files.
190
+ * @remarks
191
+ * Errors during individual file deletions are logged but do not stop the purge process.
192
+ * Files with corrupted metadata are skipped with a warning.
193
+ * Uses rolling expiration if configured (based on modifiedAt) or fixed expiration (based on createdAt).
194
+ */
195
+ purge(maxAge?: number | string): Promise<PurgeList>;
196
+ /**
197
+ * Gets an uploaded file by ID.
198
+ * @param query File query containing the file ID to retrieve.
199
+ * @param query.id File ID to retrieve.
200
+ * @returns Promise resolving to the file data including content.
201
+ * @throws {UploadError} If the file cannot be found (ERRORS.FILE_NOT_FOUND) or has expired (ERRORS.GONE).
202
+ * @remarks This method loads the entire file content into memory. For large files, use getStream() instead.
203
+ */
204
+ abstract get({ id }: FileQuery): Promise<TFileReturn>;
205
+ /**
206
+ * Gets an uploaded file as a readable stream for efficient large file handling.
207
+ * @param query File query containing the file ID to stream.
208
+ * @param query.id File ID to stream.
209
+ * @returns Promise resolving to an object containing the stream, headers, and size.
210
+ * @throws {UploadError} If the file cannot be found (ERRORS.FILE_NOT_FOUND) or has expired (ERRORS.GONE).
211
+ * @remarks
212
+ * Default implementation falls back to get() and creates a stream from the buffer.
213
+ * Storage implementations should override this for better streaming performance.
214
+ * Headers include Content-Type, Content-Length, ETag, and Last-Modified.
215
+ */
216
+ getStream({ id }: FileQuery): Promise<{
217
+ headers?: Record<string, string>;
218
+ size?: number;
219
+ stream: Readable;
220
+ }>;
221
+ /**
222
+ * Retrieves a list of uploaded files.
223
+ * @param _limit Maximum number of files to return (default: 1000).
224
+ * @returns Promise resolving to an array of file metadata objects.
225
+ * @throws {Error} If not implemented by the storage backend.
226
+ * @remarks Storage implementations must override this method.
227
+ */
228
+ list(_limit?: number): Promise<TFile[]>;
229
+ /**
230
+ * Updates file metadata with user-provided key-value pairs.
231
+ * @param query File query containing the file ID to update.
232
+ * @param query.id File ID to update.
233
+ * @param metadata Partial file object containing fields to update.
234
+ * @returns Promise resolving to the updated file object.
235
+ * @throws {UploadError} If the file cannot be found (ERRORS.FILE_NOT_FOUND).
236
+ * @remarks
237
+ * Supports TTL (time-to-live) option: if metadata contains a 'ttl' field,
238
+ * it will be converted to an 'expiredAt' timestamp.
239
+ * TTL can be a number (milliseconds) or string (e.g., "1h", "30m", "7d").
240
+ */
241
+ update({ id }: FileQuery, metadata: Partial<File>): Promise<TFile>;
242
+ /**
243
+ * Creates a new upload and saves its metadata.
244
+ * @param file File initialization configuration.
245
+ * @returns Promise resolving to the created file object.
246
+ */
247
+ abstract create(file: FileInit): Promise<TFile>;
248
+ /**
249
+ * Writes part and/or returns status of an upload.
250
+ * @param part File part, query, or full file object to write.
251
+ * @returns Promise resolving to the updated file object.
252
+ */
253
+ abstract write(part: FilePart | FileQuery | TFile): Promise<TFile>;
254
+ /**
255
+ * Deletes an upload and its metadata.
256
+ * @param query File query containing the file ID to delete.
257
+ * @param query.id File ID to delete.
258
+ * @returns Promise resolving to the deleted file object with status: "deleted".
259
+ * @throws {UploadError} If the file metadata cannot be found.
260
+ */
261
+ abstract delete(query: FileQuery): Promise<TFile>;
262
+ /**
263
+ * Copies an upload file to a new location.
264
+ * @param name Source file name/ID.
265
+ * @param destination Destination file name/ID.
266
+ * @param options Optional copy options including storage class.
267
+ * @returns Promise resolving to the copied file object.
268
+ * @throws {UploadError} If the source file cannot be found.
269
+ */
270
+ abstract copy(name: string, destination: string, options?: {
271
+ storageClass?: string;
272
+ }): Promise<TFile>;
273
+ /**
274
+ * Moves an upload file to a new location.
275
+ * @param name Source file name/ID.
276
+ * @param destination Destination file name/ID.
277
+ * @returns Promise resolving to the moved file object.
278
+ * @throws {UploadError} If the source file cannot be found.
279
+ */
280
+ abstract move(name: string, destination: string): Promise<TFile>;
281
+ /**
282
+ * Deletes multiple files in a single batch operation.
283
+ * @param ids Array of file IDs to delete.
284
+ * @returns Promise resolving to batch operation response with successful and failed deletions.
285
+ * @remarks
286
+ * Processes all deletions in parallel using Promise.allSettled.
287
+ * Individual failures do not stop the batch operation.
288
+ * Each deletion is wrapped in error handling to capture failures.
289
+ * Metrics are recorded for the batch operation and individual failures.
290
+ * Returns both successful and failed operations with detailed error information.
291
+ */
292
+ deleteBatch(ids: string[]): Promise<BatchOperationResponse<TFile>>;
293
+ /**
294
+ * Copies multiple files in a single batch operation.
295
+ * @param operations Array of copy operations, each containing:
296
+ * source: Source file ID.
297
+ * destination: Destination file ID or path.
298
+ * options: Optional copy options including storage class.
299
+ * @returns Promise resolving to batch operation response with successful and failed copies.
300
+ * @remarks
301
+ * Processes all copies in parallel using Promise.allSettled.
302
+ * Individual failures do not stop the batch operation.
303
+ * Each copy operation is wrapped in error handling to capture failures.
304
+ * Metrics are recorded for the batch operation and individual failures.
305
+ * Returns both successful and failed operations with detailed error information.
306
+ */
307
+ copyBatch(operations: {
308
+ destination: string;
309
+ options?: {
310
+ storageClass?: string;
311
+ };
312
+ source: string;
313
+ }[]): Promise<BatchOperationResponse<TFile>>;
314
+ /**
315
+ * Moves multiple files in a single batch operation.
316
+ * @param operations Array of move operations, each containing:
317
+ * source: Source file ID.
318
+ * destination: Destination file ID or path.
319
+ * @returns Promise resolving to batch operation response with successful and failed moves.
320
+ * @remarks
321
+ * Processes all moves in parallel using Promise.allSettled.
322
+ * Individual failures do not stop the batch operation.
323
+ * Each move operation is wrapped in error handling to capture failures.
324
+ * Metrics are recorded for the batch operation and individual failures.
325
+ * Returns both successful and failed operations with detailed error information.
326
+ */
327
+ moveBatch(operations: {
328
+ destination: string;
329
+ source: string;
330
+ }[]): Promise<BatchOperationResponse<TFile>>;
331
+ /**
332
+ * Prevent upload from being accessed by multiple requests
333
+ */
334
+ protected lock(key: string): Promise<string>;
335
+ protected unlock(key: string): Promise<void>;
336
+ protected isUnsupportedChecksum(algorithm?: string): boolean;
337
+ protected startAutoPurge(purgeInterval: number): void;
338
+ protected updateTimestamps(file: TFile): TFile;
339
+ /**
340
+ * Instruments a storage operation with metrics and error tracking.
341
+ * @param operation Operation name (e.g., "create", "delete", "copy").
342
+ * @param function_ The operation function to execute.
343
+ * @param attributes Additional attributes to include in metrics.
344
+ * @returns Promise resolving to the operation result.
345
+ * @throws Re-throws any errors from the operation function.
346
+ * @remarks
347
+ * Records operation count, duration, and error metrics.
348
+ * Tracks file sizes for operations returning file objects.
349
+ * Error metrics include error messages for debugging.
350
+ * All public methods should use this wrapper for consistent instrumentation.
351
+ */
352
+ protected instrumentOperation<T>(operation: string, function_: () => Promise<T>, attributes?: Record<string, string | number>): Promise<T>;
353
+ }
@@ -0,0 +1,221 @@
1
+ import type { Readable } from "node:stream";
2
+ import type { Cache } from "../utils/cache.d.ts";
3
+ import type { RetryConfig } from "../utils/retry.d.ts";
4
+ import type { HttpError, HttpErrorBody, Metrics, Validation } from "../utils/types.d.ts";
5
+ import type { LocalMetaStorageOptions } from "./local/local-meta-storage.d.ts";
6
+ import type MetaStorage from "./meta-storage.d.ts";
7
+ import type { File, FileInit, FilePart, FileQuery, FileReturn, UploadFile } from "./utils/index.d.ts";
8
+ export interface MetaStorageOptions {
9
+ logger?: Console;
10
+ prefix?: string;
11
+ suffix?: string;
12
+ }
13
+ export type OnCreate<TFile extends File = File> = (file: TFile) => Promise<void> | void;
14
+ export type OnUpdate<TFile extends File = File> = (file: TFile) => Promise<void> | void;
15
+ export type OnComplete<TFile extends File = File, TResponse = unknown, TRequest = unknown> = (file: TFile, response: TResponse, request?: TRequest) => Promise<void> | void;
16
+ export type OnDelete<TFile extends File = File> = (file: TFile) => Promise<void> | void;
17
+ export type OnError<TBody = HttpErrorBody> = (error: HttpError<TBody>) => Promise<void> | void;
18
+ export interface PurgeList {
19
+ items: UploadFile[];
20
+ maxAgeMs: number;
21
+ }
22
+ export interface ExpirationOptions {
23
+ /**
24
+ * Age of the upload, after which it is considered expired and can be deleted
25
+ */
26
+ maxAge: number | string;
27
+ /**
28
+ * Auto purging interval for expired upload
29
+ */
30
+ purgeInterval?: number | string;
31
+ /**
32
+ * Auto prolong expiring upload
33
+ */
34
+ rolling?: boolean;
35
+ }
36
+ export interface BaseStorageOptions<T extends File = File> extends GenericStorageConfig {
37
+ /** Allowed MIME types */
38
+ allowMIME?: string[];
39
+ /** The full path of the folder where the uploaded asset will be stored. */
40
+ assetFolder?: string;
41
+ /** Cache instance to use for caching */
42
+ cache?: Cache;
43
+ /**
44
+ * Automatic cleaning of abandoned and completed upload
45
+ * @example
46
+ * ```ts
47
+ * app.use(
48
+ * '/upload',
49
+ * Upload.upload({
50
+ * directory: 'upload',
51
+ * expiration: { maxAge: '6h', purgeInterval: '30min' },
52
+ * onComplete
53
+ * })
54
+ * );
55
+ * ```
56
+ */
57
+ expiration?: ExpirationOptions;
58
+ /** File naming function */
59
+ filename?: (file: T) => string;
60
+ /**
61
+ * File name validation function.
62
+ * Returns true if the filename is valid, false otherwise.
63
+ * @default Cloud storage platforms: permissive (only blocks path traversal and null bytes)
64
+ * @default DiskStorage: strict (blocks filesystem-incompatible characters)
65
+ * @example
66
+ * ```ts
67
+ * fileNameValidation: (name: string) => {
68
+ * // Custom validation logic
69
+ * return name.length > 0 && !name.includes('../');
70
+ * }
71
+ * ```
72
+ */
73
+ fileNameValidation?: (name: string) => boolean;
74
+ /** Logger injection */
75
+ logger?: Console;
76
+ /** Limiting the size of custom metadata */
77
+ maxMetadataSize?: number | string;
78
+ /** File size limit */
79
+ maxUploadSize?: number | string;
80
+ /** Provide custom meta storage */
81
+ metaStorage?: MetaStorage<T>;
82
+ /** Metrics injection for observability */
83
+ metrics?: Metrics;
84
+ /** Callback function that is called when an upload is completed */
85
+ onComplete?: OnComplete<T>;
86
+ /** Callback function that is called when a new upload is created */
87
+ onCreate?: OnCreate<T>;
88
+ /** Callback function that is called when an upload is cancelled */
89
+ onDelete?: OnDelete<T>;
90
+ /** Customize error response */
91
+ onError?: OnError;
92
+ /** Callback function that is called when an upload is updated */
93
+ onUpdate?: OnUpdate<T>;
94
+ /** Force relative URI in Location header */
95
+ useRelativeLocation?: boolean;
96
+ /** Upload validation options */
97
+ validation?: Validation<T>;
98
+ }
99
+ export type DiskStorageOptions<T extends File> = BaseStorageOptions<T> & {
100
+ /**
101
+ * Uploads directory.
102
+ */
103
+ directory: string;
104
+ /**
105
+ * Configuring metafile storage on the local disk
106
+ * @example
107
+ * ```ts
108
+ * const storage = new DiskStorage({
109
+ * directory: 'upload',
110
+ * metaStorageConfig: { directory: '/tmp/upload-metafiles', prefix: '.' }
111
+ * });
112
+ * ```
113
+ */
114
+ metaStorageConfig?: LocalMetaStorageOptions;
115
+ };
116
+ export type DiskStorageWithChecksumOptions<T extends File> = DiskStorageOptions<T> & {
117
+ /**
118
+ * Enable/disable file/range checksum calculation
119
+ */
120
+ checksum?: boolean | "md5" | "sha1";
121
+ };
122
+ /**
123
+ * Unified storage configuration
124
+ */
125
+ export interface GenericStorageConfig {
126
+ /** Allow additional properties for specific storage backends */
127
+ [key: string]: unknown;
128
+ /** Base path/prefix for all operations */
129
+ basePath?: string;
130
+ /** Cache TTL */
131
+ cacheTTL?: number;
132
+ /** Supported checksum algorithms */
133
+ checksumTypes?: string[];
134
+ /** Logger instance */
135
+ logger?: Console;
136
+ /** Maximum file size */
137
+ maxFileSize?: number | string;
138
+ /** Metrics instance for observability */
139
+ metrics?: Metrics;
140
+ /** Retry configuration for transient failures */
141
+ retryConfig?: RetryConfig;
142
+ }
143
+ /**
144
+ * Batch operation result for a single file
145
+ */
146
+ export interface BatchOperationResult<T extends File = File> {
147
+ /** Error message if operation failed */
148
+ error?: string;
149
+ /** File that was successfully operated on */
150
+ file?: T;
151
+ /** File ID */
152
+ id: string;
153
+ /** Whether the operation was successful */
154
+ success: boolean;
155
+ }
156
+ /**
157
+ * Response from batch operations (deleteBatch, copyBatch, moveBatch)
158
+ */
159
+ export interface BatchOperationResponse<T extends File = File> {
160
+ /** Failed operations with error details */
161
+ failed: {
162
+ error: string;
163
+ id: string;
164
+ }[];
165
+ /** Total number of failed operations */
166
+ failedCount: number;
167
+ /** Successfully processed files */
168
+ successful: T[];
169
+ /** Total number of successful operations */
170
+ successfulCount: number;
171
+ }
172
+ /**
173
+ * Generic storage operations that all backends should support
174
+ */
175
+ export interface GenericStorageOperations<T extends File = File, TReturn extends FileReturn = FileReturn> {
176
+ /** Copy a file */
177
+ copy: (source: string, destination: string, options?: {
178
+ storageClass?: string;
179
+ }) => Promise<T>;
180
+ /** Copy multiple files */
181
+ copyBatch?: (operations: {
182
+ destination: string;
183
+ options?: {
184
+ storageClass?: string;
185
+ };
186
+ source: string;
187
+ }[]) => Promise<BatchOperationResponse<T>>;
188
+ /** Create a new file upload */
189
+ create: (config: FileInit) => Promise<T>;
190
+ /** Delete a file */
191
+ delete: (query: FileQuery) => Promise<T>;
192
+ /** Delete multiple files */
193
+ deleteBatch?: (ids: string[]) => Promise<BatchOperationResponse<T>>;
194
+ /** Check if file exists */
195
+ exists: (query: FileQuery) => Promise<boolean>;
196
+ /** Get file data */
197
+ get: (query: FileQuery) => Promise<TReturn>;
198
+ /** Get file as a readable stream */
199
+ getStream?: (query: FileQuery) => Promise<{
200
+ headers?: Record<string, string>;
201
+ size?: number;
202
+ stream: Readable;
203
+ }>;
204
+ /** Get signed URL for upload (if supported) */
205
+ getUploadUrl?: (query: FileQuery, expiresIn?: number) => Promise<string>;
206
+ /** Get file URL (if supported) */
207
+ getUrl?: (query: FileQuery, expiresIn?: number) => Promise<string>;
208
+ /** List files */
209
+ list: (limit?: number) => Promise<T[]>;
210
+ /** Move a file */
211
+ move: (source: string, destination: string) => Promise<T>;
212
+ /** Move multiple files */
213
+ moveBatch?: (operations: {
214
+ destination: string;
215
+ source: string;
216
+ }[]) => Promise<BatchOperationResponse<T>>;
217
+ /** Update file metadata */
218
+ update: (query: FileQuery, metadata: Partial<T>) => Promise<T>;
219
+ /** Write data to a file */
220
+ write: (part: FilePart | FileQuery) => Promise<T>;
221
+ }
@@ -0,0 +1,25 @@
1
+ import type { Metadata } from "./metadata.d.ts";
2
+ import type { FileInit, UploadEventType } from "./types.d.ts";
3
+ type DateType = Date | number | string;
4
+ declare class File implements FileInit {
5
+ bytesWritten: number;
6
+ contentType: string;
7
+ originalName: string;
8
+ id: string;
9
+ metadata: Metadata;
10
+ name: string;
11
+ size?: number;
12
+ status?: UploadEventType;
13
+ expiredAt?: DateType;
14
+ createdAt?: DateType;
15
+ modifiedAt?: DateType;
16
+ hash?: {
17
+ algorithm: string;
18
+ value: string;
19
+ };
20
+ content?: Buffer;
21
+ ETag?: string;
22
+ constructor({ contentType, expiredAt, metadata, originalName, size }: FileInit);
23
+ }
24
+ export type UploadFile = Readonly<File>;
25
+ export default File;
@@ -0,0 +1,9 @@
1
+ import type File from "./file.d.ts";
2
+ import type { UploadEventType } from "./types.d.ts";
3
+ /**
4
+ * Determines the upload status of a file based on its current state.
5
+ * @param file File object to check status for
6
+ * @returns Upload event type: 'completed' if fully uploaded, 'part' if partially uploaded, 'created' if just started
7
+ */
8
+ declare const getFileStatus: (file: File) => UploadEventType;
9
+ export default getFileStatus;
@@ -0,0 +1,9 @@
1
+ import type { FilePart } from "./types.d.ts";
2
+ /**
3
+ * Type guard to check if a partial file part has valid content.
4
+ * Validates that the part has a valid start position and body stream.
5
+ * @param part Partial file part to check
6
+ * @returns True if the part has valid content (start >= 0 and body exists)
7
+ */
8
+ declare const hasContent: (part: Partial<FilePart>) => part is FilePart;
9
+ export default hasContent;
@@ -0,0 +1,10 @@
1
+ export type { UploadFile } from "./file.d.ts";
2
+ export { default as File } from "./file.d.ts";
3
+ export { default as getFileStatus } from "./get-file-status.d.ts";
4
+ export { default as hasContent } from "./has-content.d.ts";
5
+ export { default as isExpired } from "./is-expired.d.ts";
6
+ export { isMetadata, Metadata } from "./metadata.d.ts";
7
+ export { default as partMatch } from "./part-match.d.ts";
8
+ export type { Checksum, FileInit, FilePart, FileQuery, FileReturn, UploadEventType } from "./types.d.ts";
9
+ export { default as updateMetadata } from "./update-metadata.d.ts";
10
+ export { default as updateSize } from "./update-size.d.ts";
@@ -0,0 +1,8 @@
1
+ import type File from "./file.d.ts";
2
+ /**
3
+ * Checks if a file has expired based on its expiredAt timestamp.
4
+ * @param file File object to check expiration for
5
+ * @returns True if the file has expired, false otherwise
6
+ */
7
+ declare const isExpired: (file: File) => boolean;
8
+ export default isExpired;
@@ -0,0 +1,18 @@
1
+ export declare class Metadata {
2
+ [key: string]: unknown;
3
+ psize?: number | string;
4
+ pname?: string;
5
+ pfiletype?: string;
6
+ ptype?: string;
7
+ pmimeType?: string;
8
+ pcontentType?: string;
9
+ ptitle?: string;
10
+ pfilename?: string;
11
+ poriginalName?: string;
12
+ plastModified?: number | string;
13
+ }
14
+ export declare const isMetadata: (raw: unknown) => raw is Metadata;
15
+ export declare const validateKey: (key: string) => boolean;
16
+ export declare const validateValue: (value: string) => boolean;
17
+ export declare const stringifyMetadata: (metadata: NonNullable<Metadata>) => string;
18
+ export declare const parseMetadata: (string_?: string) => Metadata;
@@ -0,0 +1,11 @@
1
+ import type File from "./file.d.ts";
2
+ import type { FilePart } from "./types.d.ts";
3
+ /**
4
+ * Validates if a file part matches the expected file size and offset constraints.
5
+ * Checks that the part doesn't exceed file size boundaries.
6
+ * @param part Partial file part to validate
7
+ * @param file File object to validate against
8
+ * @returns True if the part is valid for the file, false otherwise
9
+ */
10
+ declare const partMatch: (part: Partial<FilePart>, file: File) => boolean;
11
+ export default partMatch;