@forklaunch/infrastructure-s3 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 forklaunch
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.
@@ -0,0 +1,230 @@
1
+ import {
2
+ DeleteObjectCommand,
3
+ DeleteObjectsCommand,
4
+ DeleteObjectsCommandInput,
5
+ GetObjectCommand,
6
+ PutObjectCommand,
7
+ PutObjectCommandInput,
8
+ S3Client
9
+ } from '@aws-sdk/client-s3';
10
+ import {
11
+ MetricsDefinition,
12
+ OpenTelemetryCollector,
13
+ TelemetryOptions
14
+ } from '@forklaunch/core/http';
15
+ import { ObjectStore } from '@forklaunch/core/objectstore';
16
+ import { Readable } from 'stream';
17
+
18
+ /**
19
+ * Options for configuring the S3ObjectStore.
20
+ *
21
+ * @example
22
+ * const options: S3ObjectStoreOptions = {
23
+ * bucket: 'my-bucket',
24
+ * clientConfig: { region: 'us-west-2' }
25
+ * };
26
+ */
27
+ interface S3ObjectStoreOptions {
28
+ /** The S3 bucket name. */
29
+ bucket: string;
30
+ /** Optional existing S3 client instance. */
31
+ client?: S3Client;
32
+ /** Optional configuration for creating a new S3 client. */
33
+ clientConfig?: ConstructorParameters<typeof S3Client>[0];
34
+ }
35
+
36
+ /**
37
+ * S3-backed implementation of the ObjectStore interface.
38
+ * Provides methods for storing, retrieving, streaming, and deleting objects in S3.
39
+ *
40
+ * @example
41
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
42
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
43
+ * const user = await store.readObject<{ name: string }>('user-1');
44
+ */
45
+ export class S3ObjectStore implements ObjectStore<S3Client> {
46
+ private s3: S3Client;
47
+ private bucket: string;
48
+
49
+ /**
50
+ * Creates a new S3ObjectStore instance.
51
+ * @param openTelemetryCollector - Collector for OpenTelemetry metrics.
52
+ * @param options - S3 configuration options.
53
+ * @param telemetryOptions - Telemetry configuration options.
54
+ *
55
+ * @example
56
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
57
+ */
58
+ constructor(
59
+ private openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,
60
+ options: S3ObjectStoreOptions,
61
+ private telemetryOptions: TelemetryOptions
62
+ ) {
63
+ this.s3 = options.client || new S3Client(options.clientConfig || {});
64
+ this.bucket = options.bucket;
65
+ }
66
+
67
+ /**
68
+ * Stores an object in the S3 bucket.
69
+ * @template T - The type of the object being stored.
70
+ * @param object - The object to store. Must include a `key` property.
71
+ *
72
+ * @example
73
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
74
+ */
75
+ async putObject<T>(object: T & { key: string }): Promise<void> {
76
+ const { key, ...rest } = object;
77
+ const params: PutObjectCommandInput = {
78
+ Bucket: this.bucket,
79
+ Key: key,
80
+ Body: JSON.stringify(rest),
81
+ ContentType: 'application/json'
82
+ };
83
+ await this.s3.send(new PutObjectCommand(params));
84
+ }
85
+
86
+ /**
87
+ * Stores multiple objects in the S3 bucket.
88
+ * @template T - The type of the objects being stored.
89
+ * @param objects - The objects to store. Each must include a `key` property.
90
+ *
91
+ * @example
92
+ * await store.putBatchObjects([
93
+ * { key: 'user-1', name: 'Alice' },
94
+ * { key: 'user-2', name: 'Bob' }
95
+ * ]);
96
+ */
97
+ async putBatchObjects<T>(objects: (T & { key: string })[]): Promise<void> {
98
+ await Promise.all(objects.map((obj) => this.putObject(obj)));
99
+ }
100
+
101
+ /**
102
+ * Streams an object upload to the S3 bucket.
103
+ * For compatibility; uses putObject internally.
104
+ * @template T - The type of the object being stored.
105
+ * @param object - The object to stream-upload. Must include a `key` property.
106
+ */
107
+ async streamUploadObject<T>(object: T & { key: string }): Promise<void> {
108
+ await this.putObject(object);
109
+ }
110
+
111
+ /**
112
+ * Streams multiple object uploads to the S3 bucket.
113
+ * For compatibility; uses putBatchObjects internally.
114
+ * @template T - The type of the objects being stored.
115
+ * @param objects - The objects to stream-upload. Each must include a `key` property.
116
+ */
117
+ async streamUploadBatchObjects<T>(
118
+ objects: (T & { key: string })[]
119
+ ): Promise<void> {
120
+ await this.putBatchObjects(objects);
121
+ }
122
+
123
+ /**
124
+ * Deletes an object from the S3 bucket.
125
+ * @param objectKey - The key of the object to delete.
126
+ *
127
+ * @example
128
+ * await store.deleteObject('user-1');
129
+ */
130
+ async deleteObject(objectKey: string): Promise<void> {
131
+ await this.s3.send(
132
+ new DeleteObjectCommand({ Bucket: this.bucket, Key: objectKey })
133
+ );
134
+ }
135
+
136
+ /**
137
+ * Deletes multiple objects from the S3 bucket.
138
+ * @param objectKeys - The keys of the objects to delete.
139
+ *
140
+ * @example
141
+ * await store.deleteBatchObjects(['user-1', 'user-2']);
142
+ */
143
+ async deleteBatchObjects(objectKeys: string[]): Promise<void> {
144
+ const params: DeleteObjectsCommandInput = {
145
+ Bucket: this.bucket,
146
+ Delete: {
147
+ Objects: objectKeys.map((Key) => ({ Key }))
148
+ }
149
+ };
150
+ await this.s3.send(new DeleteObjectsCommand(params));
151
+ }
152
+
153
+ /**
154
+ * Reads an object from the S3 bucket.
155
+ * @template T - The expected type of the object.
156
+ * @param objectKey - The key of the object to read.
157
+ * @returns The parsed object.
158
+ *
159
+ * @example
160
+ * const user = await store.readObject<{ name: string }>('user-1');
161
+ */
162
+ async readObject<T>(objectKey: string): Promise<T> {
163
+ const resp = await this.s3.send(
164
+ new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })
165
+ );
166
+ const body = resp.Body as Readable;
167
+ const chunks: Buffer[] = [];
168
+ for await (const chunk of body) {
169
+ chunks.push(chunk);
170
+ }
171
+ return JSON.parse(Buffer.concat(chunks).toString()) as T;
172
+ }
173
+
174
+ /**
175
+ * Reads multiple objects from the S3 bucket.
176
+ * @template T - The expected type of the objects.
177
+ * @param objectKeys - The keys of the objects to read.
178
+ * @returns An array of parsed objects.
179
+ *
180
+ * @example
181
+ * const users = await store.readBatchObjects<{ name: string }>(['user-1', 'user-2']);
182
+ */
183
+ async readBatchObjects<T>(objectKeys: string[]): Promise<T[]> {
184
+ return Promise.all(objectKeys.map((key) => this.readObject<T>(key)));
185
+ }
186
+
187
+ /**
188
+ * Streams an object download from the S3 bucket.
189
+ * @param objectKey - The key of the object to download.
190
+ * @returns A readable stream of the object's contents.
191
+ * @throws If the S3 response does not include a readable stream.
192
+ *
193
+ * @example
194
+ * const stream = await store.streamDownloadObject('user-1');
195
+ * stream.pipe(fs.createWriteStream('user-1.json'));
196
+ */
197
+ async streamDownloadObject(objectKey: string): Promise<Readable> {
198
+ const resp = await this.s3.send(
199
+ new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })
200
+ );
201
+ if (!resp.Body || !(resp.Body instanceof Readable)) {
202
+ throw new Error('S3 did not return a stream');
203
+ }
204
+ return resp.Body;
205
+ }
206
+
207
+ /**
208
+ * Streams multiple object downloads from the S3 bucket.
209
+ * @param objectKeys - The keys of the objects to download.
210
+ * @returns An array of readable streams.
211
+ *
212
+ * @example
213
+ * const streams = await store.streamDownloadBatchObjects(['user-1', 'user-2']);
214
+ * streams[0].pipe(fs.createWriteStream('user-1.json'));
215
+ */
216
+ async streamDownloadBatchObjects(objectKeys: string[]): Promise<Readable[]> {
217
+ return Promise.all(objectKeys.map((key) => this.streamDownloadObject(key)));
218
+ }
219
+
220
+ /**
221
+ * Gets the underlying S3 client instance.
222
+ * @returns The S3Client instance used by this store.
223
+ *
224
+ * @example
225
+ * const s3Client = store.getClient();
226
+ */
227
+ getClient(): S3Client {
228
+ return this.s3;
229
+ }
230
+ }
@@ -0,0 +1,157 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+ import { OpenTelemetryCollector, MetricsDefinition, TelemetryOptions } from '@forklaunch/core/http';
3
+ import { ObjectStore } from '@forklaunch/core/objectstore';
4
+ import { Readable } from 'stream';
5
+
6
+ /**
7
+ * Options for configuring the S3ObjectStore.
8
+ *
9
+ * @example
10
+ * const options: S3ObjectStoreOptions = {
11
+ * bucket: 'my-bucket',
12
+ * clientConfig: { region: 'us-west-2' }
13
+ * };
14
+ */
15
+ interface S3ObjectStoreOptions {
16
+ /** The S3 bucket name. */
17
+ bucket: string;
18
+ /** Optional existing S3 client instance. */
19
+ client?: S3Client;
20
+ /** Optional configuration for creating a new S3 client. */
21
+ clientConfig?: ConstructorParameters<typeof S3Client>[0];
22
+ }
23
+ /**
24
+ * S3-backed implementation of the ObjectStore interface.
25
+ * Provides methods for storing, retrieving, streaming, and deleting objects in S3.
26
+ *
27
+ * @example
28
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
29
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
30
+ * const user = await store.readObject<{ name: string }>('user-1');
31
+ */
32
+ declare class S3ObjectStore implements ObjectStore<S3Client> {
33
+ private openTelemetryCollector;
34
+ private telemetryOptions;
35
+ private s3;
36
+ private bucket;
37
+ /**
38
+ * Creates a new S3ObjectStore instance.
39
+ * @param openTelemetryCollector - Collector for OpenTelemetry metrics.
40
+ * @param options - S3 configuration options.
41
+ * @param telemetryOptions - Telemetry configuration options.
42
+ *
43
+ * @example
44
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
45
+ */
46
+ constructor(openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options: S3ObjectStoreOptions, telemetryOptions: TelemetryOptions);
47
+ /**
48
+ * Stores an object in the S3 bucket.
49
+ * @template T - The type of the object being stored.
50
+ * @param object - The object to store. Must include a `key` property.
51
+ *
52
+ * @example
53
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
54
+ */
55
+ putObject<T>(object: T & {
56
+ key: string;
57
+ }): Promise<void>;
58
+ /**
59
+ * Stores multiple objects in the S3 bucket.
60
+ * @template T - The type of the objects being stored.
61
+ * @param objects - The objects to store. Each must include a `key` property.
62
+ *
63
+ * @example
64
+ * await store.putBatchObjects([
65
+ * { key: 'user-1', name: 'Alice' },
66
+ * { key: 'user-2', name: 'Bob' }
67
+ * ]);
68
+ */
69
+ putBatchObjects<T>(objects: (T & {
70
+ key: string;
71
+ })[]): Promise<void>;
72
+ /**
73
+ * Streams an object upload to the S3 bucket.
74
+ * For compatibility; uses putObject internally.
75
+ * @template T - The type of the object being stored.
76
+ * @param object - The object to stream-upload. Must include a `key` property.
77
+ */
78
+ streamUploadObject<T>(object: T & {
79
+ key: string;
80
+ }): Promise<void>;
81
+ /**
82
+ * Streams multiple object uploads to the S3 bucket.
83
+ * For compatibility; uses putBatchObjects internally.
84
+ * @template T - The type of the objects being stored.
85
+ * @param objects - The objects to stream-upload. Each must include a `key` property.
86
+ */
87
+ streamUploadBatchObjects<T>(objects: (T & {
88
+ key: string;
89
+ })[]): Promise<void>;
90
+ /**
91
+ * Deletes an object from the S3 bucket.
92
+ * @param objectKey - The key of the object to delete.
93
+ *
94
+ * @example
95
+ * await store.deleteObject('user-1');
96
+ */
97
+ deleteObject(objectKey: string): Promise<void>;
98
+ /**
99
+ * Deletes multiple objects from the S3 bucket.
100
+ * @param objectKeys - The keys of the objects to delete.
101
+ *
102
+ * @example
103
+ * await store.deleteBatchObjects(['user-1', 'user-2']);
104
+ */
105
+ deleteBatchObjects(objectKeys: string[]): Promise<void>;
106
+ /**
107
+ * Reads an object from the S3 bucket.
108
+ * @template T - The expected type of the object.
109
+ * @param objectKey - The key of the object to read.
110
+ * @returns The parsed object.
111
+ *
112
+ * @example
113
+ * const user = await store.readObject<{ name: string }>('user-1');
114
+ */
115
+ readObject<T>(objectKey: string): Promise<T>;
116
+ /**
117
+ * Reads multiple objects from the S3 bucket.
118
+ * @template T - The expected type of the objects.
119
+ * @param objectKeys - The keys of the objects to read.
120
+ * @returns An array of parsed objects.
121
+ *
122
+ * @example
123
+ * const users = await store.readBatchObjects<{ name: string }>(['user-1', 'user-2']);
124
+ */
125
+ readBatchObjects<T>(objectKeys: string[]): Promise<T[]>;
126
+ /**
127
+ * Streams an object download from the S3 bucket.
128
+ * @param objectKey - The key of the object to download.
129
+ * @returns A readable stream of the object's contents.
130
+ * @throws If the S3 response does not include a readable stream.
131
+ *
132
+ * @example
133
+ * const stream = await store.streamDownloadObject('user-1');
134
+ * stream.pipe(fs.createWriteStream('user-1.json'));
135
+ */
136
+ streamDownloadObject(objectKey: string): Promise<Readable>;
137
+ /**
138
+ * Streams multiple object downloads from the S3 bucket.
139
+ * @param objectKeys - The keys of the objects to download.
140
+ * @returns An array of readable streams.
141
+ *
142
+ * @example
143
+ * const streams = await store.streamDownloadBatchObjects(['user-1', 'user-2']);
144
+ * streams[0].pipe(fs.createWriteStream('user-1.json'));
145
+ */
146
+ streamDownloadBatchObjects(objectKeys: string[]): Promise<Readable[]>;
147
+ /**
148
+ * Gets the underlying S3 client instance.
149
+ * @returns The S3Client instance used by this store.
150
+ *
151
+ * @example
152
+ * const s3Client = store.getClient();
153
+ */
154
+ getClient(): S3Client;
155
+ }
156
+
157
+ export { S3ObjectStore };
package/lib/index.d.ts ADDED
@@ -0,0 +1,157 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+ import { OpenTelemetryCollector, MetricsDefinition, TelemetryOptions } from '@forklaunch/core/http';
3
+ import { ObjectStore } from '@forklaunch/core/objectstore';
4
+ import { Readable } from 'stream';
5
+
6
+ /**
7
+ * Options for configuring the S3ObjectStore.
8
+ *
9
+ * @example
10
+ * const options: S3ObjectStoreOptions = {
11
+ * bucket: 'my-bucket',
12
+ * clientConfig: { region: 'us-west-2' }
13
+ * };
14
+ */
15
+ interface S3ObjectStoreOptions {
16
+ /** The S3 bucket name. */
17
+ bucket: string;
18
+ /** Optional existing S3 client instance. */
19
+ client?: S3Client;
20
+ /** Optional configuration for creating a new S3 client. */
21
+ clientConfig?: ConstructorParameters<typeof S3Client>[0];
22
+ }
23
+ /**
24
+ * S3-backed implementation of the ObjectStore interface.
25
+ * Provides methods for storing, retrieving, streaming, and deleting objects in S3.
26
+ *
27
+ * @example
28
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
29
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
30
+ * const user = await store.readObject<{ name: string }>('user-1');
31
+ */
32
+ declare class S3ObjectStore implements ObjectStore<S3Client> {
33
+ private openTelemetryCollector;
34
+ private telemetryOptions;
35
+ private s3;
36
+ private bucket;
37
+ /**
38
+ * Creates a new S3ObjectStore instance.
39
+ * @param openTelemetryCollector - Collector for OpenTelemetry metrics.
40
+ * @param options - S3 configuration options.
41
+ * @param telemetryOptions - Telemetry configuration options.
42
+ *
43
+ * @example
44
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
45
+ */
46
+ constructor(openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, options: S3ObjectStoreOptions, telemetryOptions: TelemetryOptions);
47
+ /**
48
+ * Stores an object in the S3 bucket.
49
+ * @template T - The type of the object being stored.
50
+ * @param object - The object to store. Must include a `key` property.
51
+ *
52
+ * @example
53
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
54
+ */
55
+ putObject<T>(object: T & {
56
+ key: string;
57
+ }): Promise<void>;
58
+ /**
59
+ * Stores multiple objects in the S3 bucket.
60
+ * @template T - The type of the objects being stored.
61
+ * @param objects - The objects to store. Each must include a `key` property.
62
+ *
63
+ * @example
64
+ * await store.putBatchObjects([
65
+ * { key: 'user-1', name: 'Alice' },
66
+ * { key: 'user-2', name: 'Bob' }
67
+ * ]);
68
+ */
69
+ putBatchObjects<T>(objects: (T & {
70
+ key: string;
71
+ })[]): Promise<void>;
72
+ /**
73
+ * Streams an object upload to the S3 bucket.
74
+ * For compatibility; uses putObject internally.
75
+ * @template T - The type of the object being stored.
76
+ * @param object - The object to stream-upload. Must include a `key` property.
77
+ */
78
+ streamUploadObject<T>(object: T & {
79
+ key: string;
80
+ }): Promise<void>;
81
+ /**
82
+ * Streams multiple object uploads to the S3 bucket.
83
+ * For compatibility; uses putBatchObjects internally.
84
+ * @template T - The type of the objects being stored.
85
+ * @param objects - The objects to stream-upload. Each must include a `key` property.
86
+ */
87
+ streamUploadBatchObjects<T>(objects: (T & {
88
+ key: string;
89
+ })[]): Promise<void>;
90
+ /**
91
+ * Deletes an object from the S3 bucket.
92
+ * @param objectKey - The key of the object to delete.
93
+ *
94
+ * @example
95
+ * await store.deleteObject('user-1');
96
+ */
97
+ deleteObject(objectKey: string): Promise<void>;
98
+ /**
99
+ * Deletes multiple objects from the S3 bucket.
100
+ * @param objectKeys - The keys of the objects to delete.
101
+ *
102
+ * @example
103
+ * await store.deleteBatchObjects(['user-1', 'user-2']);
104
+ */
105
+ deleteBatchObjects(objectKeys: string[]): Promise<void>;
106
+ /**
107
+ * Reads an object from the S3 bucket.
108
+ * @template T - The expected type of the object.
109
+ * @param objectKey - The key of the object to read.
110
+ * @returns The parsed object.
111
+ *
112
+ * @example
113
+ * const user = await store.readObject<{ name: string }>('user-1');
114
+ */
115
+ readObject<T>(objectKey: string): Promise<T>;
116
+ /**
117
+ * Reads multiple objects from the S3 bucket.
118
+ * @template T - The expected type of the objects.
119
+ * @param objectKeys - The keys of the objects to read.
120
+ * @returns An array of parsed objects.
121
+ *
122
+ * @example
123
+ * const users = await store.readBatchObjects<{ name: string }>(['user-1', 'user-2']);
124
+ */
125
+ readBatchObjects<T>(objectKeys: string[]): Promise<T[]>;
126
+ /**
127
+ * Streams an object download from the S3 bucket.
128
+ * @param objectKey - The key of the object to download.
129
+ * @returns A readable stream of the object's contents.
130
+ * @throws If the S3 response does not include a readable stream.
131
+ *
132
+ * @example
133
+ * const stream = await store.streamDownloadObject('user-1');
134
+ * stream.pipe(fs.createWriteStream('user-1.json'));
135
+ */
136
+ streamDownloadObject(objectKey: string): Promise<Readable>;
137
+ /**
138
+ * Streams multiple object downloads from the S3 bucket.
139
+ * @param objectKeys - The keys of the objects to download.
140
+ * @returns An array of readable streams.
141
+ *
142
+ * @example
143
+ * const streams = await store.streamDownloadBatchObjects(['user-1', 'user-2']);
144
+ * streams[0].pipe(fs.createWriteStream('user-1.json'));
145
+ */
146
+ streamDownloadBatchObjects(objectKeys: string[]): Promise<Readable[]>;
147
+ /**
148
+ * Gets the underlying S3 client instance.
149
+ * @returns The S3Client instance used by this store.
150
+ *
151
+ * @example
152
+ * const s3Client = store.getClient();
153
+ */
154
+ getClient(): S3Client;
155
+ }
156
+
157
+ export { S3ObjectStore };
package/lib/index.js ADDED
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ S3ObjectStore: () => S3ObjectStore
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_client_s3 = require("@aws-sdk/client-s3");
27
+ var import_stream = require("stream");
28
+ var S3ObjectStore = class {
29
+ /**
30
+ * Creates a new S3ObjectStore instance.
31
+ * @param openTelemetryCollector - Collector for OpenTelemetry metrics.
32
+ * @param options - S3 configuration options.
33
+ * @param telemetryOptions - Telemetry configuration options.
34
+ *
35
+ * @example
36
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
37
+ */
38
+ constructor(openTelemetryCollector, options, telemetryOptions) {
39
+ this.openTelemetryCollector = openTelemetryCollector;
40
+ this.telemetryOptions = telemetryOptions;
41
+ this.s3 = options.client || new import_client_s3.S3Client(options.clientConfig || {});
42
+ this.bucket = options.bucket;
43
+ }
44
+ s3;
45
+ bucket;
46
+ /**
47
+ * Stores an object in the S3 bucket.
48
+ * @template T - The type of the object being stored.
49
+ * @param object - The object to store. Must include a `key` property.
50
+ *
51
+ * @example
52
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
53
+ */
54
+ async putObject(object) {
55
+ const { key, ...rest } = object;
56
+ const params = {
57
+ Bucket: this.bucket,
58
+ Key: key,
59
+ Body: JSON.stringify(rest),
60
+ ContentType: "application/json"
61
+ };
62
+ await this.s3.send(new import_client_s3.PutObjectCommand(params));
63
+ }
64
+ /**
65
+ * Stores multiple objects in the S3 bucket.
66
+ * @template T - The type of the objects being stored.
67
+ * @param objects - The objects to store. Each must include a `key` property.
68
+ *
69
+ * @example
70
+ * await store.putBatchObjects([
71
+ * { key: 'user-1', name: 'Alice' },
72
+ * { key: 'user-2', name: 'Bob' }
73
+ * ]);
74
+ */
75
+ async putBatchObjects(objects) {
76
+ await Promise.all(objects.map((obj) => this.putObject(obj)));
77
+ }
78
+ /**
79
+ * Streams an object upload to the S3 bucket.
80
+ * For compatibility; uses putObject internally.
81
+ * @template T - The type of the object being stored.
82
+ * @param object - The object to stream-upload. Must include a `key` property.
83
+ */
84
+ async streamUploadObject(object) {
85
+ await this.putObject(object);
86
+ }
87
+ /**
88
+ * Streams multiple object uploads to the S3 bucket.
89
+ * For compatibility; uses putBatchObjects internally.
90
+ * @template T - The type of the objects being stored.
91
+ * @param objects - The objects to stream-upload. Each must include a `key` property.
92
+ */
93
+ async streamUploadBatchObjects(objects) {
94
+ await this.putBatchObjects(objects);
95
+ }
96
+ /**
97
+ * Deletes an object from the S3 bucket.
98
+ * @param objectKey - The key of the object to delete.
99
+ *
100
+ * @example
101
+ * await store.deleteObject('user-1');
102
+ */
103
+ async deleteObject(objectKey) {
104
+ await this.s3.send(
105
+ new import_client_s3.DeleteObjectCommand({ Bucket: this.bucket, Key: objectKey })
106
+ );
107
+ }
108
+ /**
109
+ * Deletes multiple objects from the S3 bucket.
110
+ * @param objectKeys - The keys of the objects to delete.
111
+ *
112
+ * @example
113
+ * await store.deleteBatchObjects(['user-1', 'user-2']);
114
+ */
115
+ async deleteBatchObjects(objectKeys) {
116
+ const params = {
117
+ Bucket: this.bucket,
118
+ Delete: {
119
+ Objects: objectKeys.map((Key) => ({ Key }))
120
+ }
121
+ };
122
+ await this.s3.send(new import_client_s3.DeleteObjectsCommand(params));
123
+ }
124
+ /**
125
+ * Reads an object from the S3 bucket.
126
+ * @template T - The expected type of the object.
127
+ * @param objectKey - The key of the object to read.
128
+ * @returns The parsed object.
129
+ *
130
+ * @example
131
+ * const user = await store.readObject<{ name: string }>('user-1');
132
+ */
133
+ async readObject(objectKey) {
134
+ const resp = await this.s3.send(
135
+ new import_client_s3.GetObjectCommand({ Bucket: this.bucket, Key: objectKey })
136
+ );
137
+ const body = resp.Body;
138
+ const chunks = [];
139
+ for await (const chunk of body) {
140
+ chunks.push(chunk);
141
+ }
142
+ return JSON.parse(Buffer.concat(chunks).toString());
143
+ }
144
+ /**
145
+ * Reads multiple objects from the S3 bucket.
146
+ * @template T - The expected type of the objects.
147
+ * @param objectKeys - The keys of the objects to read.
148
+ * @returns An array of parsed objects.
149
+ *
150
+ * @example
151
+ * const users = await store.readBatchObjects<{ name: string }>(['user-1', 'user-2']);
152
+ */
153
+ async readBatchObjects(objectKeys) {
154
+ return Promise.all(objectKeys.map((key) => this.readObject(key)));
155
+ }
156
+ /**
157
+ * Streams an object download from the S3 bucket.
158
+ * @param objectKey - The key of the object to download.
159
+ * @returns A readable stream of the object's contents.
160
+ * @throws If the S3 response does not include a readable stream.
161
+ *
162
+ * @example
163
+ * const stream = await store.streamDownloadObject('user-1');
164
+ * stream.pipe(fs.createWriteStream('user-1.json'));
165
+ */
166
+ async streamDownloadObject(objectKey) {
167
+ const resp = await this.s3.send(
168
+ new import_client_s3.GetObjectCommand({ Bucket: this.bucket, Key: objectKey })
169
+ );
170
+ if (!resp.Body || !(resp.Body instanceof import_stream.Readable)) {
171
+ throw new Error("S3 did not return a stream");
172
+ }
173
+ return resp.Body;
174
+ }
175
+ /**
176
+ * Streams multiple object downloads from the S3 bucket.
177
+ * @param objectKeys - The keys of the objects to download.
178
+ * @returns An array of readable streams.
179
+ *
180
+ * @example
181
+ * const streams = await store.streamDownloadBatchObjects(['user-1', 'user-2']);
182
+ * streams[0].pipe(fs.createWriteStream('user-1.json'));
183
+ */
184
+ async streamDownloadBatchObjects(objectKeys) {
185
+ return Promise.all(objectKeys.map((key) => this.streamDownloadObject(key)));
186
+ }
187
+ /**
188
+ * Gets the underlying S3 client instance.
189
+ * @returns The S3Client instance used by this store.
190
+ *
191
+ * @example
192
+ * const s3Client = store.getClient();
193
+ */
194
+ getClient() {
195
+ return this.s3;
196
+ }
197
+ };
198
+ // Annotate the CommonJS export names for ESM import in node:
199
+ 0 && (module.exports = {
200
+ S3ObjectStore
201
+ });
202
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n DeleteObjectCommand,\n DeleteObjectsCommand,\n DeleteObjectsCommandInput,\n GetObjectCommand,\n PutObjectCommand,\n PutObjectCommandInput,\n S3Client\n} from '@aws-sdk/client-s3';\nimport {\n MetricsDefinition,\n OpenTelemetryCollector,\n TelemetryOptions\n} from '@forklaunch/core/http';\nimport { ObjectStore } from '@forklaunch/core/objectstore';\nimport { Readable } from 'stream';\n\n/**\n * Options for configuring the S3ObjectStore.\n *\n * @example\n * const options: S3ObjectStoreOptions = {\n * bucket: 'my-bucket',\n * clientConfig: { region: 'us-west-2' }\n * };\n */\ninterface S3ObjectStoreOptions {\n /** The S3 bucket name. */\n bucket: string;\n /** Optional existing S3 client instance. */\n client?: S3Client;\n /** Optional configuration for creating a new S3 client. */\n clientConfig?: ConstructorParameters<typeof S3Client>[0];\n}\n\n/**\n * S3-backed implementation of the ObjectStore interface.\n * Provides methods for storing, retrieving, streaming, and deleting objects in S3.\n *\n * @example\n * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);\n * await store.putObject({ key: 'user-1', name: 'Alice' });\n * const user = await store.readObject<{ name: string }>('user-1');\n */\nexport class S3ObjectStore implements ObjectStore<S3Client> {\n private s3: S3Client;\n private bucket: string;\n\n /**\n * Creates a new S3ObjectStore instance.\n * @param openTelemetryCollector - Collector for OpenTelemetry metrics.\n * @param options - S3 configuration options.\n * @param telemetryOptions - Telemetry configuration options.\n *\n * @example\n * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);\n */\n constructor(\n private openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,\n options: S3ObjectStoreOptions,\n private telemetryOptions: TelemetryOptions\n ) {\n this.s3 = options.client || new S3Client(options.clientConfig || {});\n this.bucket = options.bucket;\n }\n\n /**\n * Stores an object in the S3 bucket.\n * @template T - The type of the object being stored.\n * @param object - The object to store. Must include a `key` property.\n *\n * @example\n * await store.putObject({ key: 'user-1', name: 'Alice' });\n */\n async putObject<T>(object: T & { key: string }): Promise<void> {\n const { key, ...rest } = object;\n const params: PutObjectCommandInput = {\n Bucket: this.bucket,\n Key: key,\n Body: JSON.stringify(rest),\n ContentType: 'application/json'\n };\n await this.s3.send(new PutObjectCommand(params));\n }\n\n /**\n * Stores multiple objects in the S3 bucket.\n * @template T - The type of the objects being stored.\n * @param objects - The objects to store. Each must include a `key` property.\n *\n * @example\n * await store.putBatchObjects([\n * { key: 'user-1', name: 'Alice' },\n * { key: 'user-2', name: 'Bob' }\n * ]);\n */\n async putBatchObjects<T>(objects: (T & { key: string })[]): Promise<void> {\n await Promise.all(objects.map((obj) => this.putObject(obj)));\n }\n\n /**\n * Streams an object upload to the S3 bucket.\n * For compatibility; uses putObject internally.\n * @template T - The type of the object being stored.\n * @param object - The object to stream-upload. Must include a `key` property.\n */\n async streamUploadObject<T>(object: T & { key: string }): Promise<void> {\n await this.putObject(object);\n }\n\n /**\n * Streams multiple object uploads to the S3 bucket.\n * For compatibility; uses putBatchObjects internally.\n * @template T - The type of the objects being stored.\n * @param objects - The objects to stream-upload. Each must include a `key` property.\n */\n async streamUploadBatchObjects<T>(\n objects: (T & { key: string })[]\n ): Promise<void> {\n await this.putBatchObjects(objects);\n }\n\n /**\n * Deletes an object from the S3 bucket.\n * @param objectKey - The key of the object to delete.\n *\n * @example\n * await store.deleteObject('user-1');\n */\n async deleteObject(objectKey: string): Promise<void> {\n await this.s3.send(\n new DeleteObjectCommand({ Bucket: this.bucket, Key: objectKey })\n );\n }\n\n /**\n * Deletes multiple objects from the S3 bucket.\n * @param objectKeys - The keys of the objects to delete.\n *\n * @example\n * await store.deleteBatchObjects(['user-1', 'user-2']);\n */\n async deleteBatchObjects(objectKeys: string[]): Promise<void> {\n const params: DeleteObjectsCommandInput = {\n Bucket: this.bucket,\n Delete: {\n Objects: objectKeys.map((Key) => ({ Key }))\n }\n };\n await this.s3.send(new DeleteObjectsCommand(params));\n }\n\n /**\n * Reads an object from the S3 bucket.\n * @template T - The expected type of the object.\n * @param objectKey - The key of the object to read.\n * @returns The parsed object.\n *\n * @example\n * const user = await store.readObject<{ name: string }>('user-1');\n */\n async readObject<T>(objectKey: string): Promise<T> {\n const resp = await this.s3.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })\n );\n const body = resp.Body as Readable;\n const chunks: Buffer[] = [];\n for await (const chunk of body) {\n chunks.push(chunk);\n }\n return JSON.parse(Buffer.concat(chunks).toString()) as T;\n }\n\n /**\n * Reads multiple objects from the S3 bucket.\n * @template T - The expected type of the objects.\n * @param objectKeys - The keys of the objects to read.\n * @returns An array of parsed objects.\n *\n * @example\n * const users = await store.readBatchObjects<{ name: string }>(['user-1', 'user-2']);\n */\n async readBatchObjects<T>(objectKeys: string[]): Promise<T[]> {\n return Promise.all(objectKeys.map((key) => this.readObject<T>(key)));\n }\n\n /**\n * Streams an object download from the S3 bucket.\n * @param objectKey - The key of the object to download.\n * @returns A readable stream of the object's contents.\n * @throws If the S3 response does not include a readable stream.\n *\n * @example\n * const stream = await store.streamDownloadObject('user-1');\n * stream.pipe(fs.createWriteStream('user-1.json'));\n */\n async streamDownloadObject(objectKey: string): Promise<Readable> {\n const resp = await this.s3.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })\n );\n if (!resp.Body || !(resp.Body instanceof Readable)) {\n throw new Error('S3 did not return a stream');\n }\n return resp.Body;\n }\n\n /**\n * Streams multiple object downloads from the S3 bucket.\n * @param objectKeys - The keys of the objects to download.\n * @returns An array of readable streams.\n *\n * @example\n * const streams = await store.streamDownloadBatchObjects(['user-1', 'user-2']);\n * streams[0].pipe(fs.createWriteStream('user-1.json'));\n */\n async streamDownloadBatchObjects(objectKeys: string[]): Promise<Readable[]> {\n return Promise.all(objectKeys.map((key) => this.streamDownloadObject(key)));\n }\n\n /**\n * Gets the underlying S3 client instance.\n * @returns The S3Client instance used by this store.\n *\n * @example\n * const s3Client = store.getClient();\n */\n getClient(): S3Client {\n return this.s3;\n }\n}\n"],"mappings":"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","names":[]}
package/lib/index.mjs ADDED
@@ -0,0 +1,183 @@
1
+ // index.ts
2
+ import {
3
+ DeleteObjectCommand,
4
+ DeleteObjectsCommand,
5
+ GetObjectCommand,
6
+ PutObjectCommand,
7
+ S3Client
8
+ } from "@aws-sdk/client-s3";
9
+ import { Readable } from "stream";
10
+ var S3ObjectStore = class {
11
+ /**
12
+ * Creates a new S3ObjectStore instance.
13
+ * @param openTelemetryCollector - Collector for OpenTelemetry metrics.
14
+ * @param options - S3 configuration options.
15
+ * @param telemetryOptions - Telemetry configuration options.
16
+ *
17
+ * @example
18
+ * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);
19
+ */
20
+ constructor(openTelemetryCollector, options, telemetryOptions) {
21
+ this.openTelemetryCollector = openTelemetryCollector;
22
+ this.telemetryOptions = telemetryOptions;
23
+ this.s3 = options.client || new S3Client(options.clientConfig || {});
24
+ this.bucket = options.bucket;
25
+ }
26
+ s3;
27
+ bucket;
28
+ /**
29
+ * Stores an object in the S3 bucket.
30
+ * @template T - The type of the object being stored.
31
+ * @param object - The object to store. Must include a `key` property.
32
+ *
33
+ * @example
34
+ * await store.putObject({ key: 'user-1', name: 'Alice' });
35
+ */
36
+ async putObject(object) {
37
+ const { key, ...rest } = object;
38
+ const params = {
39
+ Bucket: this.bucket,
40
+ Key: key,
41
+ Body: JSON.stringify(rest),
42
+ ContentType: "application/json"
43
+ };
44
+ await this.s3.send(new PutObjectCommand(params));
45
+ }
46
+ /**
47
+ * Stores multiple objects in the S3 bucket.
48
+ * @template T - The type of the objects being stored.
49
+ * @param objects - The objects to store. Each must include a `key` property.
50
+ *
51
+ * @example
52
+ * await store.putBatchObjects([
53
+ * { key: 'user-1', name: 'Alice' },
54
+ * { key: 'user-2', name: 'Bob' }
55
+ * ]);
56
+ */
57
+ async putBatchObjects(objects) {
58
+ await Promise.all(objects.map((obj) => this.putObject(obj)));
59
+ }
60
+ /**
61
+ * Streams an object upload to the S3 bucket.
62
+ * For compatibility; uses putObject internally.
63
+ * @template T - The type of the object being stored.
64
+ * @param object - The object to stream-upload. Must include a `key` property.
65
+ */
66
+ async streamUploadObject(object) {
67
+ await this.putObject(object);
68
+ }
69
+ /**
70
+ * Streams multiple object uploads to the S3 bucket.
71
+ * For compatibility; uses putBatchObjects internally.
72
+ * @template T - The type of the objects being stored.
73
+ * @param objects - The objects to stream-upload. Each must include a `key` property.
74
+ */
75
+ async streamUploadBatchObjects(objects) {
76
+ await this.putBatchObjects(objects);
77
+ }
78
+ /**
79
+ * Deletes an object from the S3 bucket.
80
+ * @param objectKey - The key of the object to delete.
81
+ *
82
+ * @example
83
+ * await store.deleteObject('user-1');
84
+ */
85
+ async deleteObject(objectKey) {
86
+ await this.s3.send(
87
+ new DeleteObjectCommand({ Bucket: this.bucket, Key: objectKey })
88
+ );
89
+ }
90
+ /**
91
+ * Deletes multiple objects from the S3 bucket.
92
+ * @param objectKeys - The keys of the objects to delete.
93
+ *
94
+ * @example
95
+ * await store.deleteBatchObjects(['user-1', 'user-2']);
96
+ */
97
+ async deleteBatchObjects(objectKeys) {
98
+ const params = {
99
+ Bucket: this.bucket,
100
+ Delete: {
101
+ Objects: objectKeys.map((Key) => ({ Key }))
102
+ }
103
+ };
104
+ await this.s3.send(new DeleteObjectsCommand(params));
105
+ }
106
+ /**
107
+ * Reads an object from the S3 bucket.
108
+ * @template T - The expected type of the object.
109
+ * @param objectKey - The key of the object to read.
110
+ * @returns The parsed object.
111
+ *
112
+ * @example
113
+ * const user = await store.readObject<{ name: string }>('user-1');
114
+ */
115
+ async readObject(objectKey) {
116
+ const resp = await this.s3.send(
117
+ new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })
118
+ );
119
+ const body = resp.Body;
120
+ const chunks = [];
121
+ for await (const chunk of body) {
122
+ chunks.push(chunk);
123
+ }
124
+ return JSON.parse(Buffer.concat(chunks).toString());
125
+ }
126
+ /**
127
+ * Reads multiple objects from the S3 bucket.
128
+ * @template T - The expected type of the objects.
129
+ * @param objectKeys - The keys of the objects to read.
130
+ * @returns An array of parsed objects.
131
+ *
132
+ * @example
133
+ * const users = await store.readBatchObjects<{ name: string }>(['user-1', 'user-2']);
134
+ */
135
+ async readBatchObjects(objectKeys) {
136
+ return Promise.all(objectKeys.map((key) => this.readObject(key)));
137
+ }
138
+ /**
139
+ * Streams an object download from the S3 bucket.
140
+ * @param objectKey - The key of the object to download.
141
+ * @returns A readable stream of the object's contents.
142
+ * @throws If the S3 response does not include a readable stream.
143
+ *
144
+ * @example
145
+ * const stream = await store.streamDownloadObject('user-1');
146
+ * stream.pipe(fs.createWriteStream('user-1.json'));
147
+ */
148
+ async streamDownloadObject(objectKey) {
149
+ const resp = await this.s3.send(
150
+ new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })
151
+ );
152
+ if (!resp.Body || !(resp.Body instanceof Readable)) {
153
+ throw new Error("S3 did not return a stream");
154
+ }
155
+ return resp.Body;
156
+ }
157
+ /**
158
+ * Streams multiple object downloads from the S3 bucket.
159
+ * @param objectKeys - The keys of the objects to download.
160
+ * @returns An array of readable streams.
161
+ *
162
+ * @example
163
+ * const streams = await store.streamDownloadBatchObjects(['user-1', 'user-2']);
164
+ * streams[0].pipe(fs.createWriteStream('user-1.json'));
165
+ */
166
+ async streamDownloadBatchObjects(objectKeys) {
167
+ return Promise.all(objectKeys.map((key) => this.streamDownloadObject(key)));
168
+ }
169
+ /**
170
+ * Gets the underlying S3 client instance.
171
+ * @returns The S3Client instance used by this store.
172
+ *
173
+ * @example
174
+ * const s3Client = store.getClient();
175
+ */
176
+ getClient() {
177
+ return this.s3;
178
+ }
179
+ };
180
+ export {
181
+ S3ObjectStore
182
+ };
183
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../index.ts"],"sourcesContent":["import {\n DeleteObjectCommand,\n DeleteObjectsCommand,\n DeleteObjectsCommandInput,\n GetObjectCommand,\n PutObjectCommand,\n PutObjectCommandInput,\n S3Client\n} from '@aws-sdk/client-s3';\nimport {\n MetricsDefinition,\n OpenTelemetryCollector,\n TelemetryOptions\n} from '@forklaunch/core/http';\nimport { ObjectStore } from '@forklaunch/core/objectstore';\nimport { Readable } from 'stream';\n\n/**\n * Options for configuring the S3ObjectStore.\n *\n * @example\n * const options: S3ObjectStoreOptions = {\n * bucket: 'my-bucket',\n * clientConfig: { region: 'us-west-2' }\n * };\n */\ninterface S3ObjectStoreOptions {\n /** The S3 bucket name. */\n bucket: string;\n /** Optional existing S3 client instance. */\n client?: S3Client;\n /** Optional configuration for creating a new S3 client. */\n clientConfig?: ConstructorParameters<typeof S3Client>[0];\n}\n\n/**\n * S3-backed implementation of the ObjectStore interface.\n * Provides methods for storing, retrieving, streaming, and deleting objects in S3.\n *\n * @example\n * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);\n * await store.putObject({ key: 'user-1', name: 'Alice' });\n * const user = await store.readObject<{ name: string }>('user-1');\n */\nexport class S3ObjectStore implements ObjectStore<S3Client> {\n private s3: S3Client;\n private bucket: string;\n\n /**\n * Creates a new S3ObjectStore instance.\n * @param openTelemetryCollector - Collector for OpenTelemetry metrics.\n * @param options - S3 configuration options.\n * @param telemetryOptions - Telemetry configuration options.\n *\n * @example\n * const store = new S3ObjectStore(otelCollector, { bucket: 'my-bucket' }, telemetryOptions);\n */\n constructor(\n private openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,\n options: S3ObjectStoreOptions,\n private telemetryOptions: TelemetryOptions\n ) {\n this.s3 = options.client || new S3Client(options.clientConfig || {});\n this.bucket = options.bucket;\n }\n\n /**\n * Stores an object in the S3 bucket.\n * @template T - The type of the object being stored.\n * @param object - The object to store. Must include a `key` property.\n *\n * @example\n * await store.putObject({ key: 'user-1', name: 'Alice' });\n */\n async putObject<T>(object: T & { key: string }): Promise<void> {\n const { key, ...rest } = object;\n const params: PutObjectCommandInput = {\n Bucket: this.bucket,\n Key: key,\n Body: JSON.stringify(rest),\n ContentType: 'application/json'\n };\n await this.s3.send(new PutObjectCommand(params));\n }\n\n /**\n * Stores multiple objects in the S3 bucket.\n * @template T - The type of the objects being stored.\n * @param objects - The objects to store. Each must include a `key` property.\n *\n * @example\n * await store.putBatchObjects([\n * { key: 'user-1', name: 'Alice' },\n * { key: 'user-2', name: 'Bob' }\n * ]);\n */\n async putBatchObjects<T>(objects: (T & { key: string })[]): Promise<void> {\n await Promise.all(objects.map((obj) => this.putObject(obj)));\n }\n\n /**\n * Streams an object upload to the S3 bucket.\n * For compatibility; uses putObject internally.\n * @template T - The type of the object being stored.\n * @param object - The object to stream-upload. Must include a `key` property.\n */\n async streamUploadObject<T>(object: T & { key: string }): Promise<void> {\n await this.putObject(object);\n }\n\n /**\n * Streams multiple object uploads to the S3 bucket.\n * For compatibility; uses putBatchObjects internally.\n * @template T - The type of the objects being stored.\n * @param objects - The objects to stream-upload. Each must include a `key` property.\n */\n async streamUploadBatchObjects<T>(\n objects: (T & { key: string })[]\n ): Promise<void> {\n await this.putBatchObjects(objects);\n }\n\n /**\n * Deletes an object from the S3 bucket.\n * @param objectKey - The key of the object to delete.\n *\n * @example\n * await store.deleteObject('user-1');\n */\n async deleteObject(objectKey: string): Promise<void> {\n await this.s3.send(\n new DeleteObjectCommand({ Bucket: this.bucket, Key: objectKey })\n );\n }\n\n /**\n * Deletes multiple objects from the S3 bucket.\n * @param objectKeys - The keys of the objects to delete.\n *\n * @example\n * await store.deleteBatchObjects(['user-1', 'user-2']);\n */\n async deleteBatchObjects(objectKeys: string[]): Promise<void> {\n const params: DeleteObjectsCommandInput = {\n Bucket: this.bucket,\n Delete: {\n Objects: objectKeys.map((Key) => ({ Key }))\n }\n };\n await this.s3.send(new DeleteObjectsCommand(params));\n }\n\n /**\n * Reads an object from the S3 bucket.\n * @template T - The expected type of the object.\n * @param objectKey - The key of the object to read.\n * @returns The parsed object.\n *\n * @example\n * const user = await store.readObject<{ name: string }>('user-1');\n */\n async readObject<T>(objectKey: string): Promise<T> {\n const resp = await this.s3.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })\n );\n const body = resp.Body as Readable;\n const chunks: Buffer[] = [];\n for await (const chunk of body) {\n chunks.push(chunk);\n }\n return JSON.parse(Buffer.concat(chunks).toString()) as T;\n }\n\n /**\n * Reads multiple objects from the S3 bucket.\n * @template T - The expected type of the objects.\n * @param objectKeys - The keys of the objects to read.\n * @returns An array of parsed objects.\n *\n * @example\n * const users = await store.readBatchObjects<{ name: string }>(['user-1', 'user-2']);\n */\n async readBatchObjects<T>(objectKeys: string[]): Promise<T[]> {\n return Promise.all(objectKeys.map((key) => this.readObject<T>(key)));\n }\n\n /**\n * Streams an object download from the S3 bucket.\n * @param objectKey - The key of the object to download.\n * @returns A readable stream of the object's contents.\n * @throws If the S3 response does not include a readable stream.\n *\n * @example\n * const stream = await store.streamDownloadObject('user-1');\n * stream.pipe(fs.createWriteStream('user-1.json'));\n */\n async streamDownloadObject(objectKey: string): Promise<Readable> {\n const resp = await this.s3.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: objectKey })\n );\n if (!resp.Body || !(resp.Body instanceof Readable)) {\n throw new Error('S3 did not return a stream');\n }\n return resp.Body;\n }\n\n /**\n * Streams multiple object downloads from the S3 bucket.\n * @param objectKeys - The keys of the objects to download.\n * @returns An array of readable streams.\n *\n * @example\n * const streams = await store.streamDownloadBatchObjects(['user-1', 'user-2']);\n * streams[0].pipe(fs.createWriteStream('user-1.json'));\n */\n async streamDownloadBatchObjects(objectKeys: string[]): Promise<Readable[]> {\n return Promise.all(objectKeys.map((key) => this.streamDownloadObject(key)));\n }\n\n /**\n * Gets the underlying S3 client instance.\n * @returns The S3Client instance used by this store.\n *\n * @example\n * const s3Client = store.getClient();\n */\n getClient(): S3Client {\n return this.s3;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@forklaunch/infrastructure-s3",
3
+ "version": "0.0.1",
4
+ "description": "S3 infrastructure for ForkLaunch components.",
5
+ "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/forklaunch/forklaunch-js/issues"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/forklaunch/forklaunch-js.git"
12
+ },
13
+ "license": "MIT",
14
+ "author": "Rohin Bhargava",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./lib/src/index.d.ts",
18
+ "import": "./lib/src/index.mjs",
19
+ "require": "./lib/src/index.js",
20
+ "default": "./lib/src/index.js"
21
+ }
22
+ },
23
+ "types": "lib/index.d.ts",
24
+ "directories": {
25
+ "test": "__test__"
26
+ },
27
+ "files": [
28
+ "lib/**"
29
+ ],
30
+ "dependencies": {
31
+ "@aws-sdk/client-s3": "^3.821.0",
32
+ "@forklaunch/core": "0.8.3",
33
+ "@forklaunch/common": "0.3.2"
34
+ },
35
+ "devDependencies": {
36
+ "@eslint/js": "^9.27.0",
37
+ "@types/jest": "^29.5.14",
38
+ "globals": "^16.2.0",
39
+ "jest": "^29.7.0",
40
+ "prettier": "^3.5.3",
41
+ "ts-jest": "^29.3.4",
42
+ "ts-node": "^10.9.2",
43
+ "tsup": "^8.5.0",
44
+ "typedoc": "^0.28.5",
45
+ "typescript": "^5.8.3",
46
+ "typescript-eslint": "^8.33.0"
47
+ },
48
+ "scripts": {
49
+ "build": "tsc --noEmit && tsup ./index.ts --format cjs,esm --no-splitting --dts --tsconfig tsconfig.json --out-dir lib --clean --sourcemap && if [ -f eject-infrastructure-package.bash ]; then pnpm package:eject; fi",
50
+ "clean": "rm -rf lib pnpm.lock.yaml node_modules",
51
+ "docs": "typedoc --out docs *",
52
+ "format": "prettier --ignore-path=.prettierignore --config .prettierrc '**/*.{ts,tsx,json}' --write",
53
+ "lint": "eslint . -c eslint.config.mjs",
54
+ "lint:fix": "eslint . -c eslint.config.mjs --fix",
55
+ "package:eject": "./eject-infrastructure-package.bash s3",
56
+ "publish:package": "./publish-package.bash",
57
+ "test": "vitest --passWithNoTests"
58
+ }
59
+ }