@anabranch/storage 0.1.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/LICENSE +21 -0
- package/README.md +6 -0
- package/esm/adapter.d.ts +78 -0
- package/esm/adapter.d.ts.map +1 -0
- package/esm/adapter.js +1 -0
- package/esm/connector.d.ts +14 -0
- package/esm/connector.d.ts.map +1 -0
- package/esm/connector.js +114 -0
- package/esm/errors.d.ts +37 -0
- package/esm/errors.d.ts.map +1 -0
- package/esm/errors.js +99 -0
- package/esm/index.d.ts +87 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +85 -0
- package/esm/package.json +3 -0
- package/esm/storage.d.ts +108 -0
- package/esm/storage.d.ts.map +1 -0
- package/esm/storage.js +184 -0
- package/package.json +24 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Frodi Karlsson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/esm/adapter.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input body types for put operations.
|
|
3
|
+
*/
|
|
4
|
+
export type BodyInput = Uint8Array | ReadableStream | string;
|
|
5
|
+
/**
|
|
6
|
+
* Object metadata returned by head operations and included with get results.
|
|
7
|
+
*/
|
|
8
|
+
export interface StorageMetadata {
|
|
9
|
+
key: string;
|
|
10
|
+
size: number;
|
|
11
|
+
etag?: string;
|
|
12
|
+
lastModified: Date;
|
|
13
|
+
contentType?: string;
|
|
14
|
+
custom?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Object returned by get operations, containing a body stream and metadata.
|
|
18
|
+
*/
|
|
19
|
+
export interface StorageObject {
|
|
20
|
+
body: ReadableStream;
|
|
21
|
+
metadata: StorageMetadata;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Entry returned by list operations.
|
|
25
|
+
*/
|
|
26
|
+
export interface StorageEntry {
|
|
27
|
+
key: string;
|
|
28
|
+
size: number;
|
|
29
|
+
lastModified: Date;
|
|
30
|
+
}
|
|
31
|
+
/** Options for put operations. */
|
|
32
|
+
export interface PutOptions {
|
|
33
|
+
contentType?: string;
|
|
34
|
+
custom?: Record<string, string>;
|
|
35
|
+
}
|
|
36
|
+
/** Options for presign operations. */
|
|
37
|
+
export interface PresignOptions {
|
|
38
|
+
expiresIn: number;
|
|
39
|
+
method?: "GET" | "PUT";
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Low-level storage adapter interface.
|
|
43
|
+
* Implement this to create drivers for specific storage backends.
|
|
44
|
+
*/
|
|
45
|
+
export interface StorageAdapter {
|
|
46
|
+
/** Put an object into storage. */
|
|
47
|
+
put(key: string, body: BodyInput, options?: PutOptions): Promise<void>;
|
|
48
|
+
/** Get an object from storage. */
|
|
49
|
+
get(key: string): Promise<StorageObject>;
|
|
50
|
+
/** Delete an object from storage. */
|
|
51
|
+
delete(key: string): Promise<void>;
|
|
52
|
+
/** Get metadata without fetching the body. */
|
|
53
|
+
head(key: string): Promise<StorageMetadata>;
|
|
54
|
+
/** List objects with optional prefix. */
|
|
55
|
+
list(prefix?: string): AsyncIterable<StorageEntry>;
|
|
56
|
+
/** Release the connection back to its source. */
|
|
57
|
+
close(): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extended adapter interface for backends that support presigned URLs.
|
|
61
|
+
*/
|
|
62
|
+
export interface PresignableAdapter extends StorageAdapter {
|
|
63
|
+
/** Generate a presigned URL for direct access. */
|
|
64
|
+
presign(key: string, options?: PresignOptions): Promise<string>;
|
|
65
|
+
}
|
|
66
|
+
/** Connector that produces connected StorageAdapter instances. */
|
|
67
|
+
export interface StorageConnector {
|
|
68
|
+
/** Acquire a connected adapter. */
|
|
69
|
+
connect(signal?: AbortSignal): Promise<StorageAdapter>;
|
|
70
|
+
/** Close all connections and clean up resources. */
|
|
71
|
+
end(): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
/** Storage configuration options. */
|
|
74
|
+
export interface StorageOptions {
|
|
75
|
+
/** Prefix for all keys in this storage. */
|
|
76
|
+
prefix?: string;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,cAAc,GAAG,MAAM,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,IAAI,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,cAAc,CAAC;IACrB,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,IAAI,CAAC;CACpB;AAED,kCAAkC;AAClC,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,kCAAkC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACzC,qCAAqC;IACrC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,8CAA8C;IAC9C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5C,yCAAyC;IACzC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC;IACnD,iDAAiD;IACjD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,kDAAkD;IAClD,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACjE;AAED,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,OAAO,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACvD,oDAAoD;IACpD,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,qCAAqC;AACrC,MAAM,WAAW,cAAc;IAC7B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
package/esm/adapter.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { StorageConnector, StorageOptions } from "./adapter.js";
|
|
2
|
+
/**
|
|
3
|
+
* Creates an in-memory storage connector for testing.
|
|
4
|
+
* Data is stored in memory and lost when the process ends.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const connector = createMemory({ prefix: "files/" });
|
|
9
|
+
* const storage = await connector.connect();
|
|
10
|
+
* await storage.put("test.txt", "Hello");
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare function createMemory(options?: StorageOptions): StorageConnector;
|
|
14
|
+
//# sourceMappingURL=connector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connector.d.ts","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,gBAAgB,EAEhB,cAAc,EACf,MAAM,cAAc,CAAC;AAKtB;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,gBAAgB,CAuFvE"}
|
package/esm/connector.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { StorageObjectNotFound } from "./errors.js";
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
/**
|
|
4
|
+
* Creates an in-memory storage connector for testing.
|
|
5
|
+
* Data is stored in memory and lost when the process ends.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const connector = createMemory({ prefix: "files/" });
|
|
10
|
+
* const storage = await connector.connect();
|
|
11
|
+
* await storage.put("test.txt", "Hello");
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function createMemory(options) {
|
|
15
|
+
const prefix = options?.prefix ?? "";
|
|
16
|
+
const data = new Map();
|
|
17
|
+
const metadata = new Map();
|
|
18
|
+
return {
|
|
19
|
+
connect: () => {
|
|
20
|
+
return Promise.resolve({
|
|
21
|
+
put: async (key, body, opts) => {
|
|
22
|
+
const fullKey = prefix + key;
|
|
23
|
+
const bytes = typeof body === "string"
|
|
24
|
+
? encoder.encode(body)
|
|
25
|
+
: body instanceof ReadableStream
|
|
26
|
+
? await bytesFromStream(body)
|
|
27
|
+
: body;
|
|
28
|
+
data.set(fullKey, bytes);
|
|
29
|
+
const size = bytes.length;
|
|
30
|
+
const lastModified = new Date();
|
|
31
|
+
metadata.set(fullKey, {
|
|
32
|
+
key: fullKey,
|
|
33
|
+
size,
|
|
34
|
+
lastModified,
|
|
35
|
+
contentType: opts?.contentType,
|
|
36
|
+
custom: opts?.custom,
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
get: (key) => {
|
|
40
|
+
const fullKey = prefix + key;
|
|
41
|
+
const bytes = data.get(fullKey);
|
|
42
|
+
if (!bytes) {
|
|
43
|
+
return Promise.reject(new StorageObjectNotFound(key));
|
|
44
|
+
}
|
|
45
|
+
const meta = metadata.get(fullKey);
|
|
46
|
+
if (!meta) {
|
|
47
|
+
return Promise.reject(new StorageObjectNotFound(key));
|
|
48
|
+
}
|
|
49
|
+
return Promise.resolve({
|
|
50
|
+
body: new ReadableStream({
|
|
51
|
+
start(controller) {
|
|
52
|
+
controller.enqueue(bytes);
|
|
53
|
+
controller.close();
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
metadata: { ...meta },
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
delete: (key) => {
|
|
60
|
+
const fullKey = prefix + key;
|
|
61
|
+
data.delete(fullKey);
|
|
62
|
+
metadata.delete(fullKey);
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
},
|
|
65
|
+
head: (key) => {
|
|
66
|
+
const fullKey = prefix + key;
|
|
67
|
+
const meta = metadata.get(fullKey);
|
|
68
|
+
if (!meta) {
|
|
69
|
+
return Promise.reject(new StorageObjectNotFound(key));
|
|
70
|
+
}
|
|
71
|
+
return Promise.resolve({ ...meta });
|
|
72
|
+
},
|
|
73
|
+
list: (p) => {
|
|
74
|
+
const searchPrefix = prefix + (p ?? "");
|
|
75
|
+
return (async function* () {
|
|
76
|
+
for (const [key, meta] of metadata) {
|
|
77
|
+
if (key.startsWith(searchPrefix)) {
|
|
78
|
+
yield {
|
|
79
|
+
key,
|
|
80
|
+
size: meta.size,
|
|
81
|
+
lastModified: meta.lastModified,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
})();
|
|
86
|
+
},
|
|
87
|
+
close: () => Promise.resolve(),
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
end: () => {
|
|
91
|
+
data.clear();
|
|
92
|
+
metadata.clear();
|
|
93
|
+
return Promise.resolve();
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
async function bytesFromStream(stream) {
|
|
98
|
+
const reader = stream.getReader();
|
|
99
|
+
const chunks = [];
|
|
100
|
+
while (true) {
|
|
101
|
+
const { done, value } = await reader.read();
|
|
102
|
+
if (done)
|
|
103
|
+
break;
|
|
104
|
+
chunks.push(value);
|
|
105
|
+
}
|
|
106
|
+
const total = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
107
|
+
const result = new Uint8Array(total);
|
|
108
|
+
let offset = 0;
|
|
109
|
+
for (const chunk of chunks) {
|
|
110
|
+
result.set(chunk, offset);
|
|
111
|
+
offset += chunk.length;
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
package/esm/errors.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare class StorageConnectionFailed extends Error {
|
|
2
|
+
name: string;
|
|
3
|
+
constructor(message: string, cause?: unknown);
|
|
4
|
+
}
|
|
5
|
+
export declare class StorageObjectNotFound extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
constructor(key: string);
|
|
8
|
+
}
|
|
9
|
+
export declare class StorageCloseFailed extends Error {
|
|
10
|
+
name: string;
|
|
11
|
+
constructor(message: string, cause?: unknown);
|
|
12
|
+
}
|
|
13
|
+
export declare class StoragePutFailed extends Error {
|
|
14
|
+
name: string;
|
|
15
|
+
constructor(key: string, message: string, cause?: unknown);
|
|
16
|
+
}
|
|
17
|
+
export declare class StorageGetFailed extends Error {
|
|
18
|
+
name: string;
|
|
19
|
+
constructor(key: string, message: string, cause?: unknown);
|
|
20
|
+
}
|
|
21
|
+
export declare class StorageDeleteFailed extends Error {
|
|
22
|
+
name: string;
|
|
23
|
+
constructor(key: string, message: string, cause?: unknown);
|
|
24
|
+
}
|
|
25
|
+
export declare class StorageHeadFailed extends Error {
|
|
26
|
+
name: string;
|
|
27
|
+
constructor(key: string, message: string, cause?: unknown);
|
|
28
|
+
}
|
|
29
|
+
export declare class StoragePresignFailed extends Error {
|
|
30
|
+
name: string;
|
|
31
|
+
constructor(key: string, message: string, cause?: unknown);
|
|
32
|
+
}
|
|
33
|
+
export declare class StorageListFailed extends Error {
|
|
34
|
+
name: string;
|
|
35
|
+
constructor(prefix: string | undefined, message: string, cause?: unknown);
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,uBAAwB,SAAQ,KAAK;IACvC,IAAI,SAA6B;gBAExC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CAIlB;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IACrC,IAAI,SAA2B;gBAC5B,GAAG,EAAE,MAAM;CAGxB;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAClC,IAAI,SAAwB;gBAEnC,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CAIlB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,SAAsB;gBAEjC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CAIlB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAChC,IAAI,SAAsB;gBAEjC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CAIlB;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IACnC,IAAI,SAAyB;gBAEpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CAIlB;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IACjC,IAAI,SAAuB;gBAElC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CAIlB;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IACpC,IAAI,SAA0B;gBAErC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CAIlB;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IACjC,IAAI,SAAuB;gBAElC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO;CASlB"}
|
package/esm/errors.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export class StorageConnectionFailed extends Error {
|
|
2
|
+
constructor(message, cause) {
|
|
3
|
+
super(`Storage connection failed: ${message}`, { cause });
|
|
4
|
+
Object.defineProperty(this, "name", {
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true,
|
|
8
|
+
value: "StorageConnectionFailed"
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class StorageObjectNotFound extends Error {
|
|
13
|
+
constructor(key) {
|
|
14
|
+
super(`Storage object not found: ${key}`);
|
|
15
|
+
Object.defineProperty(this, "name", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: "StorageObjectNotFound"
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export class StorageCloseFailed extends Error {
|
|
24
|
+
constructor(message, cause) {
|
|
25
|
+
super(`Storage close failed: ${message}`, { cause });
|
|
26
|
+
Object.defineProperty(this, "name", {
|
|
27
|
+
enumerable: true,
|
|
28
|
+
configurable: true,
|
|
29
|
+
writable: true,
|
|
30
|
+
value: "StorageCloseFailed"
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export class StoragePutFailed extends Error {
|
|
35
|
+
constructor(key, message, cause) {
|
|
36
|
+
super(`Failed to put object ${key}: ${message}`, { cause });
|
|
37
|
+
Object.defineProperty(this, "name", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
configurable: true,
|
|
40
|
+
writable: true,
|
|
41
|
+
value: "StoragePutFailed"
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class StorageGetFailed extends Error {
|
|
46
|
+
constructor(key, message, cause) {
|
|
47
|
+
super(`Failed to get object ${key}: ${message}`, { cause });
|
|
48
|
+
Object.defineProperty(this, "name", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true,
|
|
52
|
+
value: "StorageGetFailed"
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export class StorageDeleteFailed extends Error {
|
|
57
|
+
constructor(key, message, cause) {
|
|
58
|
+
super(`Failed to delete object ${key}: ${message}`, { cause });
|
|
59
|
+
Object.defineProperty(this, "name", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
configurable: true,
|
|
62
|
+
writable: true,
|
|
63
|
+
value: "StorageDeleteFailed"
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export class StorageHeadFailed extends Error {
|
|
68
|
+
constructor(key, message, cause) {
|
|
69
|
+
super(`Failed to head object ${key}: ${message}`, { cause });
|
|
70
|
+
Object.defineProperty(this, "name", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: "StorageHeadFailed"
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export class StoragePresignFailed extends Error {
|
|
79
|
+
constructor(key, message, cause) {
|
|
80
|
+
super(`Failed to presign object ${key}: ${message}`, { cause });
|
|
81
|
+
Object.defineProperty(this, "name", {
|
|
82
|
+
enumerable: true,
|
|
83
|
+
configurable: true,
|
|
84
|
+
writable: true,
|
|
85
|
+
value: "StoragePresignFailed"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export class StorageListFailed extends Error {
|
|
90
|
+
constructor(prefix, message, cause) {
|
|
91
|
+
super(`Failed to list objects${prefix ? ` with prefix "${prefix}"` : ""}: ${message}`, { cause });
|
|
92
|
+
Object.defineProperty(this, "name", {
|
|
93
|
+
enumerable: true,
|
|
94
|
+
configurable: true,
|
|
95
|
+
writable: true,
|
|
96
|
+
value: "StorageListFailed"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @anabranch/storage
|
|
3
|
+
*
|
|
4
|
+
* Storage primitives with Task/Stream semantics for error-tolerant object operations.
|
|
5
|
+
* Integrates with anabranch's {@linkcode Task}, {@linkcode Stream}, {@linkcode Source},
|
|
6
|
+
* and {@linkcode Channel} types for composable error handling and concurrent processing.
|
|
7
|
+
*
|
|
8
|
+
* ## Adapters vs Connectors
|
|
9
|
+
*
|
|
10
|
+
* A **StorageConnector** produces connected **StorageAdapter** instances. Use connectors
|
|
11
|
+
* for production code to properly manage connection lifecycles:
|
|
12
|
+
*
|
|
13
|
+
* - **Connector**: Manages connection pool/lifecycle, produces adapters
|
|
14
|
+
* - **Adapter**: Low-level put/get/delete/list interface
|
|
15
|
+
* - **Storage**: High-level wrapper with Task/Stream methods
|
|
16
|
+
*
|
|
17
|
+
* ## Core Types
|
|
18
|
+
*
|
|
19
|
+
* - {@link StorageConnector} - Interface for connection factories
|
|
20
|
+
* - {@link StorageAdapter} - Low-level storage operations interface
|
|
21
|
+
* - {@link StorageObject} - Retrieved object with body stream and metadata
|
|
22
|
+
* - {@link StorageMetadata} - Object metadata (size, contentType, etag, etc.)
|
|
23
|
+
*
|
|
24
|
+
* ## Error Types
|
|
25
|
+
*
|
|
26
|
+
* All errors are typed for catchable handling:
|
|
27
|
+
* - {@link StorageConnectionFailed} - Connection establishment failed
|
|
28
|
+
* - {@link StorageObjectNotFound} - Object does not exist
|
|
29
|
+
* - {@link StoragePutFailed} - Put operation failed
|
|
30
|
+
* - {@link StorageGetFailed} - Get operation failed
|
|
31
|
+
* - {@link StorageDeleteFailed} - Delete operation failed
|
|
32
|
+
* - {@link StorageHeadFailed} - Head operation failed
|
|
33
|
+
*
|
|
34
|
+
* @example Basic put/get operations with Storage wrapper
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { Storage, createMemory } from "@anabranch/storage";
|
|
37
|
+
*
|
|
38
|
+
* const connector = createMemory({ prefix: "files/" });
|
|
39
|
+
* const storage = await Storage.connect(connector).run();
|
|
40
|
+
*
|
|
41
|
+
* await storage.put("hello.txt", "Hello, World!").run();
|
|
42
|
+
* const object = await storage.get("hello.txt").run();
|
|
43
|
+
* console.log(await new Response(object.body).text());
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example Stream listing with concurrent processing
|
|
47
|
+
* ```ts
|
|
48
|
+
* const connector = createMemory();
|
|
49
|
+
* const storage = await Storage.connect(connector).run();
|
|
50
|
+
*
|
|
51
|
+
* await storage.put("users/1.json", '{"name": "Alice"}');
|
|
52
|
+
* await storage.put("users/2.json", '{"name": "Bob"}');
|
|
53
|
+
*
|
|
54
|
+
* const { successes, errors } = await storage.list("users/")
|
|
55
|
+
* .withConcurrency(5)
|
|
56
|
+
* .map(async (entry) => await processEntry(entry))
|
|
57
|
+
* .tapErr((err) => console.error("Failed:", err))
|
|
58
|
+
* .partition();
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Head request for metadata
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { Storage, createMemory } from "@anabranch/storage";
|
|
64
|
+
*
|
|
65
|
+
* const connector = createMemory();
|
|
66
|
+
* const storage = await Storage.connect(connector).run();
|
|
67
|
+
*
|
|
68
|
+
* await storage.put("image.png", imageBytes, { contentType: "image/png" }).run();
|
|
69
|
+
* const metadata = await storage.head("image.png").run();
|
|
70
|
+
* console.log(metadata.contentType, metadata.size);
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example With retry and timeout
|
|
74
|
+
* ```ts
|
|
75
|
+
* await storage.put("important.txt", data)
|
|
76
|
+
* .retry({ attempts: 3, delay: (attempt) => 100 * Math.pow(2, attempt) })
|
|
77
|
+
* .timeout(30_000)
|
|
78
|
+
* .run();
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @module
|
|
82
|
+
*/
|
|
83
|
+
export { Storage } from "./storage.js";
|
|
84
|
+
export type { BodyInput, PresignableAdapter, PresignOptions, PutOptions, StorageAdapter, StorageConnector, StorageEntry, StorageMetadata, StorageObject, StorageOptions, } from "./adapter.js";
|
|
85
|
+
export * from "./errors.js";
|
|
86
|
+
export { createMemory } from "./connector.js";
|
|
87
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiFG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EACV,SAAS,EACT,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,aAAa,EACb,cAAc,GACf,MAAM,cAAc,CAAC;AACtB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @anabranch/storage
|
|
3
|
+
*
|
|
4
|
+
* Storage primitives with Task/Stream semantics for error-tolerant object operations.
|
|
5
|
+
* Integrates with anabranch's {@linkcode Task}, {@linkcode Stream}, {@linkcode Source},
|
|
6
|
+
* and {@linkcode Channel} types for composable error handling and concurrent processing.
|
|
7
|
+
*
|
|
8
|
+
* ## Adapters vs Connectors
|
|
9
|
+
*
|
|
10
|
+
* A **StorageConnector** produces connected **StorageAdapter** instances. Use connectors
|
|
11
|
+
* for production code to properly manage connection lifecycles:
|
|
12
|
+
*
|
|
13
|
+
* - **Connector**: Manages connection pool/lifecycle, produces adapters
|
|
14
|
+
* - **Adapter**: Low-level put/get/delete/list interface
|
|
15
|
+
* - **Storage**: High-level wrapper with Task/Stream methods
|
|
16
|
+
*
|
|
17
|
+
* ## Core Types
|
|
18
|
+
*
|
|
19
|
+
* - {@link StorageConnector} - Interface for connection factories
|
|
20
|
+
* - {@link StorageAdapter} - Low-level storage operations interface
|
|
21
|
+
* - {@link StorageObject} - Retrieved object with body stream and metadata
|
|
22
|
+
* - {@link StorageMetadata} - Object metadata (size, contentType, etag, etc.)
|
|
23
|
+
*
|
|
24
|
+
* ## Error Types
|
|
25
|
+
*
|
|
26
|
+
* All errors are typed for catchable handling:
|
|
27
|
+
* - {@link StorageConnectionFailed} - Connection establishment failed
|
|
28
|
+
* - {@link StorageObjectNotFound} - Object does not exist
|
|
29
|
+
* - {@link StoragePutFailed} - Put operation failed
|
|
30
|
+
* - {@link StorageGetFailed} - Get operation failed
|
|
31
|
+
* - {@link StorageDeleteFailed} - Delete operation failed
|
|
32
|
+
* - {@link StorageHeadFailed} - Head operation failed
|
|
33
|
+
*
|
|
34
|
+
* @example Basic put/get operations with Storage wrapper
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { Storage, createMemory } from "@anabranch/storage";
|
|
37
|
+
*
|
|
38
|
+
* const connector = createMemory({ prefix: "files/" });
|
|
39
|
+
* const storage = await Storage.connect(connector).run();
|
|
40
|
+
*
|
|
41
|
+
* await storage.put("hello.txt", "Hello, World!").run();
|
|
42
|
+
* const object = await storage.get("hello.txt").run();
|
|
43
|
+
* console.log(await new Response(object.body).text());
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* @example Stream listing with concurrent processing
|
|
47
|
+
* ```ts
|
|
48
|
+
* const connector = createMemory();
|
|
49
|
+
* const storage = await Storage.connect(connector).run();
|
|
50
|
+
*
|
|
51
|
+
* await storage.put("users/1.json", '{"name": "Alice"}');
|
|
52
|
+
* await storage.put("users/2.json", '{"name": "Bob"}');
|
|
53
|
+
*
|
|
54
|
+
* const { successes, errors } = await storage.list("users/")
|
|
55
|
+
* .withConcurrency(5)
|
|
56
|
+
* .map(async (entry) => await processEntry(entry))
|
|
57
|
+
* .tapErr((err) => console.error("Failed:", err))
|
|
58
|
+
* .partition();
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @example Head request for metadata
|
|
62
|
+
* ```ts
|
|
63
|
+
* import { Storage, createMemory } from "@anabranch/storage";
|
|
64
|
+
*
|
|
65
|
+
* const connector = createMemory();
|
|
66
|
+
* const storage = await Storage.connect(connector).run();
|
|
67
|
+
*
|
|
68
|
+
* await storage.put("image.png", imageBytes, { contentType: "image/png" }).run();
|
|
69
|
+
* const metadata = await storage.head("image.png").run();
|
|
70
|
+
* console.log(metadata.contentType, metadata.size);
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example With retry and timeout
|
|
74
|
+
* ```ts
|
|
75
|
+
* await storage.put("important.txt", data)
|
|
76
|
+
* .retry({ attempts: 3, delay: (attempt) => 100 * Math.pow(2, attempt) })
|
|
77
|
+
* .timeout(30_000)
|
|
78
|
+
* .run();
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @module
|
|
82
|
+
*/
|
|
83
|
+
export { Storage } from "./storage.js";
|
|
84
|
+
export * from "./errors.js";
|
|
85
|
+
export { createMemory } from "./connector.js";
|
package/esm/package.json
ADDED
package/esm/storage.d.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Source, Task } from "anabranch";
|
|
2
|
+
import type { BodyInput, PutOptions, StorageAdapter, StorageConnector, StorageEntry, StorageMetadata, StorageObject } from "./adapter.js";
|
|
3
|
+
import { StorageCloseFailed, StorageConnectionFailed, StorageDeleteFailed, StorageGetFailed, StorageHeadFailed, StorageListFailed, StorageObjectNotFound, StoragePutFailed } from "./errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Storage wrapper with Task/Stream semantics for error-tolerant object operations.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic put/get operations
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { Storage, createMemory } from "@anabranch/storage";
|
|
10
|
+
*
|
|
11
|
+
* const connector = createMemory();
|
|
12
|
+
* const storage = await Storage.connect(connector).run();
|
|
13
|
+
*
|
|
14
|
+
* await storage.put("hello.txt", "Hello, World!").run();
|
|
15
|
+
* const object = await storage.get("hello.txt").run();
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example Stream listing objects
|
|
19
|
+
* ```ts
|
|
20
|
+
* const { successes, errors } = await storage.list("users/")
|
|
21
|
+
* .withConcurrency(5)
|
|
22
|
+
* .map(async (entry) => await processEntry(entry))
|
|
23
|
+
* .tapErr((err) => console.error("Failed:", err))
|
|
24
|
+
* .partition();
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example With retry and timeout
|
|
28
|
+
* ```ts
|
|
29
|
+
* await storage.put("important.txt", data)
|
|
30
|
+
* .retry({ attempts: 3, delay: (attempt) => 100 * Math.pow(2, attempt) })
|
|
31
|
+
* .timeout(30_000)
|
|
32
|
+
* .run();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare class Storage {
|
|
36
|
+
private readonly adapter;
|
|
37
|
+
constructor(adapter: StorageAdapter);
|
|
38
|
+
/**
|
|
39
|
+
* Connect to storage via a connector.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const storage = await Storage.connect(createMemory()).run();
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
static connect(connector: StorageConnector): Task<Storage, StorageConnectionFailed>;
|
|
47
|
+
/**
|
|
48
|
+
* Release the connection back to its source (e.g., pool).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* await storage.close().run();
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
close(): Task<void, StorageCloseFailed>;
|
|
56
|
+
/**
|
|
57
|
+
* Put an object into storage.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* await storage.put("image.png", imageBytes, { contentType: "image/png" }).run();
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
put(key: string, body: BodyInput, options?: PutOptions): Task<void, StoragePutFailed>;
|
|
65
|
+
/**
|
|
66
|
+
* Get an object from storage.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* const object = await storage.get("file.txt").run();
|
|
71
|
+
* const text = await new Response(object.body).text();
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
get(key: string): Task<StorageObject, StorageGetFailed | StorageObjectNotFound>;
|
|
75
|
+
/**
|
|
76
|
+
* Delete an object from storage.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* await storage.delete("old-file.txt").run();
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
delete(key: string): Task<void, StorageDeleteFailed>;
|
|
84
|
+
/**
|
|
85
|
+
* Get metadata for an object without fetching the body.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* const metadata = await storage.head("file.txt").run();
|
|
90
|
+
* console.log(metadata.size, metadata.contentType);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
head(key: string): Task<StorageMetadata, StorageHeadFailed | StorageObjectNotFound>;
|
|
94
|
+
/**
|
|
95
|
+
* List objects in storage with optional prefix.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* const { successes, errors } = await storage.list("images/")
|
|
100
|
+
* .withConcurrency(10)
|
|
101
|
+
* .map(async (entry) => await processImage(entry))
|
|
102
|
+
* .tapErr((err) => console.error("Failed:", err))
|
|
103
|
+
* .partition();
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
list(prefix?: string): Source<StorageEntry, StorageListFailed>;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EACV,SAAS,EACT,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,aAAa,EACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,kBAAkB,EAClB,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,OAAO;IACN,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAEpD;;;;;;;OAOG;IACH,MAAM,CAAC,OAAO,CACZ,SAAS,EAAE,gBAAgB,GAC1B,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC;IAazC;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAavC;;;;;;;OAOG;IACH,GAAG,CACD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,UAAU,GACnB,IAAI,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAc/B;;;;;;;;OAQG;IACH,GAAG,CACD,GAAG,EAAE,MAAM,GACV,IAAI,CAAC,aAAa,EAAE,gBAAgB,GAAG,qBAAqB,CAAC;IAiBhE;;;;;;;OAOG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAcpD;;;;;;;;OAQG;IACH,IAAI,CACF,GAAG,EAAE,MAAM,GACV,IAAI,CAAC,eAAe,EAAE,iBAAiB,GAAG,qBAAqB,CAAC;IAiBnE;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,YAAY,EAAE,iBAAiB,CAAC;CAkB/D"}
|
package/esm/storage.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { Source, Task } from "anabranch";
|
|
2
|
+
import { StorageCloseFailed, StorageConnectionFailed, StorageDeleteFailed, StorageGetFailed, StorageHeadFailed, StorageListFailed, StorageObjectNotFound, StoragePutFailed, } from "./errors.js";
|
|
3
|
+
/**
|
|
4
|
+
* Storage wrapper with Task/Stream semantics for error-tolerant object operations.
|
|
5
|
+
*
|
|
6
|
+
* @example Basic put/get operations
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { Storage, createMemory } from "@anabranch/storage";
|
|
9
|
+
*
|
|
10
|
+
* const connector = createMemory();
|
|
11
|
+
* const storage = await Storage.connect(connector).run();
|
|
12
|
+
*
|
|
13
|
+
* await storage.put("hello.txt", "Hello, World!").run();
|
|
14
|
+
* const object = await storage.get("hello.txt").run();
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @example Stream listing objects
|
|
18
|
+
* ```ts
|
|
19
|
+
* const { successes, errors } = await storage.list("users/")
|
|
20
|
+
* .withConcurrency(5)
|
|
21
|
+
* .map(async (entry) => await processEntry(entry))
|
|
22
|
+
* .tapErr((err) => console.error("Failed:", err))
|
|
23
|
+
* .partition();
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example With retry and timeout
|
|
27
|
+
* ```ts
|
|
28
|
+
* await storage.put("important.txt", data)
|
|
29
|
+
* .retry({ attempts: 3, delay: (attempt) => 100 * Math.pow(2, attempt) })
|
|
30
|
+
* .timeout(30_000)
|
|
31
|
+
* .run();
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class Storage {
|
|
35
|
+
constructor(adapter) {
|
|
36
|
+
Object.defineProperty(this, "adapter", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: true,
|
|
40
|
+
value: adapter
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Connect to storage via a connector.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const storage = await Storage.connect(createMemory()).run();
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
static connect(connector) {
|
|
52
|
+
return Task.of(async () => {
|
|
53
|
+
try {
|
|
54
|
+
return new Storage(await connector.connect());
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw new StorageConnectionFailed(error instanceof Error ? error.message : String(error), error);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Release the connection back to its source (e.g., pool).
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* await storage.close().run();
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
close() {
|
|
70
|
+
return Task.of(async () => {
|
|
71
|
+
try {
|
|
72
|
+
await this.adapter.close();
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new StorageCloseFailed(error instanceof Error ? error.message : String(error), error);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Put an object into storage.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* await storage.put("image.png", imageBytes, { contentType: "image/png" }).run();
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
put(key, body, options) {
|
|
88
|
+
return Task.of(async () => {
|
|
89
|
+
try {
|
|
90
|
+
await this.adapter.put(key, body, options);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
throw new StoragePutFailed(key, error instanceof Error ? error.message : String(error), error);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get an object from storage.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const object = await storage.get("file.txt").run();
|
|
103
|
+
* const text = await new Response(object.body).text();
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
get(key) {
|
|
107
|
+
return Task.of(async () => {
|
|
108
|
+
try {
|
|
109
|
+
return await this.adapter.get(key);
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error instanceof StorageObjectNotFound) {
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
throw new StorageGetFailed(key, error instanceof Error ? error.message : String(error), error);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Delete an object from storage.
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* await storage.delete("old-file.txt").run();
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
delete(key) {
|
|
128
|
+
return Task.of(async () => {
|
|
129
|
+
try {
|
|
130
|
+
await this.adapter.delete(key);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
throw new StorageDeleteFailed(key, error instanceof Error ? error.message : String(error), error);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get metadata for an object without fetching the body.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```ts
|
|
142
|
+
* const metadata = await storage.head("file.txt").run();
|
|
143
|
+
* console.log(metadata.size, metadata.contentType);
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
head(key) {
|
|
147
|
+
return Task.of(async () => {
|
|
148
|
+
try {
|
|
149
|
+
return await this.adapter.head(key);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (error instanceof StorageObjectNotFound) {
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
throw new StorageHeadFailed(key, error instanceof Error ? error.message : String(error), error);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* List objects in storage with optional prefix.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* const { successes, errors } = await storage.list("images/")
|
|
165
|
+
* .withConcurrency(10)
|
|
166
|
+
* .map(async (entry) => await processImage(entry))
|
|
167
|
+
* .tapErr((err) => console.error("Failed:", err))
|
|
168
|
+
* .partition();
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
list(prefix) {
|
|
172
|
+
const adapter = this.adapter;
|
|
173
|
+
return Source.from(async function* () {
|
|
174
|
+
try {
|
|
175
|
+
for await (const entry of adapter.list(prefix)) {
|
|
176
|
+
yield entry;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
throw new StorageListFailed(prefix, error instanceof Error ? error.message : String(error), error);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@anabranch/storage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TODO: Add description",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/frodi-karlsson/anabranch.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/frodi-karlsson/anabranch.git"
|
|
12
|
+
},
|
|
13
|
+
"module": "./esm/index.js",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"import": "./esm/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"anabranch": "^0"
|
|
22
|
+
},
|
|
23
|
+
"_generatedBy": "dnt@dev"
|
|
24
|
+
}
|