@flystorage/file-storage 0.0.3 → 0.0.5

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 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.
@@ -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
- const listing = this.adapter.list(this.pathNormalizer.normalizePath(path), { deep });
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);
@@ -0,0 +1 @@
1
+ {"type": "commonjs"}
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checksumFromStream = void 0;
4
- const crypto_1 = require("crypto");
5
- async function checksumFromStream(stream, options) {
1
+ import { createHash } from 'crypto';
2
+ export async function checksumFromStream(stream, options) {
6
3
  return new Promise(async (resolve, reject) => {
7
- const hash = (0, crypto_1.createHash)(options.algo ?? 'md5');
4
+ const hash = createHash(options.algo ?? 'md5');
8
5
  stream.on('error', reject);
9
6
  stream.pipe(hash, { end: false });
10
7
  stream.on('end', () => {
@@ -13,4 +10,3 @@ async function checksumFromStream(stream, options) {
13
10
  });
14
11
  });
15
12
  }
16
- exports.checksumFromStream = checksumFromStream;
@@ -1,11 +1,7 @@
1
- "use strict";
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.UnableToGetFileSize = exports.UnableToGetLastModified = exports.UnableToGetMimeType = exports.UnableToGetChecksum = exports.ChecksumIsNotAvailable = exports.FlystorageError = exports.errorToMessage = void 0;
4
- function errorToMessage(error) {
1
+ export function errorToMessage(error) {
5
2
  return error instanceof Error ? error.message : String(error);
6
3
  }
7
- exports.errorToMessage = errorToMessage;
8
- class FlystorageError extends Error {
4
+ export class FlystorageError extends Error {
9
5
  context;
10
6
  code = 'unknown_error';
11
7
  constructor(message, context = {}, cause = undefined) {
@@ -15,12 +11,11 @@ class FlystorageError extends Error {
15
11
  this.context = context;
16
12
  }
17
13
  }
18
- exports.FlystorageError = FlystorageError;
19
14
  /**
20
15
  * Thrown when the checksum algo is not supported or not pre-computed. This error
21
16
  * is thrown with the intention of falling back to computing it based on a file read.
22
17
  */
23
- class ChecksumIsNotAvailable extends FlystorageError {
18
+ export class ChecksumIsNotAvailable extends FlystorageError {
24
19
  algo;
25
20
  code = 'flystorage.checksum_not_supported';
26
21
  constructor(message, algo, context = {}, cause = undefined) {
@@ -29,90 +24,72 @@ class ChecksumIsNotAvailable extends FlystorageError {
29
24
  }
30
25
  static checksumNotSupported = (algo, { context = {}, cause = undefined } = {}) => new ChecksumIsNotAvailable(`Checksum algo "${algo}" is not supported`, algo, { ...context, algo }, cause);
31
26
  }
32
- exports.ChecksumIsNotAvailable = ChecksumIsNotAvailable;
33
- class UnableToGetChecksum extends FlystorageError {
27
+ export class UnableToGetChecksum extends FlystorageError {
34
28
  code = 'flystorage.unable_to_get_checksum';
35
29
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetChecksum(`Unable to write the file. Reason: ${reason}`, context, cause);
36
30
  }
37
- exports.UnableToGetChecksum = UnableToGetChecksum;
38
- class UnableToGetMimeType extends FlystorageError {
31
+ export class UnableToGetMimeType extends FlystorageError {
39
32
  code = 'flystorage.unable_to_get_mimetype';
40
33
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetMimeType(`Unable to get mime-type. Reason: ${reason}`, context, cause);
41
34
  }
42
- exports.UnableToGetMimeType = UnableToGetMimeType;
43
- class UnableToGetLastModified extends FlystorageError {
35
+ export class UnableToGetLastModified extends FlystorageError {
44
36
  code = 'flystorage.unable_to_get_last_modified';
45
37
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetLastModified(`Unable to get last modified. Reason: ${reason}`, context, cause);
46
38
  }
47
- exports.UnableToGetLastModified = UnableToGetLastModified;
48
- class UnableToGetFileSize extends FlystorageError {
39
+ export class UnableToGetFileSize extends FlystorageError {
49
40
  code = 'flystorage.unable_to_get_file_size';
50
41
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetFileSize(`Unable to get file size. Reason: ${reason}`, context, cause);
51
42
  }
52
- exports.UnableToGetFileSize = UnableToGetFileSize;
53
- class UnableToWriteFile extends FlystorageError {
43
+ export class UnableToWriteFile extends FlystorageError {
54
44
  code = 'flystorage.unable_to_write_file';
55
45
  static because = (reason, { context = {}, cause = undefined }) => new UnableToWriteFile(`Unable to write the file. Reason: ${reason}`, context, cause);
56
46
  }
57
- exports.UnableToWriteFile = UnableToWriteFile;
58
- class UnableToReadFile extends FlystorageError {
47
+ export class UnableToReadFile extends FlystorageError {
59
48
  code = 'flystorage.unable_to_read_file';
60
49
  static because = (reason, { context = {}, cause = undefined }) => new UnableToReadFile(`Unable to read the file. Reason: ${reason}`, context, cause);
61
50
  }
62
- exports.UnableToReadFile = UnableToReadFile;
63
- class UnableToSetVisibility extends FlystorageError {
51
+ export class UnableToSetVisibility extends FlystorageError {
64
52
  code = 'flystorage.unable_to_set_visibility';
65
53
  static because = (reason, { context = {}, cause = undefined }) => new UnableToSetVisibility(`Unable to set visibility. Reason: ${reason}`, context, cause);
66
54
  }
67
- exports.UnableToSetVisibility = UnableToSetVisibility;
68
- class UnableToGetVisibility extends FlystorageError {
55
+ export class UnableToGetVisibility extends FlystorageError {
69
56
  code = 'flystorage.unable_to_get_visibility';
70
57
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetVisibility(`Unable to get visibility. Reason: ${reason}`, context, cause);
71
58
  }
72
- exports.UnableToGetVisibility = UnableToGetVisibility;
73
- class UnableToGetPublicUrl extends FlystorageError {
59
+ export class UnableToGetPublicUrl extends FlystorageError {
74
60
  code = 'flystorage.unable_to_get_public_url';
75
61
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetPublicUrl(`Unable to get public URL. Reason: ${reason}`, context, cause);
76
62
  }
77
- exports.UnableToGetPublicUrl = UnableToGetPublicUrl;
78
- class UnableToGetTemporaryUrl extends FlystorageError {
63
+ export class UnableToGetTemporaryUrl extends FlystorageError {
79
64
  code = 'flystorage.unable_to_get_temporary_url';
80
65
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetTemporaryUrl(`Unable to get temporary URL. Reason: ${reason}`, context, cause);
81
66
  }
82
- exports.UnableToGetTemporaryUrl = UnableToGetTemporaryUrl;
83
- class UnableToGetStat extends FlystorageError {
67
+ export class UnableToGetStat extends FlystorageError {
84
68
  code = 'flystorage.unable_to_get_stat';
85
69
  static because = (reason, { context = {}, cause = undefined }) => new UnableToGetStat(`Unable to get stat. Reason: ${reason}`, context, cause);
86
70
  static noFileStatResolved = ({ context = {}, cause = undefined }) => new UnableToGetStat(`Stat was not a file.`, context, cause);
87
71
  }
88
- exports.UnableToGetStat = UnableToGetStat;
89
- class UnableToCreateDirectory extends FlystorageError {
72
+ export class UnableToCreateDirectory extends FlystorageError {
90
73
  code = 'flystorage.unable_to_create_directory';
91
74
  static because = (reason, { context = {}, cause = undefined }) => new UnableToCreateDirectory(`Unable to create directory. Reason: ${reason}`, context, cause);
92
75
  }
93
- exports.UnableToCreateDirectory = UnableToCreateDirectory;
94
- class UnableToDeleteDirectory extends FlystorageError {
76
+ export class UnableToDeleteDirectory extends FlystorageError {
95
77
  code = 'flystorage.unable_to_delete_directory';
96
78
  static because = (reason, { context = {}, cause = undefined }) => new UnableToDeleteDirectory(`Unable to delete directory. Reason: ${reason}`, context, cause);
97
79
  }
98
- exports.UnableToDeleteDirectory = UnableToDeleteDirectory;
99
- class UnableToDeleteFile extends FlystorageError {
80
+ export class UnableToDeleteFile extends FlystorageError {
100
81
  code = 'flystorage.unable_to_delete_file';
101
82
  static because = (reason, { context = {}, cause = undefined }) => new UnableToDeleteFile(`Unable to delete file. Reason: ${reason}`, context, cause);
102
83
  }
103
- exports.UnableToDeleteFile = UnableToDeleteFile;
104
- class UnableToCheckFileExistence extends FlystorageError {
84
+ export class UnableToCheckFileExistence extends FlystorageError {
105
85
  code = 'flystorage.unable_to_check_file_existence';
106
86
  static because = (reason, { context = {}, cause = undefined }) => new UnableToCheckFileExistence(`Unable to check file existence. Reason: ${reason}`, context, cause);
107
87
  }
108
- exports.UnableToCheckFileExistence = UnableToCheckFileExistence;
109
- class UnableToCheckDirectoryExistence extends FlystorageError {
88
+ export class UnableToCheckDirectoryExistence extends FlystorageError {
110
89
  code = 'flystorage.unable_to_check_directory_existence';
111
90
  static because = (reason, { context = {}, cause = undefined }) => new UnableToCheckDirectoryExistence(`Unable to check directory existence. Reason: ${reason}`, context, cause);
112
91
  }
113
- exports.UnableToCheckDirectoryExistence = UnableToCheckDirectoryExistence;
114
- class UnableToListDirectory extends FlystorageError {
92
+ export class UnableToListDirectory extends FlystorageError {
115
93
  code = 'flystorage.unable_to_list_directory_contents';
116
94
  static because = (reason, { context = {}, cause = undefined }) => new UnableToListDirectory(`Unable to list directory contents. Reason: ${reason}`, context, cause);
117
95
  }
118
- exports.UnableToListDirectory = UnableToListDirectory;
@@ -1,34 +1,67 @@
1
- "use strict";
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;
4
- const stream_1 = require("stream");
5
- const checksum_from_stream_js_1 = require("./checksum-from-stream.js");
6
- const errors = require("./errors.js");
7
- const errors_js_1 = require("./errors.js");
8
- const path_normalizer_js_1 = require("./path-normalizer.js");
9
- function isFile(stat) {
1
+ import { Readable } from 'stream';
2
+ import { checksumFromStream } from './checksum-from-stream.js';
3
+ import * as errors from './errors.js';
4
+ import { ChecksumIsNotAvailable } from './errors.js';
5
+ import { PathNormalizerV1 } from './path-normalizer.js';
6
+ export function isFile(stat) {
10
7
  return stat.isFile;
11
8
  }
12
- exports.isFile = isFile;
13
- function isDirectory(stat) {
9
+ export function isDirectory(stat) {
14
10
  return stat.isDirectory;
15
11
  }
16
- exports.isDirectory = isDirectory;
12
+ export class DirectoryListing {
13
+ listing;
14
+ path;
15
+ deep;
16
+ constructor(listing, path, deep) {
17
+ this.listing = listing;
18
+ this.path = path;
19
+ this.deep = deep;
20
+ }
21
+ async toArray(sorted = true) {
22
+ const items = [];
23
+ for await (const item of this.listing) {
24
+ items.push(item);
25
+ }
26
+ return sorted ? items.sort((a, b) => naturalSorting.compare(a.path, b.path)) : items;
27
+ }
28
+ filter(filter) {
29
+ const listing = this.listing;
30
+ const filtered = (async function* () {
31
+ for await (const entry of listing) {
32
+ if (filter(entry)) {
33
+ yield entry;
34
+ }
35
+ }
36
+ })();
37
+ return new DirectoryListing(filtered, this.path, this.deep);
38
+ }
39
+ async *[Symbol.asyncIterator]() {
40
+ try {
41
+ for await (const item of this.listing) {
42
+ yield item;
43
+ }
44
+ }
45
+ catch (error) {
46
+ throw errors.UnableToListDirectory.because(errors.errorToMessage(error), { cause: error, context: { path: this.path, deep: this.deep } });
47
+ }
48
+ }
49
+ }
17
50
  function toReadable(contents) {
18
- if (contents instanceof stream_1.Readable) {
51
+ if (contents instanceof Readable) {
19
52
  return contents;
20
53
  }
21
- return stream_1.Readable.from(contents);
54
+ return Readable.from(contents);
22
55
  }
23
56
  const naturalSorting = new Intl.Collator(undefined, {
24
57
  numeric: true,
25
58
  sensitivity: 'base'
26
59
  });
27
- class FileStorage {
60
+ export class FileStorage {
28
61
  adapter;
29
62
  pathNormalizer;
30
63
  options;
31
- constructor(adapter, pathNormalizer = new path_normalizer_js_1.PathNormalizerV1(), options = {}) {
64
+ constructor(adapter, pathNormalizer = new PathNormalizerV1(), options = {}) {
32
65
  this.adapter = adapter;
33
66
  this.pathNormalizer = pathNormalizer;
34
67
  this.options = options;
@@ -45,7 +78,7 @@ class FileStorage {
45
78
  }
46
79
  async read(path) {
47
80
  try {
48
- return stream_1.Readable.from(await this.adapter.read(this.pathNormalizer.normalizePath(path)));
81
+ return Readable.from(await this.adapter.read(this.pathNormalizer.normalizePath(path)));
49
82
  }
50
83
  catch (error) {
51
84
  throw errors.UnableToReadFile.because(errors.errorToMessage(error), { cause: error, context: { path } });
@@ -57,6 +90,9 @@ class FileStorage {
57
90
  async readToUint8Array(path) {
58
91
  return await readableToUint8Array(await this.read(path));
59
92
  }
93
+ async readToBuffer(path) {
94
+ return Buffer.from(await this.readToUint8Array(path));
95
+ }
60
96
  async deleteFile(path) {
61
97
  try {
62
98
  await this.adapter.deleteFile(this.pathNormalizer.normalizePath(path));
@@ -114,26 +150,7 @@ class FileStorage {
114
150
  }
115
151
  }
116
152
  list(path, { deep = false } = {}) {
117
- const listing = this.adapter.list(this.pathNormalizer.normalizePath(path), { deep });
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
- };
153
+ return new DirectoryListing(this.adapter.list(this.pathNormalizer.normalizePath(path), { deep }), path, deep);
137
154
  }
138
155
  async statFile(path) {
139
156
  const stat = await this.stat(path);
@@ -171,7 +188,7 @@ class FileStorage {
171
188
  return await this.adapter.checksum(this.pathNormalizer.normalizePath(path), options);
172
189
  }
173
190
  catch (error) {
174
- if (error instanceof errors_js_1.ChecksumIsNotAvailable) {
191
+ if (error instanceof ChecksumIsNotAvailable) {
175
192
  return this.calculateChecksum(path, options);
176
193
  }
177
194
  throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
@@ -203,23 +220,20 @@ class FileStorage {
203
220
  }
204
221
  async calculateChecksum(path, options) {
205
222
  try {
206
- return await (0, checksum_from_stream_js_1.checksumFromStream)(await this.read(path), options);
223
+ return await checksumFromStream(await this.read(path), options);
207
224
  }
208
225
  catch (error) {
209
226
  throw errors.UnableToGetChecksum.because(errors.errorToMessage(error), { cause: error, context: { path, options } });
210
227
  }
211
228
  }
212
229
  }
213
- exports.FileStorage = FileStorage;
214
- function normalizeExpiryToDate(expiresAt) {
230
+ export function normalizeExpiryToDate(expiresAt) {
215
231
  return expiresAt instanceof Date ? expiresAt : new Date(expiresAt);
216
232
  }
217
- exports.normalizeExpiryToDate = normalizeExpiryToDate;
218
- function normalizeExpiryToMilliseconds(expiresAt) {
233
+ export function normalizeExpiryToMilliseconds(expiresAt) {
219
234
  return expiresAt instanceof Date ? expiresAt.getTime() : expiresAt;
220
235
  }
221
- exports.normalizeExpiryToMilliseconds = normalizeExpiryToMilliseconds;
222
- async function closeReadable(body) {
236
+ export async function closeReadable(body) {
223
237
  if (body.closed) {
224
238
  return;
225
239
  }
@@ -230,13 +244,11 @@ async function closeReadable(body) {
230
244
  body.destroy();
231
245
  });
232
246
  }
233
- exports.closeReadable = closeReadable;
234
247
  const decoder = new TextDecoder();
235
- async function readableToString(stream) {
248
+ export async function readableToString(stream) {
236
249
  return decoder.decode(await readableToUint8Array(stream));
237
250
  }
238
- exports.readableToString = readableToString;
239
- function readableToUint8Array(stream) {
251
+ export function readableToUint8Array(stream) {
240
252
  return new Promise((resolve, reject) => {
241
253
  const parts = [];
242
254
  stream.on('data', (chunk) => {
@@ -246,7 +258,6 @@ function readableToUint8Array(stream) {
246
258
  stream.on('end', () => resolve(concatUint8Arrays(parts)));
247
259
  });
248
260
  }
249
- exports.readableToUint8Array = readableToUint8Array;
250
261
  function concatUint8Arrays(input) {
251
262
  const length = input.reduce((l, a) => l + a.byteLength, 0);
252
263
  const output = new Uint8Array(length);
package/dist/esm/index.js CHANGED
@@ -1,22 +1,6 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./checksum-from-stream.js"), exports);
18
- __exportStar(require("./file-storage.js"), exports);
19
- __exportStar(require("./errors.js"), exports);
20
- __exportStar(require("./path-normalizer.js"), exports);
21
- __exportStar(require("./path-prefixer.js"), exports);
22
- __exportStar(require("./portable-visibility.js"), exports);
1
+ export * from './checksum-from-stream.js';
2
+ export * from './file-storage.js';
3
+ export * from './errors.js';
4
+ export * from './path-normalizer.js';
5
+ export * from './path-prefixer.js';
6
+ export * from './portable-visibility.js';
@@ -1,26 +1,20 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PathTraversalDetected = exports.CorruptedPathDetected = exports.PathNormalizerV1 = void 0;
4
- const node_path_1 = require("node:path");
1
+ import { join } from 'node:path';
5
2
  const funkyWhiteSpaceRegex = new RegExp('\\p{C}+', 'u');
6
- class PathNormalizerV1 {
3
+ export class PathNormalizerV1 {
7
4
  normalizePath(path) {
8
5
  if (funkyWhiteSpaceRegex.test(path)) {
9
6
  throw CorruptedPathDetected.unexpectedWhitespace(path);
10
7
  }
11
- const normalized = (0, node_path_1.join)(...(path.split('/')));
8
+ const normalized = join(...(path.split('/')));
12
9
  if (normalized.indexOf('../') !== -1 || normalized == '..') {
13
10
  throw PathTraversalDetected.forPath(path);
14
11
  }
15
12
  return normalized === '.' ? '' : normalized;
16
13
  }
17
14
  }
18
- exports.PathNormalizerV1 = PathNormalizerV1;
19
- class CorruptedPathDetected extends Error {
15
+ export class CorruptedPathDetected extends Error {
20
16
  static unexpectedWhitespace = (path) => new CorruptedPathDetected(`Corrupted path detected with unexpected whitespace: ${path}`);
21
17
  }
22
- exports.CorruptedPathDetected = CorruptedPathDetected;
23
- class PathTraversalDetected extends Error {
18
+ export class PathTraversalDetected extends Error {
24
19
  static forPath = (path) => new PathTraversalDetected(`Path traversal detected for: ${path}`);
25
20
  }
26
- exports.PathTraversalDetected = PathTraversalDetected;
@@ -1,19 +1,16 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PathPrefixer = void 0;
4
- const node_path_1 = require("node:path");
5
- class PathPrefixer {
1
+ import { join } from 'node:path';
2
+ export class PathPrefixer {
6
3
  prefix = '';
7
4
  constructor(prefix = '') {
8
5
  if (prefix.length > 0) {
9
- this.prefix = (0, node_path_1.join)(prefix, '/');
6
+ this.prefix = join(prefix, '/');
10
7
  }
11
8
  }
12
9
  prefixFilePath(path) {
13
- return this.prefix.length > 0 ? (0, node_path_1.join)(this.prefix, path) : path;
10
+ return this.prefix.length > 0 ? join(this.prefix, path) : path;
14
11
  }
15
12
  prefixDirectoryPath(path) {
16
- return this.prefix.length > 0 ? (0, node_path_1.join)(this.prefix, path, '/') : (0, node_path_1.join)(path, '/');
13
+ return this.prefix.length > 0 ? join(this.prefix, path, '/') : join(path, '/');
17
14
  }
18
15
  stripFilePath(path) {
19
16
  return path.substring(this.prefix.length);
@@ -22,4 +19,3 @@ class PathPrefixer {
22
19
  return this.stripFilePath(path).replace(/\/+$/g, '');
23
20
  }
24
21
  }
25
- exports.PathPrefixer = PathPrefixer;
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Visibility = void 0;
4
- var Visibility;
1
+ export var Visibility;
5
2
  (function (Visibility) {
6
3
  Visibility["PUBLIC"] = "public";
7
4
  Visibility["PRIVATE"] = "private";
8
- })(Visibility || (exports.Visibility = Visibility = {}));
5
+ })(Visibility || (Visibility = {}));
@@ -1,5 +1,5 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
3
  import { BinaryToTextEncoding } from 'crypto';
4
4
  import { Readable } from 'node:stream';
5
5
  export declare function checksumFromStream(stream: Readable, options: {
@@ -1,6 +1,7 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference types="node" resolution-mode="require"/>
3
+ /// <reference types="node" resolution-mode="require"/>
4
+ /// <reference types="node" resolution-mode="require"/>
4
5
  import { BinaryToTextEncoding } from 'crypto';
5
6
  import { Readable } from 'stream';
6
7
  import { PathNormalizer } from './path-normalizer.js';
@@ -45,8 +46,14 @@ export interface StorageAdapter {
45
46
  lastModified(path: string): Promise<number>;
46
47
  fileSize(path: string): Promise<number>;
47
48
  }
48
- export interface DirectoryListing extends AsyncIterable<StatEntry> {
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);
49
54
  toArray(sorted?: boolean): Promise<StatEntry[]>;
55
+ filter(filter: (entry: StatEntry) => boolean): DirectoryListing;
56
+ [Symbol.asyncIterator](): AsyncGenerator<StatEntry, void, unknown>;
50
57
  }
51
58
  export type FileContents = Iterable<any> | AsyncIterable<any> | NodeJS.ReadableStream | Readable;
52
59
  export type MiscellaneousOptions = {
@@ -86,6 +93,7 @@ export declare class FileStorage {
86
93
  read(path: string): Promise<Readable>;
87
94
  readToString(path: string): Promise<string>;
88
95
  readToUint8Array(path: string): Promise<Uint8Array>;
96
+ readToBuffer(path: string): Promise<Buffer>;
89
97
  deleteFile(path: string): Promise<void>;
90
98
  createDirectory(path: string, options?: CreateDirectoryOptions): Promise<void>;
91
99
  deleteDirectory(path: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@flystorage/file-storage",
3
- "version": "0.0.3",
3
+ "type": "module",
4
+ "version": "0.0.5",
4
5
  "description": "File-storage abstraction: multiple filesystems, one API.",
5
6
  "main": "./dist/cjs/index.js",
6
7
  "types": "./dist/types/index.d.ts",
@@ -17,13 +18,18 @@
17
18
  }
18
19
  },
19
20
  "scripts": {
20
- "compile": "concurrently npm:compile:*",
21
+ "compile": "concurrently npm:compile:* && echo '{\"type\": \"commonjs\"}' > ./dist/cjs/package.json",
21
22
  "compile:esm": "tsc --outDir ./dist/esm/ --declaration false",
22
23
  "compile:cjs": "tsc --outDir ./dist/cjs/ --declaration false --module commonjs --moduleResolution node",
23
24
  "compile:types": "tsc --outDir ./dist/types/ --declaration --emitDeclarationOnly",
24
25
  "watch": "tsc --watch"
25
26
  },
26
27
  "author": "Frank de Jonge (https://frankdejonge.nl)",
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "https://github.com/flystorage/flystorage.git",
31
+ "directory": "packages/file-storage"
32
+ },
27
33
  "keywords": ["fs", "file", "files", "filesystem", "filesystems", "storage"],
28
34
  "license": "MIT"
29
35
  }