@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 CHANGED
@@ -18,4 +18,5 @@ export * from './lib/phone';
18
18
  export * from './lib/http';
19
19
  export * from './lib/jobs';
20
20
  export * from './lib/validation';
21
+ export * from './lib/s3';
21
22
  export * from './types';
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.13.1",
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
  }