@kumix/storage 0.1.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/README.md +291 -0
- package/dist/cloudinary.d.ts +93 -0
- package/dist/cloudinary.d.ts.map +1 -0
- package/dist/cloudinary.js +516 -0
- package/dist/cloudinary.js.map +1 -0
- package/dist/config-J6AdDh_c.d.ts +594 -0
- package/dist/config-J6AdDh_c.d.ts.map +1 -0
- package/dist/config-t-NVJICl.js +410 -0
- package/dist/config-t-NVJICl.js.map +1 -0
- package/dist/helpers.d.ts +764 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +1042 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/s3.d.ts +109 -0
- package/dist/s3.d.ts.map +1 -0
- package/dist/s3.js +779 -0
- package/dist/s3.js.map +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
//#region src/config.ts
|
|
2
|
+
function getDefaultEnv() {
|
|
3
|
+
if (typeof process !== "undefined" && process?.env) return process.env;
|
|
4
|
+
}
|
|
5
|
+
function resolveEnv(env) {
|
|
6
|
+
return env ?? getDefaultEnv();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Environment variable names for storage configuration
|
|
10
|
+
* @public
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { ENV_VARS } from '@kumix/storage';
|
|
15
|
+
*
|
|
16
|
+
* // Check if specific env vars are set
|
|
17
|
+
* console.log(process.env[ENV_VARS.KUMIX_S3_BUCKET]);
|
|
18
|
+
* console.log(process.env[ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME]);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
const ENV_VARS = {
|
|
22
|
+
KUMIX_S3_PROVIDER: "KUMIX_S3_PROVIDER",
|
|
23
|
+
KUMIX_S3_REGION: "KUMIX_S3_REGION",
|
|
24
|
+
KUMIX_S3_BUCKET: "KUMIX_S3_BUCKET",
|
|
25
|
+
KUMIX_S3_ACCESS_KEY_ID: "KUMIX_S3_ACCESS_KEY_ID",
|
|
26
|
+
KUMIX_S3_SECRET_ACCESS_KEY: "KUMIX_S3_SECRET_ACCESS_KEY",
|
|
27
|
+
KUMIX_S3_ENDPOINT: "KUMIX_S3_ENDPOINT",
|
|
28
|
+
KUMIX_S3_FORCE_PATH_STYLE: "KUMIX_S3_FORCE_PATH_STYLE",
|
|
29
|
+
KUMIX_S3_PUBLIC_URL: "KUMIX_S3_PUBLIC_URL",
|
|
30
|
+
KUMIX_CLOUDINARY_CLOUD_NAME: "KUMIX_CLOUDINARY_CLOUD_NAME",
|
|
31
|
+
KUMIX_CLOUDINARY_API_KEY: "KUMIX_CLOUDINARY_API_KEY",
|
|
32
|
+
KUMIX_CLOUDINARY_API_SECRET: "KUMIX_CLOUDINARY_API_SECRET",
|
|
33
|
+
KUMIX_CLOUDINARY_SECURE: "KUMIX_CLOUDINARY_SECURE",
|
|
34
|
+
KUMIX_CLOUDINARY_FOLDER: "KUMIX_CLOUDINARY_FOLDER"
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Get environment variable value
|
|
38
|
+
* @param key - Environment variable name
|
|
39
|
+
* @param env - Optional environment record
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
function getEnvVar(key, env) {
|
|
43
|
+
const e = resolveEnv(env);
|
|
44
|
+
if (!e) return void 0;
|
|
45
|
+
return e[key];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get required environment variable value
|
|
49
|
+
* @param key - Environment variable name
|
|
50
|
+
* @param env - Optional environment record
|
|
51
|
+
* @internal
|
|
52
|
+
*/
|
|
53
|
+
function getRequiredEnvVar(key, env) {
|
|
54
|
+
const value = getEnvVar(key, env);
|
|
55
|
+
if (!value) throw new Error(`Missing required environment variable: ${key}`);
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get boolean environment variable value
|
|
60
|
+
* @param key - Environment variable name
|
|
61
|
+
* @param defaultValue - Default boolean value if not set
|
|
62
|
+
* @param env - Optional environment record
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
function getBooleanEnvVar(key, defaultValue = false, env) {
|
|
66
|
+
const value = getEnvVar(key, env);
|
|
67
|
+
if (!value) return defaultValue;
|
|
68
|
+
return value.toLowerCase() === "true" || value === "1";
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Load S3 configuration from environment variables
|
|
72
|
+
* @param env - Optional environment record
|
|
73
|
+
* @returns S3 configuration object
|
|
74
|
+
* @throws Error if required environment variables are missing
|
|
75
|
+
* @public
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* import { loadS3Config } from '@kumix/storage';
|
|
80
|
+
*
|
|
81
|
+
* // Set environment variables first:
|
|
82
|
+
* // KUMIX_S3_PROVIDER=aws
|
|
83
|
+
* // KUMIX_S3_REGION=us-east-1
|
|
84
|
+
* // KUMIX_S3_BUCKET=my-bucket
|
|
85
|
+
* // KUMIX_S3_ACCESS_KEY_ID=AKIA...
|
|
86
|
+
* // KUMIX_S3_SECRET_ACCESS_KEY=secret...
|
|
87
|
+
*
|
|
88
|
+
* try {
|
|
89
|
+
* const config = loadS3Config();
|
|
90
|
+
* console.log(config);
|
|
91
|
+
* // Returns: {
|
|
92
|
+
* // provider: 'aws',
|
|
93
|
+
* // region: 'us-east-1',
|
|
94
|
+
* // bucket: 'my-bucket',
|
|
95
|
+
* // accessKeyId: 'AKIA...',
|
|
96
|
+
* // secretAccessKey: 'secret...'
|
|
97
|
+
* // }
|
|
98
|
+
* } catch (error) {
|
|
99
|
+
* console.error('Missing S3 configuration:', error.message);
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
function loadS3Config(env) {
|
|
104
|
+
const provider = getRequiredEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);
|
|
105
|
+
const validProviders = [
|
|
106
|
+
"aws",
|
|
107
|
+
"cloudflare-r2",
|
|
108
|
+
"minio",
|
|
109
|
+
"digitalocean",
|
|
110
|
+
"supabase",
|
|
111
|
+
"custom"
|
|
112
|
+
];
|
|
113
|
+
if (!validProviders.includes(provider)) throw new Error(`Invalid S3 provider: ${provider}. Must be one of: ${validProviders.join(", ")}`);
|
|
114
|
+
return {
|
|
115
|
+
provider,
|
|
116
|
+
region: getRequiredEnvVar(ENV_VARS.KUMIX_S3_REGION, env),
|
|
117
|
+
bucket: getRequiredEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env),
|
|
118
|
+
accessKeyId: getRequiredEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env),
|
|
119
|
+
secretAccessKey: getRequiredEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env),
|
|
120
|
+
endpoint: getEnvVar(ENV_VARS.KUMIX_S3_ENDPOINT, env),
|
|
121
|
+
forcePathStyle: getBooleanEnvVar(ENV_VARS.KUMIX_S3_FORCE_PATH_STYLE, false, env),
|
|
122
|
+
publicUrl: getEnvVar(ENV_VARS.KUMIX_S3_PUBLIC_URL, env)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Load Cloudinary configuration from environment variables
|
|
127
|
+
* @param env - Optional environment record
|
|
128
|
+
* @returns Cloudinary configuration object
|
|
129
|
+
* @throws Error if required environment variables are missing
|
|
130
|
+
* @public
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* import { loadCloudinaryConfig } from '@kumix/storage';
|
|
135
|
+
*
|
|
136
|
+
* // Set environment variables first:
|
|
137
|
+
* // KUMIX_CLOUDINARY_CLOUD_NAME=my-cloud
|
|
138
|
+
* // KUMIX_CLOUDINARY_API_KEY=123456789
|
|
139
|
+
* // KUMIX_CLOUDINARY_API_SECRET=secret...
|
|
140
|
+
* // KUMIX_CLOUDINARY_SECURE=true (optional)
|
|
141
|
+
* // KUMIX_CLOUDINARY_FOLDER=uploads (optional)
|
|
142
|
+
*
|
|
143
|
+
* try {
|
|
144
|
+
* const config = loadCloudinaryConfig();
|
|
145
|
+
* console.log(config);
|
|
146
|
+
* // Returns: {
|
|
147
|
+
* // provider: 'cloudinary',
|
|
148
|
+
* // cloudName: 'my-cloud',
|
|
149
|
+
* // apiKey: '123456789',
|
|
150
|
+
* // apiSecret: 'secret...',
|
|
151
|
+
* // secure: true,
|
|
152
|
+
* // folder: 'uploads'
|
|
153
|
+
* // }
|
|
154
|
+
* } catch (error) {
|
|
155
|
+
* console.error('Missing Cloudinary configuration:', error.message);
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
function loadCloudinaryConfig(env) {
|
|
160
|
+
return {
|
|
161
|
+
provider: "cloudinary",
|
|
162
|
+
cloudName: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env),
|
|
163
|
+
apiKey: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_KEY, env),
|
|
164
|
+
apiSecret: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_SECRET, env),
|
|
165
|
+
secure: getBooleanEnvVar(ENV_VARS.KUMIX_CLOUDINARY_SECURE, true, env),
|
|
166
|
+
folder: getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_FOLDER, env)
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Auto-detect and load storage configuration from environment variables
|
|
171
|
+
* Determines the provider based on available environment variables
|
|
172
|
+
* @param env - Optional environment record
|
|
173
|
+
* @returns Storage configuration object (S3 or Cloudinary)
|
|
174
|
+
* @throws Error if no valid configuration is found
|
|
175
|
+
* @public
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* import { loadStorageConfig } from '@kumix/storage';
|
|
180
|
+
*
|
|
181
|
+
* // Auto-detection based on available env vars
|
|
182
|
+
* try {
|
|
183
|
+
* const config = loadStorageConfig();
|
|
184
|
+
*
|
|
185
|
+
* if (config.provider === 'cloudinary') {
|
|
186
|
+
* console.log('Using Cloudinary:', config.cloudName);
|
|
187
|
+
* } else {
|
|
188
|
+
* console.log('Using S3:', config.provider, config.bucket);
|
|
189
|
+
* }
|
|
190
|
+
* } catch (error) {
|
|
191
|
+
* console.error('No storage configuration found:', error.message);
|
|
192
|
+
* }
|
|
193
|
+
*
|
|
194
|
+
* // Explicit provider (set KUMIX_S3_PROVIDER=cloudinary)
|
|
195
|
+
* // Will load Cloudinary config even if S3 vars are also present
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
function loadStorageConfig(env) {
|
|
199
|
+
const provider = getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);
|
|
200
|
+
if (provider) {
|
|
201
|
+
if (provider === "cloudinary") return loadCloudinaryConfig(env);
|
|
202
|
+
return loadS3Config(env);
|
|
203
|
+
}
|
|
204
|
+
const hasCloudinaryVars = getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env) && getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_KEY, env) && getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_SECRET, env);
|
|
205
|
+
const hasS3Vars = getEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env) && getEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env) && getEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env);
|
|
206
|
+
if (hasCloudinaryVars) return loadCloudinaryConfig(env);
|
|
207
|
+
if (hasS3Vars) {
|
|
208
|
+
const config = loadS3Config(env);
|
|
209
|
+
if (!config.provider) config.provider = "aws";
|
|
210
|
+
return config;
|
|
211
|
+
}
|
|
212
|
+
throw new Error("No storage configuration found. Please set either:\n- S3: KUMIX_S3_PROVIDER, KUMIX_S3_REGION, KUMIX_S3_BUCKET, KUMIX_S3_ACCESS_KEY_ID, KUMIX_S3_SECRET_ACCESS_KEY\n- Cloudinary: KUMIX_CLOUDINARY_CLOUD_NAME, KUMIX_CLOUDINARY_API_KEY, KUMIX_CLOUDINARY_API_SECRET");
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Check if storage configuration is available in environment variables
|
|
216
|
+
* @param env - Optional environment record
|
|
217
|
+
* @returns True if valid storage configuration is found
|
|
218
|
+
* @public
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* import { hasStorageConfig, loadStorageConfig } from '@kumix/storage';
|
|
223
|
+
*
|
|
224
|
+
* if (hasStorageConfig()) {
|
|
225
|
+
* const config = loadStorageConfig();
|
|
226
|
+
* console.log('Storage configured:', config.provider);
|
|
227
|
+
* } else {
|
|
228
|
+
* console.log('Please set storage environment variables');
|
|
229
|
+
* }
|
|
230
|
+
*
|
|
231
|
+
* // Use in conditional initialization
|
|
232
|
+
* const storage = hasStorageConfig() ? createStorage() : null;
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
function hasStorageConfig(env) {
|
|
236
|
+
try {
|
|
237
|
+
loadStorageConfig(env);
|
|
238
|
+
return true;
|
|
239
|
+
} catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Check if storage configuration is available in environment variables
|
|
245
|
+
* @param env - Optional environment record
|
|
246
|
+
* @returns True if either S3 or Cloudinary configuration is available
|
|
247
|
+
* @public
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* import { isStorageConfigured, createS3, createCloudinary } from '@kumix/storage';
|
|
252
|
+
*
|
|
253
|
+
* if (isStorageConfigured()) {
|
|
254
|
+
* // Choose your preferred provider
|
|
255
|
+
* const s3 = createS3();
|
|
256
|
+
* // OR
|
|
257
|
+
* const cloudinary = createCloudinary();
|
|
258
|
+
*
|
|
259
|
+
* await s3.uploadFile('test.txt', 'Hello World');
|
|
260
|
+
* } else {
|
|
261
|
+
* console.log('Storage not configured. Please set environment variables.');
|
|
262
|
+
* }
|
|
263
|
+
*
|
|
264
|
+
* // Use in conditional initialization
|
|
265
|
+
* const storage = isStorageConfigured() ? createS3() : null;
|
|
266
|
+
* if (storage) {
|
|
267
|
+
* await storage.uploadFile('file.txt', content);
|
|
268
|
+
* }
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
function isStorageConfigured(env) {
|
|
272
|
+
return hasStorageConfig(env);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get storage provider name from environment variables without loading full config
|
|
276
|
+
* @param env - Optional environment record
|
|
277
|
+
* @returns Provider name or undefined if not configured
|
|
278
|
+
* @public
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```typescript
|
|
282
|
+
* import { getStorageProvider } from '@kumix/storage';
|
|
283
|
+
*
|
|
284
|
+
* const provider = getStorageProvider();
|
|
285
|
+
*
|
|
286
|
+
* switch (provider) {
|
|
287
|
+
* case 'aws':
|
|
288
|
+
* console.log('Using AWS S3');
|
|
289
|
+
* break;
|
|
290
|
+
* case 'cloudinary':
|
|
291
|
+
* console.log('Using Cloudinary');
|
|
292
|
+
* break;
|
|
293
|
+
* case 'cloudflare-r2':
|
|
294
|
+
* console.log('Using Cloudflare R2');
|
|
295
|
+
* break;
|
|
296
|
+
* default:
|
|
297
|
+
* console.log('No storage provider configured');
|
|
298
|
+
* }
|
|
299
|
+
*
|
|
300
|
+
* // Quick check without throwing errors
|
|
301
|
+
* if (getStorageProvider()) {
|
|
302
|
+
* // Storage is configured
|
|
303
|
+
* }
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
function getStorageProvider(env) {
|
|
307
|
+
const provider = getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);
|
|
308
|
+
if (provider) return provider;
|
|
309
|
+
const hasCloudinaryVars = getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env);
|
|
310
|
+
const hasS3Vars = getEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env);
|
|
311
|
+
if (hasCloudinaryVars) return "cloudinary";
|
|
312
|
+
if (hasS3Vars) return "aws";
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Validate if all required environment variables are set for S3
|
|
316
|
+
* @param env - Optional environment record
|
|
317
|
+
* @returns Validation result with missing variables
|
|
318
|
+
* @public
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* import { validateS3EnvVars } from '@kumix/storage';
|
|
323
|
+
*
|
|
324
|
+
* const validation = validateS3EnvVars();
|
|
325
|
+
*
|
|
326
|
+
* if (validation.valid) {
|
|
327
|
+
* console.log('S3 configuration is complete');
|
|
328
|
+
* } else {
|
|
329
|
+
* console.log('Missing S3 variables:', validation.missing);
|
|
330
|
+
* // Returns: ["KUMIX_S3_REGION", "KUMIX_S3_BUCKET"]
|
|
331
|
+
* }
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
function validateS3EnvVars(env) {
|
|
335
|
+
const missing = [
|
|
336
|
+
ENV_VARS.KUMIX_S3_PROVIDER,
|
|
337
|
+
ENV_VARS.KUMIX_S3_REGION,
|
|
338
|
+
ENV_VARS.KUMIX_S3_BUCKET,
|
|
339
|
+
ENV_VARS.KUMIX_S3_ACCESS_KEY_ID,
|
|
340
|
+
ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY
|
|
341
|
+
].filter((key) => !getEnvVar(key, env));
|
|
342
|
+
return {
|
|
343
|
+
valid: missing.length === 0,
|
|
344
|
+
missing
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Validate if all required environment variables are set for Cloudinary
|
|
349
|
+
* @param env - Optional environment record
|
|
350
|
+
* @returns Validation result with missing variables
|
|
351
|
+
* @public
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```typescript
|
|
355
|
+
* import { validateCloudinaryEnvVars } from '@kumix/storage';
|
|
356
|
+
*
|
|
357
|
+
* const validation = validateCloudinaryEnvVars();
|
|
358
|
+
*
|
|
359
|
+
* if (validation.valid) {
|
|
360
|
+
* console.log('Cloudinary configuration is complete');
|
|
361
|
+
* } else {
|
|
362
|
+
* console.log('Missing Cloudinary variables:', validation.missing);
|
|
363
|
+
* // Returns: ["KUMIX_CLOUDINARY_API_KEY"]
|
|
364
|
+
* }
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
function validateCloudinaryEnvVars(env) {
|
|
368
|
+
const missing = [
|
|
369
|
+
ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME,
|
|
370
|
+
ENV_VARS.KUMIX_CLOUDINARY_API_KEY,
|
|
371
|
+
ENV_VARS.KUMIX_CLOUDINARY_API_SECRET
|
|
372
|
+
].filter((key) => !getEnvVar(key, env));
|
|
373
|
+
return {
|
|
374
|
+
valid: missing.length === 0,
|
|
375
|
+
missing
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get all available environment variables for debugging
|
|
380
|
+
* @param env - Optional environment record
|
|
381
|
+
* @returns Object containing all storage-related env vars
|
|
382
|
+
* @public
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```typescript
|
|
386
|
+
* import { getStorageEnvVars } from '@kumix/storage';
|
|
387
|
+
*
|
|
388
|
+
* const envVars = getStorageEnvVars();
|
|
389
|
+
* console.log('Storage environment variables:', envVars);
|
|
390
|
+
* // Returns: {
|
|
391
|
+
* // KUMIX_S3_PROVIDER: 'aws',
|
|
392
|
+
* // KUMIX_S3_REGION: 'us-east-1',
|
|
393
|
+
* // KUMIX_S3_BUCKET: 'my-bucket',
|
|
394
|
+
* // // ... other set variables (secrets are masked)
|
|
395
|
+
* // }
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
function getStorageEnvVars(env) {
|
|
399
|
+
const result = {};
|
|
400
|
+
Object.values(ENV_VARS).forEach((key) => {
|
|
401
|
+
const value = getEnvVar(key, env);
|
|
402
|
+
if (value) if (key.includes("SECRET") || key.includes("API_SECRET")) result[key] = `${value.substring(0, 4)}***`;
|
|
403
|
+
else result[key] = value;
|
|
404
|
+
});
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
//#endregion
|
|
408
|
+
export { isStorageConfigured as a, loadStorageConfig as c, hasStorageConfig as i, validateCloudinaryEnvVars as l, getStorageEnvVars as n, loadCloudinaryConfig as o, getStorageProvider as r, loadS3Config as s, ENV_VARS as t, validateS3EnvVars as u };
|
|
409
|
+
|
|
410
|
+
//# sourceMappingURL=config-t-NVJICl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-t-NVJICl.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Storage configuration from environment variables\n * Provides automatic configuration loading from environment variables\n */\n\nimport type { CloudinaryConfig, S3Config, StorageConfig } from \"./types\";\n\n/**\n * Cross-runtime environment record.\n * Pass `process.env` on Node.js, `ctx.env` on Cloudflare Workers,\n * `Deno.env.toObject()` on Deno, or a plain object in the browser.\n */\nexport type EnvRecord = Record<string, string | undefined>;\n\nfunction getDefaultEnv(): EnvRecord | undefined {\n if (typeof process !== \"undefined\" && process?.env) {\n return process.env as EnvRecord;\n }\n return undefined;\n}\n\nfunction resolveEnv(env?: EnvRecord): EnvRecord | undefined {\n return env ?? getDefaultEnv();\n}\n\n/**\n * Environment variable names for storage configuration\n * @public\n *\n * @example\n * ```typescript\n * import { ENV_VARS } from '@kumix/storage';\n *\n * // Check if specific env vars are set\n * console.log(process.env[ENV_VARS.KUMIX_S3_BUCKET]);\n * console.log(process.env[ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME]);\n * ```\n */\nexport const ENV_VARS = {\n // S3 Configuration\n KUMIX_S3_PROVIDER: \"KUMIX_S3_PROVIDER\",\n KUMIX_S3_REGION: \"KUMIX_S3_REGION\",\n KUMIX_S3_BUCKET: \"KUMIX_S3_BUCKET\",\n KUMIX_S3_ACCESS_KEY_ID: \"KUMIX_S3_ACCESS_KEY_ID\",\n KUMIX_S3_SECRET_ACCESS_KEY: \"KUMIX_S3_SECRET_ACCESS_KEY\",\n KUMIX_S3_ENDPOINT: \"KUMIX_S3_ENDPOINT\",\n KUMIX_S3_FORCE_PATH_STYLE: \"KUMIX_S3_FORCE_PATH_STYLE\",\n KUMIX_S3_PUBLIC_URL: \"KUMIX_S3_PUBLIC_URL\",\n\n // Cloudinary Configuration\n KUMIX_CLOUDINARY_CLOUD_NAME: \"KUMIX_CLOUDINARY_CLOUD_NAME\",\n KUMIX_CLOUDINARY_API_KEY: \"KUMIX_CLOUDINARY_API_KEY\",\n KUMIX_CLOUDINARY_API_SECRET: \"KUMIX_CLOUDINARY_API_SECRET\",\n KUMIX_CLOUDINARY_SECURE: \"KUMIX_CLOUDINARY_SECURE\",\n KUMIX_CLOUDINARY_FOLDER: \"KUMIX_CLOUDINARY_FOLDER\",\n} as const;\n\n/**\n * Get environment variable value\n * @param key - Environment variable name\n * @param env - Optional environment record\n * @internal\n */\nfunction getEnvVar(key: string, env?: EnvRecord): string | undefined {\n const e = resolveEnv(env);\n if (!e) return undefined;\n return e[key];\n}\n\n/**\n * Get required environment variable value\n * @param key - Environment variable name\n * @param env - Optional environment record\n * @internal\n */\nfunction getRequiredEnvVar(key: string, env?: EnvRecord): string {\n const value = getEnvVar(key, env);\n if (!value) {\n throw new Error(`Missing required environment variable: ${key}`);\n }\n return value;\n}\n\n/**\n * Get boolean environment variable value\n * @param key - Environment variable name\n * @param defaultValue - Default boolean value if not set\n * @param env - Optional environment record\n * @internal\n */\nfunction getBooleanEnvVar(key: string, defaultValue = false, env?: EnvRecord): boolean {\n const value = getEnvVar(key, env);\n if (!value) return defaultValue;\n return value.toLowerCase() === \"true\" || value === \"1\";\n}\n\n/**\n * Load S3 configuration from environment variables\n * @param env - Optional environment record\n * @returns S3 configuration object\n * @throws Error if required environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { loadS3Config } from '@kumix/storage';\n *\n * // Set environment variables first:\n * // KUMIX_S3_PROVIDER=aws\n * // KUMIX_S3_REGION=us-east-1\n * // KUMIX_S3_BUCKET=my-bucket\n * // KUMIX_S3_ACCESS_KEY_ID=AKIA...\n * // KUMIX_S3_SECRET_ACCESS_KEY=secret...\n *\n * try {\n * const config = loadS3Config();\n * console.log(config);\n * // Returns: {\n * // provider: 'aws',\n * // region: 'us-east-1',\n * // bucket: 'my-bucket',\n * // accessKeyId: 'AKIA...',\n * // secretAccessKey: 'secret...'\n * // }\n * } catch (error) {\n * console.error('Missing S3 configuration:', error.message);\n * }\n * ```\n */\nexport function loadS3Config(env?: EnvRecord): S3Config {\n const provider = getRequiredEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env) as S3Config[\"provider\"];\n\n // Validate provider\n const validProviders: S3Config[\"provider\"][] = [\n \"aws\",\n \"cloudflare-r2\",\n \"minio\",\n \"digitalocean\",\n \"supabase\",\n \"custom\",\n ];\n if (!validProviders.includes(provider)) {\n throw new Error(\n `Invalid S3 provider: ${provider}. Must be one of: ${validProviders.join(\", \")}`,\n );\n }\n\n return {\n provider,\n region: getRequiredEnvVar(ENV_VARS.KUMIX_S3_REGION, env),\n bucket: getRequiredEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env),\n accessKeyId: getRequiredEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env),\n secretAccessKey: getRequiredEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env),\n endpoint: getEnvVar(ENV_VARS.KUMIX_S3_ENDPOINT, env),\n forcePathStyle: getBooleanEnvVar(ENV_VARS.KUMIX_S3_FORCE_PATH_STYLE, false, env),\n publicUrl: getEnvVar(ENV_VARS.KUMIX_S3_PUBLIC_URL, env),\n };\n}\n\n/**\n * Load Cloudinary configuration from environment variables\n * @param env - Optional environment record\n * @returns Cloudinary configuration object\n * @throws Error if required environment variables are missing\n * @public\n *\n * @example\n * ```typescript\n * import { loadCloudinaryConfig } from '@kumix/storage';\n *\n * // Set environment variables first:\n * // KUMIX_CLOUDINARY_CLOUD_NAME=my-cloud\n * // KUMIX_CLOUDINARY_API_KEY=123456789\n * // KUMIX_CLOUDINARY_API_SECRET=secret...\n * // KUMIX_CLOUDINARY_SECURE=true (optional)\n * // KUMIX_CLOUDINARY_FOLDER=uploads (optional)\n *\n * try {\n * const config = loadCloudinaryConfig();\n * console.log(config);\n * // Returns: {\n * // provider: 'cloudinary',\n * // cloudName: 'my-cloud',\n * // apiKey: '123456789',\n * // apiSecret: 'secret...',\n * // secure: true,\n * // folder: 'uploads'\n * // }\n * } catch (error) {\n * console.error('Missing Cloudinary configuration:', error.message);\n * }\n * ```\n */\nexport function loadCloudinaryConfig(env?: EnvRecord): CloudinaryConfig {\n return {\n provider: \"cloudinary\",\n cloudName: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env),\n apiKey: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_KEY, env),\n apiSecret: getRequiredEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_SECRET, env),\n secure: getBooleanEnvVar(ENV_VARS.KUMIX_CLOUDINARY_SECURE, true, env),\n folder: getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_FOLDER, env),\n };\n}\n\n/**\n * Auto-detect and load storage configuration from environment variables\n * Determines the provider based on available environment variables\n * @param env - Optional environment record\n * @returns Storage configuration object (S3 or Cloudinary)\n * @throws Error if no valid configuration is found\n * @public\n *\n * @example\n * ```typescript\n * import { loadStorageConfig } from '@kumix/storage';\n *\n * // Auto-detection based on available env vars\n * try {\n * const config = loadStorageConfig();\n *\n * if (config.provider === 'cloudinary') {\n * console.log('Using Cloudinary:', config.cloudName);\n * } else {\n * console.log('Using S3:', config.provider, config.bucket);\n * }\n * } catch (error) {\n * console.error('No storage configuration found:', error.message);\n * }\n *\n * // Explicit provider (set KUMIX_S3_PROVIDER=cloudinary)\n * // Will load Cloudinary config even if S3 vars are also present\n * ```\n */\nexport function loadStorageConfig(env?: EnvRecord): StorageConfig {\n const provider = getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);\n\n // If provider is explicitly set, use it\n if (provider) {\n if (provider === \"cloudinary\") {\n return loadCloudinaryConfig(env);\n }\n return loadS3Config(env);\n }\n\n // Auto-detect based on available environment variables\n const hasCloudinaryVars =\n getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env) &&\n getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_KEY, env) &&\n getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_API_SECRET, env);\n\n const hasS3Vars =\n getEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env) &&\n getEnvVar(ENV_VARS.KUMIX_S3_ACCESS_KEY_ID, env) &&\n getEnvVar(ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY, env);\n\n if (hasCloudinaryVars) {\n return loadCloudinaryConfig(env);\n }\n if (hasS3Vars) {\n // Default to AWS if no provider specified\n const config = loadS3Config(env);\n if (!config.provider) {\n config.provider = \"aws\";\n }\n return config;\n }\n\n throw new Error(\n \"No storage configuration found. Please set either:\\n\" +\n \"- S3: KUMIX_S3_PROVIDER, KUMIX_S3_REGION, KUMIX_S3_BUCKET, KUMIX_S3_ACCESS_KEY_ID, KUMIX_S3_SECRET_ACCESS_KEY\\n\" +\n \"- Cloudinary: KUMIX_CLOUDINARY_CLOUD_NAME, KUMIX_CLOUDINARY_API_KEY, KUMIX_CLOUDINARY_API_SECRET\",\n );\n}\n\n/**\n * Check if storage configuration is available in environment variables\n * @param env - Optional environment record\n * @returns True if valid storage configuration is found\n * @public\n *\n * @example\n * ```typescript\n * import { hasStorageConfig, loadStorageConfig } from '@kumix/storage';\n *\n * if (hasStorageConfig()) {\n * const config = loadStorageConfig();\n * console.log('Storage configured:', config.provider);\n * } else {\n * console.log('Please set storage environment variables');\n * }\n *\n * // Use in conditional initialization\n * const storage = hasStorageConfig() ? createStorage() : null;\n * ```\n */\nexport function hasStorageConfig(env?: EnvRecord): boolean {\n try {\n loadStorageConfig(env);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if storage configuration is available in environment variables\n * @param env - Optional environment record\n * @returns True if either S3 or Cloudinary configuration is available\n * @public\n *\n * @example\n * ```typescript\n * import { isStorageConfigured, createS3, createCloudinary } from '@kumix/storage';\n *\n * if (isStorageConfigured()) {\n * // Choose your preferred provider\n * const s3 = createS3();\n * // OR\n * const cloudinary = createCloudinary();\n *\n * await s3.uploadFile('test.txt', 'Hello World');\n * } else {\n * console.log('Storage not configured. Please set environment variables.');\n * }\n *\n * // Use in conditional initialization\n * const storage = isStorageConfigured() ? createS3() : null;\n * if (storage) {\n * await storage.uploadFile('file.txt', content);\n * }\n * ```\n */\nexport function isStorageConfigured(env?: EnvRecord): boolean {\n return hasStorageConfig(env);\n}\n\n/**\n * Get storage provider name from environment variables without loading full config\n * @param env - Optional environment record\n * @returns Provider name or undefined if not configured\n * @public\n *\n * @example\n * ```typescript\n * import { getStorageProvider } from '@kumix/storage';\n *\n * const provider = getStorageProvider();\n *\n * switch (provider) {\n * case 'aws':\n * console.log('Using AWS S3');\n * break;\n * case 'cloudinary':\n * console.log('Using Cloudinary');\n * break;\n * case 'cloudflare-r2':\n * console.log('Using Cloudflare R2');\n * break;\n * default:\n * console.log('No storage provider configured');\n * }\n *\n * // Quick check without throwing errors\n * if (getStorageProvider()) {\n * // Storage is configured\n * }\n * ```\n */\nexport function getStorageProvider(env?: EnvRecord): string | undefined {\n const provider = getEnvVar(ENV_VARS.KUMIX_S3_PROVIDER, env);\n if (provider) return provider;\n\n // Auto-detect\n const hasCloudinaryVars = getEnvVar(ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME, env);\n const hasS3Vars = getEnvVar(ENV_VARS.KUMIX_S3_BUCKET, env);\n\n if (hasCloudinaryVars) return \"cloudinary\";\n if (hasS3Vars) return \"aws\"; // Default S3 provider\n\n return undefined;\n}\n\n/**\n * Validate if all required environment variables are set for S3\n * @param env - Optional environment record\n * @returns Validation result with missing variables\n * @public\n *\n * @example\n * ```typescript\n * import { validateS3EnvVars } from '@kumix/storage';\n *\n * const validation = validateS3EnvVars();\n *\n * if (validation.valid) {\n * console.log('S3 configuration is complete');\n * } else {\n * console.log('Missing S3 variables:', validation.missing);\n * // Returns: [\"KUMIX_S3_REGION\", \"KUMIX_S3_BUCKET\"]\n * }\n * ```\n */\nexport function validateS3EnvVars(env?: EnvRecord): {\n valid: boolean;\n missing: string[];\n} {\n const required = [\n ENV_VARS.KUMIX_S3_PROVIDER,\n ENV_VARS.KUMIX_S3_REGION,\n ENV_VARS.KUMIX_S3_BUCKET,\n ENV_VARS.KUMIX_S3_ACCESS_KEY_ID,\n ENV_VARS.KUMIX_S3_SECRET_ACCESS_KEY,\n ];\n\n const missing = required.filter((key) => !getEnvVar(key, env));\n\n return {\n valid: missing.length === 0,\n missing,\n };\n}\n\n/**\n * Validate if all required environment variables are set for Cloudinary\n * @param env - Optional environment record\n * @returns Validation result with missing variables\n * @public\n *\n * @example\n * ```typescript\n * import { validateCloudinaryEnvVars } from '@kumix/storage';\n *\n * const validation = validateCloudinaryEnvVars();\n *\n * if (validation.valid) {\n * console.log('Cloudinary configuration is complete');\n * } else {\n * console.log('Missing Cloudinary variables:', validation.missing);\n * // Returns: [\"KUMIX_CLOUDINARY_API_KEY\"]\n * }\n * ```\n */\nexport function validateCloudinaryEnvVars(env?: EnvRecord): {\n valid: boolean;\n missing: string[];\n} {\n const required = [\n ENV_VARS.KUMIX_CLOUDINARY_CLOUD_NAME,\n ENV_VARS.KUMIX_CLOUDINARY_API_KEY,\n ENV_VARS.KUMIX_CLOUDINARY_API_SECRET,\n ];\n\n const missing = required.filter((key) => !getEnvVar(key, env));\n\n return {\n valid: missing.length === 0,\n missing,\n };\n}\n\n/**\n * Get all available environment variables for debugging\n * @param env - Optional environment record\n * @returns Object containing all storage-related env vars\n * @public\n *\n * @example\n * ```typescript\n * import { getStorageEnvVars } from '@kumix/storage';\n *\n * const envVars = getStorageEnvVars();\n * console.log('Storage environment variables:', envVars);\n * // Returns: {\n * // KUMIX_S3_PROVIDER: 'aws',\n * // KUMIX_S3_REGION: 'us-east-1',\n * // KUMIX_S3_BUCKET: 'my-bucket',\n * // // ... other set variables (secrets are masked)\n * // }\n * ```\n */\nexport function getStorageEnvVars(env?: EnvRecord): Record<string, string | undefined> {\n const result: Record<string, string | undefined> = {};\n\n Object.values(ENV_VARS).forEach((key) => {\n const value = getEnvVar(key, env);\n if (value) {\n // Mask sensitive values\n if (key.includes(\"SECRET\") || key.includes(\"API_SECRET\")) {\n result[key] = `${value.substring(0, 4)}***`;\n } else {\n result[key] = value;\n }\n }\n });\n\n return result;\n}\n"],"mappings":";AAcA,SAAS,gBAAuC;CAC9C,IAAI,OAAO,YAAY,eAAe,SAAS,KAC7C,OAAO,QAAQ;AAGnB;AAEA,SAAS,WAAW,KAAwC;CAC1D,OAAO,OAAO,cAAc;AAC9B;;;;;;;;;;;;;;AAeA,MAAa,WAAW;CAEtB,mBAAmB;CACnB,iBAAiB;CACjB,iBAAiB;CACjB,wBAAwB;CACxB,4BAA4B;CAC5B,mBAAmB;CACnB,2BAA2B;CAC3B,qBAAqB;CAGrB,6BAA6B;CAC7B,0BAA0B;CAC1B,6BAA6B;CAC7B,yBAAyB;CACzB,yBAAyB;AAC3B;;;;;;;AAQA,SAAS,UAAU,KAAa,KAAqC;CACnE,MAAM,IAAI,WAAW,GAAG;CACxB,IAAI,CAAC,GAAG,OAAO,KAAA;CACf,OAAO,EAAE;AACX;;;;;;;AAQA,SAAS,kBAAkB,KAAa,KAAyB;CAC/D,MAAM,QAAQ,UAAU,KAAK,GAAG;CAChC,IAAI,CAAC,OACH,MAAM,IAAI,MAAM,0CAA0C,KAAK;CAEjE,OAAO;AACT;;;;;;;;AASA,SAAS,iBAAiB,KAAa,eAAe,OAAO,KAA0B;CACrF,MAAM,QAAQ,UAAU,KAAK,GAAG;CAChC,IAAI,CAAC,OAAO,OAAO;CACnB,OAAO,MAAM,YAAY,MAAM,UAAU,UAAU;AACrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,aAAa,KAA2B;CACtD,MAAM,WAAW,kBAAkB,SAAS,mBAAmB,GAAG;CAGlE,MAAM,iBAAyC;EAC7C;EACA;EACA;EACA;EACA;EACA;CACF;CACA,IAAI,CAAC,eAAe,SAAS,QAAQ,GACnC,MAAM,IAAI,MACR,wBAAwB,SAAS,oBAAoB,eAAe,KAAK,IAAI,GAC/E;CAGF,OAAO;EACL;EACA,QAAQ,kBAAkB,SAAS,iBAAiB,GAAG;EACvD,QAAQ,kBAAkB,SAAS,iBAAiB,GAAG;EACvD,aAAa,kBAAkB,SAAS,wBAAwB,GAAG;EACnE,iBAAiB,kBAAkB,SAAS,4BAA4B,GAAG;EAC3E,UAAU,UAAU,SAAS,mBAAmB,GAAG;EACnD,gBAAgB,iBAAiB,SAAS,2BAA2B,OAAO,GAAG;EAC/E,WAAW,UAAU,SAAS,qBAAqB,GAAG;CACxD;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,qBAAqB,KAAmC;CACtE,OAAO;EACL,UAAU;EACV,WAAW,kBAAkB,SAAS,6BAA6B,GAAG;EACtE,QAAQ,kBAAkB,SAAS,0BAA0B,GAAG;EAChE,WAAW,kBAAkB,SAAS,6BAA6B,GAAG;EACtE,QAAQ,iBAAiB,SAAS,yBAAyB,MAAM,GAAG;EACpE,QAAQ,UAAU,SAAS,yBAAyB,GAAG;CACzD;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,kBAAkB,KAAgC;CAChE,MAAM,WAAW,UAAU,SAAS,mBAAmB,GAAG;CAG1D,IAAI,UAAU;EACZ,IAAI,aAAa,cACf,OAAO,qBAAqB,GAAG;EAEjC,OAAO,aAAa,GAAG;CACzB;CAGA,MAAM,oBACJ,UAAU,SAAS,6BAA6B,GAAG,KACnD,UAAU,SAAS,0BAA0B,GAAG,KAChD,UAAU,SAAS,6BAA6B,GAAG;CAErD,MAAM,YACJ,UAAU,SAAS,iBAAiB,GAAG,KACvC,UAAU,SAAS,wBAAwB,GAAG,KAC9C,UAAU,SAAS,4BAA4B,GAAG;CAEpD,IAAI,mBACF,OAAO,qBAAqB,GAAG;CAEjC,IAAI,WAAW;EAEb,MAAM,SAAS,aAAa,GAAG;EAC/B,IAAI,CAAC,OAAO,UACV,OAAO,WAAW;EAEpB,OAAO;CACT;CAEA,MAAM,IAAI,MACR,qQAGF;AACF;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,iBAAiB,KAA0B;CACzD,IAAI;EACF,kBAAkB,GAAG;EACrB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,oBAAoB,KAA0B;CAC5D,OAAO,iBAAiB,GAAG;AAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,mBAAmB,KAAqC;CACtE,MAAM,WAAW,UAAU,SAAS,mBAAmB,GAAG;CAC1D,IAAI,UAAU,OAAO;CAGrB,MAAM,oBAAoB,UAAU,SAAS,6BAA6B,GAAG;CAC7E,MAAM,YAAY,UAAU,SAAS,iBAAiB,GAAG;CAEzD,IAAI,mBAAmB,OAAO;CAC9B,IAAI,WAAW,OAAO;AAGxB;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,kBAAkB,KAGhC;CASA,MAAM,UAAU;EAPd,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;EACT,SAAS;CAGY,CAAC,CAAC,QAAQ,QAAQ,CAAC,UAAU,KAAK,GAAG,CAAC;CAE7D,OAAO;EACL,OAAO,QAAQ,WAAW;EAC1B;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,0BAA0B,KAGxC;CAOA,MAAM,UAAU;EALd,SAAS;EACT,SAAS;EACT,SAAS;CAGY,CAAC,CAAC,QAAQ,QAAQ,CAAC,UAAU,KAAK,GAAG,CAAC;CAE7D,OAAO;EACL,OAAO,QAAQ,WAAW;EAC1B;CACF;AACF;;;;;;;;;;;;;;;;;;;;;AAsBA,SAAgB,kBAAkB,KAAqD;CACrF,MAAM,SAA6C,CAAC;CAEpD,OAAO,OAAO,QAAQ,CAAC,CAAC,SAAS,QAAQ;EACvC,MAAM,QAAQ,UAAU,KAAK,GAAG;EAChC,IAAI,OAEF,IAAI,IAAI,SAAS,QAAQ,KAAK,IAAI,SAAS,YAAY,GACrD,OAAO,OAAO,GAAG,MAAM,UAAU,GAAG,CAAC,EAAE;OAEvC,OAAO,OAAO;CAGpB,CAAC;CAED,OAAO;AACT"}
|