@fractary/core 0.3.2 → 0.4.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/__tests__/factories.test.d.ts +5 -0
- package/dist/__tests__/factories.test.d.ts.map +1 -0
- package/dist/__tests__/factories.test.js +66 -0
- package/dist/__tests__/factories.test.js.map +1 -0
- package/dist/auth/__tests__/create-token-provider.test.d.ts +5 -0
- package/dist/auth/__tests__/create-token-provider.test.d.ts.map +1 -0
- package/dist/auth/__tests__/create-token-provider.test.js +104 -0
- package/dist/auth/__tests__/create-token-provider.test.js.map +1 -0
- package/dist/auth/__tests__/github-app-auth.test.d.ts +5 -0
- package/dist/auth/__tests__/github-app-auth.test.d.ts.map +1 -0
- package/dist/auth/__tests__/github-app-auth.test.js +293 -0
- package/dist/auth/__tests__/github-app-auth.test.js.map +1 -0
- package/dist/auth/__tests__/static-token-provider.test.d.ts +5 -0
- package/dist/auth/__tests__/static-token-provider.test.d.ts.map +1 -0
- package/dist/auth/__tests__/static-token-provider.test.js +54 -0
- package/dist/auth/__tests__/static-token-provider.test.js.map +1 -0
- package/dist/auth/github-app-auth.d.ts +109 -0
- package/dist/auth/github-app-auth.d.ts.map +1 -0
- package/dist/auth/github-app-auth.js +262 -0
- package/dist/auth/github-app-auth.js.map +1 -0
- package/dist/auth/github-app-token-provider.d.ts +59 -0
- package/dist/auth/github-app-token-provider.d.ts.map +1 -0
- package/dist/auth/github-app-token-provider.js +68 -0
- package/dist/auth/github-app-token-provider.js.map +1 -0
- package/dist/auth/index.d.ts +45 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +74 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/static-token-provider.d.ts +35 -0
- package/dist/auth/static-token-provider.d.ts.map +1 -0
- package/dist/auth/static-token-provider.js +45 -0
- package/dist/auth/static-token-provider.js.map +1 -0
- package/dist/auth/types.d.ts +49 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +8 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/common/yaml-config.d.ts +10 -0
- package/dist/common/yaml-config.d.ts.map +1 -1
- package/dist/common/yaml-config.js.map +1 -1
- package/dist/config/__tests__/loader.test.d.ts +5 -0
- package/dist/config/__tests__/loader.test.d.ts.map +1 -0
- package/dist/config/__tests__/loader.test.js +129 -0
- package/dist/config/__tests__/loader.test.js.map +1 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +27 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +126 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +277 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/docs/index.d.ts +5 -0
- package/dist/docs/index.d.ts.map +1 -1
- package/dist/docs/index.js +6 -1
- package/dist/docs/index.js.map +1 -1
- package/dist/docs/manager.d.ts +27 -0
- package/dist/docs/manager.d.ts.map +1 -1
- package/dist/docs/manager.js +168 -15
- package/dist/docs/manager.js.map +1 -1
- package/dist/docs/type-registry.d.ts +123 -0
- package/dist/docs/type-registry.d.ts.map +1 -0
- package/dist/docs/type-registry.js +393 -0
- package/dist/docs/type-registry.js.map +1 -0
- package/dist/docs/types.d.ts +93 -0
- package/dist/docs/types.d.ts.map +1 -1
- package/dist/factories.d.ts +89 -0
- package/dist/factories.d.ts.map +1 -0
- package/dist/factories.js +228 -0
- package/dist/factories.js.map +1 -0
- package/dist/file/factory.d.ts +41 -0
- package/dist/file/factory.d.ts.map +1 -0
- package/dist/file/factory.js +237 -0
- package/dist/file/factory.js.map +1 -0
- package/dist/file/gcs.d.ts +66 -0
- package/dist/file/gcs.d.ts.map +1 -0
- package/dist/file/gcs.js +226 -0
- package/dist/file/gcs.js.map +1 -0
- package/dist/file/gdrive.d.ts +78 -0
- package/dist/file/gdrive.d.ts.map +1 -0
- package/dist/file/gdrive.js +302 -0
- package/dist/file/gdrive.js.map +1 -0
- package/dist/file/index.d.ts +13 -1
- package/dist/file/index.d.ts.map +1 -1
- package/dist/file/index.js +25 -1
- package/dist/file/index.js.map +1 -1
- package/dist/file/manager.d.ts +83 -2
- package/dist/file/manager.d.ts.map +1 -1
- package/dist/file/manager.js +125 -4
- package/dist/file/manager.js.map +1 -1
- package/dist/file/r2.d.ts +56 -0
- package/dist/file/r2.d.ts.map +1 -0
- package/dist/file/r2.js +96 -0
- package/dist/file/r2.js.map +1 -0
- package/dist/file/s3.d.ts +61 -0
- package/dist/file/s3.d.ts.map +1 -0
- package/dist/file/s3.js +258 -0
- package/dist/file/s3.js.map +1 -0
- package/dist/file/types.d.ts +145 -2
- package/dist/file/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/logs/index.d.ts +1 -0
- package/dist/logs/index.d.ts.map +1 -1
- package/dist/logs/index.js +3 -1
- package/dist/logs/index.js.map +1 -1
- package/dist/logs/manager.d.ts +29 -2
- package/dist/logs/manager.d.ts.map +1 -1
- package/dist/logs/manager.js +48 -7
- package/dist/logs/manager.js.map +1 -1
- package/dist/logs/type-registry.d.ts +180 -0
- package/dist/logs/type-registry.d.ts.map +1 -0
- package/dist/logs/type-registry.js +421 -0
- package/dist/logs/type-registry.js.map +1 -0
- package/dist/logs/type-registry.test.d.ts +5 -0
- package/dist/logs/type-registry.test.d.ts.map +1 -0
- package/dist/logs/type-registry.test.js +671 -0
- package/dist/logs/type-registry.test.js.map +1 -0
- package/dist/logs/types.d.ts +2 -0
- package/dist/logs/types.d.ts.map +1 -1
- package/package.json +62 -8
package/dist/file/manager.js
CHANGED
|
@@ -3,66 +3,187 @@
|
|
|
3
3
|
* @fractary/core - File Manager
|
|
4
4
|
*
|
|
5
5
|
* Unified interface for file storage operations.
|
|
6
|
+
* Supports multiple storage backends: local, S3, R2, GCS, Google Drive.
|
|
6
7
|
*/
|
|
7
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
9
|
exports.FileManager = void 0;
|
|
9
10
|
const local_1 = require("./local");
|
|
11
|
+
const factory_1 = require("./factory");
|
|
10
12
|
/**
|
|
11
13
|
* File Manager - Unified interface for file operations
|
|
14
|
+
*
|
|
15
|
+
* Provides a consistent API for file operations across different storage backends.
|
|
16
|
+
* Supports local filesystem, AWS S3, Cloudflare R2, Google Cloud Storage,
|
|
17
|
+
* and Google Drive.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Local storage (default)
|
|
21
|
+
* const localManager = new FileManager({ basePath: '.fractary/files' });
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* // S3 storage with configuration
|
|
25
|
+
* const s3Manager = new FileManager({
|
|
26
|
+
* storageConfig: {
|
|
27
|
+
* type: 's3',
|
|
28
|
+
* bucket: 'my-bucket',
|
|
29
|
+
* region: 'us-east-1',
|
|
30
|
+
* prefix: 'files/',
|
|
31
|
+
* auth: { profile: 'default' }
|
|
32
|
+
* }
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Custom storage backend
|
|
37
|
+
* const customManager = new FileManager({
|
|
38
|
+
* storage: myCustomStorage
|
|
39
|
+
* });
|
|
12
40
|
*/
|
|
13
41
|
class FileManager {
|
|
14
42
|
storage;
|
|
15
43
|
constructor(config) {
|
|
16
|
-
|
|
44
|
+
// Priority: storageConfig > storage > LocalStorage with basePath
|
|
45
|
+
if (config?.storageConfig) {
|
|
46
|
+
this.storage = (0, factory_1.createStorage)(config.storageConfig);
|
|
47
|
+
}
|
|
48
|
+
else if (config?.storage) {
|
|
49
|
+
this.storage = config.storage;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const basePath = config?.basePath || '.fractary/files';
|
|
53
|
+
this.storage = new local_1.LocalStorage(basePath);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Validate a file path for security issues
|
|
58
|
+
*
|
|
59
|
+
* @param path - Path to validate
|
|
60
|
+
* @throws Error if path is invalid or contains security issues
|
|
61
|
+
*/
|
|
62
|
+
validatePath(path) {
|
|
63
|
+
if (!path || path.trim() === '') {
|
|
64
|
+
throw new Error('Path cannot be empty');
|
|
65
|
+
}
|
|
66
|
+
// Check for directory traversal sequences
|
|
67
|
+
if (path.includes('..')) {
|
|
68
|
+
throw new Error('Path cannot contain directory traversal sequences (..)');
|
|
69
|
+
}
|
|
70
|
+
// Check for null bytes (potential security issue)
|
|
71
|
+
if (path.includes('\0')) {
|
|
72
|
+
throw new Error('Path cannot contain null bytes');
|
|
73
|
+
}
|
|
74
|
+
// Check for absolute paths on Windows
|
|
75
|
+
if (/^[a-zA-Z]:/.test(path)) {
|
|
76
|
+
throw new Error('Absolute Windows paths are not allowed');
|
|
77
|
+
}
|
|
78
|
+
// Check for absolute Unix paths (starting with /)
|
|
79
|
+
if (path.startsWith('/')) {
|
|
80
|
+
throw new Error('Absolute paths are not allowed');
|
|
81
|
+
}
|
|
17
82
|
}
|
|
18
83
|
/**
|
|
19
84
|
* Write file content
|
|
85
|
+
*
|
|
86
|
+
* @param path - Path/identifier for the file
|
|
87
|
+
* @param content - Content to write
|
|
88
|
+
* @returns URI or path where the content was written
|
|
20
89
|
*/
|
|
21
90
|
async write(path, content) {
|
|
91
|
+
this.validatePath(path);
|
|
22
92
|
return this.storage.write(path, content);
|
|
23
93
|
}
|
|
24
94
|
/**
|
|
25
95
|
* Read file content
|
|
96
|
+
*
|
|
97
|
+
* @param path - Path/identifier for the file
|
|
98
|
+
* @returns File content or null if not found
|
|
26
99
|
*/
|
|
27
100
|
async read(path) {
|
|
101
|
+
this.validatePath(path);
|
|
28
102
|
return this.storage.read(path);
|
|
29
103
|
}
|
|
30
104
|
/**
|
|
31
105
|
* Check if file exists
|
|
106
|
+
*
|
|
107
|
+
* @param path - Path/identifier for the file
|
|
108
|
+
* @returns True if the file exists
|
|
32
109
|
*/
|
|
33
110
|
async exists(path) {
|
|
111
|
+
this.validatePath(path);
|
|
34
112
|
return this.storage.exists(path);
|
|
35
113
|
}
|
|
36
114
|
/**
|
|
37
115
|
* List files (optionally with prefix)
|
|
116
|
+
*
|
|
117
|
+
* @param prefix - Optional prefix to filter results
|
|
118
|
+
* @returns List of file paths/identifiers
|
|
38
119
|
*/
|
|
39
120
|
async list(prefix) {
|
|
121
|
+
if (prefix) {
|
|
122
|
+
this.validatePath(prefix);
|
|
123
|
+
}
|
|
40
124
|
return this.storage.list(prefix);
|
|
41
125
|
}
|
|
42
126
|
/**
|
|
43
127
|
* Delete file
|
|
128
|
+
*
|
|
129
|
+
* @param path - Path/identifier for the file
|
|
44
130
|
*/
|
|
45
131
|
async delete(path) {
|
|
132
|
+
this.validatePath(path);
|
|
46
133
|
return this.storage.delete(path);
|
|
47
134
|
}
|
|
48
135
|
/**
|
|
49
136
|
* Copy file from one location to another
|
|
137
|
+
*
|
|
138
|
+
* @param sourcePath - Source path/identifier
|
|
139
|
+
* @param destPath - Destination path/identifier
|
|
140
|
+
* @returns URI or path where the content was copied
|
|
50
141
|
*/
|
|
51
142
|
async copy(sourcePath, destPath) {
|
|
52
|
-
|
|
143
|
+
this.validatePath(sourcePath);
|
|
144
|
+
this.validatePath(destPath);
|
|
145
|
+
const content = await this.storage.read(sourcePath);
|
|
53
146
|
if (!content) {
|
|
54
147
|
throw new Error(`Source file not found: ${sourcePath}`);
|
|
55
148
|
}
|
|
56
|
-
return this.write(destPath, content);
|
|
149
|
+
return this.storage.write(destPath, content);
|
|
57
150
|
}
|
|
58
151
|
/**
|
|
59
152
|
* Move file from one location to another
|
|
153
|
+
*
|
|
154
|
+
* @param sourcePath - Source path/identifier
|
|
155
|
+
* @param destPath - Destination path/identifier
|
|
156
|
+
* @returns URI or path where the content was moved
|
|
60
157
|
*/
|
|
61
158
|
async move(sourcePath, destPath) {
|
|
159
|
+
this.validatePath(sourcePath);
|
|
160
|
+
this.validatePath(destPath);
|
|
62
161
|
const result = await this.copy(sourcePath, destPath);
|
|
63
|
-
await this.delete(sourcePath);
|
|
162
|
+
await this.storage.delete(sourcePath);
|
|
64
163
|
return result;
|
|
65
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Get a URL for the file (if supported by the storage backend)
|
|
167
|
+
*
|
|
168
|
+
* @param path - Path/identifier for the file
|
|
169
|
+
* @param expiresIn - Expiration time in seconds (for presigned URLs)
|
|
170
|
+
* @returns URL or null if not supported
|
|
171
|
+
*/
|
|
172
|
+
async getUrl(path, expiresIn) {
|
|
173
|
+
this.validatePath(path);
|
|
174
|
+
if (this.storage.getUrl) {
|
|
175
|
+
return this.storage.getUrl(path, expiresIn);
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get the underlying storage backend
|
|
181
|
+
*
|
|
182
|
+
* @returns The storage instance
|
|
183
|
+
*/
|
|
184
|
+
getStorage() {
|
|
185
|
+
return this.storage;
|
|
186
|
+
}
|
|
66
187
|
}
|
|
67
188
|
exports.FileManager = FileManager;
|
|
68
189
|
//# sourceMappingURL=manager.js.map
|
package/dist/file/manager.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/file/manager.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/file/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fractary/core - R2 Storage Implementation
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare R2 storage backend for file operations.
|
|
5
|
+
* R2 is S3-compatible, so this uses the AWS SDK with R2 endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { Storage, R2StorageConfig } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* Cloudflare R2 storage implementation
|
|
10
|
+
*
|
|
11
|
+
* Uses S3Storage internally with R2-specific endpoint configuration.
|
|
12
|
+
*/
|
|
13
|
+
export declare class R2Storage implements Storage {
|
|
14
|
+
private s3Storage;
|
|
15
|
+
private config;
|
|
16
|
+
constructor(config: R2StorageConfig);
|
|
17
|
+
/**
|
|
18
|
+
* Write content to R2
|
|
19
|
+
*/
|
|
20
|
+
write(id: string, content: string): Promise<string>;
|
|
21
|
+
/**
|
|
22
|
+
* Read content from R2
|
|
23
|
+
*/
|
|
24
|
+
read(id: string): Promise<string | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Check if object exists in R2
|
|
27
|
+
*/
|
|
28
|
+
exists(id: string): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* List objects in R2 with optional prefix
|
|
31
|
+
*/
|
|
32
|
+
list(prefix?: string): Promise<string[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Delete object from R2
|
|
35
|
+
*/
|
|
36
|
+
delete(id: string): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Get a URL for the object
|
|
39
|
+
*
|
|
40
|
+
* R2 supports presigned URLs, and can also use public bucket URLs.
|
|
41
|
+
*/
|
|
42
|
+
getUrl(id: string, expiresIn?: number): Promise<string | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Get the bucket name
|
|
45
|
+
*/
|
|
46
|
+
getBucket(): string;
|
|
47
|
+
/**
|
|
48
|
+
* Get the account ID
|
|
49
|
+
*/
|
|
50
|
+
getAccountId(): string;
|
|
51
|
+
/**
|
|
52
|
+
* Get the configured prefix
|
|
53
|
+
*/
|
|
54
|
+
getPrefix(): string | undefined;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=r2.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2.d.ts","sourceRoot":"","sources":["../../src/file/r2.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,eAAe,EAAmB,MAAM,SAAS,CAAC;AAGpE;;;;GAIG;AACH,qBAAa,SAAU,YAAW,OAAO;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,EAAE,eAAe;IAoBnC;;OAEG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMzD;;OAEG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1C;;OAEG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAI9C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC;;;;OAIG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,GAAE,MAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI3E;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,YAAY,IAAI,MAAM;IAItB;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,SAAS;CAGhC"}
|
package/dist/file/r2.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fractary/core - R2 Storage Implementation
|
|
4
|
+
*
|
|
5
|
+
* Cloudflare R2 storage backend for file operations.
|
|
6
|
+
* R2 is S3-compatible, so this uses the AWS SDK with R2 endpoint.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.R2Storage = void 0;
|
|
10
|
+
const s3_1 = require("./s3");
|
|
11
|
+
/**
|
|
12
|
+
* Cloudflare R2 storage implementation
|
|
13
|
+
*
|
|
14
|
+
* Uses S3Storage internally with R2-specific endpoint configuration.
|
|
15
|
+
*/
|
|
16
|
+
class R2Storage {
|
|
17
|
+
s3Storage;
|
|
18
|
+
config;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
// Convert R2 config to S3-compatible config
|
|
22
|
+
const s3Config = {
|
|
23
|
+
type: 's3',
|
|
24
|
+
bucket: config.bucket,
|
|
25
|
+
region: 'auto', // R2 uses 'auto' region
|
|
26
|
+
prefix: config.prefix,
|
|
27
|
+
endpoint: `https://${config.accountId}.r2.cloudflarestorage.com`,
|
|
28
|
+
publicUrl: config.publicUrl,
|
|
29
|
+
auth: {
|
|
30
|
+
accessKeyId: config.accessKeyId,
|
|
31
|
+
secretAccessKey: config.secretAccessKey,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
this.s3Storage = new s3_1.S3Storage(s3Config);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Write content to R2
|
|
38
|
+
*/
|
|
39
|
+
async write(id, content) {
|
|
40
|
+
const result = await this.s3Storage.write(id, content);
|
|
41
|
+
// Return R2-style URI
|
|
42
|
+
return result.replace('s3://', 'r2://');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Read content from R2
|
|
46
|
+
*/
|
|
47
|
+
async read(id) {
|
|
48
|
+
return this.s3Storage.read(id);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if object exists in R2
|
|
52
|
+
*/
|
|
53
|
+
async exists(id) {
|
|
54
|
+
return this.s3Storage.exists(id);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* List objects in R2 with optional prefix
|
|
58
|
+
*/
|
|
59
|
+
async list(prefix) {
|
|
60
|
+
return this.s3Storage.list(prefix);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Delete object from R2
|
|
64
|
+
*/
|
|
65
|
+
async delete(id) {
|
|
66
|
+
return this.s3Storage.delete(id);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get a URL for the object
|
|
70
|
+
*
|
|
71
|
+
* R2 supports presigned URLs, and can also use public bucket URLs.
|
|
72
|
+
*/
|
|
73
|
+
async getUrl(id, expiresIn = 86400) {
|
|
74
|
+
return this.s3Storage.getUrl(id, expiresIn);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the bucket name
|
|
78
|
+
*/
|
|
79
|
+
getBucket() {
|
|
80
|
+
return this.config.bucket;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the account ID
|
|
84
|
+
*/
|
|
85
|
+
getAccountId() {
|
|
86
|
+
return this.config.accountId;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the configured prefix
|
|
90
|
+
*/
|
|
91
|
+
getPrefix() {
|
|
92
|
+
return this.config.prefix;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.R2Storage = R2Storage;
|
|
96
|
+
//# sourceMappingURL=r2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2.js","sourceRoot":"","sources":["../../src/file/r2.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAGH,6BAAiC;AAEjC;;;;GAIG;AACH,MAAa,SAAS;IACZ,SAAS,CAAY;IACrB,MAAM,CAAkB;IAEhC,YAAY,MAAuB;QACjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QAErB,4CAA4C;QAC5C,MAAM,QAAQ,GAAoB;YAChC,IAAI,EAAE,IAAI;YACV,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,EAAE,wBAAwB;YACxC,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,QAAQ,EAAE,WAAW,MAAM,CAAC,SAAS,2BAA2B;YAChE,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,IAAI,EAAE;gBACJ,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,eAAe,EAAE,MAAM,CAAC,eAAe;aACxC;SACF,CAAC;QAEF,IAAI,CAAC,SAAS,GAAG,IAAI,cAAS,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,EAAU,EAAE,OAAe;QACrC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACvD,sBAAsB;QACtB,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,EAAU;QACnB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,MAAe;QACxB,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,YAAoB,KAAK;QAChD,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC9C,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC5B,CAAC;CACF;AA1FD,8BA0FC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fractary/core - S3 Storage Implementation
|
|
3
|
+
*
|
|
4
|
+
* AWS S3 storage backend for file operations.
|
|
5
|
+
* Uses AWS SDK v3 with lazy loading to avoid hard dependencies.
|
|
6
|
+
*/
|
|
7
|
+
import { Storage, S3StorageConfig } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* AWS S3 storage implementation
|
|
10
|
+
*/
|
|
11
|
+
export declare class S3Storage implements Storage {
|
|
12
|
+
private config;
|
|
13
|
+
private s3Client;
|
|
14
|
+
constructor(config: S3StorageConfig);
|
|
15
|
+
/**
|
|
16
|
+
* Get or create the S3 client (lazy loaded)
|
|
17
|
+
*/
|
|
18
|
+
private getClient;
|
|
19
|
+
/**
|
|
20
|
+
* Get the full S3 key with optional prefix
|
|
21
|
+
*/
|
|
22
|
+
private getKey;
|
|
23
|
+
/**
|
|
24
|
+
* Check if an error is a "not found" error
|
|
25
|
+
* AWS SDK v3 uses error.name and error.$metadata.httpStatusCode
|
|
26
|
+
*/
|
|
27
|
+
private isNotFoundError;
|
|
28
|
+
/**
|
|
29
|
+
* Write content to S3
|
|
30
|
+
*/
|
|
31
|
+
write(id: string, content: string): Promise<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Read content from S3
|
|
34
|
+
*/
|
|
35
|
+
read(id: string): Promise<string | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if object exists in S3
|
|
38
|
+
*/
|
|
39
|
+
exists(id: string): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* List objects in S3 with optional prefix
|
|
42
|
+
*/
|
|
43
|
+
list(prefix?: string): Promise<string[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Delete object from S3
|
|
46
|
+
*/
|
|
47
|
+
delete(id: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Get a presigned URL for the object
|
|
50
|
+
*/
|
|
51
|
+
getUrl(id: string, expiresIn?: number): Promise<string | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Get the bucket name
|
|
54
|
+
*/
|
|
55
|
+
getBucket(): string;
|
|
56
|
+
/**
|
|
57
|
+
* Get the configured prefix
|
|
58
|
+
*/
|
|
59
|
+
getPrefix(): string | undefined;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=s3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../src/file/s3.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAKnD;;GAEG;AACH,qBAAa,SAAU,YAAW,OAAO;IACvC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,QAAQ,CAAyB;gBAE7B,MAAM,EAAE,eAAe;IAInC;;OAEG;YACW,SAAS;IAwCvB;;OAEG;IACH,OAAO,CAAC,MAAM;IAOd;;;OAGG;IACH,OAAO,CAAC,eAAe;IAQvB;;OAEG;IACG,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBzD;;OAEG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA4B9C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsB1C;;OAEG;IACG,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAoC9C;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAavC;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,SAAS,GAAE,MAAc,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA4B3E;;OAEG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,SAAS,IAAI,MAAM,GAAG,SAAS;CAGhC"}
|
package/dist/file/s3.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fractary/core - S3 Storage Implementation
|
|
4
|
+
*
|
|
5
|
+
* AWS S3 storage backend for file operations.
|
|
6
|
+
* Uses AWS SDK v3 with lazy loading to avoid hard dependencies.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.S3Storage = void 0;
|
|
43
|
+
/**
|
|
44
|
+
* AWS S3 storage implementation
|
|
45
|
+
*/
|
|
46
|
+
class S3Storage {
|
|
47
|
+
config;
|
|
48
|
+
s3Client = null;
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.config = config;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Get or create the S3 client (lazy loaded)
|
|
54
|
+
*/
|
|
55
|
+
async getClient() {
|
|
56
|
+
if (this.s3Client) {
|
|
57
|
+
return this.s3Client;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
// Dynamic import to avoid hard dependency
|
|
61
|
+
const { S3Client } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
62
|
+
const { fromIni } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/credential-providers')));
|
|
63
|
+
const clientConfig = {
|
|
64
|
+
region: this.config.region,
|
|
65
|
+
};
|
|
66
|
+
// Configure endpoint for S3-compatible services
|
|
67
|
+
if (this.config.endpoint) {
|
|
68
|
+
clientConfig.endpoint = this.config.endpoint;
|
|
69
|
+
clientConfig.forcePathStyle = true;
|
|
70
|
+
}
|
|
71
|
+
// Configure credentials
|
|
72
|
+
if (this.config.auth?.accessKeyId && this.config.auth?.secretAccessKey) {
|
|
73
|
+
clientConfig.credentials = {
|
|
74
|
+
accessKeyId: this.config.auth.accessKeyId,
|
|
75
|
+
secretAccessKey: this.config.auth.secretAccessKey,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else if (this.config.auth?.profile) {
|
|
79
|
+
clientConfig.credentials = fromIni({ profile: this.config.auth.profile });
|
|
80
|
+
}
|
|
81
|
+
// Otherwise, use default credential chain (IAM role, env vars, etc.)
|
|
82
|
+
this.s3Client = new S3Client(clientConfig);
|
|
83
|
+
return this.s3Client;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw new Error('AWS SDK not available. Install with: npm install @aws-sdk/client-s3 @aws-sdk/credential-providers');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get the full S3 key with optional prefix
|
|
91
|
+
*/
|
|
92
|
+
getKey(id) {
|
|
93
|
+
if (this.config.prefix) {
|
|
94
|
+
return `${this.config.prefix.replace(/\/$/, '')}/${id}`;
|
|
95
|
+
}
|
|
96
|
+
return id;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if an error is a "not found" error
|
|
100
|
+
* AWS SDK v3 uses error.name and error.$metadata.httpStatusCode
|
|
101
|
+
*/
|
|
102
|
+
isNotFoundError(error) {
|
|
103
|
+
return (error.name === 'NoSuchKey' ||
|
|
104
|
+
error.name === 'NotFound' ||
|
|
105
|
+
error.$metadata?.httpStatusCode === 404);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Write content to S3
|
|
109
|
+
*/
|
|
110
|
+
async write(id, content) {
|
|
111
|
+
const client = await this.getClient();
|
|
112
|
+
const { PutObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
113
|
+
const key = this.getKey(id);
|
|
114
|
+
const command = new PutObjectCommand({
|
|
115
|
+
Bucket: this.config.bucket,
|
|
116
|
+
Key: key,
|
|
117
|
+
Body: content,
|
|
118
|
+
ContentType: 'text/plain; charset=utf-8',
|
|
119
|
+
});
|
|
120
|
+
await client.send(command);
|
|
121
|
+
return `s3://${this.config.bucket}/${key}`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Read content from S3
|
|
125
|
+
*/
|
|
126
|
+
async read(id) {
|
|
127
|
+
const client = await this.getClient();
|
|
128
|
+
const { GetObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
129
|
+
const key = this.getKey(id);
|
|
130
|
+
try {
|
|
131
|
+
const command = new GetObjectCommand({
|
|
132
|
+
Bucket: this.config.bucket,
|
|
133
|
+
Key: key,
|
|
134
|
+
});
|
|
135
|
+
const response = await client.send(command);
|
|
136
|
+
// Convert stream to string
|
|
137
|
+
if (response.Body) {
|
|
138
|
+
const bodyContents = await response.Body.transformToString();
|
|
139
|
+
return bodyContents;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (this.isNotFoundError(error)) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if object exists in S3
|
|
152
|
+
*/
|
|
153
|
+
async exists(id) {
|
|
154
|
+
const client = await this.getClient();
|
|
155
|
+
const { HeadObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
156
|
+
const key = this.getKey(id);
|
|
157
|
+
try {
|
|
158
|
+
const command = new HeadObjectCommand({
|
|
159
|
+
Bucket: this.config.bucket,
|
|
160
|
+
Key: key,
|
|
161
|
+
});
|
|
162
|
+
await client.send(command);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (this.isNotFoundError(error)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* List objects in S3 with optional prefix
|
|
174
|
+
*/
|
|
175
|
+
async list(prefix) {
|
|
176
|
+
const client = await this.getClient();
|
|
177
|
+
const { ListObjectsV2Command } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
178
|
+
const fullPrefix = prefix ? this.getKey(prefix) : this.config.prefix || '';
|
|
179
|
+
const results = [];
|
|
180
|
+
let continuationToken;
|
|
181
|
+
do {
|
|
182
|
+
const command = new ListObjectsV2Command({
|
|
183
|
+
Bucket: this.config.bucket,
|
|
184
|
+
Prefix: fullPrefix,
|
|
185
|
+
ContinuationToken: continuationToken,
|
|
186
|
+
});
|
|
187
|
+
const response = await client.send(command);
|
|
188
|
+
if (response.Contents) {
|
|
189
|
+
for (const item of response.Contents) {
|
|
190
|
+
if (item.Key) {
|
|
191
|
+
// Remove the configured prefix from the key for consistency
|
|
192
|
+
let key = item.Key;
|
|
193
|
+
if (this.config.prefix && key.startsWith(this.config.prefix)) {
|
|
194
|
+
key = key.slice(this.config.prefix.length).replace(/^\//, '');
|
|
195
|
+
}
|
|
196
|
+
results.push(key);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
continuationToken = response.IsTruncated ? response.NextContinuationToken : undefined;
|
|
201
|
+
} while (continuationToken);
|
|
202
|
+
return results;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Delete object from S3
|
|
206
|
+
*/
|
|
207
|
+
async delete(id) {
|
|
208
|
+
const client = await this.getClient();
|
|
209
|
+
const { DeleteObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
210
|
+
const key = this.getKey(id);
|
|
211
|
+
const command = new DeleteObjectCommand({
|
|
212
|
+
Bucket: this.config.bucket,
|
|
213
|
+
Key: key,
|
|
214
|
+
});
|
|
215
|
+
await client.send(command);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Get a presigned URL for the object
|
|
219
|
+
*/
|
|
220
|
+
async getUrl(id, expiresIn = 86400) {
|
|
221
|
+
// If a public URL is configured, use it
|
|
222
|
+
if (this.config.publicUrl) {
|
|
223
|
+
const key = this.getKey(id);
|
|
224
|
+
return `${this.config.publicUrl.replace(/\/$/, '')}/${key}`;
|
|
225
|
+
}
|
|
226
|
+
const client = await this.getClient();
|
|
227
|
+
try {
|
|
228
|
+
const { getSignedUrl } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/s3-request-presigner')));
|
|
229
|
+
const { GetObjectCommand } = await Promise.resolve().then(() => __importStar(require('@aws-sdk/client-s3')));
|
|
230
|
+
const key = this.getKey(id);
|
|
231
|
+
const command = new GetObjectCommand({
|
|
232
|
+
Bucket: this.config.bucket,
|
|
233
|
+
Key: key,
|
|
234
|
+
});
|
|
235
|
+
const url = await getSignedUrl(client, command, { expiresIn });
|
|
236
|
+
return url;
|
|
237
|
+
}
|
|
238
|
+
catch (error) {
|
|
239
|
+
// Fall back to S3 URI if presigner not available
|
|
240
|
+
const key = this.getKey(id);
|
|
241
|
+
return `s3://${this.config.bucket}/${key}`;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get the bucket name
|
|
246
|
+
*/
|
|
247
|
+
getBucket() {
|
|
248
|
+
return this.config.bucket;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Get the configured prefix
|
|
252
|
+
*/
|
|
253
|
+
getPrefix() {
|
|
254
|
+
return this.config.prefix;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
exports.S3Storage = S3Storage;
|
|
258
|
+
//# sourceMappingURL=s3.js.map
|