@travetto/model-s3 4.1.4 → 5.0.0-rc.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 +1 -1
- package/package.json +6 -6
- package/src/service.ts +30 -26
package/LICENSE
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-s3",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-rc.0",
|
|
4
4
|
"description": "S3 backing for the travetto model module.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"s3",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"directory": "module/model-s3"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@aws-sdk/client-s3": "^3.
|
|
29
|
-
"@aws-sdk/credential-provider-ini": "^3.
|
|
30
|
-
"@travetto/config": "^
|
|
31
|
-
"@travetto/model": "^
|
|
28
|
+
"@aws-sdk/client-s3": "^3.609.0",
|
|
29
|
+
"@aws-sdk/credential-provider-ini": "^3.609.0",
|
|
30
|
+
"@travetto/config": "^5.0.0-rc.0",
|
|
31
|
+
"@travetto/model": "^5.0.0-rc.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@travetto/command": "^
|
|
34
|
+
"@travetto/command": "^5.0.0-rc.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependenciesMeta": {
|
|
37
37
|
"@travetto/command": {
|
package/src/service.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
+
import { buffer as toBuffer } from 'node:stream/consumers';
|
|
2
3
|
import { Agent } from 'node:https';
|
|
3
4
|
|
|
4
5
|
import { S3, CompletedPart, type CreateMultipartUploadRequest } from '@aws-sdk/client-s3';
|
|
@@ -7,10 +8,11 @@ import { NodeHttpHandler } from '@smithy/node-http-handler';
|
|
|
7
8
|
|
|
8
9
|
import {
|
|
9
10
|
ModelCrudSupport, ModelStreamSupport, ModelStorageSupport, StreamMeta,
|
|
10
|
-
ModelType, ModelRegistry, ExistsError, NotFoundError, OptionalId,
|
|
11
|
+
ModelType, ModelRegistry, ExistsError, NotFoundError, OptionalId,
|
|
12
|
+
StreamRange
|
|
11
13
|
} from '@travetto/model';
|
|
12
14
|
import { Injectable } from '@travetto/di';
|
|
13
|
-
import {
|
|
15
|
+
import { Class, AppError } from '@travetto/base';
|
|
14
16
|
|
|
15
17
|
import { ModelCrudUtil } from '@travetto/model/src/internal/service/crud';
|
|
16
18
|
import { ModelExpirySupport } from '@travetto/model/src/service/expiry';
|
|
@@ -18,6 +20,7 @@ import { ModelExpiryUtil } from '@travetto/model/src/internal/service/expiry';
|
|
|
18
20
|
import { ModelStorageUtil } from '@travetto/model/src/internal/service/storage';
|
|
19
21
|
|
|
20
22
|
import { S3ModelConfig } from './config';
|
|
23
|
+
import { enforceRange } from '@travetto/model/src/internal/service/stream';
|
|
21
24
|
|
|
22
25
|
function isMetadataBearer(o: unknown): o is MetadataBearer {
|
|
23
26
|
return !!o && typeof o === 'object' && '$metadata' in o;
|
|
@@ -201,7 +204,7 @@ export class S3ModelService implements ModelCrudSupport, ModelStreamSupport, Mod
|
|
|
201
204
|
const result = await this.client.getObject(this.#q(cls, id));
|
|
202
205
|
if (result.Body) {
|
|
203
206
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
204
|
-
const body = (await
|
|
207
|
+
const body = (await toBuffer(result.Body as Readable)).toString('utf8');
|
|
205
208
|
const output = await ModelCrudUtil.load(cls, body);
|
|
206
209
|
if (output) {
|
|
207
210
|
const { expiresAt } = ModelRegistry.get(cls);
|
|
@@ -300,7 +303,7 @@ export class S3ModelService implements ModelCrudSupport, ModelStreamSupport, Mod
|
|
|
300
303
|
if (meta.size < this.config.chunkSize) { // If smaller than chunk size
|
|
301
304
|
// Upload to s3
|
|
302
305
|
await this.client.putObject(this.#q(STREAM_SPACE, location, {
|
|
303
|
-
Body: await
|
|
306
|
+
Body: await toBuffer(input),
|
|
304
307
|
ContentLength: meta.size,
|
|
305
308
|
...this.#getMetaBase(meta),
|
|
306
309
|
}));
|
|
@@ -309,34 +312,35 @@ export class S3ModelService implements ModelCrudSupport, ModelStreamSupport, Mod
|
|
|
309
312
|
}
|
|
310
313
|
}
|
|
311
314
|
|
|
312
|
-
async
|
|
315
|
+
async #getObject(location: string, range: Required<StreamRange>): Promise<Readable> {
|
|
313
316
|
// Read from s3
|
|
314
|
-
const res = await this.client.getObject(this.#q(STREAM_SPACE, location
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
) {
|
|
319
|
-
|
|
317
|
+
const res = await this.client.getObject(this.#q(STREAM_SPACE, location, range ? {
|
|
318
|
+
Range: `bytes=${range.start}-${range.end}`
|
|
319
|
+
} : {}));
|
|
320
|
+
|
|
321
|
+
if (!res.Body) {
|
|
322
|
+
throw new AppError('Unable to read type: undefined');
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (typeof res.Body === 'string') { // string
|
|
326
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
327
|
+
return Readable.from(res.Body, { encoding: (res.Body as string).endsWith('=') ? 'base64' : 'utf8' });
|
|
328
|
+
} else if (res.Body instanceof Buffer) { // Buffer
|
|
329
|
+
return Readable.from(res.Body);
|
|
330
|
+
} else if ('pipe' in res.Body) { // Stream
|
|
331
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
332
|
+
return res.Body as Readable;
|
|
320
333
|
}
|
|
321
334
|
throw new AppError(`Unable to read type: ${typeof res.Body}`);
|
|
322
335
|
}
|
|
323
336
|
|
|
324
|
-
async
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
// Read from s3
|
|
330
|
-
const res = await this.client.getObject(this.#q(STREAM_SPACE, location, {
|
|
331
|
-
Range: `bytes=${start}-${end}`
|
|
332
|
-
}));
|
|
333
|
-
if (res.Body instanceof Buffer || // Buffer
|
|
334
|
-
typeof res.Body === 'string' || // string
|
|
335
|
-
res.Body && ('pipe' in res.Body) // Stream
|
|
336
|
-
) {
|
|
337
|
-
return { stream: await StreamUtil.toStream(res.Body), range: [start, end] };
|
|
337
|
+
async getStream(location: string, range?: StreamRange): Promise<Readable> {
|
|
338
|
+
if (range) {
|
|
339
|
+
const meta = await this.describeStream(location);
|
|
340
|
+
range = enforceRange(range, meta.size);
|
|
338
341
|
}
|
|
339
|
-
|
|
342
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
343
|
+
return this.#getObject(location, range as Required<StreamRange>);
|
|
340
344
|
}
|
|
341
345
|
|
|
342
346
|
async headStream(location: string): Promise<{ Metadata?: Partial<StreamMeta>, ContentLength?: number }> {
|