@bloomneo/appkit 1.2.9
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/LICENSE +21 -0
- package/README.md +902 -0
- package/bin/appkit.js +71 -0
- package/bin/commands/generate.js +1050 -0
- package/bin/templates/backend/README.md.template +39 -0
- package/bin/templates/backend/api.http.template +0 -0
- package/bin/templates/backend/docs/APPKIT_CLI.md +507 -0
- package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +61 -0
- package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +2539 -0
- package/bin/templates/backend/package.json.template +34 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.http.template +29 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.route.ts.template +36 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.service.ts.template +88 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.types.ts.template +18 -0
- package/bin/templates/backend/src/api/lib/api-router.ts.template +84 -0
- package/bin/templates/backend/src/api/server.ts.template +188 -0
- package/bin/templates/backend/tsconfig.api.json.template +24 -0
- package/bin/templates/backend/tsconfig.json.template +40 -0
- package/bin/templates/feature/feature.http.template +63 -0
- package/bin/templates/feature/feature.route.ts.template +36 -0
- package/bin/templates/feature/feature.service.ts.template +81 -0
- package/bin/templates/feature/feature.types.ts.template +23 -0
- package/bin/templates/feature-db/feature.http.template +63 -0
- package/bin/templates/feature-db/feature.model.ts.template +74 -0
- package/bin/templates/feature-db/feature.route.ts.template +58 -0
- package/bin/templates/feature-db/feature.service.ts.template +231 -0
- package/bin/templates/feature-db/feature.types.ts.template +25 -0
- package/bin/templates/feature-db/schema-addition.prisma.template +9 -0
- package/bin/templates/feature-db/seeding/README.md.template +57 -0
- package/bin/templates/feature-db/seeding/feature.seed.js.template +67 -0
- package/bin/templates/feature-user/schema-addition.prisma.template +19 -0
- package/bin/templates/feature-user/user.http.template +157 -0
- package/bin/templates/feature-user/user.model.ts.template +244 -0
- package/bin/templates/feature-user/user.route.ts.template +379 -0
- package/bin/templates/feature-user/user.seed.js.template +182 -0
- package/bin/templates/feature-user/user.service.ts.template +426 -0
- package/bin/templates/feature-user/user.types.ts.template +127 -0
- package/dist/auth/auth.d.ts +182 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +477 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/defaults.d.ts +104 -0
- package/dist/auth/defaults.d.ts.map +1 -0
- package/dist/auth/defaults.js +374 -0
- package/dist/auth/defaults.js.map +1 -0
- package/dist/auth/index.d.ts +70 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +94 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cache/cache.d.ts +118 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +249 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/defaults.d.ts +63 -0
- package/dist/cache/defaults.d.ts.map +1 -0
- package/dist/cache/defaults.js +193 -0
- package/dist/cache/defaults.js.map +1 -0
- package/dist/cache/index.d.ts +101 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +203 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/strategies/memory.d.ts +138 -0
- package/dist/cache/strategies/memory.d.ts.map +1 -0
- package/dist/cache/strategies/memory.js +348 -0
- package/dist/cache/strategies/memory.js.map +1 -0
- package/dist/cache/strategies/redis.d.ts +105 -0
- package/dist/cache/strategies/redis.d.ts.map +1 -0
- package/dist/cache/strategies/redis.js +318 -0
- package/dist/cache/strategies/redis.js.map +1 -0
- package/dist/config/config.d.ts +62 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +107 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/defaults.d.ts +44 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +217 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +163 -0
- package/dist/config/index.js.map +1 -0
- package/dist/database/adapters/mongoose.d.ts +106 -0
- package/dist/database/adapters/mongoose.d.ts.map +1 -0
- package/dist/database/adapters/mongoose.js +480 -0
- package/dist/database/adapters/mongoose.js.map +1 -0
- package/dist/database/adapters/prisma.d.ts +106 -0
- package/dist/database/adapters/prisma.d.ts.map +1 -0
- package/dist/database/adapters/prisma.js +494 -0
- package/dist/database/adapters/prisma.js.map +1 -0
- package/dist/database/defaults.d.ts +87 -0
- package/dist/database/defaults.d.ts.map +1 -0
- package/dist/database/defaults.js +271 -0
- package/dist/database/defaults.js.map +1 -0
- package/dist/database/index.d.ts +137 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +490 -0
- package/dist/database/index.js.map +1 -0
- package/dist/email/defaults.d.ts +100 -0
- package/dist/email/defaults.d.ts.map +1 -0
- package/dist/email/defaults.js +400 -0
- package/dist/email/defaults.js.map +1 -0
- package/dist/email/email.d.ts +139 -0
- package/dist/email/email.d.ts.map +1 -0
- package/dist/email/email.js +316 -0
- package/dist/email/email.js.map +1 -0
- package/dist/email/index.d.ts +176 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +251 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/strategies/console.d.ts +90 -0
- package/dist/email/strategies/console.d.ts.map +1 -0
- package/dist/email/strategies/console.js +268 -0
- package/dist/email/strategies/console.js.map +1 -0
- package/dist/email/strategies/resend.d.ts +84 -0
- package/dist/email/strategies/resend.d.ts.map +1 -0
- package/dist/email/strategies/resend.js +266 -0
- package/dist/email/strategies/resend.js.map +1 -0
- package/dist/email/strategies/smtp.d.ts +77 -0
- package/dist/email/strategies/smtp.d.ts.map +1 -0
- package/dist/email/strategies/smtp.js +286 -0
- package/dist/email/strategies/smtp.js.map +1 -0
- package/dist/error/defaults.d.ts +40 -0
- package/dist/error/defaults.d.ts.map +1 -0
- package/dist/error/defaults.js +75 -0
- package/dist/error/defaults.js.map +1 -0
- package/dist/error/error.d.ts +140 -0
- package/dist/error/error.d.ts.map +1 -0
- package/dist/error/error.js +200 -0
- package/dist/error/error.js.map +1 -0
- package/dist/error/index.d.ts +145 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +145 -0
- package/dist/error/index.js.map +1 -0
- package/dist/event/defaults.d.ts +111 -0
- package/dist/event/defaults.d.ts.map +1 -0
- package/dist/event/defaults.js +378 -0
- package/dist/event/defaults.js.map +1 -0
- package/dist/event/event.d.ts +171 -0
- package/dist/event/event.d.ts.map +1 -0
- package/dist/event/event.js +391 -0
- package/dist/event/event.js.map +1 -0
- package/dist/event/index.d.ts +173 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/event/index.js +302 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/strategies/memory.d.ts +122 -0
- package/dist/event/strategies/memory.d.ts.map +1 -0
- package/dist/event/strategies/memory.js +331 -0
- package/dist/event/strategies/memory.js.map +1 -0
- package/dist/event/strategies/redis.d.ts +115 -0
- package/dist/event/strategies/redis.d.ts.map +1 -0
- package/dist/event/strategies/redis.js +434 -0
- package/dist/event/strategies/redis.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/defaults.d.ts +67 -0
- package/dist/logger/defaults.d.ts.map +1 -0
- package/dist/logger/defaults.js +213 -0
- package/dist/logger/defaults.js.map +1 -0
- package/dist/logger/index.d.ts +84 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +101 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/logger.d.ts +165 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +843 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/logger/transports/console.d.ts +102 -0
- package/dist/logger/transports/console.d.ts.map +1 -0
- package/dist/logger/transports/console.js +276 -0
- package/dist/logger/transports/console.js.map +1 -0
- package/dist/logger/transports/database.d.ts +153 -0
- package/dist/logger/transports/database.d.ts.map +1 -0
- package/dist/logger/transports/database.js +539 -0
- package/dist/logger/transports/database.js.map +1 -0
- package/dist/logger/transports/file.d.ts +146 -0
- package/dist/logger/transports/file.d.ts.map +1 -0
- package/dist/logger/transports/file.js +464 -0
- package/dist/logger/transports/file.js.map +1 -0
- package/dist/logger/transports/http.d.ts +128 -0
- package/dist/logger/transports/http.d.ts.map +1 -0
- package/dist/logger/transports/http.js +401 -0
- package/dist/logger/transports/http.js.map +1 -0
- package/dist/logger/transports/webhook.d.ts +152 -0
- package/dist/logger/transports/webhook.d.ts.map +1 -0
- package/dist/logger/transports/webhook.js +485 -0
- package/dist/logger/transports/webhook.js.map +1 -0
- package/dist/queue/defaults.d.ts +66 -0
- package/dist/queue/defaults.d.ts.map +1 -0
- package/dist/queue/defaults.js +205 -0
- package/dist/queue/defaults.js.map +1 -0
- package/dist/queue/index.d.ts +124 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.js +116 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/queue.d.ts +156 -0
- package/dist/queue/queue.d.ts.map +1 -0
- package/dist/queue/queue.js +387 -0
- package/dist/queue/queue.js.map +1 -0
- package/dist/queue/transports/database.d.ts +165 -0
- package/dist/queue/transports/database.d.ts.map +1 -0
- package/dist/queue/transports/database.js +595 -0
- package/dist/queue/transports/database.js.map +1 -0
- package/dist/queue/transports/memory.d.ts +143 -0
- package/dist/queue/transports/memory.d.ts.map +1 -0
- package/dist/queue/transports/memory.js +415 -0
- package/dist/queue/transports/memory.js.map +1 -0
- package/dist/queue/transports/redis.d.ts +203 -0
- package/dist/queue/transports/redis.d.ts.map +1 -0
- package/dist/queue/transports/redis.js +744 -0
- package/dist/queue/transports/redis.js.map +1 -0
- package/dist/security/defaults.d.ts +64 -0
- package/dist/security/defaults.d.ts.map +1 -0
- package/dist/security/defaults.js +159 -0
- package/dist/security/defaults.js.map +1 -0
- package/dist/security/index.d.ts +110 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +160 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/security.d.ts +138 -0
- package/dist/security/security.d.ts.map +1 -0
- package/dist/security/security.js +419 -0
- package/dist/security/security.js.map +1 -0
- package/dist/storage/defaults.d.ts +79 -0
- package/dist/storage/defaults.d.ts.map +1 -0
- package/dist/storage/defaults.js +358 -0
- package/dist/storage/defaults.js.map +1 -0
- package/dist/storage/index.d.ts +153 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +242 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +151 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +439 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/storage/strategies/local.d.ts +117 -0
- package/dist/storage/strategies/local.d.ts.map +1 -0
- package/dist/storage/strategies/local.js +368 -0
- package/dist/storage/strategies/local.js.map +1 -0
- package/dist/storage/strategies/r2.d.ts +130 -0
- package/dist/storage/strategies/r2.d.ts.map +1 -0
- package/dist/storage/strategies/r2.js +470 -0
- package/dist/storage/strategies/r2.js.map +1 -0
- package/dist/storage/strategies/s3.d.ts +121 -0
- package/dist/storage/strategies/s3.d.ts.map +1 -0
- package/dist/storage/strategies/s3.js +461 -0
- package/dist/storage/strategies/s3.js.map +1 -0
- package/dist/util/defaults.d.ts +77 -0
- package/dist/util/defaults.d.ts.map +1 -0
- package/dist/util/defaults.js +193 -0
- package/dist/util/defaults.js.map +1 -0
- package/dist/util/index.d.ts +97 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +165 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/util.d.ts +145 -0
- package/dist/util/util.d.ts.map +1 -0
- package/dist/util/util.js +481 -0
- package/dist/util/util.js.map +1 -0
- package/package.json +234 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare R2 storage strategy with zero egress fees and automatic CDN integration
|
|
3
|
+
* @module @bloomneo/appkit/storage
|
|
4
|
+
* @file src/storage/strategies/r2.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: App has CLOUDFLARE_R2_BUCKET env var for cost-effective distributed storage
|
|
7
|
+
* @llm-rule AVOID: Manual R2 setup - this handles connection, CDN integration, and S3-compatible API
|
|
8
|
+
* @llm-rule NOTE: Production-ready with zero egress fees, automatic CDN, S3-compatible operations
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Cloudflare R2 storage strategy with cost optimization and CDN integration
|
|
12
|
+
*/
|
|
13
|
+
export class R2Strategy {
|
|
14
|
+
config;
|
|
15
|
+
r2Client = null;
|
|
16
|
+
connected = false;
|
|
17
|
+
bucket;
|
|
18
|
+
accountId;
|
|
19
|
+
cdnUrl;
|
|
20
|
+
endpoint;
|
|
21
|
+
/**
|
|
22
|
+
* Creates R2 strategy with direct environment access (like auth pattern)
|
|
23
|
+
* @llm-rule WHEN: Storage initialization with Cloudflare R2 environment variables detected
|
|
24
|
+
* @llm-rule AVOID: Manual R2 configuration - environment detection handles Cloudflare specifics
|
|
25
|
+
*/
|
|
26
|
+
constructor(config) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
if (!config.r2) {
|
|
29
|
+
throw new Error('R2 storage configuration missing');
|
|
30
|
+
}
|
|
31
|
+
this.bucket = config.r2.bucket;
|
|
32
|
+
this.accountId = config.r2.accountId;
|
|
33
|
+
this.cdnUrl = config.r2.cdnUrl;
|
|
34
|
+
// Build R2 endpoint URL
|
|
35
|
+
this.endpoint = `https://${this.accountId}.r2.cloudflarestorage.com`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Connects to Cloudflare R2 with automatic account validation
|
|
39
|
+
* @llm-rule WHEN: Storage initialization or reconnection after failure
|
|
40
|
+
* @llm-rule AVOID: Manual R2 client setup - this handles Cloudflare-specific configuration
|
|
41
|
+
*/
|
|
42
|
+
async connect() {
|
|
43
|
+
if (this.connected)
|
|
44
|
+
return;
|
|
45
|
+
try {
|
|
46
|
+
// Dynamic import for S3 client (R2 is S3-compatible)
|
|
47
|
+
const { S3Client } = await import('@aws-sdk/client-s3');
|
|
48
|
+
const r2Config = this.config.r2;
|
|
49
|
+
// Build R2 client configuration (S3-compatible)
|
|
50
|
+
const clientConfig = {
|
|
51
|
+
region: 'auto', // R2 uses 'auto' region
|
|
52
|
+
endpoint: this.endpoint,
|
|
53
|
+
credentials: {
|
|
54
|
+
accessKeyId: r2Config.accessKeyId,
|
|
55
|
+
secretAccessKey: r2Config.secretAccessKey,
|
|
56
|
+
},
|
|
57
|
+
forcePathStyle: false, // R2 uses virtual-hosted-style
|
|
58
|
+
maxAttempts: 3, // Built-in retry logic
|
|
59
|
+
};
|
|
60
|
+
// Create R2 client using S3 SDK
|
|
61
|
+
this.r2Client = new S3Client(clientConfig);
|
|
62
|
+
// Test connection by checking if bucket exists
|
|
63
|
+
await this.testConnection();
|
|
64
|
+
this.connected = true;
|
|
65
|
+
if (this.config.environment.isDevelopment) {
|
|
66
|
+
console.log(`✅ [AppKit] R2 storage connected (account: ${this.accountId}, bucket: ${this.bucket})`);
|
|
67
|
+
if (this.cdnUrl) {
|
|
68
|
+
console.log(`🚀 [AppKit] R2 CDN enabled: ${this.cdnUrl}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
this.connected = false;
|
|
74
|
+
this.r2Client = null;
|
|
75
|
+
throw new Error(`R2 storage connection failed: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Tests R2 connection by checking bucket access
|
|
80
|
+
*/
|
|
81
|
+
async testConnection() {
|
|
82
|
+
const { HeadBucketCommand } = await import('@aws-sdk/client-s3');
|
|
83
|
+
try {
|
|
84
|
+
await this.r2Client.send(new HeadBucketCommand({ Bucket: this.bucket }));
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (error.name === 'NotFound') {
|
|
88
|
+
throw new Error(`R2 bucket not found: ${this.bucket}`);
|
|
89
|
+
}
|
|
90
|
+
if (error.name === 'Forbidden') {
|
|
91
|
+
throw new Error(`R2 bucket access denied: ${this.bucket}. Check API token permissions.`);
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Stores file to R2 with automatic content type detection and zero egress cost
|
|
98
|
+
* @llm-rule WHEN: Uploading files to Cloudflare R2 for cost-effective storage
|
|
99
|
+
* @llm-rule AVOID: Manual R2 operations - this handles R2-specific optimizations
|
|
100
|
+
*/
|
|
101
|
+
async put(key, data, options) {
|
|
102
|
+
if (!this.connected || !this.r2Client) {
|
|
103
|
+
throw new Error('R2 storage not connected');
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const { PutObjectCommand } = await import('@aws-sdk/client-s3');
|
|
107
|
+
// Build R2 put parameters (S3-compatible)
|
|
108
|
+
const params = {
|
|
109
|
+
Bucket: this.bucket,
|
|
110
|
+
Key: key,
|
|
111
|
+
Body: data,
|
|
112
|
+
ContentType: options?.contentType || this.detectContentType(key, data),
|
|
113
|
+
};
|
|
114
|
+
// Add R2-optimized parameters
|
|
115
|
+
if (options?.cacheControl) {
|
|
116
|
+
params.CacheControl = options.cacheControl;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// R2 default cache control for better CDN performance
|
|
120
|
+
params.CacheControl = 'public, max-age=31536000'; // 1 year for static assets
|
|
121
|
+
}
|
|
122
|
+
if (options?.expires) {
|
|
123
|
+
params.Expires = options.expires;
|
|
124
|
+
}
|
|
125
|
+
if (options?.metadata) {
|
|
126
|
+
params.Metadata = options.metadata;
|
|
127
|
+
}
|
|
128
|
+
// Execute upload to R2
|
|
129
|
+
await this.r2Client.send(new PutObjectCommand(params));
|
|
130
|
+
if (this.config.environment.isDevelopment) {
|
|
131
|
+
console.log(`☁️ [AppKit] R2 file uploaded: ${key} (${data.length} bytes, zero egress cost)`);
|
|
132
|
+
}
|
|
133
|
+
return key;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
throw new Error(`R2 upload failed: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Retrieves file from R2 with streaming support and zero egress cost
|
|
141
|
+
* @llm-rule WHEN: Downloading files from Cloudflare R2
|
|
142
|
+
* @llm-rule AVOID: Manual R2 operations - this handles streaming and cost optimization
|
|
143
|
+
*/
|
|
144
|
+
async get(key) {
|
|
145
|
+
if (!this.connected || !this.r2Client) {
|
|
146
|
+
throw new Error('R2 storage not connected');
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
150
|
+
const params = {
|
|
151
|
+
Bucket: this.bucket,
|
|
152
|
+
Key: key,
|
|
153
|
+
};
|
|
154
|
+
const result = await this.r2Client.send(new GetObjectCommand(params));
|
|
155
|
+
if (!result.Body) {
|
|
156
|
+
throw new Error(`R2 object has no body: ${key}`);
|
|
157
|
+
}
|
|
158
|
+
// Convert stream to buffer
|
|
159
|
+
const buffer = await this.streamToBuffer(result.Body);
|
|
160
|
+
if (this.config.environment.isDevelopment) {
|
|
161
|
+
console.log(`☁️ [AppKit] R2 file downloaded: ${key} (${buffer.length} bytes, zero egress cost)`);
|
|
162
|
+
}
|
|
163
|
+
return buffer;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
if (error.name === 'NoSuchKey') {
|
|
167
|
+
throw new Error(`File not found: ${key}`);
|
|
168
|
+
}
|
|
169
|
+
throw new Error(`R2 download failed: ${error.message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Deletes file from R2 with confirmation
|
|
174
|
+
* @llm-rule WHEN: Removing files from Cloudflare R2 storage
|
|
175
|
+
* @llm-rule AVOID: Silent failures - this confirms deletion success
|
|
176
|
+
*/
|
|
177
|
+
async delete(key) {
|
|
178
|
+
if (!this.connected || !this.r2Client) {
|
|
179
|
+
console.error('R2 storage not connected');
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const { DeleteObjectCommand } = await import('@aws-sdk/client-s3');
|
|
184
|
+
const params = {
|
|
185
|
+
Bucket: this.bucket,
|
|
186
|
+
Key: key,
|
|
187
|
+
};
|
|
188
|
+
await this.r2Client.send(new DeleteObjectCommand(params));
|
|
189
|
+
if (this.config.environment.isDevelopment) {
|
|
190
|
+
console.log(`🗑️ [AppKit] R2 file deleted: ${key}`);
|
|
191
|
+
}
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
console.error(`[AppKit] R2 delete error for "${key}":`, error.message);
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Lists files with prefix filtering and R2-optimized pagination
|
|
201
|
+
* @llm-rule WHEN: Browsing R2 files or implementing file managers
|
|
202
|
+
* @llm-rule AVOID: Loading all objects - R2 has same limits as S3
|
|
203
|
+
*/
|
|
204
|
+
async list(prefix = '') {
|
|
205
|
+
if (!this.connected || !this.r2Client) {
|
|
206
|
+
throw new Error('R2 storage not connected');
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
const { ListObjectsV2Command } = await import('@aws-sdk/client-s3');
|
|
210
|
+
const params = {
|
|
211
|
+
Bucket: this.bucket,
|
|
212
|
+
MaxKeys: 1000, // R2 limit same as S3
|
|
213
|
+
};
|
|
214
|
+
if (prefix) {
|
|
215
|
+
params.Prefix = prefix;
|
|
216
|
+
}
|
|
217
|
+
const result = await this.r2Client.send(new ListObjectsV2Command(params));
|
|
218
|
+
const files = [];
|
|
219
|
+
if (result.Contents) {
|
|
220
|
+
for (const object of result.Contents) {
|
|
221
|
+
if (object.Key) {
|
|
222
|
+
files.push({
|
|
223
|
+
key: object.Key,
|
|
224
|
+
size: object.Size || 0,
|
|
225
|
+
lastModified: object.LastModified || new Date(),
|
|
226
|
+
etag: object.ETag?.replace(/"/g, ''), // Remove quotes from ETag
|
|
227
|
+
contentType: await this.getObjectContentType(object.Key),
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (this.config.environment.isDevelopment) {
|
|
233
|
+
console.log(`☁️ [AppKit] R2 files listed: ${prefix}* (${files.length} files)`);
|
|
234
|
+
}
|
|
235
|
+
return files;
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
console.error(`[AppKit] R2 list error for prefix "${prefix}":`, error.message);
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Gets CDN or public URL for R2 object with automatic CDN detection
|
|
244
|
+
* @llm-rule WHEN: Generating URLs for R2 file access with CDN optimization
|
|
245
|
+
* @llm-rule AVOID: Hardcoded URLs - this handles CDN and R2-specific URLs
|
|
246
|
+
*/
|
|
247
|
+
url(key) {
|
|
248
|
+
// Use custom CDN URL if configured (recommended for production)
|
|
249
|
+
if (this.cdnUrl) {
|
|
250
|
+
const baseUrl = this.cdnUrl.endsWith('/') ? this.cdnUrl : this.cdnUrl + '/';
|
|
251
|
+
const cleanKey = key.startsWith('/') ? key.slice(1) : key;
|
|
252
|
+
return baseUrl + cleanKey;
|
|
253
|
+
}
|
|
254
|
+
// Use R2 public URL (has rate limits, CDN recommended)
|
|
255
|
+
return `https://pub-${this.generatePublicHash()}.r2.dev/${key}`;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Generates R2 public URL hash (simplified for demo)
|
|
259
|
+
*/
|
|
260
|
+
generatePublicHash() {
|
|
261
|
+
// In production, this would be the actual R2 public URL hash
|
|
262
|
+
// For now, we'll use a placeholder that works with the R2 endpoint
|
|
263
|
+
return this.accountId.slice(0, 8);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Generates signed URL for temporary R2 object access
|
|
267
|
+
* @llm-rule WHEN: Creating temporary download/upload links for R2 objects
|
|
268
|
+
* @llm-rule AVOID: Public URLs for private files - use signed URLs with expiration
|
|
269
|
+
*/
|
|
270
|
+
async signedUrl(key, expiresIn = 3600) {
|
|
271
|
+
if (!this.connected || !this.r2Client) {
|
|
272
|
+
throw new Error('R2 storage not connected');
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const { GetObjectCommand } = await import('@aws-sdk/client-s3');
|
|
276
|
+
const { getSignedUrl } = await import('@aws-sdk/s3-request-presigner');
|
|
277
|
+
const command = new GetObjectCommand({
|
|
278
|
+
Bucket: this.bucket,
|
|
279
|
+
Key: key,
|
|
280
|
+
});
|
|
281
|
+
const signedUrl = await getSignedUrl(this.r2Client, command, {
|
|
282
|
+
expiresIn,
|
|
283
|
+
});
|
|
284
|
+
if (this.config.environment.isDevelopment) {
|
|
285
|
+
console.log(`🔐 [AppKit] R2 signed URL generated: ${key} (expires in ${expiresIn}s)`);
|
|
286
|
+
}
|
|
287
|
+
return signedUrl;
|
|
288
|
+
}
|
|
289
|
+
catch (error) {
|
|
290
|
+
throw new Error(`R2 signed URL generation failed: ${error.message}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Checks if R2 object exists without downloading
|
|
295
|
+
* @llm-rule WHEN: Validating R2 object existence efficiently
|
|
296
|
+
* @llm-rule AVOID: Downloading objects just to check existence
|
|
297
|
+
*/
|
|
298
|
+
async exists(key) {
|
|
299
|
+
if (!this.connected || !this.r2Client) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
try {
|
|
303
|
+
const { HeadObjectCommand } = await import('@aws-sdk/client-s3');
|
|
304
|
+
const params = {
|
|
305
|
+
Bucket: this.bucket,
|
|
306
|
+
Key: key,
|
|
307
|
+
};
|
|
308
|
+
await this.r2Client.send(new HeadObjectCommand(params));
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
if (error.name === 'NotFound' || error.name === 'NoSuchKey') {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
console.error(`[AppKit] R2 exists check error for "${key}":`, error.message);
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Copies R2 object efficiently using server-side copy (zero egress cost)
|
|
321
|
+
* @llm-rule WHEN: Duplicating R2 objects without bandwidth costs
|
|
322
|
+
* @llm-rule AVOID: Download and upload - R2 server-side copy has zero egress fees
|
|
323
|
+
*/
|
|
324
|
+
async copy(sourceKey, destKey) {
|
|
325
|
+
if (!this.connected || !this.r2Client) {
|
|
326
|
+
throw new Error('R2 storage not connected');
|
|
327
|
+
}
|
|
328
|
+
try {
|
|
329
|
+
const { CopyObjectCommand } = await import('@aws-sdk/client-s3');
|
|
330
|
+
const params = {
|
|
331
|
+
Bucket: this.bucket,
|
|
332
|
+
Key: destKey,
|
|
333
|
+
CopySource: `${this.bucket}/${sourceKey}`,
|
|
334
|
+
};
|
|
335
|
+
await this.r2Client.send(new CopyObjectCommand(params));
|
|
336
|
+
if (this.config.environment.isDevelopment) {
|
|
337
|
+
console.log(`☁️ [AppKit] R2 file copied: ${sourceKey} → ${destKey} (zero egress cost)`);
|
|
338
|
+
}
|
|
339
|
+
return destKey;
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
throw new Error(`R2 copy failed: ${error.message}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Disconnects R2 strategy gracefully
|
|
347
|
+
* @llm-rule WHEN: App shutdown or storage cleanup
|
|
348
|
+
* @llm-rule AVOID: Abrupt disconnection - graceful shutdown prevents connection issues
|
|
349
|
+
*/
|
|
350
|
+
async disconnect() {
|
|
351
|
+
if (!this.connected)
|
|
352
|
+
return;
|
|
353
|
+
try {
|
|
354
|
+
// R2 client doesn't need explicit disconnection (uses S3 SDK)
|
|
355
|
+
this.connected = false;
|
|
356
|
+
this.r2Client = null;
|
|
357
|
+
if (this.config.environment.isDevelopment) {
|
|
358
|
+
console.log(`👋 [AppKit] R2 storage strategy disconnected`);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
console.error(`[AppKit] R2 disconnect error:`, error.message);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// Private helper methods
|
|
366
|
+
/**
|
|
367
|
+
* Converts readable stream to buffer
|
|
368
|
+
*/
|
|
369
|
+
async streamToBuffer(stream) {
|
|
370
|
+
const chunks = [];
|
|
371
|
+
return new Promise((resolve, reject) => {
|
|
372
|
+
stream.on('data', (chunk) => chunks.push(chunk));
|
|
373
|
+
stream.on('error', reject);
|
|
374
|
+
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Detects content type from file extension and buffer
|
|
379
|
+
*/
|
|
380
|
+
detectContentType(key, buffer) {
|
|
381
|
+
// Get extension from key
|
|
382
|
+
const ext = key.split('.').pop()?.toLowerCase();
|
|
383
|
+
// Common MIME types optimized for web delivery
|
|
384
|
+
const mimeTypes = {
|
|
385
|
+
'jpg': 'image/jpeg',
|
|
386
|
+
'jpeg': 'image/jpeg',
|
|
387
|
+
'png': 'image/png',
|
|
388
|
+
'gif': 'image/gif',
|
|
389
|
+
'webp': 'image/webp',
|
|
390
|
+
'avif': 'image/avif', // Modern format support
|
|
391
|
+
'svg': 'image/svg+xml',
|
|
392
|
+
'pdf': 'application/pdf',
|
|
393
|
+
'txt': 'text/plain',
|
|
394
|
+
'json': 'application/json',
|
|
395
|
+
'csv': 'text/csv',
|
|
396
|
+
'zip': 'application/zip',
|
|
397
|
+
'mp4': 'video/mp4',
|
|
398
|
+
'webm': 'video/webm',
|
|
399
|
+
'mp3': 'audio/mpeg',
|
|
400
|
+
'wav': 'audio/wav',
|
|
401
|
+
'woff2': 'font/woff2',
|
|
402
|
+
'woff': 'font/woff',
|
|
403
|
+
'css': 'text/css',
|
|
404
|
+
'js': 'text/javascript',
|
|
405
|
+
};
|
|
406
|
+
if (ext && mimeTypes[ext]) {
|
|
407
|
+
return mimeTypes[ext];
|
|
408
|
+
}
|
|
409
|
+
// Buffer-based detection for common formats
|
|
410
|
+
const magic = buffer.subarray(0, 4);
|
|
411
|
+
if (magic[0] === 0xFF && magic[1] === 0xD8 && magic[2] === 0xFF) {
|
|
412
|
+
return 'image/jpeg';
|
|
413
|
+
}
|
|
414
|
+
if (magic[0] === 0x89 && magic[1] === 0x50 && magic[2] === 0x4E && magic[3] === 0x47) {
|
|
415
|
+
return 'image/png';
|
|
416
|
+
}
|
|
417
|
+
if (magic[0] === 0x47 && magic[1] === 0x49 && magic[2] === 0x46) {
|
|
418
|
+
return 'image/gif';
|
|
419
|
+
}
|
|
420
|
+
return 'application/octet-stream';
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Gets content type for existing R2 object
|
|
424
|
+
*/
|
|
425
|
+
async getObjectContentType(key) {
|
|
426
|
+
try {
|
|
427
|
+
const { HeadObjectCommand } = await import('@aws-sdk/client-s3');
|
|
428
|
+
const params = {
|
|
429
|
+
Bucket: this.bucket,
|
|
430
|
+
Key: key,
|
|
431
|
+
};
|
|
432
|
+
const result = await this.r2Client.send(new HeadObjectCommand(params));
|
|
433
|
+
return result.ContentType;
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
// If we can't get content type, return undefined
|
|
437
|
+
return undefined;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Gets R2 connection info for debugging
|
|
442
|
+
*/
|
|
443
|
+
getConnectionInfo() {
|
|
444
|
+
return {
|
|
445
|
+
connected: this.connected,
|
|
446
|
+
bucket: this.bucket,
|
|
447
|
+
accountId: this.accountId,
|
|
448
|
+
endpoint: this.endpoint,
|
|
449
|
+
cdnEnabled: !!this.cdnUrl,
|
|
450
|
+
zeroEgressFees: true, // R2's key advantage
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Gets R2-specific cost optimization info
|
|
455
|
+
*/
|
|
456
|
+
getCostInfo() {
|
|
457
|
+
return {
|
|
458
|
+
egressFees: 'Zero egress fees',
|
|
459
|
+
storageClass: 'Hot storage with instant access',
|
|
460
|
+
cdnIntegration: !!this.cdnUrl,
|
|
461
|
+
recommendedFor: [
|
|
462
|
+
'High-bandwidth applications',
|
|
463
|
+
'Media streaming',
|
|
464
|
+
'Global CDN delivery',
|
|
465
|
+
'Cost-sensitive workloads'
|
|
466
|
+
],
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
//# sourceMappingURL=r2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2.js","sourceRoot":"","sources":["../../../src/storage/strategies/r2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3-compatible storage strategy with automatic connection management and multi-provider support
|
|
3
|
+
* @module @bloomneo/appkit/storage
|
|
4
|
+
* @file src/storage/strategies/s3.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: App has AWS_S3_BUCKET or S3_ENDPOINT env vars for distributed cloud storage
|
|
7
|
+
* @llm-rule AVOID: Manual S3 setup - this handles AWS, Wasabi, MinIO, DigitalOcean Spaces automatically
|
|
8
|
+
* @llm-rule NOTE: Production-ready with retry logic, signed URLs, CDN support, automatic MIME detection
|
|
9
|
+
*/
|
|
10
|
+
import type { StorageStrategy, StorageFile, PutOptions } from '../storage.js';
|
|
11
|
+
import type { StorageConfig } from '../defaults.js';
|
|
12
|
+
/**
|
|
13
|
+
* S3-compatible storage strategy with multi-provider support and reliability features
|
|
14
|
+
*/
|
|
15
|
+
export declare class S3Strategy implements StorageStrategy {
|
|
16
|
+
private config;
|
|
17
|
+
private s3Client;
|
|
18
|
+
private connected;
|
|
19
|
+
private bucket;
|
|
20
|
+
private region;
|
|
21
|
+
private endpoint?;
|
|
22
|
+
private cdnUrl?;
|
|
23
|
+
/**
|
|
24
|
+
* Creates S3 strategy with direct environment access (like auth pattern)
|
|
25
|
+
* @llm-rule WHEN: Storage initialization with S3-compatible environment variables detected
|
|
26
|
+
* @llm-rule AVOID: Manual S3 configuration - environment detection handles AWS/Wasabi/MinIO
|
|
27
|
+
*/
|
|
28
|
+
constructor(config: StorageConfig);
|
|
29
|
+
/**
|
|
30
|
+
* Connects to S3-compatible service with automatic retry and provider detection
|
|
31
|
+
* @llm-rule WHEN: Storage initialization or reconnection after failure
|
|
32
|
+
* @llm-rule AVOID: Manual S3 client setup - this handles all provider configurations
|
|
33
|
+
*/
|
|
34
|
+
connect(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Tests S3 connection by checking bucket access
|
|
37
|
+
*/
|
|
38
|
+
private testConnection;
|
|
39
|
+
/**
|
|
40
|
+
* Detects S3 provider from configuration
|
|
41
|
+
*/
|
|
42
|
+
private detectProvider;
|
|
43
|
+
/**
|
|
44
|
+
* Stores file to S3 with automatic content type detection and metadata
|
|
45
|
+
* @llm-rule WHEN: Uploading files to S3-compatible cloud storage
|
|
46
|
+
* @llm-rule AVOID: Manual S3 operations - this handles multipart uploads and metadata
|
|
47
|
+
*/
|
|
48
|
+
put(key: string, data: Buffer, options?: PutOptions): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Retrieves file from S3 with streaming support
|
|
51
|
+
* @llm-rule WHEN: Downloading files from S3-compatible storage
|
|
52
|
+
* @llm-rule AVOID: Manual S3 operations - this handles streaming and errors
|
|
53
|
+
*/
|
|
54
|
+
get(key: string): Promise<Buffer>;
|
|
55
|
+
/**
|
|
56
|
+
* Deletes file from S3 with confirmation
|
|
57
|
+
* @llm-rule WHEN: Removing files from S3-compatible storage
|
|
58
|
+
* @llm-rule AVOID: Silent failures - this confirms deletion success
|
|
59
|
+
*/
|
|
60
|
+
delete(key: string): Promise<boolean>;
|
|
61
|
+
/**
|
|
62
|
+
* Lists files with prefix filtering and pagination
|
|
63
|
+
* @llm-rule WHEN: Browsing S3 files or implementing file managers
|
|
64
|
+
* @llm-rule AVOID: Loading all objects - use prefix filtering and pagination
|
|
65
|
+
*/
|
|
66
|
+
list(prefix?: string): Promise<StorageFile[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Gets public or CDN URL for S3 object
|
|
69
|
+
* @llm-rule WHEN: Generating URLs for S3 file access with CDN support
|
|
70
|
+
* @llm-rule AVOID: Hardcoded URLs - this handles CDN and region-specific URLs
|
|
71
|
+
*/
|
|
72
|
+
url(key: string): string;
|
|
73
|
+
/**
|
|
74
|
+
* Generates signed URL for temporary S3 object access
|
|
75
|
+
* @llm-rule WHEN: Creating temporary download/upload links for S3 objects
|
|
76
|
+
* @llm-rule AVOID: Permanent URLs for private files - use signed URLs with expiration
|
|
77
|
+
*/
|
|
78
|
+
signedUrl(key: string, expiresIn?: number): Promise<string>;
|
|
79
|
+
/**
|
|
80
|
+
* Checks if S3 object exists without downloading
|
|
81
|
+
* @llm-rule WHEN: Validating S3 object existence efficiently
|
|
82
|
+
* @llm-rule AVOID: Downloading objects just to check existence
|
|
83
|
+
*/
|
|
84
|
+
exists(key: string): Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Copies S3 object efficiently using server-side copy
|
|
87
|
+
* @llm-rule WHEN: Duplicating S3 objects without downloading/uploading
|
|
88
|
+
* @llm-rule AVOID: Download and upload - S3 server-side copy is much faster
|
|
89
|
+
*/
|
|
90
|
+
copy(sourceKey: string, destKey: string): Promise<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Disconnects S3 strategy gracefully
|
|
93
|
+
* @llm-rule WHEN: App shutdown or storage cleanup
|
|
94
|
+
* @llm-rule AVOID: Abrupt disconnection - graceful shutdown prevents connection issues
|
|
95
|
+
*/
|
|
96
|
+
disconnect(): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Converts readable stream to buffer
|
|
99
|
+
*/
|
|
100
|
+
private streamToBuffer;
|
|
101
|
+
/**
|
|
102
|
+
* Detects content type from file extension and buffer
|
|
103
|
+
*/
|
|
104
|
+
private detectContentType;
|
|
105
|
+
/**
|
|
106
|
+
* Gets content type for existing S3 object
|
|
107
|
+
*/
|
|
108
|
+
private getObjectContentType;
|
|
109
|
+
/**
|
|
110
|
+
* Gets S3 connection info for debugging
|
|
111
|
+
*/
|
|
112
|
+
getConnectionInfo(): {
|
|
113
|
+
connected: boolean;
|
|
114
|
+
bucket: string;
|
|
115
|
+
region: string;
|
|
116
|
+
endpoint?: string;
|
|
117
|
+
provider: string;
|
|
118
|
+
cdnEnabled: boolean;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=s3.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../src/storage/strategies/s3.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD;;GAEG;AACH,qBAAa,UAAW,YAAW,eAAe;IAChD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAS;IAExB;;;;OAIG;gBACS,MAAM,EAAE,aAAa;IAajC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA4C9B;;OAEG;YACW,cAAc;IAgB5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAatB;;;;OAIG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IA0C3E;;;;OAIG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmCvC;;;;OAIG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2B3C;;;;OAIG;IACG,IAAI,CAAC,MAAM,GAAE,MAAW,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IA8CvD;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAmBxB;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,MAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IA4BvE;;;;OAIG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAwB3C;;;;OAIG;IACG,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA0B/D;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBjC;;OAEG;YACW,cAAc;IAU5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6CzB;;OAEG;YACW,oBAAoB;IAiBlC;;OAEG;IACH,iBAAiB,IAAI;QACnB,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,OAAO,CAAC;KACrB;CAUF"}
|