mbkbucket 4.0.0 → 4.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.
@@ -1,203 +1,203 @@
1
- /**
2
- * MBKBucket - Storage Manager & Provider Registry
3
- * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
- * Licensed under the MIT License.
5
- */
6
-
7
- import dotenv from "dotenv";
8
- import { mbkautheVar } from "mbkauthe";
9
- import { S3StorageProvider } from "./s3-provider.js";
10
- import { GoogleDriveStorageProvider } from "./gdrive-provider.js";
11
- import { StorageConfigError, StorageValidationError } from "./errors.js";
12
- import { createLogger } from "#logger";
13
-
14
- dotenv.config();
15
- const debugStorage = createLogger('storage-manager');
16
-
17
- function parseJsonEnv(envVar, fallback = {}) {
18
- if (!envVar) return fallback;
19
- try {
20
- return JSON.parse(envVar);
21
- } catch (e) {
22
- console.error(`❌ Error parsing JSON for envVar: ${String(envVar).slice(0, 200)} - ${e.message}`);
23
- return fallback;
24
- }
25
- }
26
-
27
- export class StorageManager {
28
- constructor() {
29
- this._providers = new Map();
30
- this._factories = new Map();
31
- this._configs = new Map();
32
-
33
- // Register built-in provider drivers
34
- this.registerDriver('s3', (name, cfg) => new S3StorageProvider(name, cfg));
35
- this.registerDriver('r2', (name, cfg) => new S3StorageProvider(name, cfg));
36
- this.registerDriver('minio', (name, cfg) => new S3StorageProvider(name, cfg));
37
- this.registerDriver('gdrive', (name, cfg) => new GoogleDriveStorageProvider(name, cfg));
38
- this.registerDriver('google-drive', (name, cfg) => new GoogleDriveStorageProvider(name, cfg));
39
- this.registerDriver('googledrive', (name, cfg) => new GoogleDriveStorageProvider(name, cfg));
40
-
41
- this.reloadFromEnv();
42
- }
43
-
44
- /**
45
- * Register a custom provider driver.
46
- * @param {string} type
47
- * @param {(name: string, config: object) => import('./storage-provider.js').StorageProvider} factory
48
- */
49
- registerDriver(type, factory) {
50
- this._factories.set(String(type).toLowerCase(), factory);
51
- }
52
-
53
- registerProviderFactory(type, factory) {
54
- this.registerDriver(type, factory);
55
- }
56
-
57
- /**
58
- * Reload all connections from environment variables.
59
- */
60
- reloadFromEnv() {
61
- this._providers.clear();
62
- this._configs.clear();
63
-
64
- const envSource = process.env.StorageConnection || process.env.BucketConnection;
65
- const rawConfigs = parseJsonEnv(envSource, {});
66
-
67
- for (const [name, config] of Object.entries(rawConfigs)) {
68
- if (config && typeof config === 'object' && !Array.isArray(config)) {
69
- this.registerConnection(name, config);
70
- }
71
- }
72
- }
73
-
74
- /**
75
- * Register a named storage connection.
76
- * @param {string} name
77
- * @param {object} config
78
- */
79
- registerConnection(name, config) {
80
- const cleanName = String(name).trim();
81
- if (!cleanName) throw new StorageValidationError("Connection name cannot be empty");
82
- this._configs.set(cleanName, Object.freeze({ ...config }));
83
- this._providers.delete(cleanName);
84
- }
85
-
86
- /**
87
- * Get all registered connection names.
88
- * @returns {string[]}
89
- */
90
- getAvailableConnectionNames() {
91
- return Array.from(this._configs.keys());
92
- }
93
-
94
- /**
95
- * Get the default connection name.
96
- * @returns {string|null}
97
- */
98
- getDefaultConnectionName() {
99
- const configuredDefault = mbkautheVar?.bucket;
100
- if (configuredDefault && this._configs.has(configuredDefault)) {
101
- return configuredDefault;
102
- }
103
- const available = this.getAvailableConnectionNames();
104
- return available.length > 0 ? available[0] : null;
105
- }
106
-
107
- /**
108
- * Resolve a candidate connection name or fall back to default.
109
- * @param {string} [name]
110
- * @returns {string}
111
- */
112
- resolveConnectionName(name) {
113
- const raw = typeof name === 'string' ? name.trim() : name;
114
- const candidate = !raw ? this.getDefaultConnectionName() : String(raw);
115
-
116
- const available = this.getAvailableConnectionNames();
117
- if (!candidate) {
118
- throw new StorageValidationError('No storage connection selected. Provide ?bucket=<name> or configure a default bucket in mbkautheVar.bucket.');
119
- }
120
- if (available.length === 0) {
121
- throw new StorageConfigError('BucketConnection environment variable is not set or empty. Please configure it with your storage connections.');
122
- }
123
- if (!this._configs.has(candidate)) {
124
- throw new StorageValidationError(`Storage connection '${candidate}' not found in BucketConnection. Available connections: ${available.join(', ')}`);
125
- }
126
- return candidate;
127
- }
128
-
129
- /**
130
- * Get connection configuration object.
131
- * @param {string} [name]
132
- * @returns {object}
133
- */
134
- getConnectionConfig(name) {
135
- const resolved = this.resolveConnectionName(name);
136
- return this._configs.get(resolved);
137
- }
138
-
139
- /**
140
- * Determine the provider type from configuration.
141
- * @param {object} config
142
- * @returns {string}
143
- */
144
- inferProviderType(config = {}) {
145
- if (config?.type) return String(config.type).toLowerCase();
146
- if (config?.provider) return String(config.provider).toLowerCase();
147
- if (config?.client_id || config?.refresh_token || config?.service_account_key || config?.folder_id || config?.service_account_email) {
148
- return 'gdrive';
149
- }
150
- return 's3';
151
- }
152
-
153
- /**
154
- * Get or instantiate the StorageProvider for a connection.
155
- * @param {string} [name]
156
- * @returns {import('./storage-provider.js').StorageProvider}
157
- */
158
- getProvider(name) {
159
- const resolved = this.resolveConnectionName(name);
160
- if (this._providers.has(resolved)) {
161
- return this._providers.get(resolved);
162
- }
163
-
164
- const config = this.getConnectionConfig(resolved);
165
- const providerType = this.inferProviderType(config);
166
- const factory = this._factories.get(providerType);
167
-
168
- if (!factory) {
169
- throw new StorageConfigError(`Unsupported storage provider type '${providerType}' for connection '${resolved}'. Registered drivers: ${Array.from(this._factories.keys()).join(', ')}`);
170
- }
171
-
172
- debugStorage('Instantiating provider "%s" (type: %s)', resolved, providerType);
173
- const provider = factory(resolved, config);
174
- this._providers.set(resolved, provider);
175
- return provider;
176
- }
177
-
178
- /**
179
- * Check health of the default or specified connection.
180
- * @param {string} [name]
181
- * @returns {Promise<object>}
182
- */
183
- async checkHealth(name) {
184
- try {
185
- const provider = this.getProvider(name);
186
- return await provider.checkHealth();
187
- } catch (error) {
188
- let bucket = 'unknown';
189
- try {
190
- const resolved = this.resolveConnectionName(name);
191
- bucket = this.getConnectionConfig(resolved)?.BUCKET_NAME || resolved;
192
- } catch {}
193
- return {
194
- status: 'unhealthy',
195
- error: error.message,
196
- bucket,
197
- checkedAt: new Date().toISOString()
198
- };
199
- }
200
- }
201
- }
202
-
203
- export const storageManager = new StorageManager();
1
+ /**
2
+ * MBKBucket - Storage Manager & Provider Registry
3
+ * Copyright (c) 2026 Muhammad Bin Khalid, MBKTech.org and contributors
4
+ * Licensed under the MIT License.
5
+ */
6
+
7
+ import dotenv from "dotenv";
8
+ import { mbkautheVar } from "mbkauthe";
9
+ import { S3StorageProvider } from "./s3-provider.js";
10
+ import { GoogleDriveStorageProvider } from "./gdrive-provider.js";
11
+ import { StorageConfigError, StorageValidationError } from "./errors.js";
12
+ import { createLogger } from "#logger";
13
+
14
+ dotenv.config();
15
+ const debugStorage = createLogger('storage-manager');
16
+
17
+ function parseJsonEnv(envVar, fallback = {}) {
18
+ if (!envVar) return fallback;
19
+ try {
20
+ return JSON.parse(envVar);
21
+ } catch (e) {
22
+ console.error(`❌ Error parsing JSON for envVar: ${String(envVar).slice(0, 200)} - ${e.message}`);
23
+ return fallback;
24
+ }
25
+ }
26
+
27
+ export class StorageManager {
28
+ constructor() {
29
+ this._providers = new Map();
30
+ this._factories = new Map();
31
+ this._configs = new Map();
32
+
33
+ // Register built-in provider drivers
34
+ this.registerDriver('s3', (name, cfg) => new S3StorageProvider(name, cfg));
35
+ this.registerDriver('r2', (name, cfg) => new S3StorageProvider(name, cfg));
36
+ this.registerDriver('minio', (name, cfg) => new S3StorageProvider(name, cfg));
37
+ this.registerDriver('gdrive', (name, cfg) => new GoogleDriveStorageProvider(name, cfg));
38
+ this.registerDriver('google-drive', (name, cfg) => new GoogleDriveStorageProvider(name, cfg));
39
+ this.registerDriver('googledrive', (name, cfg) => new GoogleDriveStorageProvider(name, cfg));
40
+
41
+ this.reloadFromEnv();
42
+ }
43
+
44
+ /**
45
+ * Register a custom provider driver.
46
+ * @param {string} type
47
+ * @param {(name: string, config: object) => import('./storage-provider.js').StorageProvider} factory
48
+ */
49
+ registerDriver(type, factory) {
50
+ this._factories.set(String(type).toLowerCase(), factory);
51
+ }
52
+
53
+ registerProviderFactory(type, factory) {
54
+ this.registerDriver(type, factory);
55
+ }
56
+
57
+ /**
58
+ * Reload all connections from environment variables.
59
+ */
60
+ reloadFromEnv() {
61
+ this._providers.clear();
62
+ this._configs.clear();
63
+
64
+ const envSource = process.env.StorageConnection || process.env.BucketConnection;
65
+ const rawConfigs = parseJsonEnv(envSource, {});
66
+
67
+ for (const [name, config] of Object.entries(rawConfigs)) {
68
+ if (config && typeof config === 'object' && !Array.isArray(config)) {
69
+ this.registerConnection(name, config);
70
+ }
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Register a named storage connection.
76
+ * @param {string} name
77
+ * @param {object} config
78
+ */
79
+ registerConnection(name, config) {
80
+ const cleanName = String(name).trim();
81
+ if (!cleanName) throw new StorageValidationError("Connection name cannot be empty");
82
+ this._configs.set(cleanName, Object.freeze({ ...config }));
83
+ this._providers.delete(cleanName);
84
+ }
85
+
86
+ /**
87
+ * Get all registered connection names.
88
+ * @returns {string[]}
89
+ */
90
+ getAvailableConnectionNames() {
91
+ return Array.from(this._configs.keys());
92
+ }
93
+
94
+ /**
95
+ * Get the default connection name.
96
+ * @returns {string|null}
97
+ */
98
+ getDefaultConnectionName() {
99
+ const configuredDefault = mbkautheVar?.bucket;
100
+ if (configuredDefault && this._configs.has(configuredDefault)) {
101
+ return configuredDefault;
102
+ }
103
+ const available = this.getAvailableConnectionNames();
104
+ return available.length > 0 ? available[0] : null;
105
+ }
106
+
107
+ /**
108
+ * Resolve a candidate connection name or fall back to default.
109
+ * @param {string} [name]
110
+ * @returns {string}
111
+ */
112
+ resolveConnectionName(name) {
113
+ const raw = typeof name === 'string' ? name.trim() : name;
114
+ const candidate = !raw ? this.getDefaultConnectionName() : String(raw);
115
+
116
+ const available = this.getAvailableConnectionNames();
117
+ if (!candidate) {
118
+ throw new StorageValidationError('No storage connection selected. Provide ?bucket=<name> or configure a default bucket in mbkautheVar.bucket.');
119
+ }
120
+ if (available.length === 0) {
121
+ throw new StorageConfigError('BucketConnection environment variable is not set or empty. Please configure it with your storage connections.');
122
+ }
123
+ if (!this._configs.has(candidate)) {
124
+ throw new StorageValidationError(`Storage connection '${candidate}' not found in BucketConnection. Available connections: ${available.join(', ')}`);
125
+ }
126
+ return candidate;
127
+ }
128
+
129
+ /**
130
+ * Get connection configuration object.
131
+ * @param {string} [name]
132
+ * @returns {object}
133
+ */
134
+ getConnectionConfig(name) {
135
+ const resolved = this.resolveConnectionName(name);
136
+ return this._configs.get(resolved);
137
+ }
138
+
139
+ /**
140
+ * Determine the provider type from configuration.
141
+ * @param {object} config
142
+ * @returns {string}
143
+ */
144
+ inferProviderType(config = {}) {
145
+ if (config?.type) return String(config.type).toLowerCase();
146
+ if (config?.provider) return String(config.provider).toLowerCase();
147
+ if (config?.client_id || config?.refresh_token || config?.service_account_key || config?.folder_id || config?.service_account_email) {
148
+ return 'gdrive';
149
+ }
150
+ return 's3';
151
+ }
152
+
153
+ /**
154
+ * Get or instantiate the StorageProvider for a connection.
155
+ * @param {string} [name]
156
+ * @returns {import('./storage-provider.js').StorageProvider}
157
+ */
158
+ getProvider(name) {
159
+ const resolved = this.resolveConnectionName(name);
160
+ if (this._providers.has(resolved)) {
161
+ return this._providers.get(resolved);
162
+ }
163
+
164
+ const config = this.getConnectionConfig(resolved);
165
+ const providerType = this.inferProviderType(config);
166
+ const factory = this._factories.get(providerType);
167
+
168
+ if (!factory) {
169
+ throw new StorageConfigError(`Unsupported storage provider type '${providerType}' for connection '${resolved}'. Registered drivers: ${Array.from(this._factories.keys()).join(', ')}`);
170
+ }
171
+
172
+ debugStorage('Instantiating provider "%s" (type: %s)', resolved, providerType);
173
+ const provider = factory(resolved, config);
174
+ this._providers.set(resolved, provider);
175
+ return provider;
176
+ }
177
+
178
+ /**
179
+ * Check health of the default or specified connection.
180
+ * @param {string} [name]
181
+ * @returns {Promise<object>}
182
+ */
183
+ async checkHealth(name) {
184
+ try {
185
+ const provider = this.getProvider(name);
186
+ return await provider.checkHealth();
187
+ } catch (error) {
188
+ let bucket = 'unknown';
189
+ try {
190
+ const resolved = this.resolveConnectionName(name);
191
+ bucket = this.getConnectionConfig(resolved)?.BUCKET_NAME || resolved;
192
+ } catch {}
193
+ return {
194
+ status: 'unhealthy',
195
+ error: error.message,
196
+ bucket,
197
+ checkedAt: new Date().toISOString()
198
+ };
199
+ }
200
+ }
201
+ }
202
+
203
+ export const storageManager = new StorageManager();