@lucaapp/service-utils 4.13.1 → 4.14.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/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/s3/index.d.ts +1 -0
- package/dist/lib/s3/index.js +17 -0
- package/dist/lib/s3/s3.d.ts +37 -0
- package/dist/lib/s3/s3.js +80 -0
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -34,4 +34,5 @@ __exportStar(require("./lib/phone"), exports);
|
|
|
34
34
|
__exportStar(require("./lib/http"), exports);
|
|
35
35
|
__exportStar(require("./lib/jobs"), exports);
|
|
36
36
|
__exportStar(require("./lib/validation"), exports);
|
|
37
|
+
__exportStar(require("./lib/s3"), exports);
|
|
37
38
|
__exportStar(require("./types"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './s3';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./s3"), exports);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { S3 } from '@aws-sdk/client-s3';
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for S3 client
|
|
5
|
+
*/
|
|
6
|
+
export interface S3Config {
|
|
7
|
+
accessKey?: string;
|
|
8
|
+
secretKey?: string;
|
|
9
|
+
endpoint: string;
|
|
10
|
+
region: string;
|
|
11
|
+
bucket: string;
|
|
12
|
+
protocol: 'https' | 'http';
|
|
13
|
+
forcePathStyle?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Options for downloading files from S3
|
|
17
|
+
*/
|
|
18
|
+
export interface DownloadOptions {
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
retries?: number;
|
|
21
|
+
retryDelay?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create an S3 client from configuration
|
|
25
|
+
*/
|
|
26
|
+
export declare const createS3Client: (config: S3Config) => S3 | null;
|
|
27
|
+
/**
|
|
28
|
+
* Download a file from S3 as a stream with timeout and retry support
|
|
29
|
+
*
|
|
30
|
+
* @param s3Client - Configured S3 client instance
|
|
31
|
+
* @param bucket - S3 bucket name
|
|
32
|
+
* @param key - S3 object key
|
|
33
|
+
* @param options - Download options (timeout, retries, retryDelay)
|
|
34
|
+
* @returns Readable stream of the file content
|
|
35
|
+
* @throws Error if S3 client is not configured or download fails after all retries
|
|
36
|
+
*/
|
|
37
|
+
export declare const downloadFileAsStream: (s3Client: S3 | null, bucket: string, key: string, options?: DownloadOptions) => Promise<Readable>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.downloadFileAsStream = exports.createS3Client = void 0;
|
|
4
|
+
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
5
|
+
/**
|
|
6
|
+
* Create an S3 client from configuration
|
|
7
|
+
*/
|
|
8
|
+
const createS3Client = (config) => {
|
|
9
|
+
if (!config.accessKey || !config.secretKey) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const clientConfig = {
|
|
13
|
+
credentials: {
|
|
14
|
+
accessKeyId: config.accessKey,
|
|
15
|
+
secretAccessKey: config.secretKey,
|
|
16
|
+
},
|
|
17
|
+
endpoint: `${config.protocol}://${config.endpoint}`,
|
|
18
|
+
region: config.region,
|
|
19
|
+
maxAttempts: 3,
|
|
20
|
+
};
|
|
21
|
+
// Add forcePathStyle if specified (needed for S3-compatible services like MinIO)
|
|
22
|
+
if (config.forcePathStyle !== undefined) {
|
|
23
|
+
clientConfig.forcePathStyle = config.forcePathStyle;
|
|
24
|
+
}
|
|
25
|
+
return new client_s3_1.S3(clientConfig);
|
|
26
|
+
};
|
|
27
|
+
exports.createS3Client = createS3Client;
|
|
28
|
+
/**
|
|
29
|
+
* Download a file from S3 as a stream with timeout and retry support
|
|
30
|
+
*
|
|
31
|
+
* @param s3Client - Configured S3 client instance
|
|
32
|
+
* @param bucket - S3 bucket name
|
|
33
|
+
* @param key - S3 object key
|
|
34
|
+
* @param options - Download options (timeout, retries, retryDelay)
|
|
35
|
+
* @returns Readable stream of the file content
|
|
36
|
+
* @throws Error if S3 client is not configured or download fails after all retries
|
|
37
|
+
*/
|
|
38
|
+
const downloadFileAsStream = async (s3Client, bucket, key, options = {}) => {
|
|
39
|
+
const { timeoutMs = 10000, retries = 0, retryDelay = 1000 } = options;
|
|
40
|
+
if (!s3Client) {
|
|
41
|
+
throw new Error('S3 client is not configured. Either set S3_REGION and S3_ENDPOINT (or use defaults) OR set USE_FILE_BASED_STORAGE=true');
|
|
42
|
+
}
|
|
43
|
+
const downloadParameters = {
|
|
44
|
+
Bucket: bucket,
|
|
45
|
+
Key: key,
|
|
46
|
+
};
|
|
47
|
+
const command = new client_s3_1.GetObjectCommand(downloadParameters);
|
|
48
|
+
let lastError;
|
|
49
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
50
|
+
const abortController = new AbortController();
|
|
51
|
+
let timeoutId;
|
|
52
|
+
const timeoutPromise = new Promise((_resolve, reject) => {
|
|
53
|
+
timeoutId = setTimeout(() => {
|
|
54
|
+
abortController.abort();
|
|
55
|
+
reject(new Error(`S3 download timeout after ${timeoutMs}ms for key: ${key}`));
|
|
56
|
+
}, timeoutMs);
|
|
57
|
+
});
|
|
58
|
+
try {
|
|
59
|
+
const s3Object = await Promise.race([
|
|
60
|
+
s3Client.send(command, { abortSignal: abortController.signal }),
|
|
61
|
+
timeoutPromise,
|
|
62
|
+
]);
|
|
63
|
+
if (timeoutId)
|
|
64
|
+
clearTimeout(timeoutId);
|
|
65
|
+
return s3Object.Body;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (timeoutId)
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
lastError = error;
|
|
71
|
+
if (attempt < retries) {
|
|
72
|
+
await new Promise(resolve => {
|
|
73
|
+
setTimeout(resolve, retryDelay);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw lastError;
|
|
79
|
+
};
|
|
80
|
+
exports.downloadFileAsStream = downloadFileAsStream;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lucaapp/service-utils",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.14.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@apidevtools/swagger-parser": "10.0.3",
|
|
22
22
|
"@asteasolutions/zod-to-openapi": "6.1.0",
|
|
23
|
+
"@aws-sdk/client-s3": "^3.937.0",
|
|
24
|
+
"@aws-sdk/lib-storage": "^3.937.0",
|
|
23
25
|
"@hapi/boom": "^10.0.1",
|
|
24
26
|
"@sentry/node": "^9.10.1",
|
|
25
27
|
"@tsconfig/node22": "22.0.0",
|
|
@@ -92,6 +94,7 @@
|
|
|
92
94
|
"esbuild": "^0.25.10",
|
|
93
95
|
"axios": "^1.12.2",
|
|
94
96
|
"vite": "6.2.5",
|
|
95
|
-
"tar-fs": "2.1.4"
|
|
97
|
+
"tar-fs": "2.1.4",
|
|
98
|
+
"js-yaml": "4.1.1"
|
|
96
99
|
}
|
|
97
100
|
}
|