@theshelf/filestore 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -4
- package/dist/FileStore.d.ts +0 -2
- package/dist/FileStore.js +3 -10
- package/dist/drivers/Memory.js +1 -1
- package/dist/drivers/{Minio.d.ts → S3.d.ts} +8 -3
- package/dist/drivers/S3.js +98 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/ConnectionManager.d.ts +0 -9
- package/dist/ConnectionManager.js +0 -53
- package/dist/definitions/constants.d.ts +0 -7
- package/dist/definitions/constants.js +0 -6
- package/dist/drivers/Minio.js +0 -85
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
|
-
* **
|
|
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 |
|
|
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
|
-
####
|
|
38
|
+
#### S3 driver
|
|
39
39
|
|
|
40
|
-
|
|
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
|
|
package/dist/FileStore.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import type { ConnectionState } from './definitions/constants.js';
|
|
2
1
|
import type { Driver } from './definitions/interfaces.js';
|
|
3
2
|
export default class FileStore implements Driver {
|
|
4
3
|
#private;
|
|
5
4
|
constructor(driver: Driver);
|
|
6
|
-
get connectionState(): ConnectionState;
|
|
7
5
|
get connected(): boolean;
|
|
8
6
|
connect(): Promise<void>;
|
|
9
7
|
disconnect(): Promise<void>;
|
package/dist/FileStore.js
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
|
-
import { ConnectionStates } from './definitions/constants.js';
|
|
2
|
-
import ConnectionManager from './ConnectionManager.js';
|
|
3
1
|
export default class FileStore {
|
|
4
2
|
#driver;
|
|
5
|
-
#connectionManager;
|
|
6
3
|
constructor(driver) {
|
|
7
4
|
this.#driver = driver;
|
|
8
|
-
this.#connectionManager = new ConnectionManager(driver);
|
|
9
|
-
}
|
|
10
|
-
get connectionState() {
|
|
11
|
-
return this.#connectionManager.state;
|
|
12
5
|
}
|
|
13
6
|
get connected() {
|
|
14
|
-
return this.
|
|
7
|
+
return this.#driver.connected;
|
|
15
8
|
}
|
|
16
9
|
connect() {
|
|
17
|
-
return this.#
|
|
10
|
+
return this.#driver.connect();
|
|
18
11
|
}
|
|
19
12
|
disconnect() {
|
|
20
|
-
return this.#
|
|
13
|
+
return this.#driver.disconnect();
|
|
21
14
|
}
|
|
22
15
|
hasFile(path) {
|
|
23
16
|
return this.#driver.hasFile(path);
|
package/dist/drivers/Memory.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { S3ClientConfig } from '@aws-sdk/client-s3';
|
|
2
2
|
import type { Driver } from '../definitions/interfaces.js';
|
|
3
|
-
|
|
3
|
+
type S3Configuration = {
|
|
4
|
+
clientConfig: S3ClientConfig;
|
|
5
|
+
bucketName: string;
|
|
6
|
+
};
|
|
7
|
+
export default class S3 implements Driver {
|
|
4
8
|
#private;
|
|
5
|
-
constructor(configuration:
|
|
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
|
|
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
|
|
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.
|
|
4
|
+
"version": "0.3.1",
|
|
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
|
-
"
|
|
25
|
+
"@aws-sdk/client-s3": "^3.968.0"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ConnectionState } from './definitions/constants.js';
|
|
2
|
-
import type { Driver } from './definitions/interfaces.js';
|
|
3
|
-
export default class ConnectionManager {
|
|
4
|
-
#private;
|
|
5
|
-
constructor(driver: Driver);
|
|
6
|
-
get state(): ConnectionState;
|
|
7
|
-
connect(): Promise<void>;
|
|
8
|
-
disconnect(): Promise<void>;
|
|
9
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { ConnectionStates } from './definitions/constants.js';
|
|
2
|
-
export default class ConnectionManager {
|
|
3
|
-
#driver;
|
|
4
|
-
#state = ConnectionStates.DISCONNECTED;
|
|
5
|
-
#connectPromise;
|
|
6
|
-
#disconnectPromise;
|
|
7
|
-
constructor(driver) {
|
|
8
|
-
this.#driver = driver;
|
|
9
|
-
}
|
|
10
|
-
get state() { return this.#state; }
|
|
11
|
-
async connect() {
|
|
12
|
-
if (this.#connectPromise !== undefined) {
|
|
13
|
-
return this.#connectPromise;
|
|
14
|
-
}
|
|
15
|
-
if (this.#state !== ConnectionStates.DISCONNECTED) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
this.#state = ConnectionStates.CONNECTING;
|
|
19
|
-
try {
|
|
20
|
-
this.#connectPromise = this.#driver.connect();
|
|
21
|
-
await this.#connectPromise;
|
|
22
|
-
this.#state = ConnectionStates.CONNECTED;
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
this.#state = ConnectionStates.DISCONNECTED;
|
|
26
|
-
throw error;
|
|
27
|
-
}
|
|
28
|
-
finally {
|
|
29
|
-
this.#connectPromise = undefined;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async disconnect() {
|
|
33
|
-
if (this.#disconnectPromise !== undefined) {
|
|
34
|
-
return this.#disconnectPromise;
|
|
35
|
-
}
|
|
36
|
-
if (this.#state !== ConnectionStates.CONNECTED) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
this.#state = ConnectionStates.DISCONNECTING;
|
|
40
|
-
try {
|
|
41
|
-
this.#disconnectPromise = this.#driver.disconnect();
|
|
42
|
-
await this.#disconnectPromise;
|
|
43
|
-
this.#state = ConnectionStates.DISCONNECTED;
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
this.#state = ConnectionStates.CONNECTED;
|
|
47
|
-
throw error;
|
|
48
|
-
}
|
|
49
|
-
finally {
|
|
50
|
-
this.#disconnectPromise = undefined;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare const ConnectionStates: {
|
|
2
|
-
readonly DISCONNECTED: "DISCONNECTED";
|
|
3
|
-
readonly DISCONNECTING: "DISCONNECTING";
|
|
4
|
-
readonly CONNECTING: "CONNECTING";
|
|
5
|
-
readonly CONNECTED: "CONNECTED";
|
|
6
|
-
};
|
|
7
|
-
export type ConnectionState = typeof ConnectionStates[keyof typeof ConnectionStates];
|
package/dist/drivers/Minio.js
DELETED
|
@@ -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
|
-
}
|