@flystorage/file-storage 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +8 -0
- package/dist/cjs/errors.js +19 -2
- package/dist/cjs/file-storage.js +110 -47
- package/dist/esm/errors.js +16 -1
- package/dist/esm/file-storage.js +111 -49
- package/dist/types/errors.d.ts +12 -0
- package/dist/types/file-storage.d.ts +37 -34
- package/package.json +2 -1
package/changelog.md
CHANGED
package/dist/cjs/errors.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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.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;
|
|
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.FileWasNotFound = exports.UnableToReadFile = exports.UnableToWriteFile = exports.UnableToGetFileSize = exports.UnableToGetLastModified = exports.UnableToGetMimeType = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = void 0;
|
|
4
4
|
exports.errorToMessage = errorToMessage;
|
|
5
|
+
exports.isFileWasNotFound = isFileWasNotFound;
|
|
5
6
|
function errorToMessage(error) {
|
|
6
7
|
return error instanceof Error ? error.message : String(error);
|
|
7
8
|
}
|
|
@@ -59,10 +60,26 @@ class UnableToWriteFile extends FlystorageError {
|
|
|
59
60
|
}
|
|
60
61
|
exports.UnableToWriteFile = UnableToWriteFile;
|
|
61
62
|
class UnableToReadFile extends FlystorageError {
|
|
63
|
+
wasFileNotFound;
|
|
64
|
+
context;
|
|
62
65
|
code = 'flystorage.unable_to_read_file';
|
|
63
|
-
|
|
66
|
+
constructor(wasFileNotFound, message, context = {}, cause = undefined) {
|
|
67
|
+
super(message, context, cause);
|
|
68
|
+
this.wasFileNotFound = wasFileNotFound;
|
|
69
|
+
this.context = context;
|
|
70
|
+
}
|
|
71
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToReadFile(false, `Unable to read the file. Reason: ${reason}`, context, cause);
|
|
72
|
+
static becauseFileWasNotFound = (error) => new UnableToReadFile(true, `Unable to read the file. Reason: ${error.message}`, error.context, error);
|
|
64
73
|
}
|
|
65
74
|
exports.UnableToReadFile = UnableToReadFile;
|
|
75
|
+
class FileWasNotFound extends FlystorageError {
|
|
76
|
+
code = 'flystorage.file_was_not_found';
|
|
77
|
+
static atLocation = (location, { context = {}, cause = undefined }) => new FileWasNotFound(`File was not found at location: ${location}`, context, cause);
|
|
78
|
+
}
|
|
79
|
+
exports.FileWasNotFound = FileWasNotFound;
|
|
80
|
+
function isFileWasNotFound(error) {
|
|
81
|
+
return (typeof error === 'object' && error.code === 'flystorage.file_was_not_found');
|
|
82
|
+
}
|
|
66
83
|
class UnableToSetVisibility extends FlystorageError {
|
|
67
84
|
code = 'flystorage.unable_to_set_visibility';
|
|
68
85
|
static because = (reason, { context = {}, cause = undefined }) => new UnableToSetVisibility(`Unable to set visibility. Reason: ${reason}`, context, cause);
|
package/dist/cjs/file-storage.js
CHANGED
|
@@ -8,12 +8,14 @@ exports.normalizeExpiryToDate = normalizeExpiryToDate;
|
|
|
8
8
|
exports.normalizeExpiryToMilliseconds = normalizeExpiryToMilliseconds;
|
|
9
9
|
exports.closeReadable = closeReadable;
|
|
10
10
|
exports.readableToString = readableToString;
|
|
11
|
+
exports.readableToBuffer = readableToBuffer;
|
|
11
12
|
exports.readableToUint8Array = readableToUint8Array;
|
|
12
13
|
const stream_1 = require("stream");
|
|
13
14
|
const checksum_from_stream_js_1 = require("./checksum-from-stream.js");
|
|
14
15
|
const path_normalizer_js_1 = require("./path-normalizer.js");
|
|
15
16
|
const util_1 = require("util");
|
|
16
17
|
const errors_js_1 = require("./errors.js");
|
|
18
|
+
const node_stream_1 = require("node:stream");
|
|
17
19
|
function isFile(stat) {
|
|
18
20
|
return stat.isFile;
|
|
19
21
|
}
|
|
@@ -67,8 +69,28 @@ function toReadable(contents) {
|
|
|
67
69
|
}
|
|
68
70
|
const naturalSorting = new Intl.Collator(undefined, {
|
|
69
71
|
numeric: true,
|
|
70
|
-
sensitivity: 'base'
|
|
72
|
+
sensitivity: 'base',
|
|
71
73
|
});
|
|
74
|
+
function instrumentAbortSignal(options) {
|
|
75
|
+
let abortSignal = options.abortSignal;
|
|
76
|
+
if (options.timeout !== undefined) {
|
|
77
|
+
const timeoutAbort = AbortSignal.timeout(options.timeout);
|
|
78
|
+
if (options.abortSignal) {
|
|
79
|
+
const originalAbortSignal = options.abortSignal;
|
|
80
|
+
abortSignal = AbortSignal.any([
|
|
81
|
+
originalAbortSignal,
|
|
82
|
+
timeoutAbort,
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
abortSignal = timeoutAbort;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (abortSignal?.aborted) {
|
|
90
|
+
throw abortSignal.reason;
|
|
91
|
+
}
|
|
92
|
+
return { ...options, abortSignal };
|
|
93
|
+
}
|
|
72
94
|
class FileStorage {
|
|
73
95
|
adapter;
|
|
74
96
|
pathNormalizer;
|
|
@@ -79,145 +101,172 @@ class FileStorage {
|
|
|
79
101
|
this.options = options;
|
|
80
102
|
}
|
|
81
103
|
async write(path, contents, options = {}) {
|
|
104
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...this.options.writes, ...options });
|
|
82
105
|
try {
|
|
83
106
|
const body = toReadable(contents);
|
|
84
|
-
await this.adapter.write(this.pathNormalizer.normalizePath(path), body,
|
|
107
|
+
await this.adapter.write(this.pathNormalizer.normalizePath(path), body, options);
|
|
85
108
|
await closeReadable(body);
|
|
86
109
|
}
|
|
87
110
|
catch (error) {
|
|
88
111
|
throw errors_js_1.UnableToWriteFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
|
|
89
112
|
}
|
|
90
113
|
}
|
|
91
|
-
async read(path) {
|
|
114
|
+
async read(path, options = {}) {
|
|
115
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
92
116
|
try {
|
|
93
|
-
|
|
117
|
+
const stream = stream_1.Readable.from(await this.adapter.read(this.pathNormalizer.normalizePath(path), options));
|
|
118
|
+
const streamOut = new node_stream_1.PassThrough();
|
|
119
|
+
stream.on('error', (error) => {
|
|
120
|
+
stream.unpipe(streamOut);
|
|
121
|
+
streamOut.destroy((0, errors_js_1.isFileWasNotFound)(error)
|
|
122
|
+
? errors_js_1.UnableToReadFile.becauseFileWasNotFound(error)
|
|
123
|
+
: error);
|
|
124
|
+
});
|
|
125
|
+
stream.pipe(streamOut);
|
|
126
|
+
return streamOut;
|
|
94
127
|
}
|
|
95
128
|
catch (error) {
|
|
129
|
+
if ((0, errors_js_1.isFileWasNotFound)(error)) {
|
|
130
|
+
throw errors_js_1.UnableToReadFile.becauseFileWasNotFound(error);
|
|
131
|
+
}
|
|
96
132
|
throw errors_js_1.UnableToReadFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
97
133
|
}
|
|
98
134
|
}
|
|
99
|
-
async readToString(path) {
|
|
100
|
-
return await readableToString(await this.read(path));
|
|
135
|
+
async readToString(path, options = {}) {
|
|
136
|
+
return await readableToString(await this.read(path, options));
|
|
101
137
|
}
|
|
102
|
-
async readToUint8Array(path) {
|
|
103
|
-
return await readableToUint8Array(await this.read(path));
|
|
138
|
+
async readToUint8Array(path, options = {}) {
|
|
139
|
+
return await readableToUint8Array(await this.read(path, options));
|
|
104
140
|
}
|
|
105
|
-
async readToBuffer(path) {
|
|
106
|
-
return Buffer.from(await this.readToUint8Array(path));
|
|
141
|
+
async readToBuffer(path, options = {}) {
|
|
142
|
+
return Buffer.from(await this.readToUint8Array(path, options));
|
|
107
143
|
}
|
|
108
|
-
async deleteFile(path) {
|
|
144
|
+
async deleteFile(path, options = {}) {
|
|
145
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
109
146
|
try {
|
|
110
|
-
await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path));
|
|
147
|
+
await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path), options);
|
|
111
148
|
}
|
|
112
149
|
catch (error) {
|
|
113
150
|
throw errors_js_1.UnableToDeleteFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
114
151
|
}
|
|
115
152
|
}
|
|
116
153
|
async createDirectory(path, options = {}) {
|
|
154
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...options });
|
|
117
155
|
try {
|
|
118
|
-
return await this.adapter.createDirectory(this.pathNormalizer.normalizePath(path),
|
|
156
|
+
return await this.adapter.createDirectory(this.pathNormalizer.normalizePath(path), options);
|
|
119
157
|
}
|
|
120
158
|
catch (error) {
|
|
121
159
|
throw errors_js_1.UnableToCreateDirectory.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
|
|
122
160
|
}
|
|
123
161
|
}
|
|
124
|
-
async deleteDirectory(path) {
|
|
162
|
+
async deleteDirectory(path, options = {}) {
|
|
163
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
125
164
|
try {
|
|
126
|
-
return await this.adapter.deleteDirectory(this.pathNormalizer.normalizePath(path));
|
|
165
|
+
return await this.adapter.deleteDirectory(this.pathNormalizer.normalizePath(path), options);
|
|
127
166
|
}
|
|
128
167
|
catch (error) {
|
|
129
168
|
throw errors_js_1.UnableToDeleteDirectory.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
130
169
|
}
|
|
131
170
|
}
|
|
132
|
-
async stat(path) {
|
|
171
|
+
async stat(path, options = {}) {
|
|
172
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
133
173
|
try {
|
|
134
|
-
return await this.adapter.stat(this.pathNormalizer.normalizePath(path));
|
|
174
|
+
return await this.adapter.stat(this.pathNormalizer.normalizePath(path), options);
|
|
135
175
|
}
|
|
136
176
|
catch (error) {
|
|
137
177
|
throw errors_js_1.UnableToGetStat.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
138
178
|
}
|
|
139
179
|
}
|
|
140
180
|
async moveFile(from, to, options = {}) {
|
|
181
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...this.options.moves, ...options });
|
|
141
182
|
try {
|
|
142
|
-
await this.adapter.moveFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to),
|
|
183
|
+
await this.adapter.moveFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), options);
|
|
143
184
|
}
|
|
144
185
|
catch (error) {
|
|
145
186
|
throw errors_js_1.UnableToMoveFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { from, to } });
|
|
146
187
|
}
|
|
147
188
|
}
|
|
148
189
|
async copyFile(from, to, options = {}) {
|
|
190
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...this.options.copies, ...options });
|
|
149
191
|
try {
|
|
150
|
-
await this.adapter.copyFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to),
|
|
192
|
+
await this.adapter.copyFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), options);
|
|
151
193
|
}
|
|
152
194
|
catch (error) {
|
|
153
195
|
throw errors_js_1.UnableToCopyFile.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { from, to } });
|
|
154
196
|
}
|
|
155
197
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
*/
|
|
159
|
-
async setVisibility(path, visibility) {
|
|
160
|
-
return this.changeVisibility(path, visibility);
|
|
161
|
-
}
|
|
162
|
-
async changeVisibility(path, visibility) {
|
|
198
|
+
async changeVisibility(path, visibility, options = {}) {
|
|
199
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
163
200
|
try {
|
|
164
|
-
return await this.adapter.changeVisibility(this.pathNormalizer.normalizePath(path), visibility);
|
|
201
|
+
return await this.adapter.changeVisibility(this.pathNormalizer.normalizePath(path), visibility, options);
|
|
165
202
|
}
|
|
166
203
|
catch (error) {
|
|
167
204
|
throw errors_js_1.UnableToSetVisibility.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, visibility } });
|
|
168
205
|
}
|
|
169
206
|
}
|
|
170
|
-
async visibility(path) {
|
|
207
|
+
async visibility(path, options = {}) {
|
|
208
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
171
209
|
try {
|
|
172
|
-
return await this.adapter.visibility(this.pathNormalizer.normalizePath(path));
|
|
210
|
+
return await this.adapter.visibility(this.pathNormalizer.normalizePath(path), options);
|
|
173
211
|
}
|
|
174
212
|
catch (error) {
|
|
175
213
|
throw errors_js_1.UnableToGetVisibility.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
176
214
|
}
|
|
177
215
|
}
|
|
178
|
-
async fileExists(path) {
|
|
216
|
+
async fileExists(path, options = {}) {
|
|
217
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
179
218
|
try {
|
|
180
|
-
return await this.adapter.fileExists(this.pathNormalizer.normalizePath(path));
|
|
219
|
+
return await this.adapter.fileExists(this.pathNormalizer.normalizePath(path), options);
|
|
181
220
|
}
|
|
182
221
|
catch (error) {
|
|
183
222
|
throw errors_js_1.UnableToCheckFileExistence.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
184
223
|
}
|
|
185
224
|
}
|
|
186
|
-
list(path,
|
|
187
|
-
|
|
225
|
+
list(path, options = {}) {
|
|
226
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
227
|
+
const adapterOptions = {
|
|
228
|
+
...options,
|
|
229
|
+
deep: options.deep ?? false,
|
|
230
|
+
};
|
|
231
|
+
return new DirectoryListing(this.adapter.list(this.pathNormalizer.normalizePath(path), adapterOptions), path, adapterOptions.deep);
|
|
188
232
|
}
|
|
189
|
-
async statFile(path) {
|
|
190
|
-
|
|
233
|
+
async statFile(path, options = {}) {
|
|
234
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
235
|
+
const stat = await this.stat(path, options);
|
|
191
236
|
if (isFile(stat)) {
|
|
192
237
|
return stat;
|
|
193
238
|
}
|
|
194
239
|
throw errors_js_1.UnableToGetStat.noFileStatResolved({ context: { path } });
|
|
195
240
|
}
|
|
196
|
-
async directoryExists(path) {
|
|
241
|
+
async directoryExists(path, options = {}) {
|
|
242
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
197
243
|
try {
|
|
198
|
-
return await this.adapter.directoryExists(this.pathNormalizer.normalizePath(path));
|
|
244
|
+
return await this.adapter.directoryExists(this.pathNormalizer.normalizePath(path), options);
|
|
199
245
|
}
|
|
200
246
|
catch (error) {
|
|
201
247
|
throw errors_js_1.UnableToCheckDirectoryExistence.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
202
248
|
}
|
|
203
249
|
}
|
|
204
250
|
async publicUrl(path, options = {}) {
|
|
251
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.publicUrls, ...options });
|
|
205
252
|
try {
|
|
206
|
-
return await this.adapter.publicUrl(this.pathNormalizer.normalizePath(path),
|
|
253
|
+
return await this.adapter.publicUrl(this.pathNormalizer.normalizePath(path), options);
|
|
207
254
|
}
|
|
208
255
|
catch (error) {
|
|
209
256
|
throw errors_js_1.UnableToGetPublicUrl.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
|
|
210
257
|
}
|
|
211
258
|
}
|
|
212
259
|
async temporaryUrl(path, options) {
|
|
260
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.temporaryUrls, ...options });
|
|
213
261
|
try {
|
|
214
|
-
return await this.adapter.temporaryUrl(this.pathNormalizer.normalizePath(path),
|
|
262
|
+
return await this.adapter.temporaryUrl(this.pathNormalizer.normalizePath(path), options);
|
|
215
263
|
}
|
|
216
264
|
catch (error) {
|
|
217
265
|
throw errors_js_1.UnableToGetTemporaryUrl.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
|
|
218
266
|
}
|
|
219
267
|
}
|
|
220
268
|
async prepareUpload(path, options) {
|
|
269
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.uploadRequest, ...options });
|
|
221
270
|
if (this.options.preparedUploadStrategy !== undefined) {
|
|
222
271
|
try {
|
|
223
272
|
return this.options.preparedUploadStrategy.prepareUpload(path, options);
|
|
@@ -230,15 +279,16 @@ class FileStorage {
|
|
|
230
279
|
throw new Error('The used adapter does not support prepared uploads.');
|
|
231
280
|
}
|
|
232
281
|
try {
|
|
233
|
-
return await this.adapter.prepareUpload(this.pathNormalizer.normalizePath(path),
|
|
282
|
+
return await this.adapter.prepareUpload(this.pathNormalizer.normalizePath(path), options);
|
|
234
283
|
}
|
|
235
284
|
catch (error) {
|
|
236
285
|
throw errors_js_1.UnableToPrepareUploadRequest.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
|
|
237
286
|
}
|
|
238
287
|
}
|
|
239
288
|
async checksum(path, options = {}) {
|
|
289
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.checksums, ...options });
|
|
240
290
|
try {
|
|
241
|
-
return await this.adapter.checksum(this.pathNormalizer.normalizePath(path),
|
|
291
|
+
return await this.adapter.checksum(this.pathNormalizer.normalizePath(path), options);
|
|
242
292
|
}
|
|
243
293
|
catch (error) {
|
|
244
294
|
if (errors_js_1.ChecksumIsNotAvailable.isErrorOfType(error)) {
|
|
@@ -248,24 +298,27 @@ class FileStorage {
|
|
|
248
298
|
}
|
|
249
299
|
}
|
|
250
300
|
async mimeType(path, options = {}) {
|
|
301
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.mimeTypes, ...options });
|
|
251
302
|
try {
|
|
252
|
-
return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path),
|
|
303
|
+
return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path), options);
|
|
253
304
|
}
|
|
254
305
|
catch (error) {
|
|
255
306
|
throw errors_js_1.UnableToGetMimeType.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
|
|
256
307
|
}
|
|
257
308
|
}
|
|
258
|
-
async lastModified(path) {
|
|
309
|
+
async lastModified(path, options = {}) {
|
|
310
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
259
311
|
try {
|
|
260
|
-
return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path));
|
|
312
|
+
return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path), options);
|
|
261
313
|
}
|
|
262
314
|
catch (error) {
|
|
263
315
|
throw errors_js_1.UnableToGetLastModified.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
264
316
|
}
|
|
265
317
|
}
|
|
266
|
-
async fileSize(path) {
|
|
318
|
+
async fileSize(path, options = {}) {
|
|
319
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
267
320
|
try {
|
|
268
|
-
return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path));
|
|
321
|
+
return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path), options);
|
|
269
322
|
}
|
|
270
323
|
catch (error) {
|
|
271
324
|
throw errors_js_1.UnableToGetFileSize.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path } });
|
|
@@ -273,7 +326,7 @@ class FileStorage {
|
|
|
273
326
|
}
|
|
274
327
|
async calculateChecksum(path, options) {
|
|
275
328
|
try {
|
|
276
|
-
return await (0, checksum_from_stream_js_1.checksumFromStream)(await this.read(path), options);
|
|
329
|
+
return await (0, checksum_from_stream_js_1.checksumFromStream)(await this.read(path, options), options);
|
|
277
330
|
}
|
|
278
331
|
catch (error) {
|
|
279
332
|
throw errors_js_1.UnableToGetChecksum.because((0, errors_js_1.errorToMessage)(error), { cause: error, context: { path, options } });
|
|
@@ -292,6 +345,7 @@ async function closeReadable(body) {
|
|
|
292
345
|
return;
|
|
293
346
|
}
|
|
294
347
|
await new Promise((resolve, reject) => {
|
|
348
|
+
body.on('error', reject);
|
|
295
349
|
body.on('close', (err) => {
|
|
296
350
|
err ? reject(err) : resolve();
|
|
297
351
|
});
|
|
@@ -304,6 +358,15 @@ async function readableToString(stream) {
|
|
|
304
358
|
await closeReadable(stream);
|
|
305
359
|
return contents;
|
|
306
360
|
}
|
|
361
|
+
async function readableToBuffer(stream) {
|
|
362
|
+
return new Promise((resolve, reject) => {
|
|
363
|
+
const buffers = [];
|
|
364
|
+
stream.on('data', chunk => buffers.push(Buffer.from(chunk)));
|
|
365
|
+
stream.on('end', () => resolve(Buffer.concat(buffers)));
|
|
366
|
+
stream.on('finish', () => resolve(Buffer.concat(buffers)));
|
|
367
|
+
stream.on('error', err => reject(err));
|
|
368
|
+
});
|
|
369
|
+
}
|
|
307
370
|
const encoder = new util_1.TextEncoder();
|
|
308
371
|
function readableToUint8Array(stream) {
|
|
309
372
|
return new Promise((resolve, reject) => {
|
package/dist/esm/errors.js
CHANGED
|
@@ -48,8 +48,23 @@ export class UnableToWriteFile extends FlystorageError {
|
|
|
48
48
|
static because = (reason, { context = {}, cause = undefined }) => new UnableToWriteFile(`Unable to write the file. Reason: ${reason}`, context, cause);
|
|
49
49
|
}
|
|
50
50
|
export class UnableToReadFile extends FlystorageError {
|
|
51
|
+
wasFileNotFound;
|
|
52
|
+
context;
|
|
51
53
|
code = 'flystorage.unable_to_read_file';
|
|
52
|
-
|
|
54
|
+
constructor(wasFileNotFound, message, context = {}, cause = undefined) {
|
|
55
|
+
super(message, context, cause);
|
|
56
|
+
this.wasFileNotFound = wasFileNotFound;
|
|
57
|
+
this.context = context;
|
|
58
|
+
}
|
|
59
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToReadFile(false, `Unable to read the file. Reason: ${reason}`, context, cause);
|
|
60
|
+
static becauseFileWasNotFound = (error) => new UnableToReadFile(true, `Unable to read the file. Reason: ${error.message}`, error.context, error);
|
|
61
|
+
}
|
|
62
|
+
export class FileWasNotFound extends FlystorageError {
|
|
63
|
+
code = 'flystorage.file_was_not_found';
|
|
64
|
+
static atLocation = (location, { context = {}, cause = undefined }) => new FileWasNotFound(`File was not found at location: ${location}`, context, cause);
|
|
65
|
+
}
|
|
66
|
+
export function isFileWasNotFound(error) {
|
|
67
|
+
return (typeof error === 'object' && error.code === 'flystorage.file_was_not_found');
|
|
53
68
|
}
|
|
54
69
|
export class UnableToSetVisibility extends FlystorageError {
|
|
55
70
|
code = 'flystorage.unable_to_set_visibility';
|
package/dist/esm/file-storage.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Readable } from 'stream';
|
|
2
2
|
import { checksumFromStream } from './checksum-from-stream.js';
|
|
3
3
|
import { PathNormalizerV1 } from './path-normalizer.js';
|
|
4
|
-
import { TextEncoder } from
|
|
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';
|
|
4
|
+
import { TextEncoder } from 'util';
|
|
5
|
+
import { ChecksumIsNotAvailable, errorToMessage, isFileWasNotFound, UnableToCheckDirectoryExistence, UnableToCheckFileExistence, UnableToCopyFile, UnableToCreateDirectory, UnableToDeleteDirectory, UnableToDeleteFile, UnableToGetChecksum, UnableToGetFileSize, UnableToGetLastModified, UnableToGetMimeType, UnableToGetPublicUrl, UnableToGetStat, UnableToGetTemporaryUrl, UnableToGetVisibility, UnableToListDirectory, UnableToMoveFile, UnableToPrepareUploadRequest, UnableToReadFile, UnableToSetVisibility, UnableToWriteFile, } from './errors.js';
|
|
6
|
+
import { PassThrough } from 'node:stream';
|
|
6
7
|
export function isFile(stat) {
|
|
7
8
|
return stat.isFile;
|
|
8
9
|
}
|
|
@@ -55,8 +56,28 @@ export function toReadable(contents) {
|
|
|
55
56
|
}
|
|
56
57
|
const naturalSorting = new Intl.Collator(undefined, {
|
|
57
58
|
numeric: true,
|
|
58
|
-
sensitivity: 'base'
|
|
59
|
+
sensitivity: 'base',
|
|
59
60
|
});
|
|
61
|
+
function instrumentAbortSignal(options) {
|
|
62
|
+
let abortSignal = options.abortSignal;
|
|
63
|
+
if (options.timeout !== undefined) {
|
|
64
|
+
const timeoutAbort = AbortSignal.timeout(options.timeout);
|
|
65
|
+
if (options.abortSignal) {
|
|
66
|
+
const originalAbortSignal = options.abortSignal;
|
|
67
|
+
abortSignal = AbortSignal.any([
|
|
68
|
+
originalAbortSignal,
|
|
69
|
+
timeoutAbort,
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
abortSignal = timeoutAbort;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (abortSignal?.aborted) {
|
|
77
|
+
throw abortSignal.reason;
|
|
78
|
+
}
|
|
79
|
+
return { ...options, abortSignal };
|
|
80
|
+
}
|
|
60
81
|
export class FileStorage {
|
|
61
82
|
adapter;
|
|
62
83
|
pathNormalizer;
|
|
@@ -67,145 +88,172 @@ export class FileStorage {
|
|
|
67
88
|
this.options = options;
|
|
68
89
|
}
|
|
69
90
|
async write(path, contents, options = {}) {
|
|
91
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...this.options.writes, ...options });
|
|
70
92
|
try {
|
|
71
93
|
const body = toReadable(contents);
|
|
72
|
-
await this.adapter.write(this.pathNormalizer.normalizePath(path), body,
|
|
94
|
+
await this.adapter.write(this.pathNormalizer.normalizePath(path), body, options);
|
|
73
95
|
await closeReadable(body);
|
|
74
96
|
}
|
|
75
97
|
catch (error) {
|
|
76
98
|
throw UnableToWriteFile.because(errorToMessage(error), { cause: error, context: { path, options } });
|
|
77
99
|
}
|
|
78
100
|
}
|
|
79
|
-
async read(path) {
|
|
101
|
+
async read(path, options = {}) {
|
|
102
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
80
103
|
try {
|
|
81
|
-
|
|
104
|
+
const stream = Readable.from(await this.adapter.read(this.pathNormalizer.normalizePath(path), options));
|
|
105
|
+
const streamOut = new PassThrough();
|
|
106
|
+
stream.on('error', (error) => {
|
|
107
|
+
stream.unpipe(streamOut);
|
|
108
|
+
streamOut.destroy(isFileWasNotFound(error)
|
|
109
|
+
? UnableToReadFile.becauseFileWasNotFound(error)
|
|
110
|
+
: error);
|
|
111
|
+
});
|
|
112
|
+
stream.pipe(streamOut);
|
|
113
|
+
return streamOut;
|
|
82
114
|
}
|
|
83
115
|
catch (error) {
|
|
116
|
+
if (isFileWasNotFound(error)) {
|
|
117
|
+
throw UnableToReadFile.becauseFileWasNotFound(error);
|
|
118
|
+
}
|
|
84
119
|
throw UnableToReadFile.because(errorToMessage(error), { cause: error, context: { path } });
|
|
85
120
|
}
|
|
86
121
|
}
|
|
87
|
-
async readToString(path) {
|
|
88
|
-
return await readableToString(await this.read(path));
|
|
122
|
+
async readToString(path, options = {}) {
|
|
123
|
+
return await readableToString(await this.read(path, options));
|
|
89
124
|
}
|
|
90
|
-
async readToUint8Array(path) {
|
|
91
|
-
return await readableToUint8Array(await this.read(path));
|
|
125
|
+
async readToUint8Array(path, options = {}) {
|
|
126
|
+
return await readableToUint8Array(await this.read(path, options));
|
|
92
127
|
}
|
|
93
|
-
async readToBuffer(path) {
|
|
94
|
-
return Buffer.from(await this.readToUint8Array(path));
|
|
128
|
+
async readToBuffer(path, options = {}) {
|
|
129
|
+
return Buffer.from(await this.readToUint8Array(path, options));
|
|
95
130
|
}
|
|
96
|
-
async deleteFile(path) {
|
|
131
|
+
async deleteFile(path, options = {}) {
|
|
132
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
97
133
|
try {
|
|
98
|
-
await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path));
|
|
134
|
+
await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path), options);
|
|
99
135
|
}
|
|
100
136
|
catch (error) {
|
|
101
137
|
throw UnableToDeleteFile.because(errorToMessage(error), { cause: error, context: { path } });
|
|
102
138
|
}
|
|
103
139
|
}
|
|
104
140
|
async createDirectory(path, options = {}) {
|
|
141
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...options });
|
|
105
142
|
try {
|
|
106
|
-
return await this.adapter.createDirectory(this.pathNormalizer.normalizePath(path),
|
|
143
|
+
return await this.adapter.createDirectory(this.pathNormalizer.normalizePath(path), options);
|
|
107
144
|
}
|
|
108
145
|
catch (error) {
|
|
109
146
|
throw UnableToCreateDirectory.because(errorToMessage(error), { cause: error, context: { path, options } });
|
|
110
147
|
}
|
|
111
148
|
}
|
|
112
|
-
async deleteDirectory(path) {
|
|
149
|
+
async deleteDirectory(path, options = {}) {
|
|
150
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
113
151
|
try {
|
|
114
|
-
return await this.adapter.deleteDirectory(this.pathNormalizer.normalizePath(path));
|
|
152
|
+
return await this.adapter.deleteDirectory(this.pathNormalizer.normalizePath(path), options);
|
|
115
153
|
}
|
|
116
154
|
catch (error) {
|
|
117
155
|
throw UnableToDeleteDirectory.because(errorToMessage(error), { cause: error, context: { path } });
|
|
118
156
|
}
|
|
119
157
|
}
|
|
120
|
-
async stat(path) {
|
|
158
|
+
async stat(path, options = {}) {
|
|
159
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
121
160
|
try {
|
|
122
|
-
return await this.adapter.stat(this.pathNormalizer.normalizePath(path));
|
|
161
|
+
return await this.adapter.stat(this.pathNormalizer.normalizePath(path), options);
|
|
123
162
|
}
|
|
124
163
|
catch (error) {
|
|
125
164
|
throw UnableToGetStat.because(errorToMessage(error), { cause: error, context: { path } });
|
|
126
165
|
}
|
|
127
166
|
}
|
|
128
167
|
async moveFile(from, to, options = {}) {
|
|
168
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...this.options.moves, ...options });
|
|
129
169
|
try {
|
|
130
|
-
await this.adapter.moveFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to),
|
|
170
|
+
await this.adapter.moveFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), options);
|
|
131
171
|
}
|
|
132
172
|
catch (error) {
|
|
133
173
|
throw UnableToMoveFile.because(errorToMessage(error), { cause: error, context: { from, to } });
|
|
134
174
|
}
|
|
135
175
|
}
|
|
136
176
|
async copyFile(from, to, options = {}) {
|
|
177
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.visibility, ...this.options.copies, ...options });
|
|
137
178
|
try {
|
|
138
|
-
await this.adapter.copyFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to),
|
|
179
|
+
await this.adapter.copyFile(this.pathNormalizer.normalizePath(from), this.pathNormalizer.normalizePath(to), options);
|
|
139
180
|
}
|
|
140
181
|
catch (error) {
|
|
141
182
|
throw UnableToCopyFile.because(errorToMessage(error), { cause: error, context: { from, to } });
|
|
142
183
|
}
|
|
143
184
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
*/
|
|
147
|
-
async setVisibility(path, visibility) {
|
|
148
|
-
return this.changeVisibility(path, visibility);
|
|
149
|
-
}
|
|
150
|
-
async changeVisibility(path, visibility) {
|
|
185
|
+
async changeVisibility(path, visibility, options = {}) {
|
|
186
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
151
187
|
try {
|
|
152
|
-
return await this.adapter.changeVisibility(this.pathNormalizer.normalizePath(path), visibility);
|
|
188
|
+
return await this.adapter.changeVisibility(this.pathNormalizer.normalizePath(path), visibility, options);
|
|
153
189
|
}
|
|
154
190
|
catch (error) {
|
|
155
191
|
throw UnableToSetVisibility.because(errorToMessage(error), { cause: error, context: { path, visibility } });
|
|
156
192
|
}
|
|
157
193
|
}
|
|
158
|
-
async visibility(path) {
|
|
194
|
+
async visibility(path, options = {}) {
|
|
195
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
159
196
|
try {
|
|
160
|
-
return await this.adapter.visibility(this.pathNormalizer.normalizePath(path));
|
|
197
|
+
return await this.adapter.visibility(this.pathNormalizer.normalizePath(path), options);
|
|
161
198
|
}
|
|
162
199
|
catch (error) {
|
|
163
200
|
throw UnableToGetVisibility.because(errorToMessage(error), { cause: error, context: { path } });
|
|
164
201
|
}
|
|
165
202
|
}
|
|
166
|
-
async fileExists(path) {
|
|
203
|
+
async fileExists(path, options = {}) {
|
|
204
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
167
205
|
try {
|
|
168
|
-
return await this.adapter.fileExists(this.pathNormalizer.normalizePath(path));
|
|
206
|
+
return await this.adapter.fileExists(this.pathNormalizer.normalizePath(path), options);
|
|
169
207
|
}
|
|
170
208
|
catch (error) {
|
|
171
209
|
throw UnableToCheckFileExistence.because(errorToMessage(error), { cause: error, context: { path } });
|
|
172
210
|
}
|
|
173
211
|
}
|
|
174
|
-
list(path,
|
|
175
|
-
|
|
212
|
+
list(path, options = {}) {
|
|
213
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
214
|
+
const adapterOptions = {
|
|
215
|
+
...options,
|
|
216
|
+
deep: options.deep ?? false,
|
|
217
|
+
};
|
|
218
|
+
return new DirectoryListing(this.adapter.list(this.pathNormalizer.normalizePath(path), adapterOptions), path, adapterOptions.deep);
|
|
176
219
|
}
|
|
177
|
-
async statFile(path) {
|
|
178
|
-
|
|
220
|
+
async statFile(path, options = {}) {
|
|
221
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
222
|
+
const stat = await this.stat(path, options);
|
|
179
223
|
if (isFile(stat)) {
|
|
180
224
|
return stat;
|
|
181
225
|
}
|
|
182
226
|
throw UnableToGetStat.noFileStatResolved({ context: { path } });
|
|
183
227
|
}
|
|
184
|
-
async directoryExists(path) {
|
|
228
|
+
async directoryExists(path, options = {}) {
|
|
229
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
185
230
|
try {
|
|
186
|
-
return await this.adapter.directoryExists(this.pathNormalizer.normalizePath(path));
|
|
231
|
+
return await this.adapter.directoryExists(this.pathNormalizer.normalizePath(path), options);
|
|
187
232
|
}
|
|
188
233
|
catch (error) {
|
|
189
234
|
throw UnableToCheckDirectoryExistence.because(errorToMessage(error), { cause: error, context: { path } });
|
|
190
235
|
}
|
|
191
236
|
}
|
|
192
237
|
async publicUrl(path, options = {}) {
|
|
238
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.publicUrls, ...options });
|
|
193
239
|
try {
|
|
194
|
-
return await this.adapter.publicUrl(this.pathNormalizer.normalizePath(path),
|
|
240
|
+
return await this.adapter.publicUrl(this.pathNormalizer.normalizePath(path), options);
|
|
195
241
|
}
|
|
196
242
|
catch (error) {
|
|
197
243
|
throw UnableToGetPublicUrl.because(errorToMessage(error), { cause: error, context: { path, options } });
|
|
198
244
|
}
|
|
199
245
|
}
|
|
200
246
|
async temporaryUrl(path, options) {
|
|
247
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.temporaryUrls, ...options });
|
|
201
248
|
try {
|
|
202
|
-
return await this.adapter.temporaryUrl(this.pathNormalizer.normalizePath(path),
|
|
249
|
+
return await this.adapter.temporaryUrl(this.pathNormalizer.normalizePath(path), options);
|
|
203
250
|
}
|
|
204
251
|
catch (error) {
|
|
205
252
|
throw UnableToGetTemporaryUrl.because(errorToMessage(error), { cause: error, context: { path, options } });
|
|
206
253
|
}
|
|
207
254
|
}
|
|
208
255
|
async prepareUpload(path, options) {
|
|
256
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.uploadRequest, ...options });
|
|
209
257
|
if (this.options.preparedUploadStrategy !== undefined) {
|
|
210
258
|
try {
|
|
211
259
|
return this.options.preparedUploadStrategy.prepareUpload(path, options);
|
|
@@ -218,15 +266,16 @@ export class FileStorage {
|
|
|
218
266
|
throw new Error('The used adapter does not support prepared uploads.');
|
|
219
267
|
}
|
|
220
268
|
try {
|
|
221
|
-
return await this.adapter.prepareUpload(this.pathNormalizer.normalizePath(path),
|
|
269
|
+
return await this.adapter.prepareUpload(this.pathNormalizer.normalizePath(path), options);
|
|
222
270
|
}
|
|
223
271
|
catch (error) {
|
|
224
272
|
throw UnableToPrepareUploadRequest.because(errorToMessage(error), { cause: error, context: { path, options } });
|
|
225
273
|
}
|
|
226
274
|
}
|
|
227
275
|
async checksum(path, options = {}) {
|
|
276
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.checksums, ...options });
|
|
228
277
|
try {
|
|
229
|
-
return await this.adapter.checksum(this.pathNormalizer.normalizePath(path),
|
|
278
|
+
return await this.adapter.checksum(this.pathNormalizer.normalizePath(path), options);
|
|
230
279
|
}
|
|
231
280
|
catch (error) {
|
|
232
281
|
if (ChecksumIsNotAvailable.isErrorOfType(error)) {
|
|
@@ -236,24 +285,27 @@ export class FileStorage {
|
|
|
236
285
|
}
|
|
237
286
|
}
|
|
238
287
|
async mimeType(path, options = {}) {
|
|
288
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...this.options.mimeTypes, ...options });
|
|
239
289
|
try {
|
|
240
|
-
return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path),
|
|
290
|
+
return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path), options);
|
|
241
291
|
}
|
|
242
292
|
catch (error) {
|
|
243
293
|
throw UnableToGetMimeType.because(errorToMessage(error), { cause: error, context: { path, options } });
|
|
244
294
|
}
|
|
245
295
|
}
|
|
246
|
-
async lastModified(path) {
|
|
296
|
+
async lastModified(path, options = {}) {
|
|
297
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
247
298
|
try {
|
|
248
|
-
return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path));
|
|
299
|
+
return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path), options);
|
|
249
300
|
}
|
|
250
301
|
catch (error) {
|
|
251
302
|
throw UnableToGetLastModified.because(errorToMessage(error), { cause: error, context: { path } });
|
|
252
303
|
}
|
|
253
304
|
}
|
|
254
|
-
async fileSize(path) {
|
|
305
|
+
async fileSize(path, options = {}) {
|
|
306
|
+
options = instrumentAbortSignal({ ...this.options.timeout, ...options });
|
|
255
307
|
try {
|
|
256
|
-
return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path));
|
|
308
|
+
return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path), options);
|
|
257
309
|
}
|
|
258
310
|
catch (error) {
|
|
259
311
|
throw UnableToGetFileSize.because(errorToMessage(error), { cause: error, context: { path } });
|
|
@@ -261,7 +313,7 @@ export class FileStorage {
|
|
|
261
313
|
}
|
|
262
314
|
async calculateChecksum(path, options) {
|
|
263
315
|
try {
|
|
264
|
-
return await checksumFromStream(await this.read(path), options);
|
|
316
|
+
return await checksumFromStream(await this.read(path, options), options);
|
|
265
317
|
}
|
|
266
318
|
catch (error) {
|
|
267
319
|
throw UnableToGetChecksum.because(errorToMessage(error), { cause: error, context: { path, options } });
|
|
@@ -279,6 +331,7 @@ export async function closeReadable(body) {
|
|
|
279
331
|
return;
|
|
280
332
|
}
|
|
281
333
|
await new Promise((resolve, reject) => {
|
|
334
|
+
body.on('error', reject);
|
|
282
335
|
body.on('close', (err) => {
|
|
283
336
|
err ? reject(err) : resolve();
|
|
284
337
|
});
|
|
@@ -291,6 +344,15 @@ export async function readableToString(stream) {
|
|
|
291
344
|
await closeReadable(stream);
|
|
292
345
|
return contents;
|
|
293
346
|
}
|
|
347
|
+
export async function readableToBuffer(stream) {
|
|
348
|
+
return new Promise((resolve, reject) => {
|
|
349
|
+
const buffers = [];
|
|
350
|
+
stream.on('data', chunk => buffers.push(Buffer.from(chunk)));
|
|
351
|
+
stream.on('end', () => resolve(Buffer.concat(buffers)));
|
|
352
|
+
stream.on('finish', () => resolve(Buffer.concat(buffers)));
|
|
353
|
+
stream.on('error', err => reject(err));
|
|
354
|
+
});
|
|
355
|
+
}
|
|
294
356
|
const encoder = new TextEncoder();
|
|
295
357
|
export function readableToUint8Array(stream) {
|
|
296
358
|
return new Promise((resolve, reject) => {
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -61,12 +61,24 @@ export declare class UnableToWriteFile extends FlystorageError {
|
|
|
61
61
|
}) => UnableToWriteFile;
|
|
62
62
|
}
|
|
63
63
|
export declare class UnableToReadFile extends FlystorageError {
|
|
64
|
+
readonly wasFileNotFound: boolean;
|
|
65
|
+
readonly context: ErrorContext;
|
|
64
66
|
readonly code = "flystorage.unable_to_read_file";
|
|
67
|
+
constructor(wasFileNotFound: boolean, message: string, context?: ErrorContext, cause?: unknown);
|
|
65
68
|
static because: (reason: string, { context, cause }: {
|
|
66
69
|
context?: ErrorContext;
|
|
67
70
|
cause?: unknown;
|
|
68
71
|
}) => UnableToReadFile;
|
|
72
|
+
static becauseFileWasNotFound: (error: FileWasNotFound) => UnableToReadFile;
|
|
73
|
+
}
|
|
74
|
+
export declare class FileWasNotFound extends FlystorageError {
|
|
75
|
+
readonly code = "flystorage.file_was_not_found";
|
|
76
|
+
static atLocation: (location: string, { context, cause }: {
|
|
77
|
+
context?: ErrorContext;
|
|
78
|
+
cause?: unknown;
|
|
79
|
+
}) => FileWasNotFound;
|
|
69
80
|
}
|
|
81
|
+
export declare function isFileWasNotFound(error: unknown): error is FileWasNotFound;
|
|
70
82
|
export declare class UnableToSetVisibility extends FlystorageError {
|
|
71
83
|
readonly code = "flystorage.unable_to_set_visibility";
|
|
72
84
|
static because: (reason: string, { context, cause }: {
|
|
@@ -21,29 +21,30 @@ export type DirectoryInfo = Readonly<{
|
|
|
21
21
|
export declare function isFile(stat: StatEntry): stat is FileInfo;
|
|
22
22
|
export declare function isDirectory(stat: StatEntry): stat is DirectoryInfo;
|
|
23
23
|
export type StatEntry = FileInfo | DirectoryInfo;
|
|
24
|
+
export type AdapterListOptions = ListOptions & {
|
|
25
|
+
deep: boolean;
|
|
26
|
+
};
|
|
24
27
|
export interface StorageAdapter {
|
|
25
28
|
write(path: string, contents: Readable, options: WriteOptions): Promise<void>;
|
|
26
|
-
read(path: string): Promise<FileContents>;
|
|
27
|
-
deleteFile(path: string): Promise<void>;
|
|
29
|
+
read(path: string, options: MiscellaneousOptions): Promise<FileContents>;
|
|
30
|
+
deleteFile(path: string, options: MiscellaneousOptions): Promise<void>;
|
|
28
31
|
createDirectory(path: string, options: CreateDirectoryOptions): Promise<void>;
|
|
29
32
|
copyFile(from: string, to: string, options: CopyFileOptions): Promise<void>;
|
|
30
33
|
moveFile(from: string, to: string, options: MoveFileOptions): Promise<void>;
|
|
31
|
-
stat(path: string): Promise<StatEntry>;
|
|
32
|
-
list(path: string, options:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
fileExists(path: string): Promise<boolean>;
|
|
39
|
-
directoryExists(path: string): Promise<boolean>;
|
|
34
|
+
stat(path: string, options: MiscellaneousOptions): Promise<StatEntry>;
|
|
35
|
+
list(path: string, options: AdapterListOptions): AsyncGenerator<StatEntry>;
|
|
36
|
+
changeVisibility(path: string, visibility: string, options: MiscellaneousOptions): Promise<void>;
|
|
37
|
+
visibility(path: string, options: MiscellaneousOptions): Promise<string>;
|
|
38
|
+
deleteDirectory(path: string, options: MiscellaneousOptions): Promise<void>;
|
|
39
|
+
fileExists(path: string, options: MiscellaneousOptions): Promise<boolean>;
|
|
40
|
+
directoryExists(path: string, options: MiscellaneousOptions): Promise<boolean>;
|
|
40
41
|
publicUrl(path: string, options: PublicUrlOptions): Promise<string>;
|
|
41
42
|
temporaryUrl(path: string, options: TemporaryUrlOptions): Promise<string>;
|
|
42
43
|
prepareUpload?(path: string, options: UploadRequestOptions): Promise<UploadRequest>;
|
|
43
44
|
checksum(path: string, options: ChecksumOptions): Promise<string>;
|
|
44
45
|
mimeType(path: string, options: MimeTypeOptions): Promise<string>;
|
|
45
|
-
lastModified(path: string): Promise<number>;
|
|
46
|
-
fileSize(path: string): Promise<number>;
|
|
46
|
+
lastModified(path: string, options: MiscellaneousOptions): Promise<number>;
|
|
47
|
+
fileSize(path: string, options: MiscellaneousOptions): Promise<number>;
|
|
47
48
|
}
|
|
48
49
|
export declare class DirectoryListing implements AsyncIterable<StatEntry> {
|
|
49
50
|
private readonly listing;
|
|
@@ -55,8 +56,12 @@ export declare class DirectoryListing implements AsyncIterable<StatEntry> {
|
|
|
55
56
|
[Symbol.asyncIterator](): AsyncGenerator<StatEntry, void, unknown>;
|
|
56
57
|
}
|
|
57
58
|
export type FileContents = Iterable<any> | AsyncIterable<any> | NodeJS.ReadableStream | Readable | string;
|
|
58
|
-
export type
|
|
59
|
+
export type TimeoutOptions = {
|
|
60
|
+
timout?: number;
|
|
61
|
+
};
|
|
62
|
+
export type MiscellaneousOptions = TimeoutOptions & {
|
|
59
63
|
[option: string]: any;
|
|
64
|
+
abortSignal?: AbortSignal;
|
|
60
65
|
};
|
|
61
66
|
export type MimeTypeOptions = MiscellaneousOptions & {
|
|
62
67
|
disallowFallback?: boolean;
|
|
@@ -84,7 +89,7 @@ export type CopyFileOptions = MiscellaneousOptions & VisibilityOptions & {
|
|
|
84
89
|
export type MoveFileOptions = MiscellaneousOptions & VisibilityOptions & {
|
|
85
90
|
retainVisibility?: boolean;
|
|
86
91
|
};
|
|
87
|
-
export type ListOptions = {
|
|
92
|
+
export type ListOptions = MiscellaneousOptions & {
|
|
88
93
|
deep?: boolean;
|
|
89
94
|
};
|
|
90
95
|
export type TemporaryUrlOptions = MiscellaneousOptions & {
|
|
@@ -108,6 +113,7 @@ export type ConfigurationOptions = {
|
|
|
108
113
|
checksums?: ChecksumOptions;
|
|
109
114
|
mimeTypes?: MimeTypeOptions;
|
|
110
115
|
preparedUploadStrategy?: PreparedUploadStrategy;
|
|
116
|
+
timeout?: TimeoutOptions;
|
|
111
117
|
};
|
|
112
118
|
export declare function toReadable(contents: FileContents): Readable;
|
|
113
119
|
export declare class FileStorage {
|
|
@@ -116,33 +122,29 @@ export declare class FileStorage {
|
|
|
116
122
|
private readonly options;
|
|
117
123
|
constructor(adapter: StorageAdapter, pathNormalizer?: PathNormalizer, options?: ConfigurationOptions);
|
|
118
124
|
write(path: string, contents: FileContents, options?: WriteOptions): Promise<void>;
|
|
119
|
-
read(path: string): Promise<Readable>;
|
|
120
|
-
readToString(path: string): Promise<string>;
|
|
121
|
-
readToUint8Array(path: string): Promise<Uint8Array>;
|
|
122
|
-
readToBuffer(path: string): Promise<Buffer>;
|
|
123
|
-
deleteFile(path: string): Promise<void>;
|
|
125
|
+
read(path: string, options?: MiscellaneousOptions): Promise<Readable>;
|
|
126
|
+
readToString(path: string, options?: MiscellaneousOptions): Promise<string>;
|
|
127
|
+
readToUint8Array(path: string, options?: MiscellaneousOptions): Promise<Uint8Array>;
|
|
128
|
+
readToBuffer(path: string, options?: MiscellaneousOptions): Promise<Buffer>;
|
|
129
|
+
deleteFile(path: string, options?: MiscellaneousOptions): Promise<void>;
|
|
124
130
|
createDirectory(path: string, options?: CreateDirectoryOptions): Promise<void>;
|
|
125
|
-
deleteDirectory(path: string): Promise<void>;
|
|
126
|
-
stat(path: string): Promise<StatEntry>;
|
|
131
|
+
deleteDirectory(path: string, options?: MiscellaneousOptions): Promise<void>;
|
|
132
|
+
stat(path: string, options?: MiscellaneousOptions): Promise<StatEntry>;
|
|
127
133
|
moveFile(from: string, to: string, options?: MoveFileOptions): Promise<void>;
|
|
128
134
|
copyFile(from: string, to: string, options?: CopyFileOptions): Promise<void>;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
fileExists(path: string): Promise<boolean>;
|
|
136
|
-
list(path: string, { deep }?: ListOptions): DirectoryListing;
|
|
137
|
-
statFile(path: string): Promise<FileInfo>;
|
|
138
|
-
directoryExists(path: string): Promise<boolean>;
|
|
135
|
+
changeVisibility(path: string, visibility: string, options?: MiscellaneousOptions): Promise<void>;
|
|
136
|
+
visibility(path: string, options?: MiscellaneousOptions): Promise<string>;
|
|
137
|
+
fileExists(path: string, options?: MiscellaneousOptions): Promise<boolean>;
|
|
138
|
+
list(path: string, options?: ListOptions): DirectoryListing;
|
|
139
|
+
statFile(path: string, options?: MiscellaneousOptions): Promise<FileInfo>;
|
|
140
|
+
directoryExists(path: string, options?: MiscellaneousOptions): Promise<boolean>;
|
|
139
141
|
publicUrl(path: string, options?: PublicUrlOptions): Promise<string>;
|
|
140
142
|
temporaryUrl(path: string, options: TemporaryUrlOptions): Promise<string>;
|
|
141
143
|
prepareUpload(path: string, options: UploadRequestOptions): Promise<UploadRequest>;
|
|
142
144
|
checksum(path: string, options?: ChecksumOptions): Promise<string>;
|
|
143
145
|
mimeType(path: string, options?: MimeTypeOptions): Promise<string>;
|
|
144
|
-
lastModified(path: string): Promise<number>;
|
|
145
|
-
fileSize(path: string): Promise<number>;
|
|
146
|
+
lastModified(path: string, options?: MiscellaneousOptions): Promise<number>;
|
|
147
|
+
fileSize(path: string, options?: MiscellaneousOptions): Promise<number>;
|
|
146
148
|
private calculateChecksum;
|
|
147
149
|
}
|
|
148
150
|
export type TimestampMs = number;
|
|
@@ -151,6 +153,7 @@ export declare function normalizeExpiryToDate(expiresAt: ExpiresAt): Date;
|
|
|
151
153
|
export declare function normalizeExpiryToMilliseconds(expiresAt: ExpiresAt): number;
|
|
152
154
|
export declare function closeReadable(body: Readable): Promise<void>;
|
|
153
155
|
export declare function readableToString(stream: Readable): Promise<string>;
|
|
156
|
+
export declare function readableToBuffer(stream: Readable): Promise<Buffer>;
|
|
154
157
|
export declare function readableToUint8Array(stream: Readable): Promise<Uint8Array>;
|
|
155
158
|
export type UploadRequestHeaders = Record<string, string | ReadonlyArray<string>>;
|
|
156
159
|
export type UploadRequest = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flystorage/file-storage",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
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",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"url": "git+https://github.com/duna-oss/flystorage.git",
|
|
31
31
|
"directory": "packages/file-storage"
|
|
32
32
|
},
|
|
33
|
+
"homepage": "https://flystorage.dev/",
|
|
33
34
|
"keywords": [
|
|
34
35
|
"fs",
|
|
35
36
|
"file",
|