@bejibun/storage 0.1.1 → 0.1.11
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/CHANGELOG.md +45 -0
- package/benchmarks/README.md +46 -0
- package/benchmarks/package.json +12 -0
- package/benchmarks/scripts/coldstart-baseline.mjs +9 -0
- package/benchmarks/scripts/coldstart-optimized.mjs +9 -0
- package/benchmarks/scripts/coldstart.mjs +93 -0
- package/benchmarks/scripts/readme-writer.mjs +35 -0
- package/benchmarks/scripts/table-format.mjs +123 -0
- package/benchmarks/scripts/throughput-baseline.mjs +68 -0
- package/benchmarks/scripts/throughput-optimized.mjs +68 -0
- package/benchmarks/scripts/throughput.mjs +83 -0
- package/builders/StorageBuilder.d.ts +117 -0
- package/builders/StorageBuilder.js +156 -29
- package/builders/storage/StorageLocalBuilder.d.ts +89 -0
- package/builders/storage/StorageLocalBuilder.js +109 -21
- package/builders/storage/StorageS3Builder.d.ts +90 -0
- package/builders/storage/StorageS3Builder.js +107 -18
- package/config/storage.d.ts +3 -0
- package/config/storage.js +8 -0
- package/configure.js +5 -0
- package/enums/StorageDiskDriverEnum.d.ts +5 -0
- package/enums/StorageDiskDriverEnum.js +5 -0
- package/enums/index.d.ts +4 -1
- package/enums/index.js +4 -1
- package/exceptions/StorageException.d.ts +10 -0
- package/exceptions/StorageException.js +10 -0
- package/exceptions/index.d.ts +4 -1
- package/exceptions/index.js +4 -1
- package/facades/Storage.d.ts +87 -0
- package/facades/Storage.js +87 -0
- package/facades/index.d.ts +4 -1
- package/facades/index.js +4 -1
- package/index.d.ts +4 -0
- package/index.js +4 -0
- package/package.json +10 -9
- package/tests/integration/storage.integration.test.ts +145 -0
- package/tests/unit/storage.test.ts +177 -0
- package/tsconfig.json +2 -1
- package/types/index.d.ts +4 -1
- package/types/storage.d.ts +24 -24
package/facades/Storage.d.ts
CHANGED
|
@@ -1,18 +1,105 @@
|
|
|
1
1
|
import type { Stats } from "fs";
|
|
2
2
|
import type { StorageDisk, StorageOptions } from "../types/storage";
|
|
3
3
|
import StorageBuilder from "../builders/StorageBuilder";
|
|
4
|
+
/**
|
|
5
|
+
* Static facade for performing storage operations via the default builder.
|
|
6
|
+
*/
|
|
4
7
|
export default class Storage {
|
|
8
|
+
/**
|
|
9
|
+
* Build a storage builder with the given disk override.
|
|
10
|
+
*
|
|
11
|
+
* @param {StorageDisk} disk - The disk configuration to use.
|
|
12
|
+
* @returns {StorageBuilder} A storage builder bound to the disk.
|
|
13
|
+
*/
|
|
5
14
|
static build(disk: StorageDisk): StorageBuilder;
|
|
15
|
+
/**
|
|
16
|
+
* Select a disk by name and return a storage builder.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} disk - The name of the disk to use.
|
|
19
|
+
* @returns {StorageBuilder} A storage builder bound to the disk name.
|
|
20
|
+
*/
|
|
6
21
|
static disk(disk: string): StorageBuilder;
|
|
22
|
+
/**
|
|
23
|
+
* Determine whether a file exists.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} path - The path to the file.
|
|
26
|
+
* @returns {Promise<boolean>} Whether the file exists.
|
|
27
|
+
*/
|
|
7
28
|
static exists(path: string): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Determine whether a file is missing.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} path - The path to the file.
|
|
33
|
+
* @returns {Promise<boolean>} Whether the file does not exist.
|
|
34
|
+
*/
|
|
8
35
|
static missing(path: string): Promise<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* Retrieve metadata for a file.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} path - The path to the file.
|
|
40
|
+
* @returns {Promise<Stats | Bun.S3Stats>} File metadata and statistics.
|
|
41
|
+
*/
|
|
9
42
|
static metadata(path: string): Promise<Stats | Bun.S3Stats>;
|
|
43
|
+
/**
|
|
44
|
+
* Get the file size in bytes.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} path - The path to the file.
|
|
47
|
+
* @returns {Promise<number>} The file size in bytes.
|
|
48
|
+
*/
|
|
10
49
|
static size(path: string): Promise<number>;
|
|
50
|
+
/**
|
|
51
|
+
* Get the file MIME type.
|
|
52
|
+
*
|
|
53
|
+
* @param {string} path - The path to the file.
|
|
54
|
+
* @returns {Promise<string>} The detected MIME type.
|
|
55
|
+
*/
|
|
11
56
|
static mimeType(path: string): Promise<string>;
|
|
57
|
+
/**
|
|
58
|
+
* Get the file's last modification date.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} path - The path to the file.
|
|
61
|
+
* @returns {Promise<Date>} The last modified timestamp.
|
|
62
|
+
*/
|
|
12
63
|
static lastModified(path: string): Promise<Date>;
|
|
64
|
+
/**
|
|
65
|
+
* Retrieve a file from storage.
|
|
66
|
+
*
|
|
67
|
+
* @param {string} path - The path to the file.
|
|
68
|
+
* @returns {Promise<Bun.BunFile | Bun.S3File>} The storage file instance.
|
|
69
|
+
*/
|
|
13
70
|
static get(path: string): Promise<Bun.BunFile | Bun.S3File>;
|
|
71
|
+
/**
|
|
72
|
+
* Store content at the given path.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} path - The destination file path.
|
|
75
|
+
* @param {any} content - The content to store.
|
|
76
|
+
* @param {StorageOptions} options - Additional storage options.
|
|
77
|
+
* @returns {Promise<void>} A promise resolving once the file is stored.
|
|
78
|
+
*/
|
|
14
79
|
static put(path: string, content: any, options?: StorageOptions): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Copy a file to a new location.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} source - The source file path.
|
|
84
|
+
* @param {string} destination - The destination file path.
|
|
85
|
+
* @param {StorageOptions} options - Additional storage options.
|
|
86
|
+
* @returns {Promise<void>} A promise resolving once the file is copied.
|
|
87
|
+
*/
|
|
15
88
|
static copy(source: string, destination: string, options?: StorageOptions): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Move a file to a new location.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} source - The source file path.
|
|
93
|
+
* @param {string} destination - The destination file path.
|
|
94
|
+
* @param {StorageOptions} options - Additional storage options.
|
|
95
|
+
* @returns {Promise<void>} A promise resolving once the file is moved.
|
|
96
|
+
*/
|
|
16
97
|
static move(source: string, destination: string, options?: StorageOptions): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Delete a file from storage.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} path - The path to the file.
|
|
102
|
+
* @returns {Promise<void>} A promise resolving once the file is deleted.
|
|
103
|
+
*/
|
|
17
104
|
static delete(path: string): Promise<any>;
|
|
18
105
|
}
|
package/facades/Storage.js
CHANGED
|
@@ -1,41 +1,128 @@
|
|
|
1
1
|
import StorageBuilder from "../builders/StorageBuilder";
|
|
2
|
+
/**
|
|
3
|
+
* Static facade for performing storage operations via the default builder.
|
|
4
|
+
*/
|
|
2
5
|
export default class Storage {
|
|
6
|
+
/**
|
|
7
|
+
* Build a storage builder with the given disk override.
|
|
8
|
+
*
|
|
9
|
+
* @param {StorageDisk} disk - The disk configuration to use.
|
|
10
|
+
* @returns {StorageBuilder} A storage builder bound to the disk.
|
|
11
|
+
*/
|
|
3
12
|
static build(disk) {
|
|
4
13
|
return new StorageBuilder().build(disk);
|
|
5
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Select a disk by name and return a storage builder.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} disk - The name of the disk to use.
|
|
19
|
+
* @returns {StorageBuilder} A storage builder bound to the disk name.
|
|
20
|
+
*/
|
|
6
21
|
static disk(disk) {
|
|
7
22
|
return new StorageBuilder().disk(disk);
|
|
8
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Determine whether a file exists.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} path - The path to the file.
|
|
28
|
+
* @returns {Promise<boolean>} Whether the file exists.
|
|
29
|
+
*/
|
|
9
30
|
static async exists(path) {
|
|
10
31
|
return await new StorageBuilder().exists(path);
|
|
11
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Determine whether a file is missing.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} path - The path to the file.
|
|
37
|
+
* @returns {Promise<boolean>} Whether the file does not exist.
|
|
38
|
+
*/
|
|
12
39
|
static async missing(path) {
|
|
13
40
|
return await new StorageBuilder().missing(path);
|
|
14
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Retrieve metadata for a file.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} path - The path to the file.
|
|
46
|
+
* @returns {Promise<Stats | Bun.S3Stats>} File metadata and statistics.
|
|
47
|
+
*/
|
|
15
48
|
static async metadata(path) {
|
|
16
49
|
return await new StorageBuilder().metadata(path);
|
|
17
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the file size in bytes.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} path - The path to the file.
|
|
55
|
+
* @returns {Promise<number>} The file size in bytes.
|
|
56
|
+
*/
|
|
18
57
|
static async size(path) {
|
|
19
58
|
return await new StorageBuilder().size(path);
|
|
20
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Get the file MIME type.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} path - The path to the file.
|
|
64
|
+
* @returns {Promise<string>} The detected MIME type.
|
|
65
|
+
*/
|
|
21
66
|
static async mimeType(path) {
|
|
22
67
|
return await new StorageBuilder().mimeType(path);
|
|
23
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the file's last modification date.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} path - The path to the file.
|
|
73
|
+
* @returns {Promise<Date>} The last modified timestamp.
|
|
74
|
+
*/
|
|
24
75
|
static async lastModified(path) {
|
|
25
76
|
return await new StorageBuilder().lastModified(path);
|
|
26
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Retrieve a file from storage.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} path - The path to the file.
|
|
82
|
+
* @returns {Promise<Bun.BunFile | Bun.S3File>} The storage file instance.
|
|
83
|
+
*/
|
|
27
84
|
static async get(path) {
|
|
28
85
|
return await new StorageBuilder().get(path);
|
|
29
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Store content at the given path.
|
|
89
|
+
*
|
|
90
|
+
* @param {string} path - The destination file path.
|
|
91
|
+
* @param {any} content - The content to store.
|
|
92
|
+
* @param {StorageOptions} options - Additional storage options.
|
|
93
|
+
* @returns {Promise<void>} A promise resolving once the file is stored.
|
|
94
|
+
*/
|
|
30
95
|
static async put(path, content, options) {
|
|
31
96
|
return await new StorageBuilder().put(path, content, options);
|
|
32
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Copy a file to a new location.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} source - The source file path.
|
|
102
|
+
* @param {string} destination - The destination file path.
|
|
103
|
+
* @param {StorageOptions} options - Additional storage options.
|
|
104
|
+
* @returns {Promise<void>} A promise resolving once the file is copied.
|
|
105
|
+
*/
|
|
33
106
|
static async copy(source, destination, options) {
|
|
34
107
|
return await new StorageBuilder().copy(source, destination, options);
|
|
35
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Move a file to a new location.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} source - The source file path.
|
|
113
|
+
* @param {string} destination - The destination file path.
|
|
114
|
+
* @param {StorageOptions} options - Additional storage options.
|
|
115
|
+
* @returns {Promise<void>} A promise resolving once the file is moved.
|
|
116
|
+
*/
|
|
36
117
|
static async move(source, destination, options) {
|
|
37
118
|
return await new StorageBuilder().move(source, destination, options);
|
|
38
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Delete a file from storage.
|
|
122
|
+
*
|
|
123
|
+
* @param {string} path - The path to the file.
|
|
124
|
+
* @returns {Promise<void>} A promise resolving once the file is deleted.
|
|
125
|
+
*/
|
|
39
126
|
static async delete(path) {
|
|
40
127
|
return await new StorageBuilder().delete(path);
|
|
41
128
|
}
|
package/facades/index.d.ts
CHANGED
package/facades/index.js
CHANGED
package/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports the Storage facade, storage enums, exception classes, and facade barrel as the package entry point.
|
|
3
|
+
*/
|
|
1
4
|
export { default } from "./facades/Storage";
|
|
2
5
|
export * from "./enums/index";
|
|
6
|
+
export * from "./exceptions/index";
|
|
3
7
|
export * from "./facades/index";
|
package/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Re-exports the Storage facade, storage enums, exception classes, and facade barrel as the package entry point.
|
|
3
|
+
*/
|
|
1
4
|
export { default } from "./facades/Storage";
|
|
2
5
|
export * from "./enums/index";
|
|
6
|
+
export * from "./exceptions/index";
|
|
3
7
|
export * from "./facades/index";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bejibun/storage",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"author": "Havea Crenata <havea.crenata@gmail.com>",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -10,14 +10,14 @@
|
|
|
10
10
|
"module": "index.js",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@eslint/js": "^10.0.1",
|
|
13
|
-
"@types/bun": "^1.
|
|
14
|
-
"eslint": "^10.
|
|
13
|
+
"@types/bun": "^1.4.0",
|
|
14
|
+
"eslint": "^10.9.1",
|
|
15
15
|
"eslint-config-prettier": "^10.1.8",
|
|
16
|
-
"globals": "^17.
|
|
16
|
+
"globals": "^17.12.0",
|
|
17
17
|
"prettier": "^3.9.6",
|
|
18
|
-
"tsc-alias": "^1.9.
|
|
18
|
+
"tsc-alias": "^1.9.4",
|
|
19
19
|
"typescript": "^6.0.3",
|
|
20
|
-
"typescript-eslint": "^8.
|
|
20
|
+
"typescript-eslint": "^8.69.0"
|
|
21
21
|
},
|
|
22
22
|
"bugs": {
|
|
23
23
|
"url": "https://github.com/Bejibun-Framework/bejibun-storage/issues"
|
|
@@ -40,14 +40,15 @@
|
|
|
40
40
|
"format": "prettier --write src --log-level=silent",
|
|
41
41
|
"eslint": "eslint src",
|
|
42
42
|
"lint": "bun run format && bun run eslint",
|
|
43
|
+
"test": "bun test tests/",
|
|
43
44
|
"build": "bun run lint && tsc -p tsconfig.json && bun run types && bun run alias && bun run rsync && bun run clean",
|
|
44
45
|
"deploy": "bun run build && bun publish --access public"
|
|
45
46
|
},
|
|
46
47
|
"type": "module",
|
|
47
48
|
"types": "index.d.ts",
|
|
48
49
|
"dependencies": {
|
|
49
|
-
"@bejibun/app": "^0.1.
|
|
50
|
-
"@bejibun/logger": "^0.1
|
|
51
|
-
"@bejibun/utils": "^0.1.
|
|
50
|
+
"@bejibun/app": "^0.1.26",
|
|
51
|
+
"@bejibun/logger": "^0.2.1",
|
|
52
|
+
"@bejibun/utils": "^0.1.30"
|
|
52
53
|
}
|
|
53
54
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAll,
|
|
3
|
+
afterEach,
|
|
4
|
+
beforeAll,
|
|
5
|
+
beforeEach,
|
|
6
|
+
describe,
|
|
7
|
+
expect,
|
|
8
|
+
mock,
|
|
9
|
+
test
|
|
10
|
+
} from "bun:test";
|
|
11
|
+
import StorageBuilder from "../../src/builders/StorageBuilder";
|
|
12
|
+
import Storage from "../../src/facades/Storage";
|
|
13
|
+
|
|
14
|
+
const rootDir = `${process.env.TMPDIR ?? "/tmp"}/bejibun-storage-int-${Date.now()}`;
|
|
15
|
+
const disk = {driver: "local", root: rootDir};
|
|
16
|
+
|
|
17
|
+
let writtenFiles: Array<string> = [];
|
|
18
|
+
|
|
19
|
+
const log = mock(console.log);
|
|
20
|
+
const error = mock(console.error);
|
|
21
|
+
|
|
22
|
+
beforeAll(async () => {
|
|
23
|
+
await Bun.write(`${rootDir}/.keep`, "");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
log.mockReset();
|
|
28
|
+
error.mockReset();
|
|
29
|
+
console.log = log;
|
|
30
|
+
console.error = error;
|
|
31
|
+
writtenFiles = [];
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
console.log = console.log;
|
|
36
|
+
console.error = console.error;
|
|
37
|
+
void cleanup();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
async function cleanup() {
|
|
41
|
+
for (const file of writtenFiles) {
|
|
42
|
+
try {
|
|
43
|
+
if (await Bun.file(file).exists()) await Bun.file(file).delete();
|
|
44
|
+
} catch {
|
|
45
|
+
/* already removed by the test */
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
writtenFiles = [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
afterAll(async () => {
|
|
52
|
+
await Bun.file(`${rootDir}/.keep`).delete();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("StorageBuilder integration (local disk)", () => {
|
|
56
|
+
test("put stores content and exists/size read it back", async () => {
|
|
57
|
+
const builder = new StorageBuilder().build(disk);
|
|
58
|
+
const filepath = `${rootDir}/hello.txt`;
|
|
59
|
+
|
|
60
|
+
writtenFiles.push(filepath);
|
|
61
|
+
|
|
62
|
+
await builder.put(filepath, "hello world");
|
|
63
|
+
|
|
64
|
+
expect(await builder.exists(filepath)).toBe(true);
|
|
65
|
+
expect(await builder.missing(filepath)).toBe(false);
|
|
66
|
+
expect(await builder.size(filepath)).toBe(11);
|
|
67
|
+
expect(await (await builder.get(filepath)).text()).toBe("hello world");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("metadata and lastModified are available after a write", async () => {
|
|
71
|
+
const builder = new StorageBuilder().build(disk);
|
|
72
|
+
const filepath = `${rootDir}/meta.txt`;
|
|
73
|
+
|
|
74
|
+
writtenFiles.push(filepath);
|
|
75
|
+
|
|
76
|
+
await builder.put(filepath, "meta");
|
|
77
|
+
|
|
78
|
+
const meta = await builder.metadata(filepath);
|
|
79
|
+
|
|
80
|
+
expect(meta.size).toBe(4);
|
|
81
|
+
expect(await builder.lastModified(filepath)).toBeInstanceOf(Date);
|
|
82
|
+
expect(builder.mimeType(filepath)).toBeTruthy();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("copy duplicates the file", async () => {
|
|
86
|
+
const builder = new StorageBuilder().build(disk);
|
|
87
|
+
const source = `${rootDir}/copy-src.txt`;
|
|
88
|
+
const destination = `${rootDir}/copy-dst.txt`;
|
|
89
|
+
|
|
90
|
+
writtenFiles.push(source, destination);
|
|
91
|
+
|
|
92
|
+
await builder.put(source, "copy me");
|
|
93
|
+
await builder.copy(source, destination);
|
|
94
|
+
|
|
95
|
+
expect(await builder.exists(destination)).toBe(true);
|
|
96
|
+
expect(await (await builder.get(destination)).text()).toBe("copy me");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("move relocates the file and removes the source", async () => {
|
|
100
|
+
const builder = new StorageBuilder().build(disk);
|
|
101
|
+
const source = `${rootDir}/move-src.txt`;
|
|
102
|
+
const destination = `${rootDir}/move-dst.txt`;
|
|
103
|
+
|
|
104
|
+
writtenFiles.push(source, destination);
|
|
105
|
+
|
|
106
|
+
await builder.put(source, "move me");
|
|
107
|
+
await builder.move(source, destination);
|
|
108
|
+
|
|
109
|
+
expect(await builder.exists(source)).toBe(false);
|
|
110
|
+
expect(await builder.exists(destination)).toBe(true);
|
|
111
|
+
expect(await (await builder.get(destination)).text()).toBe("move me");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("delete removes the file", async () => {
|
|
115
|
+
const builder = new StorageBuilder().build(disk);
|
|
116
|
+
const filepath = `${rootDir}/delete.txt`;
|
|
117
|
+
|
|
118
|
+
writtenFiles.push(filepath);
|
|
119
|
+
|
|
120
|
+
await builder.put(filepath, "gone");
|
|
121
|
+
await builder.delete(filepath);
|
|
122
|
+
|
|
123
|
+
expect(await builder.exists(filepath)).toBe(false);
|
|
124
|
+
expect(await builder.missing(filepath)).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("Storage facade integration (local disk)", () => {
|
|
129
|
+
test("disk + build resolve the same local disk", async () => {
|
|
130
|
+
const builder = Storage.build(disk).disk("local");
|
|
131
|
+
|
|
132
|
+
expect(builder).toBeInstanceOf(StorageBuilder);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("put via facade stores and get reads back relative to the disk root", async () => {
|
|
136
|
+
const builder = new StorageBuilder().build(disk);
|
|
137
|
+
const filepath = `${rootDir}/facade-put.txt`;
|
|
138
|
+
|
|
139
|
+
writtenFiles.push(filepath);
|
|
140
|
+
|
|
141
|
+
await builder.put(filepath, "facade");
|
|
142
|
+
|
|
143
|
+
expect(await builder.exists(filepath)).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterEach,
|
|
3
|
+
beforeEach,
|
|
4
|
+
describe,
|
|
5
|
+
expect,
|
|
6
|
+
mock,
|
|
7
|
+
test
|
|
8
|
+
} from "bun:test";
|
|
9
|
+
import StorageBuilder from "../../src/builders/StorageBuilder";
|
|
10
|
+
import StorageException from "../../src/exceptions/StorageException";
|
|
11
|
+
import Storage from "../../src/facades/Storage";
|
|
12
|
+
|
|
13
|
+
const log = mock(console.log);
|
|
14
|
+
const error = mock(console.error);
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
log.mockReset();
|
|
18
|
+
error.mockReset();
|
|
19
|
+
console.log = log;
|
|
20
|
+
console.error = error;
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
console.log = console.log;
|
|
25
|
+
console.error = console.error;
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("StorageBuilder", () => {
|
|
29
|
+
test("build applies the disk override and returns the builder", () => {
|
|
30
|
+
const disk = {driver: "local", root: "/tmp"};
|
|
31
|
+
const builder = new StorageBuilder().build(disk);
|
|
32
|
+
|
|
33
|
+
expect(builder).toBeInstanceOf(StorageBuilder);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("disk selects a disk by name and returns the builder", () => {
|
|
37
|
+
const builder = new StorageBuilder().disk("local");
|
|
38
|
+
|
|
39
|
+
expect(builder).toBeInstanceOf(StorageBuilder);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("exists throws when the file path is empty", async () => {
|
|
43
|
+
const builder = new StorageBuilder();
|
|
44
|
+
|
|
45
|
+
const error: any = await builder.exists("").catch((e) => e);
|
|
46
|
+
|
|
47
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
48
|
+
expect(error.message).toBe("The file path is required.");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("missing throws when the file path is empty", async () => {
|
|
52
|
+
const builder = new StorageBuilder();
|
|
53
|
+
|
|
54
|
+
const error: any = await builder.missing("").catch((e) => e);
|
|
55
|
+
|
|
56
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
57
|
+
expect(error.message).toBe("The file path is required.");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("metadata throws when the file path is empty", async () => {
|
|
61
|
+
const builder = new StorageBuilder();
|
|
62
|
+
|
|
63
|
+
const error: any = await builder.metadata("").catch((e) => e);
|
|
64
|
+
|
|
65
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
66
|
+
expect(error.message).toBe("The file path is required.");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("size throws when the file path is empty", async () => {
|
|
70
|
+
const builder = new StorageBuilder();
|
|
71
|
+
|
|
72
|
+
const error: any = await builder.size("").catch((e) => e);
|
|
73
|
+
|
|
74
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
75
|
+
expect(error.message).toBe("The file path is required.");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("mimeType throws when the file path is empty", async () => {
|
|
79
|
+
const builder = new StorageBuilder();
|
|
80
|
+
|
|
81
|
+
const error: any = await builder.mimeType("").catch((e) => e);
|
|
82
|
+
|
|
83
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
84
|
+
expect(error.message).toBe("The file path is required.");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("lastModified throws when the file path is empty", async () => {
|
|
88
|
+
const builder = new StorageBuilder();
|
|
89
|
+
|
|
90
|
+
const error: any = await builder.lastModified("").catch((e) => e);
|
|
91
|
+
|
|
92
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
93
|
+
expect(error.message).toBe("The file path is required.");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("get throws when the file path is empty", async () => {
|
|
97
|
+
const builder = new StorageBuilder();
|
|
98
|
+
|
|
99
|
+
const error: any = await builder.get("").catch((e) => e);
|
|
100
|
+
|
|
101
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
102
|
+
expect(error.message).toBe("The file path is required.");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("put throws when the file path or content is empty", async () => {
|
|
106
|
+
const builder = new StorageBuilder();
|
|
107
|
+
|
|
108
|
+
const pathError: any = await builder.put("", "x").catch((e) => e);
|
|
109
|
+
|
|
110
|
+
expect(pathError).toBeInstanceOf(StorageException);
|
|
111
|
+
expect(pathError.message).toBe("The file path is required.");
|
|
112
|
+
|
|
113
|
+
const contentError: any = await builder.put("file.txt", "").catch((e) => e);
|
|
114
|
+
|
|
115
|
+
expect(contentError).toBeInstanceOf(StorageException);
|
|
116
|
+
expect(contentError.message).toBe("The content is required.");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("copy throws when the source or destination path is empty", async () => {
|
|
120
|
+
const builder = new StorageBuilder();
|
|
121
|
+
|
|
122
|
+
const sourceError: any = await builder.copy("", "dst").catch((e) => e);
|
|
123
|
+
|
|
124
|
+
expect(sourceError.message).toBe("The source file path is required.");
|
|
125
|
+
|
|
126
|
+
const destError: any = await builder.copy("src", "").catch((e) => e);
|
|
127
|
+
|
|
128
|
+
expect(destError.message).toBe("The destination file path is required.");
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("move throws when the source or destination path is empty", async () => {
|
|
132
|
+
const builder = new StorageBuilder();
|
|
133
|
+
|
|
134
|
+
const sourceError: any = await builder.move("", "dst").catch((e) => e);
|
|
135
|
+
|
|
136
|
+
expect(sourceError.message).toBe("The source file path is required.");
|
|
137
|
+
|
|
138
|
+
const destError: any = await builder.move("src", "").catch((e) => e);
|
|
139
|
+
|
|
140
|
+
expect(destError.message).toBe("The destination file path is required.");
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("delete throws when the file path is empty", async () => {
|
|
144
|
+
const builder = new StorageBuilder();
|
|
145
|
+
|
|
146
|
+
const error: any = await builder.delete("").catch((e) => e);
|
|
147
|
+
|
|
148
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
149
|
+
expect(error.message).toBe("The file path is required.");
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe("Storage facade", () => {
|
|
154
|
+
test("build returns a StorageBuilder", () => {
|
|
155
|
+
expect(Storage.build({driver: "local", root: "/tmp"})).toBeInstanceOf(
|
|
156
|
+
StorageBuilder
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("disk returns a StorageBuilder", () => {
|
|
161
|
+
expect(Storage.disk("local")).toBeInstanceOf(StorageBuilder);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("exists throws when the file path is empty", async () => {
|
|
165
|
+
const error: any = await Storage.exists("").catch((e) => e);
|
|
166
|
+
|
|
167
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
168
|
+
expect(error.message).toBe("The file path is required.");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("put throws when the file path is empty", async () => {
|
|
172
|
+
const error: any = await Storage.put("", "x").catch((e) => e);
|
|
173
|
+
|
|
174
|
+
expect(error).toBeInstanceOf(StorageException);
|
|
175
|
+
expect(error.message).toBe("The file path is required.");
|
|
176
|
+
});
|
|
177
|
+
});
|
package/tsconfig.json
CHANGED
package/types/index.d.ts
CHANGED