@theshelf/filestore 0.2.1 → 0.3.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/README.md CHANGED
@@ -14,14 +14,14 @@ npm install @theshelf/filestore
14
14
  Currently, there are two drivers available:
15
15
 
16
16
  * **Memory** - non-persistent in memory storage (suited for testing).
17
- * **Minio** - persistent S3 compatible object storage.
17
+ * **S3** - persistent S3 (compatible) object storage.
18
18
 
19
19
  ## How to use
20
20
 
21
21
  The basic set up looks like this.
22
22
 
23
23
  ```ts
24
- import FileStore, { MemoryDriver | MinioDriver as SelectedDriver } from '@theshelf/fileStore';
24
+ import FileStore, { MemoryDriver | S3Driver as SelectedDriver } from '@theshelf/fileStore';
25
25
 
26
26
  const driver = new SelectedDriver(/* configuration */);
27
27
  const fileStore = new FileStore(driver);
@@ -35,9 +35,16 @@ const fileStore = new FileStore(driver);
35
35
 
36
36
  No configuration options.
37
37
 
38
- #### Minio driver
38
+ #### S3 driver
39
39
 
40
- The `ClientOptions` from the 'minio' package.
40
+ ```ts
41
+ type S3Configuration = {
42
+ clientConfig: S3ClientConfig;
43
+ bucketName: string;
44
+ };
45
+ ```
46
+
47
+ The exact configuration of the `clientConfig` depends on your S3-compatible storage provider. See the AWS SDK documentation for details.
41
48
 
42
49
  ### Operations
43
50
 
@@ -1,5 +1,5 @@
1
- import NotConnected from '../errors/NotConnected.js';
2
1
  import FileNotFound from '../errors/FileNotFound.js';
2
+ import NotConnected from '../errors/NotConnected.js';
3
3
  export default class Memory {
4
4
  #files = new Map();
5
5
  #connected = false;
@@ -1,8 +1,12 @@
1
- import type { ClientOptions } from 'minio';
1
+ import type { S3ClientConfig } from '@aws-sdk/client-s3';
2
2
  import type { Driver } from '../definitions/interfaces.js';
3
- export default class Minio implements Driver {
3
+ type S3Configuration = {
4
+ clientConfig: S3ClientConfig;
5
+ bucketName: string;
6
+ };
7
+ export default class S3 implements Driver {
4
8
  #private;
5
- constructor(configuration: ClientOptions);
9
+ constructor(configuration: S3Configuration);
6
10
  get connected(): boolean;
7
11
  connect(): Promise<void>;
8
12
  disconnect(): Promise<void>;
@@ -11,3 +15,4 @@ export default class Minio implements Driver {
11
15
  readFile(path: string): Promise<Buffer>;
12
16
  deleteFile(path: string): Promise<void>;
13
17
  }
18
+ export {};
@@ -0,0 +1,98 @@
1
+ import { CreateBucketCommand, DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListBucketsCommand, NotFound, PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
2
+ import FileNotFound from '../errors/FileNotFound.js';
3
+ import FileStoreError from '../errors/FileStoreError.js';
4
+ import NotConnected from '../errors/NotConnected.js';
5
+ const UNKNOWN_ERROR = 'Unknown error';
6
+ export default class S3 {
7
+ #configuration;
8
+ #bucketName;
9
+ #client;
10
+ #connected = false;
11
+ constructor(configuration) {
12
+ this.#configuration = configuration.clientConfig;
13
+ this.#bucketName = configuration.bucketName;
14
+ }
15
+ get connected() { return this.#connected; }
16
+ async connect() {
17
+ try {
18
+ this.#client = new S3Client(this.#configuration);
19
+ const buckets = await this.#client.send(new ListBucketsCommand({}));
20
+ const bucketExists = buckets.Buckets?.some(bucket => bucket.Name === this.#bucketName);
21
+ if (bucketExists === true)
22
+ return;
23
+ await this.#client.send(new CreateBucketCommand({ Bucket: this.#bucketName }));
24
+ }
25
+ catch (error) {
26
+ const message = error instanceof Error ? error.message : UNKNOWN_ERROR;
27
+ throw new FileStoreError('File store connection failed: ' + message);
28
+ }
29
+ this.#connected = true;
30
+ }
31
+ async disconnect() {
32
+ if (this.#client === undefined) {
33
+ throw new NotConnected();
34
+ }
35
+ this.#client.destroy();
36
+ this.#client = undefined;
37
+ this.#connected = false;
38
+ }
39
+ async hasFile(path) {
40
+ const client = this.#getClient();
41
+ try {
42
+ await client.send(new HeadObjectCommand({ Bucket: this.#bucketName, Key: path }));
43
+ return true;
44
+ }
45
+ catch (error) {
46
+ const customError = this.#handleError(error, path);
47
+ if (customError instanceof FileNotFound) {
48
+ return false;
49
+ }
50
+ throw error;
51
+ }
52
+ }
53
+ async writeFile(path, data) {
54
+ const client = this.#getClient();
55
+ try {
56
+ await client.send(new PutObjectCommand({ Bucket: this.#bucketName, Key: path, Body: data }));
57
+ }
58
+ catch (error) {
59
+ throw this.#handleError(error, path);
60
+ }
61
+ }
62
+ async readFile(path) {
63
+ const client = this.#getClient();
64
+ try {
65
+ const response = await client.send(new GetObjectCommand({ Bucket: this.#bucketName, Key: path }));
66
+ const body = response.Body;
67
+ if (body === undefined) {
68
+ throw new FileNotFound(path);
69
+ }
70
+ const byteArray = await body.transformToByteArray();
71
+ return Buffer.from(byteArray);
72
+ }
73
+ catch (error) {
74
+ throw this.#handleError(error, path);
75
+ }
76
+ }
77
+ async deleteFile(path) {
78
+ const client = this.#getClient();
79
+ try {
80
+ await client.send(new DeleteObjectCommand({ Bucket: this.#bucketName, Key: path }));
81
+ }
82
+ catch (error) {
83
+ throw this.#handleError(error, path);
84
+ }
85
+ }
86
+ #getClient() {
87
+ if (this.#client === undefined) {
88
+ throw new NotConnected();
89
+ }
90
+ return this.#client;
91
+ }
92
+ #handleError(error, path) {
93
+ if (error instanceof NotFound) {
94
+ return new FileNotFound(path);
95
+ }
96
+ return error;
97
+ }
98
+ }
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ export { default as FileNotFound } from './errors/FileNotFound.js';
3
3
  export { default as FileSystemError } from './errors/FileStoreError.js';
4
4
  export { default as NotConnected } from './errors/NotConnected.js';
5
5
  export { default as MemoryDriver } from './drivers/Memory.js';
6
- export { default as MinioDriver } from './drivers/Minio.js';
6
+ export { default as S3Driver } from './drivers/S3.js';
7
7
  export { default } from './FileStore.js';
package/dist/index.js CHANGED
@@ -2,5 +2,5 @@ export { default as FileNotFound } from './errors/FileNotFound.js';
2
2
  export { default as FileSystemError } from './errors/FileStoreError.js';
3
3
  export { default as NotConnected } from './errors/NotConnected.js';
4
4
  export { default as MemoryDriver } from './drivers/Memory.js';
5
- export { default as MinioDriver } from './drivers/Minio.js';
5
+ export { default as S3Driver } from './drivers/S3.js';
6
6
  export { default } from './FileStore.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@theshelf/filestore",
3
3
  "private": false,
4
- "version": "0.2.1",
4
+ "version": "0.3.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/MaskingTechnology/theshelf.git"
@@ -22,6 +22,6 @@
22
22
  "types": "dist/index.d.ts",
23
23
  "exports": "./dist/index.js",
24
24
  "dependencies": {
25
- "minio": "8.0.6"
25
+ "@aws-sdk/client-s3": "^3.968.0"
26
26
  }
27
27
  }
@@ -1,85 +0,0 @@
1
- import { Client } from 'minio';
2
- import FileNotFound from '../errors/FileNotFound.js';
3
- import NotConnected from '../errors/NotConnected.js';
4
- const BUCKET_NAME = 'comify';
5
- export default class Minio {
6
- #configuration;
7
- #client;
8
- constructor(configuration) {
9
- this.#configuration = configuration;
10
- }
11
- get connected() {
12
- return this.#client !== undefined;
13
- }
14
- async connect() {
15
- this.#client = new Client(this.#configuration);
16
- if (await this.#client.bucketExists(BUCKET_NAME) === false) {
17
- await this.#client.makeBucket(BUCKET_NAME);
18
- }
19
- }
20
- async disconnect() {
21
- if (this.#client === undefined) {
22
- throw new NotConnected();
23
- }
24
- this.#client = undefined;
25
- }
26
- async hasFile(path) {
27
- const client = this.#getClient();
28
- try {
29
- await client.statObject(BUCKET_NAME, path);
30
- return true;
31
- }
32
- catch (error) {
33
- const customError = this.#handleError(error, path);
34
- if (customError instanceof FileNotFound) {
35
- return false;
36
- }
37
- throw error;
38
- }
39
- }
40
- async writeFile(path, data) {
41
- const client = this.#getClient();
42
- try {
43
- await client.putObject(BUCKET_NAME, path, data);
44
- }
45
- catch (error) {
46
- throw this.#handleError(error, path);
47
- }
48
- }
49
- async readFile(path) {
50
- const client = this.#getClient();
51
- try {
52
- const stream = await client.getObject(BUCKET_NAME, path);
53
- const chunks = await stream.toArray();
54
- return Buffer.concat(chunks);
55
- }
56
- catch (error) {
57
- throw this.#handleError(error, path);
58
- }
59
- }
60
- async deleteFile(path) {
61
- const client = this.#getClient();
62
- try {
63
- await client.removeObject(BUCKET_NAME, path);
64
- }
65
- catch (error) {
66
- throw this.#handleError(error, path);
67
- }
68
- }
69
- #getClient() {
70
- if (this.#client === undefined) {
71
- throw new NotConnected();
72
- }
73
- return this.#client;
74
- }
75
- #handleError(error, path) {
76
- if (error instanceof Error && this.#isNotFoundError(error)) {
77
- return new FileNotFound(path);
78
- }
79
- return error;
80
- }
81
- #isNotFoundError(error) {
82
- return error.message.startsWith('The specified key does not exist')
83
- || error.message.startsWith('Not Found');
84
- }
85
- }