@flystorage/file-storage 0.1.7 → 1.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.
package/changelog.md ADDED
@@ -0,0 +1,13 @@
1
+ # `@flystorage/file-storage`
2
+
3
+ ## 1.0.1
4
+
5
+ ### Fixes
6
+
7
+ - Corrected error message for `UnableToGetChecksum` error.
8
+
9
+ ## 1.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - First 1.0.0 release
@@ -2,14 +2,22 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.checksumFromStream = checksumFromStream;
4
4
  const crypto_1 = require("crypto");
5
+ const util_1 = require("util");
6
+ const encoder = new util_1.TextEncoder();
5
7
  async function checksumFromStream(stream, options) {
6
- return new Promise(async (resolve, reject) => {
8
+ return new Promise((resolve, reject) => {
7
9
  const hash = (0, crypto_1.createHash)(options.algo ?? 'md5');
8
- stream.on('error', reject);
9
- stream.pipe(hash, { end: false });
10
- stream.on('end', () => {
11
- hash.end();
12
- resolve(hash.digest(options.encoding ?? 'hex'));
10
+ stream.on('error', err => reject(err));
11
+ stream.on('data', (chunk) => {
12
+ const type = typeof chunk;
13
+ if (type === 'string') {
14
+ chunk = encoder.encode(chunk);
15
+ }
16
+ else if (type === 'number') {
17
+ chunk = new Uint8Array([chunk]);
18
+ }
19
+ hash.update(chunk);
13
20
  });
21
+ stream.on('end', () => resolve(hash.digest(options.encoding ?? 'hex')));
14
22
  });
15
23
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.UnableToListDirectory = exports.UnableToCheckDirectoryExistence = exports.UnableToCheckFileExistence = exports.UnableToDeleteFile = exports.UnableToDeleteDirectory = exports.UnableToCreateDirectory = exports.UnableToGetStat = exports.UnableToMoveFile = exports.UnableToCopyFile = exports.UnableToGetTemporaryUrl = exports.UnableToGetPublicUrl = exports.UnableToGetVisibility = exports.UnableToSetVisibility = exports.UnableToReadFile = exports.UnableToWriteFile = exports.UnableToGetFileSize = exports.UnableToGetLastModified = exports.UnableToGetMimeType = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = void 0;
3
+ exports.UnableToListDirectory = exports.UnableToCheckDirectoryExistence = exports.UnableToCheckFileExistence = exports.UnableToDeleteFile = exports.UnableToDeleteDirectory = exports.UnableToCreateDirectory = exports.UnableToGetStat = exports.UnableToMoveFile = exports.UnableToCopyFile = exports.UnableToPrepareUploadRequest = exports.UnableToGetTemporaryUrl = exports.UnableToGetPublicUrl = exports.UnableToGetVisibility = exports.UnableToSetVisibility = exports.UnableToReadFile = exports.UnableToWriteFile = exports.UnableToGetFileSize = exports.UnableToGetLastModified = exports.UnableToGetMimeType = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = void 0;
4
4
  exports.errorToMessage = errorToMessage;
5
5
  function errorToMessage(error) {
6
6
  return error instanceof Error ? error.message : String(error);
@@ -28,11 +28,14 @@ class ChecksumIsNotAvailable extends FlystorageError {
28
28
  this.algo = algo;
29
29
  }
30
30
  static checksumNotSupported = (algo, { context = {}, cause = undefined } = {}) => new ChecksumIsNotAvailable(`Checksum algo "${algo}" is not supported`, algo, { ...context, algo }, cause);
31
+ static isErrorOfType(error) {
32
+ return (typeof error === 'object' && error.code === 'flystorage.checksum_not_supported');
33
+ }
31
34
  }
32
35
  exports.ChecksumIsNotAvailable = ChecksumIsNotAvailable;
33
36
  class UnableToGetChecksum extends FlystorageError {
34
37
  code = 'flystorage.unable_to_get_checksum';
35
- static because = (reason, { context = {}, cause = undefined }) => new UnableToGetChecksum(`Unable to write the file. Reason: ${reason}`, context, cause);
38
+ static because = (reason, { context = {}, cause = undefined }) => new UnableToGetChecksum(`Unable to get checksum for file. Reason: ${reason}`, context, cause);
36
39
  }
37
40
  exports.UnableToGetChecksum = UnableToGetChecksum;
38
41
  class UnableToGetMimeType extends FlystorageError {
@@ -80,6 +83,11 @@ class UnableToGetTemporaryUrl extends FlystorageError {
80
83
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetTemporaryUrl(`Unable to get temporary URL. Reason: ${reason}`, context, cause);
81
84
  }
82
85
  exports.UnableToGetTemporaryUrl = UnableToGetTemporaryUrl;
86
+ class UnableToPrepareUploadRequest extends FlystorageError {
87
+ code = 'flystorage.unable_to_prepare_upload_request';
88
+ static because = (reason, { context = {}, cause = undefined }) => new UnableToGetTemporaryUrl(`Unable to prepare upload request. Reason: ${reason}`, context, cause);
89
+ }
90
+ exports.UnableToPrepareUploadRequest = UnableToPrepareUploadRequest;
83
91
  class UnableToCopyFile extends FlystorageError {
84
92
  code = 'flystorage.unable_to_copy_file';
85
93
  static because = (reason, { context = {}, cause = undefined }) => new UnableToCopyFile(`Unable to copy file. Reason: ${reason}`, context, cause);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FileStorage = exports.DirectoryListing = void 0;
3
+ exports.PreparedUploadsAreNotSupported = exports.FileStorage = exports.DirectoryListing = void 0;
4
4
  exports.isFile = isFile;
5
5
  exports.isDirectory = isDirectory;
6
6
  exports.toReadable = toReadable;
@@ -11,10 +11,9 @@ exports.readableToString = readableToString;
11
11
  exports.readableToUint8Array = readableToUint8Array;
12
12
  const stream_1 = require("stream");
13
13
  const checksum_from_stream_js_1 = require("./checksum-from-stream.js");
14
- const errors = require("./errors.js");
15
- const errors_js_1 = require("./errors.js");
16
14
  const path_normalizer_js_1 = require("./path-normalizer.js");
17
15
  const util_1 = require("util");
16
+ const errors_js_1 = require("./errors.js");
18
17
  function isFile(stat) {
19
18
  return stat.isFile;
20
19
  }
@@ -55,7 +54,7 @@ class DirectoryListing {
55
54
  }
56
55
  }
57
56
  catch (error) {
58
- throw errors.UnableToListDirectory.because(errors.errorToMessage(error), { cause: error, context: { path: this.path, deep: this.deep } });
57
+ throw errors_js_1.UnableToListDirectory.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path: this.path, deep: this.deep } });
59
58
  }
60
59
  }
61
60
  }
@@ -86,7 +85,7 @@ class FileStorage {
86
85
  await closeReadable(body);
87
86
  }
88
87
  catch (error) {
89
- throw errors.UnableToWriteFile.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
88
+ throw errors_js_1.UnableToWriteFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
90
89
  }
91
90
  }
92
91
  async read(path) {
@@ -94,7 +93,7 @@ class FileStorage {
94
93
  return stream_1.Readable.from(await this.adapter.read(this.pathNormalizer.normalizePath(path)));
95
94
  }
96
95
  catch (error) {
97
- throw errors.UnableToReadFile.because(errors.errorToMessage(error), { cause: error, context: { path } });
96
+ throw errors_js_1.UnableToReadFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
98
97
  }
99
98
  }
100
99
  async readToString(path) {
@@ -111,7 +110,7 @@ class FileStorage {
111
110
  await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path));
112
111
  }
113
112
  catch (error) {
114
- throw errors.UnableToDeleteFile.because(errors.errorToMessage(error), { cause: error, context: { path } });
113
+ throw errors_js_1.UnableToDeleteFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
115
114
  }
116
115
  }
117
116
  async createDirectory(path, options = {}) {
@@ -119,7 +118,7 @@ class FileStorage {
119
118
  return await this.adapter.createDirectory(this.pathNormalizer.normalizePath(path), { ...this.options.visibility, ...options });
120
119
  }
121
120
  catch (error) {
122
- throw errors.UnableToCreateDirectory.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
121
+ throw errors_js_1.UnableToCreateDirectory.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
123
122
  }
124
123
  }
125
124
  async deleteDirectory(path) {
@@ -127,7 +126,7 @@ class FileStorage {
127
126
  return await this.adapter.deleteDirectory(this.pathNormalizer.normalizePath(path));
128
127
  }
129
128
  catch (error) {
130
- throw errors.UnableToDeleteDirectory.because(errors.errorToMessage(error), { cause: error, context: { path } });
129
+ throw errors_js_1.UnableToDeleteDirectory.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
131
130
  }
132
131
  }
133
132
  async stat(path) {
@@ -135,7 +134,7 @@ class FileStorage {
135
134
  return await this.adapter.stat(this.pathNormalizer.normalizePath(path));
136
135
  }
137
136
  catch (error) {
138
- throw errors.UnableToGetStat.because(errors.errorToMessage(error), { cause: error, context: { path } });
137
+ throw errors_js_1.UnableToGetStat.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
139
138
  }
140
139
  }
141
140
  async moveFile(from, to, options = {}) {
@@ -143,7 +142,7 @@ class FileStorage {
143
142
  await this.adapter.moveFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), { ...this.options.visibility, ...this.options.moves, ...options });
144
143
  }
145
144
  catch (error) {
146
- throw errors.UnableToMoveFile.because(errors.errorToMessage(error), { cause: error, context: { from, to } });
145
+ throw errors_js_1.UnableToMoveFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { from, to } });
147
146
  }
148
147
  }
149
148
  async copyFile(from, to, options = {}) {
@@ -151,7 +150,7 @@ class FileStorage {
151
150
  await this.adapter.copyFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), { ...this.options.visibility, ...this.options.copies, ...options });
152
151
  }
153
152
  catch (error) {
154
- throw errors.UnableToCopyFile.because(errors.errorToMessage(error), { cause: error, context: { from, to } });
153
+ throw errors_js_1.UnableToCopyFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { from, to } });
155
154
  }
156
155
  }
157
156
  /**
@@ -165,7 +164,7 @@ class FileStorage {
165
164
  return await this.adapter.changeVisibility(this.pathNormalizer.normalizePath(path), visibility);
166
165
  }
167
166
  catch (error) {
168
- throw errors.UnableToSetVisibility.because(errors.errorToMessage(error), { cause: error, context: { path, visibility } });
167
+ throw errors_js_1.UnableToSetVisibility.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, visibility } });
169
168
  }
170
169
  }
171
170
  async visibility(path) {
@@ -173,7 +172,7 @@ class FileStorage {
173
172
  return await this.adapter.visibility(this.pathNormalizer.normalizePath(path));
174
173
  }
175
174
  catch (error) {
176
- throw errors.UnableToGetVisibility.because(errors.errorToMessage(error), { cause: error, context: { path } });
175
+ throw errors_js_1.UnableToGetVisibility.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
177
176
  }
178
177
  }
179
178
  async fileExists(path) {
@@ -181,7 +180,7 @@ class FileStorage {
181
180
  return await this.adapter.fileExists(this.pathNormalizer.normalizePath(path));
182
181
  }
183
182
  catch (error) {
184
- throw errors.UnableToCheckFileExistence.because(errors.errorToMessage(error), { cause: error, context: { path } });
183
+ throw errors_js_1.UnableToCheckFileExistence.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
185
184
  }
186
185
  }
187
186
  list(path, { deep = false } = {}) {
@@ -192,14 +191,14 @@ class FileStorage {
192
191
  if (isFile(stat)) {
193
192
  return stat;
194
193
  }
195
- throw errors.UnableToGetStat.noFileStatResolved({ context: { path } });
194
+ throw errors_js_1.UnableToGetStat.noFileStatResolved({ context: { path } });
196
195
  }
197
196
  async directoryExists(path) {
198
197
  try {
199
198
  return await this.adapter.directoryExists(this.pathNormalizer.normalizePath(path));
200
199
  }
201
200
  catch (error) {
202
- throw errors.UnableToCheckDirectoryExistence.because(errors.errorToMessage(error), { cause: error, context: { path } });
201
+ throw errors_js_1.UnableToCheckDirectoryExistence.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
203
202
  }
204
203
  }
205
204
  async publicUrl(path, options = {}) {
@@ -207,7 +206,7 @@ class FileStorage {
207
206
  return await this.adapter.publicUrl(this.pathNormalizer.normalizePath(path), { ...this.options.publicUrls, ...options });
208
207
  }
209
208
  catch (error) {
210
- throw errors.UnableToGetPublicUrl.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
209
+ throw errors_js_1.UnableToGetPublicUrl.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
211
210
  }
212
211
  }
213
212
  async temporaryUrl(path, options) {
@@ -215,7 +214,26 @@ class FileStorage {
215
214
  return await this.adapter.temporaryUrl(this.pathNormalizer.normalizePath(path), { ...this.options.temporaryUrls, ...options });
216
215
  }
217
216
  catch (error) {
218
- throw errors.UnableToGetTemporaryUrl.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
217
+ throw errors_js_1.UnableToGetTemporaryUrl.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
218
+ }
219
+ }
220
+ async prepareUpload(path, options) {
221
+ if (this.options.preparedUploadStrategy !== undefined) {
222
+ try {
223
+ return this.options.preparedUploadStrategy.prepareUpload(path, options);
224
+ }
225
+ catch (error) {
226
+ throw errors_js_1.UnableToPrepareUploadRequest.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
227
+ }
228
+ }
229
+ if (typeof this.adapter.prepareUpload !== 'function') {
230
+ throw new Error('The used adapter does not support prepared uploads.');
231
+ }
232
+ try {
233
+ return await this.adapter.prepareUpload(this.pathNormalizer.normalizePath(path), { ...this.options.uploadRequest, ...options });
234
+ }
235
+ catch (error) {
236
+ throw errors_js_1.UnableToPrepareUploadRequest.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
219
237
  }
220
238
  }
221
239
  async checksum(path, options = {}) {
@@ -223,10 +241,10 @@ class FileStorage {
223
241
  return await this.adapter.checksum(this.pathNormalizer.normalizePath(path), { ...this.options.checksums, ...options });
224
242
  }
225
243
  catch (error) {
226
- if (error instanceof errors_js_1.ChecksumIsNotAvailable) {
244
+ if (errors_js_1.ChecksumIsNotAvailable.isErrorOfType(error)) {
227
245
  return this.calculateChecksum(path, options);
228
246
  }
229
- throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
247
+ throw errors_js_1.UnableToGetChecksum.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
230
248
  }
231
249
  }
232
250
  async mimeType(path, options = {}) {
@@ -234,7 +252,7 @@ class FileStorage {
234
252
  return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path), { ...this.options.mimeTypes, ...options });
235
253
  }
236
254
  catch (error) {
237
- throw errors.UnableToGetMimeType.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
255
+ throw errors_js_1.UnableToGetMimeType.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
238
256
  }
239
257
  }
240
258
  async lastModified(path) {
@@ -242,7 +260,7 @@ class FileStorage {
242
260
  return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path));
243
261
  }
244
262
  catch (error) {
245
- throw errors.UnableToGetLastModified.because(errors.errorToMessage(error), { cause: error, context: { path } });
263
+ throw errors_js_1.UnableToGetLastModified.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
246
264
  }
247
265
  }
248
266
  async fileSize(path) {
@@ -250,7 +268,7 @@ class FileStorage {
250
268
  return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path));
251
269
  }
252
270
  catch (error) {
253
- throw errors.UnableToGetFileSize.because(errors.errorToMessage(error), { cause: error, context: { path } });
271
+ throw errors_js_1.UnableToGetFileSize.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
254
272
  }
255
273
  }
256
274
  async calculateChecksum(path, options) {
@@ -258,7 +276,7 @@ class FileStorage {
258
276
  return await (0, checksum_from_stream_js_1.checksumFromStream)(await this.read(path), options);
259
277
  }
260
278
  catch (error) {
261
- throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
279
+ throw errors_js_1.UnableToGetChecksum.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
262
280
  }
263
281
  }
264
282
  }
@@ -314,3 +332,9 @@ function concatUint8Arrays(input) {
314
332
  });
315
333
  return output;
316
334
  }
335
+ class PreparedUploadsAreNotSupported {
336
+ prepareUpload() {
337
+ throw new Error('The used adapter does not support prepared uploads.');
338
+ }
339
+ }
340
+ exports.PreparedUploadsAreNotSupported = PreparedUploadsAreNotSupported;
@@ -1,12 +1,20 @@
1
1
  import { createHash } from 'crypto';
2
+ import { TextEncoder } from 'util';
3
+ const encoder = new TextEncoder();
2
4
  export async function checksumFromStream(stream, options) {
3
- return new Promise(async (resolve, reject) => {
5
+ return new Promise((resolve, reject) => {
4
6
  const hash = createHash(options.algo ?? 'md5');
5
- stream.on('error', reject);
6
- stream.pipe(hash, { end: false });
7
- stream.on('end', () => {
8
- hash.end();
9
- resolve(hash.digest(options.encoding ?? 'hex'));
7
+ stream.on('error', err => reject(err));
8
+ stream.on('data', (chunk) => {
9
+ const type = typeof chunk;
10
+ if (type === 'string') {
11
+ chunk = encoder.encode(chunk);
12
+ }
13
+ else if (type === 'number') {
14
+ chunk = new Uint8Array([chunk]);
15
+ }
16
+ hash.update(chunk);
10
17
  });
18
+ stream.on('end', () => resolve(hash.digest(options.encoding ?? 'hex')));
11
19
  });
12
20
  }
@@ -23,10 +23,13 @@ export class ChecksumIsNotAvailable extends FlystorageError {
23
23
  this.algo = algo;
24
24
  }
25
25
  static checksumNotSupported = (algo, { context = {}, cause = undefined } = {}) => new ChecksumIsNotAvailable(`Checksum algo "${algo}" is not supported`, algo, { ...context, algo }, cause);
26
+ static isErrorOfType(error) {
27
+ return (typeof error === 'object' && error.code === 'flystorage.checksum_not_supported');
28
+ }
26
29
  }
27
30
  export class UnableToGetChecksum extends FlystorageError {
28
31
  code = 'flystorage.unable_to_get_checksum';
29
- static because = (reason, { context = {}, cause = undefined }) => new UnableToGetChecksum(`Unable to write the file. Reason: ${reason}`, context, cause);
32
+ static because = (reason, { context = {}, cause = undefined }) => new UnableToGetChecksum(`Unable to get checksum for file. Reason: ${reason}`, context, cause);
30
33
  }
31
34
  export class UnableToGetMimeType extends FlystorageError {
32
35
  code = 'flystorage.unable_to_get_mimetype';
@@ -64,6 +67,10 @@ export class UnableToGetTemporaryUrl extends FlystorageError {
64
67
  code = 'flystorage.unable_to_get_temporary_url';
65
68
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetTemporaryUrl(`Unable to get temporary URL. Reason: ${reason}`, context, cause);
66
69
  }
70
+ export class UnableToPrepareUploadRequest extends FlystorageError {
71
+ code = 'flystorage.unable_to_prepare_upload_request';
72
+ static because = (reason, { context = {}, cause = undefined }) => new UnableToGetTemporaryUrl(`Unable to prepare upload request. Reason: ${reason}`, context, cause);
73
+ }
67
74
  export class UnableToCopyFile extends FlystorageError {
68
75
  code = 'flystorage.unable_to_copy_file';
69
76
  static because = (reason, { context = {}, cause = undefined }) => new UnableToCopyFile(`Unable to copy file. Reason: ${reason}`, context, cause);
@@ -1,9 +1,8 @@
1
1
  import { Readable } from 'stream';
2
2
  import { checksumFromStream } from './checksum-from-stream.js';
3
- import * as errors from './errors.js';
4
- import { ChecksumIsNotAvailable } from './errors.js';
5
3
  import { PathNormalizerV1 } from './path-normalizer.js';
6
4
  import { TextEncoder } from "util";
5
+ import { ChecksumIsNotAvailable, errorToMessage, UnableToCheckDirectoryExistence, UnableToCheckFileExistence, UnableToCopyFile, UnableToCreateDirectory, UnableToDeleteDirectory, UnableToDeleteFile, UnableToGetChecksum, UnableToGetFileSize, UnableToGetLastModified, UnableToGetMimeType, UnableToGetPublicUrl, UnableToGetStat, UnableToGetTemporaryUrl, UnableToGetVisibility, UnableToListDirectory, UnableToMoveFile, UnableToPrepareUploadRequest, UnableToReadFile, UnableToSetVisibility, UnableToWriteFile, } from './errors.js';
7
6
  export function isFile(stat) {
8
7
  return stat.isFile;
9
8
  }
@@ -44,7 +43,7 @@ export class DirectoryListing {
44
43
  }
45
44
  }
46
45
  catch (error) {
47
- throw errors.UnableToListDirectory.because(errors.errorToMessage(error), { cause: error, context: { path: this.path, deep: this.deep } });
46
+ throw UnableToListDirectory.because(errorToMessage(error), { cause: error, context: { path: this.path, deep: this.deep } });
48
47
  }
49
48
  }
50
49
  }
@@ -74,7 +73,7 @@ export class FileStorage {
74
73
  await closeReadable(body);
75
74
  }
76
75
  catch (error) {
77
- throw errors.UnableToWriteFile.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
76
+ throw UnableToWriteFile.because(errorToMessage(error), { cause: error, context: { path, options } });
78
77
  }
79
78
  }
80
79
  async read(path) {
@@ -82,7 +81,7 @@ export class FileStorage {
82
81
  return Readable.from(await this.adapter.read(this.pathNormalizer.normalizePath(path)));
83
82
  }
84
83
  catch (error) {
85
- throw errors.UnableToReadFile.because(errors.errorToMessage(error), { cause: error, context: { path } });
84
+ throw UnableToReadFile.because(errorToMessage(error), { cause: error, context: { path } });
86
85
  }
87
86
  }
88
87
  async readToString(path) {
@@ -99,7 +98,7 @@ export class FileStorage {
99
98
  await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path));
100
99
  }
101
100
  catch (error) {
102
- throw errors.UnableToDeleteFile.because(errors.errorToMessage(error), { cause: error, context: { path } });
101
+ throw UnableToDeleteFile.because(errorToMessage(error), { cause: error, context: { path } });
103
102
  }
104
103
  }
105
104
  async createDirectory(path, options = {}) {
@@ -107,7 +106,7 @@ export class FileStorage {
107
106
  return await this.adapter.createDirectory(this.pathNormalizer.normalizePath(path), { ...this.options.visibility, ...options });
108
107
  }
109
108
  catch (error) {
110
- throw errors.UnableToCreateDirectory.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
109
+ throw UnableToCreateDirectory.because(errorToMessage(error), { cause: error, context: { path, options } });
111
110
  }
112
111
  }
113
112
  async deleteDirectory(path) {
@@ -115,7 +114,7 @@ export class FileStorage {
115
114
  return await this.adapter.deleteDirectory(this.pathNormalizer.normalizePath(path));
116
115
  }
117
116
  catch (error) {
118
- throw errors.UnableToDeleteDirectory.because(errors.errorToMessage(error), { cause: error, context: { path } });
117
+ throw UnableToDeleteDirectory.because(errorToMessage(error), { cause: error, context: { path } });
119
118
  }
120
119
  }
121
120
  async stat(path) {
@@ -123,7 +122,7 @@ export class FileStorage {
123
122
  return await this.adapter.stat(this.pathNormalizer.normalizePath(path));
124
123
  }
125
124
  catch (error) {
126
- throw errors.UnableToGetStat.because(errors.errorToMessage(error), { cause: error, context: { path } });
125
+ throw UnableToGetStat.because(errorToMessage(error), { cause: error, context: { path } });
127
126
  }
128
127
  }
129
128
  async moveFile(from, to, options = {}) {
@@ -131,7 +130,7 @@ export class FileStorage {
131
130
  await this.adapter.moveFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), { ...this.options.visibility, ...this.options.moves, ...options });
132
131
  }
133
132
  catch (error) {
134
- throw errors.UnableToMoveFile.because(errors.errorToMessage(error), { cause: error, context: { from, to } });
133
+ throw UnableToMoveFile.because(errorToMessage(error), { cause: error, context: { from, to } });
135
134
  }
136
135
  }
137
136
  async copyFile(from, to, options = {}) {
@@ -139,7 +138,7 @@ export class FileStorage {
139
138
  await this.adapter.copyFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), { ...this.options.visibility, ...this.options.copies, ...options });
140
139
  }
141
140
  catch (error) {
142
- throw errors.UnableToCopyFile.because(errors.errorToMessage(error), { cause: error, context: { from, to } });
141
+ throw UnableToCopyFile.because(errorToMessage(error), { cause: error, context: { from, to } });
143
142
  }
144
143
  }
145
144
  /**
@@ -153,7 +152,7 @@ export class FileStorage {
153
152
  return await this.adapter.changeVisibility(this.pathNormalizer.normalizePath(path), visibility);
154
153
  }
155
154
  catch (error) {
156
- throw errors.UnableToSetVisibility.because(errors.errorToMessage(error), { cause: error, context: { path, visibility } });
155
+ throw UnableToSetVisibility.because(errorToMessage(error), { cause: error, context: { path, visibility } });
157
156
  }
158
157
  }
159
158
  async visibility(path) {
@@ -161,7 +160,7 @@ export class FileStorage {
161
160
  return await this.adapter.visibility(this.pathNormalizer.normalizePath(path));
162
161
  }
163
162
  catch (error) {
164
- throw errors.UnableToGetVisibility.because(errors.errorToMessage(error), { cause: error, context: { path } });
163
+ throw UnableToGetVisibility.because(errorToMessage(error), { cause: error, context: { path } });
165
164
  }
166
165
  }
167
166
  async fileExists(path) {
@@ -169,7 +168,7 @@ export class FileStorage {
169
168
  return await this.adapter.fileExists(this.pathNormalizer.normalizePath(path));
170
169
  }
171
170
  catch (error) {
172
- throw errors.UnableToCheckFileExistence.because(errors.errorToMessage(error), { cause: error, context: { path } });
171
+ throw UnableToCheckFileExistence.because(errorToMessage(error), { cause: error, context: { path } });
173
172
  }
174
173
  }
175
174
  list(path, { deep = false } = {}) {
@@ -180,14 +179,14 @@ export class FileStorage {
180
179
  if (isFile(stat)) {
181
180
  return stat;
182
181
  }
183
- throw errors.UnableToGetStat.noFileStatResolved({ context: { path } });
182
+ throw UnableToGetStat.noFileStatResolved({ context: { path } });
184
183
  }
185
184
  async directoryExists(path) {
186
185
  try {
187
186
  return await this.adapter.directoryExists(this.pathNormalizer.normalizePath(path));
188
187
  }
189
188
  catch (error) {
190
- throw errors.UnableToCheckDirectoryExistence.because(errors.errorToMessage(error), { cause: error, context: { path } });
189
+ throw UnableToCheckDirectoryExistence.because(errorToMessage(error), { cause: error, context: { path } });
191
190
  }
192
191
  }
193
192
  async publicUrl(path, options = {}) {
@@ -195,7 +194,7 @@ export class FileStorage {
195
194
  return await this.adapter.publicUrl(this.pathNormalizer.normalizePath(path), { ...this.options.publicUrls, ...options });
196
195
  }
197
196
  catch (error) {
198
- throw errors.UnableToGetPublicUrl.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
197
+ throw UnableToGetPublicUrl.because(errorToMessage(error), { cause: error, context: { path, options } });
199
198
  }
200
199
  }
201
200
  async temporaryUrl(path, options) {
@@ -203,7 +202,26 @@ export class FileStorage {
203
202
  return await this.adapter.temporaryUrl(this.pathNormalizer.normalizePath(path), { ...this.options.temporaryUrls, ...options });
204
203
  }
205
204
  catch (error) {
206
- throw errors.UnableToGetTemporaryUrl.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
205
+ throw UnableToGetTemporaryUrl.because(errorToMessage(error), { cause: error, context: { path, options } });
206
+ }
207
+ }
208
+ async prepareUpload(path, options) {
209
+ if (this.options.preparedUploadStrategy !== undefined) {
210
+ try {
211
+ return this.options.preparedUploadStrategy.prepareUpload(path, options);
212
+ }
213
+ catch (error) {
214
+ throw UnableToPrepareUploadRequest.because(errorToMessage(error), { cause: error, context: { path, options } });
215
+ }
216
+ }
217
+ if (typeof this.adapter.prepareUpload !== 'function') {
218
+ throw new Error('The used adapter does not support prepared uploads.');
219
+ }
220
+ try {
221
+ return await this.adapter.prepareUpload(this.pathNormalizer.normalizePath(path), { ...this.options.uploadRequest, ...options });
222
+ }
223
+ catch (error) {
224
+ throw UnableToPrepareUploadRequest.because(errorToMessage(error), { cause: error, context: { path, options } });
207
225
  }
208
226
  }
209
227
  async checksum(path, options = {}) {
@@ -211,10 +229,10 @@ export class FileStorage {
211
229
  return await this.adapter.checksum(this.pathNormalizer.normalizePath(path), { ...this.options.checksums, ...options });
212
230
  }
213
231
  catch (error) {
214
- if (error instanceof ChecksumIsNotAvailable) {
232
+ if (ChecksumIsNotAvailable.isErrorOfType(error)) {
215
233
  return this.calculateChecksum(path, options);
216
234
  }
217
- throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
235
+ throw UnableToGetChecksum.because(errorToMessage(error), { cause: error, context: { path, options } });
218
236
  }
219
237
  }
220
238
  async mimeType(path, options = {}) {
@@ -222,7 +240,7 @@ export class FileStorage {
222
240
  return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path), { ...this.options.mimeTypes, ...options });
223
241
  }
224
242
  catch (error) {
225
- throw errors.UnableToGetMimeType.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
243
+ throw UnableToGetMimeType.because(errorToMessage(error), { cause: error, context: { path, options } });
226
244
  }
227
245
  }
228
246
  async lastModified(path) {
@@ -230,7 +248,7 @@ export class FileStorage {
230
248
  return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path));
231
249
  }
232
250
  catch (error) {
233
- throw errors.UnableToGetLastModified.because(errors.errorToMessage(error), { cause: error, context: { path } });
251
+ throw UnableToGetLastModified.because(errorToMessage(error), { cause: error, context: { path } });
234
252
  }
235
253
  }
236
254
  async fileSize(path) {
@@ -238,7 +256,7 @@ export class FileStorage {
238
256
  return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path));
239
257
  }
240
258
  catch (error) {
241
- throw errors.UnableToGetFileSize.because(errors.errorToMessage(error), { cause: error, context: { path } });
259
+ throw UnableToGetFileSize.because(errorToMessage(error), { cause: error, context: { path } });
242
260
  }
243
261
  }
244
262
  async calculateChecksum(path, options) {
@@ -246,7 +264,7 @@ export class FileStorage {
246
264
  return await checksumFromStream(await this.read(path), options);
247
265
  }
248
266
  catch (error) {
249
- throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
267
+ throw UnableToGetChecksum.because(errorToMessage(error), { cause: error, context: { path, options } });
250
268
  }
251
269
  }
252
270
  }
@@ -301,3 +319,8 @@ function concatUint8Arrays(input) {
301
319
  });
302
320
  return output;
303
321
  }
322
+ export class PreparedUploadsAreNotSupported {
323
+ prepareUpload() {
324
+ throw new Error('The used adapter does not support prepared uploads.');
325
+ }
326
+ }
@@ -23,6 +23,7 @@ export declare class ChecksumIsNotAvailable extends FlystorageError {
23
23
  context?: ErrorContext;
24
24
  cause?: unknown;
25
25
  }) => ChecksumIsNotAvailable;
26
+ static isErrorOfType(error: unknown): error is ChecksumIsNotAvailable;
26
27
  }
27
28
  export declare class UnableToGetChecksum extends FlystorageError {
28
29
  readonly code = "flystorage.unable_to_get_checksum";
@@ -94,6 +95,13 @@ export declare class UnableToGetTemporaryUrl extends FlystorageError {
94
95
  cause?: unknown;
95
96
  }) => UnableToGetTemporaryUrl;
96
97
  }
98
+ export declare class UnableToPrepareUploadRequest extends FlystorageError {
99
+ readonly code = "flystorage.unable_to_prepare_upload_request";
100
+ static because: (reason: string, { context, cause }: {
101
+ context?: ErrorContext;
102
+ cause?: unknown;
103
+ }) => UnableToGetTemporaryUrl;
104
+ }
97
105
  export declare class UnableToCopyFile extends FlystorageError {
98
106
  readonly code = "flystorage.unable_to_copy_file";
99
107
  static because: (reason: string, { context, cause }: {
@@ -39,6 +39,7 @@ export interface StorageAdapter {
39
39
  directoryExists(path: string): Promise<boolean>;
40
40
  publicUrl(path: string, options: PublicUrlOptions): Promise<string>;
41
41
  temporaryUrl(path: string, options: TemporaryUrlOptions): Promise<string>;
42
+ prepareUpload?(path: string, options: UploadRequestOptions): Promise<UploadRequest>;
42
43
  checksum(path: string, options: ChecksumOptions): Promise<string>;
43
44
  mimeType(path: string, options: MimeTypeOptions): Promise<string>;
44
45
  lastModified(path: string): Promise<number>;
@@ -72,6 +73,11 @@ export type WriteOptions = VisibilityOptions & MiscellaneousOptions & {
72
73
  };
73
74
  export type CreateDirectoryOptions = MiscellaneousOptions & Pick<VisibilityOptions, 'directoryVisibility'> & {};
74
75
  export type PublicUrlOptions = MiscellaneousOptions & {};
76
+ export type UploadRequestOptions = MiscellaneousOptions & {
77
+ expiresAt: ExpiresAt;
78
+ contentType?: string;
79
+ headers?: UploadRequestHeaders;
80
+ };
75
81
  export type CopyFileOptions = MiscellaneousOptions & VisibilityOptions & {
76
82
  retainVisibility?: boolean;
77
83
  };
@@ -98,8 +104,10 @@ export type ConfigurationOptions = {
98
104
  copies?: CopyFileOptions;
99
105
  publicUrls?: PublicUrlOptions;
100
106
  temporaryUrls?: TemporaryUrlOptions;
107
+ uploadRequest?: UploadRequestOptions;
101
108
  checksums?: ChecksumOptions;
102
109
  mimeTypes?: MimeTypeOptions;
110
+ preparedUploadStrategy?: PreparedUploadStrategy;
103
111
  };
104
112
  export declare function toReadable(contents: FileContents): Readable;
105
113
  export declare class FileStorage {
@@ -130,6 +138,7 @@ export declare class FileStorage {
130
138
  directoryExists(path: string): Promise<boolean>;
131
139
  publicUrl(path: string, options?: PublicUrlOptions): Promise<string>;
132
140
  temporaryUrl(path: string, options: TemporaryUrlOptions): Promise<string>;
141
+ prepareUpload(path: string, options: UploadRequestOptions): Promise<UploadRequest>;
133
142
  checksum(path: string, options?: ChecksumOptions): Promise<string>;
134
143
  mimeType(path: string, options?: MimeTypeOptions): Promise<string>;
135
144
  lastModified(path: string): Promise<number>;
@@ -143,3 +152,16 @@ export declare function normalizeExpiryToMilliseconds(expiresAt: ExpiresAt): num
143
152
  export declare function closeReadable(body: Readable): Promise<void>;
144
153
  export declare function readableToString(stream: Readable): Promise<string>;
145
154
  export declare function readableToUint8Array(stream: Readable): Promise<Uint8Array>;
155
+ export type UploadRequestHeaders = Record<string, string | ReadonlyArray<string>>;
156
+ export type UploadRequest = {
157
+ url: string;
158
+ provider?: string;
159
+ method: 'PUT' | 'POST';
160
+ headers: UploadRequestHeaders;
161
+ };
162
+ export interface PreparedUploadStrategy {
163
+ prepareUpload(path: string, options: UploadRequestOptions): Promise<UploadRequest>;
164
+ }
165
+ export declare class PreparedUploadsAreNotSupported implements PreparedUploadStrategy {
166
+ prepareUpload(): Promise<UploadRequest>;
167
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flystorage/file-storage",
3
3
  "type": "module",
4
- "version": "0.1.7",
4
+ "version": "1.0.1",
5
5
  "description": "File-storage abstraction: multiple filesystems, one API.",
6
6
  "main": "./dist/cjs/index.js",
7
7
  "types": "./dist/types/index.d.ts",