@flystorage/file-storage 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -0
- package/dist/cjs/errors.js +16 -1
- package/dist/cjs/file-storage.js +68 -21
- package/dist/esm/errors.js +16 -1
- package/dist/esm/file-storage.js +68 -21
- package/dist/types/errors.d.ts +21 -0
- package/dist/types/file-storage.d.ts +19 -1
- package/package.json +6 -1
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
<img src="https://avatars.githubusercontent.com/u/151840999" width="50px" height="50px" />
|
|
2
|
+
|
|
3
|
+
# Flystorage
|
|
4
|
+
Flystorage is a file storage abstraction for NodeJS and TypeScript. It is an 80/20 solution
|
|
5
|
+
that is built around a set of goals:
|
|
6
|
+
|
|
7
|
+
- Provide a straight-forward API that is easy to use.
|
|
8
|
+
- Allow application code to be unaware WHERE files are stored.
|
|
9
|
+
- Pragmatically smooth over underlying storage differences.
|
|
10
|
+
- Expose an async/await based API, promises all the way.
|
|
11
|
+
- Abstract over file permissions using "visibility".
|
|
12
|
+
- Actually tested using real integrations, _mocks are not welcome_.
|
|
13
|
+
- Stand on the shoulders of giants, use official vendor packages when possible.
|
|
14
|
+
|
|
15
|
+
### What is Flystorage NOT:
|
|
16
|
+
Flystorage is meant to be used in cases for generic file storage use-cases. It's not an API for
|
|
17
|
+
any specific filesystem. It's a generalised solution and will not implement feature only
|
|
18
|
+
specific to one particular storage implementation. There will be use-cases that are not catered
|
|
19
|
+
to, simply because they cannot be abstracted over in a reasonable manner.
|
|
20
|
+
|
|
21
|
+
## Capabilities
|
|
22
|
+
|
|
23
|
+
### Implemented
|
|
24
|
+
- [x] Write files using string | buffer | readable/stream
|
|
25
|
+
- [x] Read files as stream, string, or Uint8Array
|
|
26
|
+
- [x] Set permissions using abstracted visibility
|
|
27
|
+
- [x] List the contents of a directory/prefix, (shallow and deep).
|
|
28
|
+
- [x] Delete files without failing when they don't exist.
|
|
29
|
+
- [x] Delete directories (and any files it contains)
|
|
30
|
+
- [x] Generate public URLs.
|
|
31
|
+
- [x] Generate temporary (signed) URLs.
|
|
32
|
+
- [x] Expose or calculate checksums for files.
|
|
33
|
+
- [x] Mime-type resolving
|
|
34
|
+
- [x] Last modified fetching
|
|
35
|
+
- [x] File size
|
|
36
|
+
|
|
37
|
+
### Planned
|
|
38
|
+
- [ ] Moving files
|
|
39
|
+
- [ ] Copying files
|
|
40
|
+
|
|
41
|
+
## Implementations / Adapters
|
|
42
|
+
|
|
43
|
+
### Implemented
|
|
44
|
+
- [x] Local Filesystem
|
|
45
|
+
- [x] AWS S3 (using the V3 SDK)
|
|
46
|
+
|
|
47
|
+
### Planned
|
|
48
|
+
|
|
49
|
+
#### Prio 1
|
|
50
|
+
- [ ] Azure Blob Storage
|
|
51
|
+
- [ ] Test implementation (in-memory, with staged errors)
|
|
52
|
+
- [ ] Google Cloud Storage
|
|
53
|
+
|
|
54
|
+
### Prio 2
|
|
55
|
+
- [ ] FTP (using `basic-ftp`)
|
|
56
|
+
- [ ] SFTP (?)
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
Install the main package and any adapters you might need:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm i -S @flystorage/file-storage
|
|
63
|
+
|
|
64
|
+
# for using AWS S3
|
|
65
|
+
npm i -S @flystorage/aws-s3
|
|
66
|
+
|
|
67
|
+
# for using the local filesystem
|
|
68
|
+
npm i -S @flystorage/local-fs
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Local Usage
|
|
72
|
+
```typescript
|
|
73
|
+
import {resolve} from 'node:path';
|
|
74
|
+
import {createReadStream} from 'node:fs';
|
|
75
|
+
import {FileStorage, Visibility} from '@flystorage/file-storage';
|
|
76
|
+
import {LocalFileStorage} from '@flystorage/local-fs';
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* SETUP
|
|
80
|
+
**/
|
|
81
|
+
|
|
82
|
+
const rootDirectory = resolve(process.cwd(), 'my-files');
|
|
83
|
+
const storage = new FileStorage(new LocalFileStorage(rootDirectory));
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* USAGE
|
|
87
|
+
**/
|
|
88
|
+
|
|
89
|
+
// Write using a string
|
|
90
|
+
await storage.write('write-from-a-string.txt', 'file contents');
|
|
91
|
+
|
|
92
|
+
// Write using a stream
|
|
93
|
+
const stream = createReadStream(resolve(process.cwd(), 'test-files/picture.png'));
|
|
94
|
+
await storage.write('picture.png', stream);
|
|
95
|
+
|
|
96
|
+
// Write with visibility (permissions).
|
|
97
|
+
await storage.write('public.txt', 'debug', {
|
|
98
|
+
visibility: Visibility.PUBLIC, // mode: 0o644
|
|
99
|
+
});
|
|
100
|
+
await storage.write('private.txt', 'debug', {
|
|
101
|
+
visibility: Visibility.PRIVATE, // mode: 0o600
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// List directory contents
|
|
105
|
+
const contentsAsAsyncGenerator = storage.list('', {deep: true});
|
|
106
|
+
|
|
107
|
+
for await (const item of contentsAsAsyncGenerator) {
|
|
108
|
+
console.log(item.path);
|
|
109
|
+
|
|
110
|
+
if (item.isFile) {
|
|
111
|
+
// do something with the file
|
|
112
|
+
} else if (item.isDirectory) {
|
|
113
|
+
// do something with the directory
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Delete a file
|
|
118
|
+
await storage.deleteFile('some-file.txt');
|
|
119
|
+
|
|
120
|
+
// Delete a directory (with all contents)
|
|
121
|
+
await storage.deleteDirectory('some-directory');
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Author
|
|
125
|
+
Flystorage is built by the maintainer of [Flysystem](https://flysystem.thephpleague.com), a
|
|
126
|
+
filesystem abstraction for PHP. This brings along more than
|
|
127
|
+
a decade of smoothing over filesystem implementation differences
|
|
128
|
+
and weighing trade-offs to make a usable API.
|
package/dist/cjs/errors.js
CHANGED
|
@@ -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.UnableToGetTemporaryUrl = exports.UnableToGetPublicUrl = exports.UnableToGetVisibility = exports.UnableToSetVisibility = exports.UnableToReadFile = exports.UnableToWriteFile = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = exports.errorToMessage = void 0;
|
|
3
|
+
exports.UnableToListDirectory = exports.UnableToCheckDirectoryExistence = exports.UnableToCheckFileExistence = exports.UnableToDeleteFile = exports.UnableToDeleteDirectory = exports.UnableToCreateDirectory = exports.UnableToGetStat = exports.UnableToGetTemporaryUrl = exports.UnableToGetPublicUrl = exports.UnableToGetVisibility = exports.UnableToSetVisibility = exports.UnableToReadFile = exports.UnableToWriteFile = exports.UnableToGetFileSize = exports.UnableToGetLastModified = exports.UnableToGetMimeType = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = exports.errorToMessage = void 0;
|
|
4
4
|
function errorToMessage(error) {
|
|
5
5
|
return error instanceof Error ? error.message : String(error);
|
|
6
6
|
}
|
|
@@ -35,6 +35,21 @@ class UnableToGetChecksum extends FlystorageError {
|
|
|
35
35
|
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetChecksum(`Unable to write the file. Reason: ${reason}`, context, cause);
|
|
36
36
|
}
|
|
37
37
|
exports.UnableToGetChecksum = UnableToGetChecksum;
|
|
38
|
+
class UnableToGetMimeType extends FlystorageError {
|
|
39
|
+
code = 'flystorage.unable_to_get_mimetype';
|
|
40
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetMimeType(`Unable to get mime-type. Reason: ${reason}`, context, cause);
|
|
41
|
+
}
|
|
42
|
+
exports.UnableToGetMimeType = UnableToGetMimeType;
|
|
43
|
+
class UnableToGetLastModified extends FlystorageError {
|
|
44
|
+
code = 'flystorage.unable_to_get_last_modified';
|
|
45
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetLastModified(`Unable to get last modified. Reason: ${reason}`, context, cause);
|
|
46
|
+
}
|
|
47
|
+
exports.UnableToGetLastModified = UnableToGetLastModified;
|
|
48
|
+
class UnableToGetFileSize extends FlystorageError {
|
|
49
|
+
code = 'flystorage.unable_to_get_file_size';
|
|
50
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetFileSize(`Unable to get file size. Reason: ${reason}`, context, cause);
|
|
51
|
+
}
|
|
52
|
+
exports.UnableToGetFileSize = UnableToGetFileSize;
|
|
38
53
|
class UnableToWriteFile extends FlystorageError {
|
|
39
54
|
code = 'flystorage.unable_to_write_file';
|
|
40
55
|
static because = (reason, { context = {}, cause = undefined }) => new UnableToWriteFile(`Unable to write the file. Reason: ${reason}`, context, cause);
|
package/dist/cjs/file-storage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readableToUint8Array = exports.readableToString = exports.closeReadable = exports.normalizeExpiryToMilliseconds = exports.normalizeExpiryToDate = exports.FileStorage = exports.isDirectory = exports.isFile = void 0;
|
|
3
|
+
exports.readableToUint8Array = exports.readableToString = exports.closeReadable = exports.normalizeExpiryToMilliseconds = exports.normalizeExpiryToDate = exports.FileStorage = exports.DirectoryListing = exports.isDirectory = exports.isFile = void 0;
|
|
4
4
|
const stream_1 = require("stream");
|
|
5
5
|
const checksum_from_stream_js_1 = require("./checksum-from-stream.js");
|
|
6
6
|
const errors = require("./errors.js");
|
|
@@ -14,6 +14,45 @@ function isDirectory(stat) {
|
|
|
14
14
|
return stat.isDirectory;
|
|
15
15
|
}
|
|
16
16
|
exports.isDirectory = isDirectory;
|
|
17
|
+
class DirectoryListing {
|
|
18
|
+
listing;
|
|
19
|
+
path;
|
|
20
|
+
deep;
|
|
21
|
+
constructor(listing, path, deep) {
|
|
22
|
+
this.listing = listing;
|
|
23
|
+
this.path = path;
|
|
24
|
+
this.deep = deep;
|
|
25
|
+
}
|
|
26
|
+
async toArray(sorted = true) {
|
|
27
|
+
const items = [];
|
|
28
|
+
for await (const item of this.listing) {
|
|
29
|
+
items.push(item);
|
|
30
|
+
}
|
|
31
|
+
return sorted ? items.sort((a, b) => naturalSorting.compare(a.path, b.path)) : items;
|
|
32
|
+
}
|
|
33
|
+
filter(filter) {
|
|
34
|
+
const listing = this.listing;
|
|
35
|
+
const filtered = (async function* () {
|
|
36
|
+
for await (const entry of listing) {
|
|
37
|
+
if (filter(entry)) {
|
|
38
|
+
yield entry;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
42
|
+
return new DirectoryListing(filtered, this.path, this.deep);
|
|
43
|
+
}
|
|
44
|
+
async *[Symbol.asyncIterator]() {
|
|
45
|
+
try {
|
|
46
|
+
for await (const item of this.listing) {
|
|
47
|
+
yield item;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw errors.UnableToListDirectory.because(errors.errorToMessage(error), { cause: error, context: { path: this.path, deep: this.deep } });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.DirectoryListing = DirectoryListing;
|
|
17
56
|
function toReadable(contents) {
|
|
18
57
|
if (contents instanceof stream_1.Readable) {
|
|
19
58
|
return contents;
|
|
@@ -57,6 +96,9 @@ class FileStorage {
|
|
|
57
96
|
async readToUint8Array(path) {
|
|
58
97
|
return await readableToUint8Array(await this.read(path));
|
|
59
98
|
}
|
|
99
|
+
async readToBuffer(path) {
|
|
100
|
+
return Buffer.from(await this.readToUint8Array(path));
|
|
101
|
+
}
|
|
60
102
|
async deleteFile(path) {
|
|
61
103
|
try {
|
|
62
104
|
await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path));
|
|
@@ -114,26 +156,7 @@ class FileStorage {
|
|
|
114
156
|
}
|
|
115
157
|
}
|
|
116
158
|
list(path, { deep = false } = {}) {
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
async toArray(sorted = true) {
|
|
120
|
-
const items = [];
|
|
121
|
-
for await (const item of listing) {
|
|
122
|
-
items.push(item);
|
|
123
|
-
}
|
|
124
|
-
return sorted ? items.sort((a, b) => naturalSorting.compare(a.path, b.path)) : items;
|
|
125
|
-
},
|
|
126
|
-
async *[Symbol.asyncIterator]() {
|
|
127
|
-
try {
|
|
128
|
-
for await (const item of listing) {
|
|
129
|
-
yield item;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
throw errors.UnableToListDirectory.because(errors.errorToMessage(error), { cause: error, context: { path, deep } });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
};
|
|
159
|
+
return new DirectoryListing(this.adapter.list(this.pathNormalizer.normalizePath(path), { deep }), path, deep);
|
|
137
160
|
}
|
|
138
161
|
async statFile(path) {
|
|
139
162
|
const stat = await this.stat(path);
|
|
@@ -177,6 +200,30 @@ class FileStorage {
|
|
|
177
200
|
throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
|
|
178
201
|
}
|
|
179
202
|
}
|
|
203
|
+
async mimeType(path, options = {}) {
|
|
204
|
+
try {
|
|
205
|
+
return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path), options);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
throw errors.UnableToGetMimeType.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async lastModified(path) {
|
|
212
|
+
try {
|
|
213
|
+
return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path));
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
throw errors.UnableToGetLastModified.because(errors.errorToMessage(error), { cause: error, context: { path } });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async fileSize(path) {
|
|
220
|
+
try {
|
|
221
|
+
return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path));
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
throw errors.UnableToGetFileSize.because(errors.errorToMessage(error), { cause: error, context: { path } });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
180
227
|
async calculateChecksum(path, options) {
|
|
181
228
|
try {
|
|
182
229
|
return await (0, checksum_from_stream_js_1.checksumFromStream)(await this.read(path), options);
|
package/dist/esm/errors.js
CHANGED
|
@@ -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.UnableToGetTemporaryUrl = exports.UnableToGetPublicUrl = exports.UnableToGetVisibility = exports.UnableToSetVisibility = exports.UnableToReadFile = exports.UnableToWriteFile = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = exports.errorToMessage = void 0;
|
|
3
|
+
exports.UnableToListDirectory = exports.UnableToCheckDirectoryExistence = exports.UnableToCheckFileExistence = exports.UnableToDeleteFile = exports.UnableToDeleteDirectory = exports.UnableToCreateDirectory = exports.UnableToGetStat = exports.UnableToGetTemporaryUrl = exports.UnableToGetPublicUrl = exports.UnableToGetVisibility = exports.UnableToSetVisibility = exports.UnableToReadFile = exports.UnableToWriteFile = exports.UnableToGetFileSize = exports.UnableToGetLastModified = exports.UnableToGetMimeType = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = exports.errorToMessage = void 0;
|
|
4
4
|
function errorToMessage(error) {
|
|
5
5
|
return error instanceof Error ? error.message : String(error);
|
|
6
6
|
}
|
|
@@ -35,6 +35,21 @@ class UnableToGetChecksum extends FlystorageError {
|
|
|
35
35
|
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetChecksum(`Unable to write the file. Reason: ${reason}`, context, cause);
|
|
36
36
|
}
|
|
37
37
|
exports.UnableToGetChecksum = UnableToGetChecksum;
|
|
38
|
+
class UnableToGetMimeType extends FlystorageError {
|
|
39
|
+
code = 'flystorage.unable_to_get_mimetype';
|
|
40
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetMimeType(`Unable to get mime-type. Reason: ${reason}`, context, cause);
|
|
41
|
+
}
|
|
42
|
+
exports.UnableToGetMimeType = UnableToGetMimeType;
|
|
43
|
+
class UnableToGetLastModified extends FlystorageError {
|
|
44
|
+
code = 'flystorage.unable_to_get_last_modified';
|
|
45
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetLastModified(`Unable to get last modified. Reason: ${reason}`, context, cause);
|
|
46
|
+
}
|
|
47
|
+
exports.UnableToGetLastModified = UnableToGetLastModified;
|
|
48
|
+
class UnableToGetFileSize extends FlystorageError {
|
|
49
|
+
code = 'flystorage.unable_to_get_file_size';
|
|
50
|
+
static because = (reason, { context = {}, cause = undefined }) => new UnableToGetFileSize(`Unable to get file size. Reason: ${reason}`, context, cause);
|
|
51
|
+
}
|
|
52
|
+
exports.UnableToGetFileSize = UnableToGetFileSize;
|
|
38
53
|
class UnableToWriteFile extends FlystorageError {
|
|
39
54
|
code = 'flystorage.unable_to_write_file';
|
|
40
55
|
static because = (reason, { context = {}, cause = undefined }) => new UnableToWriteFile(`Unable to write the file. Reason: ${reason}`, context, cause);
|
package/dist/esm/file-storage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readableToUint8Array = exports.readableToString = exports.closeReadable = exports.normalizeExpiryToMilliseconds = exports.normalizeExpiryToDate = exports.FileStorage = exports.isDirectory = exports.isFile = void 0;
|
|
3
|
+
exports.readableToUint8Array = exports.readableToString = exports.closeReadable = exports.normalizeExpiryToMilliseconds = exports.normalizeExpiryToDate = exports.FileStorage = exports.DirectoryListing = exports.isDirectory = exports.isFile = void 0;
|
|
4
4
|
const stream_1 = require("stream");
|
|
5
5
|
const checksum_from_stream_js_1 = require("./checksum-from-stream.js");
|
|
6
6
|
const errors = require("./errors.js");
|
|
@@ -14,6 +14,45 @@ function isDirectory(stat) {
|
|
|
14
14
|
return stat.isDirectory;
|
|
15
15
|
}
|
|
16
16
|
exports.isDirectory = isDirectory;
|
|
17
|
+
class DirectoryListing {
|
|
18
|
+
listing;
|
|
19
|
+
path;
|
|
20
|
+
deep;
|
|
21
|
+
constructor(listing, path, deep) {
|
|
22
|
+
this.listing = listing;
|
|
23
|
+
this.path = path;
|
|
24
|
+
this.deep = deep;
|
|
25
|
+
}
|
|
26
|
+
async toArray(sorted = true) {
|
|
27
|
+
const items = [];
|
|
28
|
+
for await (const item of this.listing) {
|
|
29
|
+
items.push(item);
|
|
30
|
+
}
|
|
31
|
+
return sorted ? items.sort((a, b) => naturalSorting.compare(a.path, b.path)) : items;
|
|
32
|
+
}
|
|
33
|
+
filter(filter) {
|
|
34
|
+
const listing = this.listing;
|
|
35
|
+
const filtered = (async function* () {
|
|
36
|
+
for await (const entry of listing) {
|
|
37
|
+
if (filter(entry)) {
|
|
38
|
+
yield entry;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
})();
|
|
42
|
+
return new DirectoryListing(filtered, this.path, this.deep);
|
|
43
|
+
}
|
|
44
|
+
async *[Symbol.asyncIterator]() {
|
|
45
|
+
try {
|
|
46
|
+
for await (const item of this.listing) {
|
|
47
|
+
yield item;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
throw errors.UnableToListDirectory.because(errors.errorToMessage(error), { cause: error, context: { path: this.path, deep: this.deep } });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.DirectoryListing = DirectoryListing;
|
|
17
56
|
function toReadable(contents) {
|
|
18
57
|
if (contents instanceof stream_1.Readable) {
|
|
19
58
|
return contents;
|
|
@@ -57,6 +96,9 @@ class FileStorage {
|
|
|
57
96
|
async readToUint8Array(path) {
|
|
58
97
|
return await readableToUint8Array(await this.read(path));
|
|
59
98
|
}
|
|
99
|
+
async readToBuffer(path) {
|
|
100
|
+
return Buffer.from(await this.readToUint8Array(path));
|
|
101
|
+
}
|
|
60
102
|
async deleteFile(path) {
|
|
61
103
|
try {
|
|
62
104
|
await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path));
|
|
@@ -114,26 +156,7 @@ class FileStorage {
|
|
|
114
156
|
}
|
|
115
157
|
}
|
|
116
158
|
list(path, { deep = false } = {}) {
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
async toArray(sorted = true) {
|
|
120
|
-
const items = [];
|
|
121
|
-
for await (const item of listing) {
|
|
122
|
-
items.push(item);
|
|
123
|
-
}
|
|
124
|
-
return sorted ? items.sort((a, b) => naturalSorting.compare(a.path, b.path)) : items;
|
|
125
|
-
},
|
|
126
|
-
async *[Symbol.asyncIterator]() {
|
|
127
|
-
try {
|
|
128
|
-
for await (const item of listing) {
|
|
129
|
-
yield item;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
throw errors.UnableToListDirectory.because(errors.errorToMessage(error), { cause: error, context: { path, deep } });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
};
|
|
159
|
+
return new DirectoryListing(this.adapter.list(this.pathNormalizer.normalizePath(path), { deep }), path, deep);
|
|
137
160
|
}
|
|
138
161
|
async statFile(path) {
|
|
139
162
|
const stat = await this.stat(path);
|
|
@@ -177,6 +200,30 @@ class FileStorage {
|
|
|
177
200
|
throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
|
|
178
201
|
}
|
|
179
202
|
}
|
|
203
|
+
async mimeType(path, options = {}) {
|
|
204
|
+
try {
|
|
205
|
+
return await this.adapter.mimeType(this.pathNormalizer.normalizePath(path), options);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
throw errors.UnableToGetMimeType.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async lastModified(path) {
|
|
212
|
+
try {
|
|
213
|
+
return await this.adapter.lastModified(this.pathNormalizer.normalizePath(path));
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
throw errors.UnableToGetLastModified.because(errors.errorToMessage(error), { cause: error, context: { path } });
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async fileSize(path) {
|
|
220
|
+
try {
|
|
221
|
+
return await this.adapter.fileSize(this.pathNormalizer.normalizePath(path));
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
throw errors.UnableToGetFileSize.because(errors.errorToMessage(error), { cause: error, context: { path } });
|
|
225
|
+
}
|
|
226
|
+
}
|
|
180
227
|
async calculateChecksum(path, options) {
|
|
181
228
|
try {
|
|
182
229
|
return await (0, checksum_from_stream_js_1.checksumFromStream)(await this.read(path), options);
|
package/dist/types/errors.d.ts
CHANGED
|
@@ -31,6 +31,27 @@ export declare class UnableToGetChecksum extends FlystorageError {
|
|
|
31
31
|
cause?: unknown;
|
|
32
32
|
}) => UnableToGetChecksum;
|
|
33
33
|
}
|
|
34
|
+
export declare class UnableToGetMimeType extends FlystorageError {
|
|
35
|
+
readonly code = "flystorage.unable_to_get_mimetype";
|
|
36
|
+
static because: (reason: string, { context, cause }: {
|
|
37
|
+
context?: ErrorContext | undefined;
|
|
38
|
+
cause?: unknown;
|
|
39
|
+
}) => UnableToGetMimeType;
|
|
40
|
+
}
|
|
41
|
+
export declare class UnableToGetLastModified extends FlystorageError {
|
|
42
|
+
readonly code = "flystorage.unable_to_get_last_modified";
|
|
43
|
+
static because: (reason: string, { context, cause }: {
|
|
44
|
+
context?: ErrorContext | undefined;
|
|
45
|
+
cause?: unknown;
|
|
46
|
+
}) => UnableToGetLastModified;
|
|
47
|
+
}
|
|
48
|
+
export declare class UnableToGetFileSize extends FlystorageError {
|
|
49
|
+
readonly code = "flystorage.unable_to_get_file_size";
|
|
50
|
+
static because: (reason: string, { context, cause }: {
|
|
51
|
+
context?: ErrorContext | undefined;
|
|
52
|
+
cause?: unknown;
|
|
53
|
+
}) => UnableToGetFileSize;
|
|
54
|
+
}
|
|
34
55
|
export declare class UnableToWriteFile extends FlystorageError {
|
|
35
56
|
readonly code = "flystorage.unable_to_write_file";
|
|
36
57
|
static because: (reason: string, { context, cause }: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
/// <reference types="node" />
|
|
4
|
+
/// <reference types="node" />
|
|
4
5
|
import { BinaryToTextEncoding } from 'crypto';
|
|
5
6
|
import { Readable } from 'stream';
|
|
6
7
|
import { PathNormalizer } from './path-normalizer.js';
|
|
@@ -41,14 +42,27 @@ export interface StorageAdapter {
|
|
|
41
42
|
publicUrl(path: string, options: PublicUrlOptions): Promise<string>;
|
|
42
43
|
temporaryUrl(path: string, options: TemporaryUrlOptions): Promise<string>;
|
|
43
44
|
checksum(path: string, options: ChecksumOptions): Promise<string>;
|
|
45
|
+
mimeType(path: string, options: MimeTypeOptions): Promise<string>;
|
|
46
|
+
lastModified(path: string): Promise<number>;
|
|
47
|
+
fileSize(path: string): Promise<number>;
|
|
44
48
|
}
|
|
45
|
-
export
|
|
49
|
+
export declare class DirectoryListing implements AsyncIterable<StatEntry> {
|
|
50
|
+
private readonly listing;
|
|
51
|
+
private readonly path;
|
|
52
|
+
private readonly deep;
|
|
53
|
+
constructor(listing: AsyncGenerator<StatEntry>, path: string, deep: boolean);
|
|
46
54
|
toArray(sorted?: boolean): Promise<StatEntry[]>;
|
|
55
|
+
filter(filter: (entry: StatEntry) => boolean): DirectoryListing;
|
|
56
|
+
[Symbol.asyncIterator](): AsyncGenerator<StatEntry, void, unknown>;
|
|
47
57
|
}
|
|
48
58
|
export type FileContents = Iterable<any> | AsyncIterable<any> | NodeJS.ReadableStream | Readable;
|
|
49
59
|
export type MiscellaneousOptions = {
|
|
50
60
|
[option: string]: any;
|
|
51
61
|
};
|
|
62
|
+
export type MimeTypeOptions = MiscellaneousOptions & {
|
|
63
|
+
disallowFallback?: boolean;
|
|
64
|
+
fallbackMethod?: 'contents' | 'path';
|
|
65
|
+
};
|
|
52
66
|
export type VisibilityOptions = {
|
|
53
67
|
visibility?: string;
|
|
54
68
|
directoryVisibility?: string;
|
|
@@ -79,6 +93,7 @@ export declare class FileStorage {
|
|
|
79
93
|
read(path: string): Promise<Readable>;
|
|
80
94
|
readToString(path: string): Promise<string>;
|
|
81
95
|
readToUint8Array(path: string): Promise<Uint8Array>;
|
|
96
|
+
readToBuffer(path: string): Promise<Buffer>;
|
|
82
97
|
deleteFile(path: string): Promise<void>;
|
|
83
98
|
createDirectory(path: string, options?: CreateDirectoryOptions): Promise<void>;
|
|
84
99
|
deleteDirectory(path: string): Promise<void>;
|
|
@@ -94,6 +109,9 @@ export declare class FileStorage {
|
|
|
94
109
|
publicUrl(path: string, options?: PublicUrlOptions): Promise<string>;
|
|
95
110
|
temporaryUrl(path: string, options: TemporaryUrlOptions): Promise<string>;
|
|
96
111
|
checksum(path: string, options?: ChecksumOptions): Promise<string>;
|
|
112
|
+
mimeType(path: string, options?: MimeTypeOptions): Promise<string>;
|
|
113
|
+
lastModified(path: string): Promise<number>;
|
|
114
|
+
fileSize(path: string): Promise<number>;
|
|
97
115
|
private calculateChecksum;
|
|
98
116
|
}
|
|
99
117
|
export type TimestampMs = number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flystorage/file-storage",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "File-storage abstraction: multiple filesystems, one API.",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
"watch": "tsc --watch"
|
|
25
25
|
},
|
|
26
26
|
"author": "Frank de Jonge (https://frankdejonge.nl)",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/flystorage/flystorage.git",
|
|
30
|
+
"directory": "packages/file-storage"
|
|
31
|
+
},
|
|
27
32
|
"keywords": ["fs", "file", "files", "filesystem", "filesystems", "storage"],
|
|
28
33
|
"license": "MIT"
|
|
29
34
|
}
|