@theshelf/filestore 0.3.2 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
- # File Store | The Shelf
2
+ # File Store core | The Shelf
3
3
 
4
- The file store package provides a universal interaction layer with an actual file storage solution.
4
+ This package contains the definition of the file operations. It uses a interchangeable driver system for performing the actual operations. An in-memory driver is included.
5
5
 
6
6
  ## Installation
7
7
 
@@ -9,44 +9,20 @@ The file store package provides a universal interaction layer with an actual fil
9
9
  npm install @theshelf/filestore
10
10
  ```
11
11
 
12
- ## Drivers
13
-
14
- Currently, there are two drivers available:
15
-
16
- * **Memory** - non-persistent in memory storage (suited for testing).
17
- * **S3** - persistent S3 (compatible) object storage.
18
-
19
12
  ## How to use
20
13
 
21
14
  The basic set up looks like this.
22
15
 
23
16
  ```ts
24
- import FileStore, { MemoryDriver | S3Driver as SelectedDriver } from '@theshelf/fileStore';
17
+ import FileStore, { MemoryDriver } from '@theshelf/fileStore';
25
18
 
26
- const driver = new SelectedDriver(/* configuration */);
19
+ const driver = new MemoryDriver();
27
20
  const fileStore = new FileStore(driver);
28
21
 
29
22
  // Perform operations with the fileStore instance
30
23
  ```
31
24
 
32
- ### Configuration
33
-
34
- #### Memory driver
35
-
36
- No configuration options.
37
-
38
- #### S3 driver
39
-
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.
48
-
49
- ### Operations
25
+ ## Operations
50
26
 
51
27
  ```ts
52
28
  // Open connection
@@ -70,3 +46,16 @@ const data: Buffer = await fileStore.readFile('path/to/file.txt');
70
46
  // Throws FileNotFound if not found
71
47
  await fileStore.deleteFile('path/to/file.txt');
72
48
  ```
49
+
50
+ ## Drivers
51
+
52
+ There is one driver included in this package. Other drivers are available in separate packages.
53
+
54
+ ### Memory
55
+
56
+ In memory file store (suited for testing). It doesn't have any configuration options, but has an additional operation.
57
+
58
+ ```ts
59
+ // Clear the memory
60
+ driver.clear();
61
+ ```
@@ -1,7 +1,8 @@
1
+ import type Logger from '@theshelf/logging';
1
2
  import type { Driver } from './definitions/interfaces.js';
2
- export default class FileStore implements Driver {
3
+ export default class FileStore {
3
4
  #private;
4
- constructor(driver: Driver);
5
+ constructor(driver: Driver, logger?: Logger);
5
6
  get connected(): boolean;
6
7
  connect(): Promise<void>;
7
8
  disconnect(): Promise<void>;
package/dist/FileStore.js CHANGED
@@ -1,27 +1,89 @@
1
+ import NotConnected from './errors/NotConnected.js';
1
2
  export default class FileStore {
2
3
  #driver;
3
- constructor(driver) {
4
+ #logger;
5
+ #logPrefix;
6
+ constructor(driver, logger) {
4
7
  this.#driver = driver;
8
+ this.#logger = logger?.for(FileStore.name);
9
+ this.#logPrefix = `${this.#driver.name} ->`;
5
10
  }
6
11
  get connected() {
7
12
  return this.#driver.connected;
8
13
  }
9
- connect() {
10
- return this.#driver.connect();
14
+ async connect() {
15
+ if (this.connected === true) {
16
+ return;
17
+ }
18
+ this.#logger?.debug(this.#logPrefix, 'Connecting');
19
+ try {
20
+ await this.#driver.connect();
21
+ }
22
+ catch (error) {
23
+ this.#logger?.error(this.#logPrefix, 'Connect failed with error', error);
24
+ throw error;
25
+ }
11
26
  }
12
- disconnect() {
13
- return this.#driver.disconnect();
27
+ async disconnect() {
28
+ if (this.connected === false) {
29
+ return;
30
+ }
31
+ this.#logger?.debug(this.#logPrefix, 'Disconnecting');
32
+ try {
33
+ return await this.#driver.disconnect();
34
+ }
35
+ catch (error) {
36
+ this.#logger?.error(this.#logPrefix, 'Disconnect failed with error', error);
37
+ throw error;
38
+ }
14
39
  }
15
- hasFile(path) {
16
- return this.#driver.hasFile(path);
40
+ async hasFile(path) {
41
+ this.#logger?.debug(this.#logPrefix, 'Checking has file', path);
42
+ try {
43
+ this.#validateConnection();
44
+ return await this.#driver.hasFile(path);
45
+ }
46
+ catch (error) {
47
+ this.#logger?.error(this.#logPrefix, 'Check has file', path, 'failed with error', error);
48
+ throw error;
49
+ }
17
50
  }
18
- writeFile(path, data) {
19
- return this.#driver.writeFile(path, data);
51
+ async writeFile(path, data) {
52
+ this.#logger?.debug(this.#logPrefix, 'Writing file', path);
53
+ try {
54
+ this.#validateConnection();
55
+ return await this.#driver.writeFile(path, data);
56
+ }
57
+ catch (error) {
58
+ this.#logger?.error(this.#logPrefix, 'Write file', path, 'failed with error', error);
59
+ throw error;
60
+ }
20
61
  }
21
- readFile(path) {
22
- return this.#driver.readFile(path);
62
+ async readFile(path) {
63
+ this.#logger?.debug(this.#logPrefix, 'Reading file', path);
64
+ try {
65
+ this.#validateConnection();
66
+ return await this.#driver.readFile(path);
67
+ }
68
+ catch (error) {
69
+ this.#logger?.error(this.#logPrefix, 'Read file', path, 'failed with error', error);
70
+ throw error;
71
+ }
23
72
  }
24
- deleteFile(path) {
25
- return this.#driver.deleteFile(path);
73
+ async deleteFile(path) {
74
+ this.#logger?.debug(this.#logPrefix, 'Deleting file', path);
75
+ try {
76
+ this.#validateConnection();
77
+ return await this.#driver.deleteFile(path);
78
+ }
79
+ catch (error) {
80
+ this.#logger?.error(this.#logPrefix, 'Delete file', path, 'failed with error', error);
81
+ throw error;
82
+ }
83
+ }
84
+ #validateConnection() {
85
+ if (this.connected === false) {
86
+ throw new NotConnected();
87
+ }
26
88
  }
27
89
  }
@@ -1,4 +1,5 @@
1
1
  export interface Driver {
2
+ get name(): string;
2
3
  get connected(): boolean;
3
4
  connect(): Promise<void>;
4
5
  disconnect(): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import type { Driver } from '../definitions/interfaces.js';
2
2
  export default class Memory implements Driver {
3
3
  #private;
4
+ get name(): string;
4
5
  get connected(): boolean;
5
6
  get files(): Map<string, Buffer>;
6
7
  connect(): Promise<void>;
@@ -3,6 +3,7 @@ import NotConnected from '../errors/NotConnected.js';
3
3
  export default class Memory {
4
4
  #files = new Map();
5
5
  #connected = false;
6
+ get name() { return Memory.name; }
6
7
  get connected() { return this.#connected; }
7
8
  get files() {
8
9
  if (this.#connected === false) {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- export type * from './definitions/interfaces.js';
1
+ export type { Driver } from './definitions/interfaces.js';
2
2
  export { default as FileNotFound } from './errors/FileNotFound.js';
3
- export { default as FileSystemError } from './errors/FileStoreError.js';
3
+ export { default as FileStoreError } 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 S3Driver } from './drivers/S3.js';
7
6
  export { default } from './FileStore.js';
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  export { default as FileNotFound } from './errors/FileNotFound.js';
2
- export { default as FileSystemError } from './errors/FileStoreError.js';
2
+ export { default as FileStoreError } 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 S3Driver } from './drivers/S3.js';
6
5
  export { default } from './FileStore.js';
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@theshelf/filestore",
3
3
  "private": false,
4
- "version": "0.3.2",
4
+ "version": "0.4.1",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/MaskingTechnology/theshelf.git"
8
8
  },
9
+ "license": "MIT",
9
10
  "scripts": {
10
11
  "build": "tsc",
11
12
  "clean": "rimraf dist",
@@ -19,9 +20,9 @@
19
20
  "README.md",
20
21
  "dist"
21
22
  ],
22
- "types": "dist/index.d.ts",
23
+ "types": "./dist/index.d.ts",
23
24
  "exports": "./dist/index.js",
24
- "dependencies": {
25
- "@aws-sdk/client-s3": "^3.968.0"
25
+ "peerDependencies": {
26
+ "@theshelf/logging": "^0.4.0"
26
27
  }
27
28
  }
@@ -1,18 +0,0 @@
1
- import type { S3ClientConfig } from '@aws-sdk/client-s3';
2
- import type { Driver } from '../definitions/interfaces.js';
3
- type S3Configuration = {
4
- clientConfig: S3ClientConfig;
5
- bucketName: string;
6
- };
7
- export default class S3 implements Driver {
8
- #private;
9
- constructor(configuration: S3Configuration);
10
- get connected(): boolean;
11
- connect(): Promise<void>;
12
- disconnect(): Promise<void>;
13
- hasFile(path: string): Promise<boolean>;
14
- writeFile(path: string, data: Buffer): Promise<void>;
15
- readFile(path: string): Promise<Buffer>;
16
- deleteFile(path: string): Promise<void>;
17
- }
18
- export {};
@@ -1,105 +0,0 @@
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
- const createBucket = new CreateBucketCommand({ Bucket: this.#bucketName });
23
- await this.#client.send(createBucket);
24
- }
25
- this.#connected = true;
26
- }
27
- catch (error) {
28
- const message = error instanceof Error ? error.message : UNKNOWN_ERROR;
29
- throw new FileStoreError('File store connection failed: ' + message);
30
- }
31
- }
32
- async disconnect() {
33
- if (this.#client === undefined) {
34
- throw new NotConnected();
35
- }
36
- try {
37
- this.#client.destroy();
38
- this.#client = undefined;
39
- this.#connected = false;
40
- }
41
- catch (error) {
42
- const message = error instanceof Error ? error.message : UNKNOWN_ERROR;
43
- throw new FileStoreError('File store disconnection failed: ' + message);
44
- }
45
- }
46
- async hasFile(path) {
47
- const client = this.#getClient();
48
- try {
49
- await client.send(new HeadObjectCommand({ Bucket: this.#bucketName, Key: path }));
50
- return true;
51
- }
52
- catch (error) {
53
- const customError = this.#handleError(error, path);
54
- if (customError instanceof FileNotFound) {
55
- return false;
56
- }
57
- throw error;
58
- }
59
- }
60
- async writeFile(path, data) {
61
- const client = this.#getClient();
62
- try {
63
- await client.send(new PutObjectCommand({ Bucket: this.#bucketName, Key: path, Body: data }));
64
- }
65
- catch (error) {
66
- throw this.#handleError(error, path);
67
- }
68
- }
69
- async readFile(path) {
70
- const client = this.#getClient();
71
- try {
72
- const response = await client.send(new GetObjectCommand({ Bucket: this.#bucketName, Key: path }));
73
- const body = response.Body;
74
- if (body === undefined) {
75
- throw new FileNotFound(path);
76
- }
77
- const byteArray = await body.transformToByteArray();
78
- return Buffer.from(byteArray);
79
- }
80
- catch (error) {
81
- throw this.#handleError(error, path);
82
- }
83
- }
84
- async deleteFile(path) {
85
- const client = this.#getClient();
86
- try {
87
- await client.send(new DeleteObjectCommand({ Bucket: this.#bucketName, Key: path }));
88
- }
89
- catch (error) {
90
- throw this.#handleError(error, path);
91
- }
92
- }
93
- #getClient() {
94
- if (this.#client === undefined) {
95
- throw new NotConnected();
96
- }
97
- return this.#client;
98
- }
99
- #handleError(error, path) {
100
- if (error instanceof NotFound) {
101
- return new FileNotFound(path);
102
- }
103
- return error;
104
- }
105
- }