@rovela-ai/sdk 0.1.25 → 0.1.27
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/admin/components/CategoryForm.d.ts.map +1 -1
- package/dist/admin/components/CategoryForm.js +2 -2
- package/dist/admin/components/CategoryForm.js.map +1 -1
- package/dist/admin/components/ProductForm.d.ts.map +1 -1
- package/dist/admin/components/ProductForm.js +3 -35
- package/dist/admin/components/ProductForm.js.map +1 -1
- package/dist/admin/components/ProductTable.d.ts.map +1 -1
- package/dist/admin/components/ProductTable.js +6 -1
- package/dist/admin/components/ProductTable.js.map +1 -1
- package/dist/admin/server/admin-service.d.ts +3 -3
- package/dist/admin/server/admin-service.d.ts.map +1 -1
- package/dist/admin/server/admin-service.js +12 -22
- package/dist/admin/server/admin-service.js.map +1 -1
- package/dist/auth/server/customer-service.d.ts +2 -2
- package/dist/auth/server/customer-service.d.ts.map +1 -1
- package/dist/auth/server/customer-service.js +11 -20
- package/dist/auth/server/customer-service.js.map +1 -1
- package/dist/auth/server/password-reset-service.d.ts +1 -0
- package/dist/auth/server/password-reset-service.d.ts.map +1 -1
- package/dist/auth/server/password-reset-service.js +5 -7
- package/dist/auth/server/password-reset-service.js.map +1 -1
- package/dist/auth/server/verification-service.d.ts +1 -0
- package/dist/auth/server/verification-service.d.ts.map +1 -1
- package/dist/auth/server/verification-service.js +6 -9
- package/dist/auth/server/verification-service.js.map +1 -1
- package/dist/core/db/client.d.ts +2 -44
- package/dist/core/db/client.d.ts.map +1 -1
- package/dist/core/db/client.js +2 -106
- package/dist/core/db/client.js.map +1 -1
- package/dist/core/db/index.d.ts +1 -1
- package/dist/core/db/index.d.ts.map +1 -1
- package/dist/core/db/index.js +2 -2
- package/dist/core/db/index.js.map +1 -1
- package/dist/core/db/queries.d.ts +18 -35
- package/dist/core/db/queries.d.ts.map +1 -1
- package/dist/core/db/queries.js +69 -110
- package/dist/core/db/queries.js.map +1 -1
- package/dist/core/db/schema.d.ts +1 -137
- package/dist/core/db/schema.d.ts.map +1 -1
- package/dist/core/db/schema.js +6 -23
- package/dist/core/db/schema.js.map +1 -1
- package/dist/core/server/index.d.ts +1 -1
- package/dist/core/server/index.d.ts.map +1 -1
- package/dist/core/server/index.js +1 -3
- package/dist/core/server/index.js.map +1 -1
- package/dist/core/types.d.ts +0 -5
- package/dist/core/types.d.ts.map +1 -1
- package/dist/emails/config.d.ts.map +1 -1
- package/dist/emails/config.js +11 -17
- package/dist/emails/config.js.map +1 -1
- package/dist/media/api/delete.d.ts +44 -0
- package/dist/media/api/delete.d.ts.map +1 -0
- package/dist/media/api/delete.js +134 -0
- package/dist/media/api/delete.js.map +1 -0
- package/dist/media/api/index.d.ts +17 -0
- package/dist/media/api/index.d.ts.map +1 -0
- package/dist/media/api/index.js +17 -0
- package/dist/media/api/index.js.map +1 -0
- package/dist/media/api/presign.d.ts +39 -0
- package/dist/media/api/presign.d.ts.map +1 -0
- package/dist/media/api/presign.js +138 -0
- package/dist/media/api/presign.js.map +1 -0
- package/dist/media/components/DropZone.d.ts +18 -0
- package/dist/media/components/DropZone.d.ts.map +1 -0
- package/dist/media/components/DropZone.js +112 -0
- package/dist/media/components/DropZone.js.map +1 -0
- package/dist/media/components/ImageGalleryUpload.d.ts +18 -0
- package/dist/media/components/ImageGalleryUpload.d.ts.map +1 -0
- package/dist/media/components/ImageGalleryUpload.js +156 -0
- package/dist/media/components/ImageGalleryUpload.js.map +1 -0
- package/dist/media/components/ImageUpload.d.ts +17 -0
- package/dist/media/components/ImageUpload.d.ts.map +1 -0
- package/dist/media/components/ImageUpload.js +95 -0
- package/dist/media/components/ImageUpload.js.map +1 -0
- package/dist/media/components/index.d.ts +10 -0
- package/dist/media/components/index.d.ts.map +1 -0
- package/dist/media/components/index.js +9 -0
- package/dist/media/components/index.js.map +1 -0
- package/dist/media/config.d.ts +57 -0
- package/dist/media/config.d.ts.map +1 -0
- package/dist/media/config.js +142 -0
- package/dist/media/config.js.map +1 -0
- package/dist/media/hooks/index.d.ts +8 -0
- package/dist/media/hooks/index.d.ts.map +1 -0
- package/dist/media/hooks/index.js +7 -0
- package/dist/media/hooks/index.js.map +1 -0
- package/dist/media/hooks/useUpload.d.ts +32 -0
- package/dist/media/hooks/useUpload.d.ts.map +1 -0
- package/dist/media/hooks/useUpload.js +258 -0
- package/dist/media/hooks/useUpload.js.map +1 -0
- package/dist/media/index.d.ts +57 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/index.js +68 -0
- package/dist/media/index.js.map +1 -0
- package/dist/media/server/delete.d.ts +59 -0
- package/dist/media/server/delete.d.ts.map +1 -0
- package/dist/media/server/delete.js +176 -0
- package/dist/media/server/delete.js.map +1 -0
- package/dist/media/server/index.d.ts +10 -0
- package/dist/media/server/index.d.ts.map +1 -0
- package/dist/media/server/index.js +13 -0
- package/dist/media/server/index.js.map +1 -0
- package/dist/media/server/presign.d.ts +57 -0
- package/dist/media/server/presign.d.ts.map +1 -0
- package/dist/media/server/presign.js +112 -0
- package/dist/media/server/presign.js.map +1 -0
- package/dist/media/server/r2-client.d.ts +30 -0
- package/dist/media/server/r2-client.d.ts.map +1 -0
- package/dist/media/server/r2-client.js +76 -0
- package/dist/media/server/r2-client.js.map +1 -0
- package/dist/media/types.d.ts +271 -0
- package/dist/media/types.d.ts.map +1 -0
- package/dist/media/types.js +52 -0
- package/dist/media/types.js.map +1 -0
- package/package.json +15 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.d.ts","sourceRoot":"","sources":["../../../src/media/server/delete.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AASlD;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,OAAO,CAAC,CA+BlB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAuBlB;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAoEpD;AAMD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAMlB"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela-ai/sdk/media/server/delete
|
|
3
|
+
*
|
|
4
|
+
* File deletion from R2 storage.
|
|
5
|
+
* Server-side only - do not import in client components.
|
|
6
|
+
*/
|
|
7
|
+
import { DeleteObjectCommand, DeleteObjectsCommand } from '@aws-sdk/client-s3';
|
|
8
|
+
import { extractKeyFromUrl, requireMediaConfig } from '../config';
|
|
9
|
+
import { getR2Client, getBucketName, getPublicUrlBase } from './r2-client';
|
|
10
|
+
import { validateStoreUrl } from './presign';
|
|
11
|
+
// =============================================================================
|
|
12
|
+
// Single File Deletion
|
|
13
|
+
// =============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Delete a file from R2 storage.
|
|
16
|
+
*
|
|
17
|
+
* @param url - Public URL of the file to delete
|
|
18
|
+
* @param config - Optional storage configuration override
|
|
19
|
+
* @returns True if deletion was successful
|
|
20
|
+
* @throws Error if URL is invalid or doesn't belong to this store
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* await deleteFile('https://pub-xxx.r2.dev/stores/abc123/products/img.jpg');
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export async function deleteFile(url, config) {
|
|
28
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
29
|
+
// Validate URL belongs to this store
|
|
30
|
+
if (!validateStoreUrl(url, resolvedConfig)) {
|
|
31
|
+
throw new Error('Invalid URL: does not belong to this store');
|
|
32
|
+
}
|
|
33
|
+
// Extract key from URL
|
|
34
|
+
const publicUrlBase = getPublicUrlBase(resolvedConfig);
|
|
35
|
+
const key = extractKeyFromUrl(publicUrlBase, url);
|
|
36
|
+
if (!key) {
|
|
37
|
+
throw new Error('Invalid URL: could not extract storage key');
|
|
38
|
+
}
|
|
39
|
+
const client = getR2Client(resolvedConfig);
|
|
40
|
+
const bucketName = getBucketName(resolvedConfig);
|
|
41
|
+
try {
|
|
42
|
+
await client.send(new DeleteObjectCommand({
|
|
43
|
+
Bucket: bucketName,
|
|
44
|
+
Key: key,
|
|
45
|
+
}));
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('[Media] Delete failed:', error);
|
|
50
|
+
throw new Error('Failed to delete file');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Delete a file by its storage key.
|
|
55
|
+
* Use this when you have the key directly instead of the URL.
|
|
56
|
+
*
|
|
57
|
+
* @param key - Storage key of the file
|
|
58
|
+
* @param config - Optional storage configuration override
|
|
59
|
+
* @returns True if deletion was successful
|
|
60
|
+
*/
|
|
61
|
+
export async function deleteFileByKey(key, config) {
|
|
62
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
63
|
+
const client = getR2Client(resolvedConfig);
|
|
64
|
+
const bucketName = getBucketName(resolvedConfig);
|
|
65
|
+
// Validate key belongs to this store
|
|
66
|
+
const expectedPrefix = `stores/${resolvedConfig.storeId}/`;
|
|
67
|
+
if (!key.startsWith(expectedPrefix)) {
|
|
68
|
+
throw new Error('Invalid key: does not belong to this store');
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
await client.send(new DeleteObjectCommand({
|
|
72
|
+
Bucket: bucketName,
|
|
73
|
+
Key: key,
|
|
74
|
+
}));
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
console.error('[Media] Delete by key failed:', error);
|
|
79
|
+
throw new Error('Failed to delete file');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// Batch Deletion
|
|
84
|
+
// =============================================================================
|
|
85
|
+
/**
|
|
86
|
+
* Delete multiple files from R2 storage.
|
|
87
|
+
*
|
|
88
|
+
* @param urls - Array of public URLs to delete
|
|
89
|
+
* @param config - Optional storage configuration override
|
|
90
|
+
* @returns Object with results for each URL
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const results = await deleteFiles([
|
|
95
|
+
* 'https://pub-xxx.r2.dev/stores/abc123/products/img1.jpg',
|
|
96
|
+
* 'https://pub-xxx.r2.dev/stores/abc123/products/img2.jpg',
|
|
97
|
+
* ]);
|
|
98
|
+
* // { succeeded: ['url1', 'url2'], failed: [] }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export async function deleteFiles(urls, config) {
|
|
102
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
103
|
+
const publicUrlBase = getPublicUrlBase(resolvedConfig);
|
|
104
|
+
// Filter and extract keys
|
|
105
|
+
const validEntries = [];
|
|
106
|
+
const invalidUrls = [];
|
|
107
|
+
for (const url of urls) {
|
|
108
|
+
if (!validateStoreUrl(url, resolvedConfig)) {
|
|
109
|
+
invalidUrls.push(url);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const key = extractKeyFromUrl(publicUrlBase, url);
|
|
113
|
+
if (!key) {
|
|
114
|
+
invalidUrls.push(url);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
validEntries.push({ url, key });
|
|
118
|
+
}
|
|
119
|
+
if (validEntries.length === 0) {
|
|
120
|
+
return { succeeded: [], failed: urls };
|
|
121
|
+
}
|
|
122
|
+
const client = getR2Client(resolvedConfig);
|
|
123
|
+
const bucketName = getBucketName(resolvedConfig);
|
|
124
|
+
try {
|
|
125
|
+
// Use batch delete for efficiency
|
|
126
|
+
await client.send(new DeleteObjectsCommand({
|
|
127
|
+
Bucket: bucketName,
|
|
128
|
+
Delete: {
|
|
129
|
+
Objects: validEntries.map(({ key }) => ({ Key: key })),
|
|
130
|
+
Quiet: true,
|
|
131
|
+
},
|
|
132
|
+
}));
|
|
133
|
+
return {
|
|
134
|
+
succeeded: validEntries.map(({ url }) => url),
|
|
135
|
+
failed: invalidUrls,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error('[Media] Batch delete failed:', error);
|
|
140
|
+
// On error, try to delete individually
|
|
141
|
+
const succeeded = [];
|
|
142
|
+
const failed = [...invalidUrls];
|
|
143
|
+
for (const { url, key } of validEntries) {
|
|
144
|
+
try {
|
|
145
|
+
await client.send(new DeleteObjectCommand({
|
|
146
|
+
Bucket: bucketName,
|
|
147
|
+
Key: key,
|
|
148
|
+
}));
|
|
149
|
+
succeeded.push(url);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
failed.push(url);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return { succeeded, failed };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// =============================================================================
|
|
159
|
+
// Safe Deletion (with existence check)
|
|
160
|
+
// =============================================================================
|
|
161
|
+
/**
|
|
162
|
+
* Safely delete a file - returns false instead of throwing if file doesn't exist.
|
|
163
|
+
*
|
|
164
|
+
* @param url - Public URL of the file to delete
|
|
165
|
+
* @param config - Optional storage configuration override
|
|
166
|
+
* @returns True if deleted, false if not found or invalid
|
|
167
|
+
*/
|
|
168
|
+
export async function safeDeleteFile(url, config) {
|
|
169
|
+
try {
|
|
170
|
+
return await deleteFile(url, config);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete.js","sourceRoot":"","sources":["../../../src/media/server/delete.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAE9E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAE5C,gFAAgF;AAChF,uBAAuB;AACvB,gFAAgF;AAEhF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,MAA2B;IAE3B,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IAErD,qCAAqC;IACrC,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IAED,uBAAuB;IACvB,MAAM,aAAa,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACtD,MAAM,GAAG,GAAG,iBAAiB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAA;IAEjD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,CAAC,CAAA;IAC1C,MAAM,UAAU,GAAG,aAAa,CAAC,cAAc,CAAC,CAAA;IAEhD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CACf,IAAI,mBAAmB,CAAC;YACtB,MAAM,EAAE,UAAU;YAClB,GAAG,EAAE,GAAG;SACT,CAAC,CACH,CAAA;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAA;QAC9C,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA;IAC1C,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,MAA2B;IAE3B,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,CAAC,CAAA;IAC1C,MAAM,UAAU,GAAG,aAAa,CAAC,cAAc,CAAC,CAAA;IAEhD,qCAAqC;IACrC,MAAM,cAAc,GAAG,UAAU,cAAc,CAAC,OAAO,GAAG,CAAA;IAC1D,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IAC/D,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,IAAI,CACf,IAAI,mBAAmB,CAAC;YACtB,MAAM,EAAE,UAAU;YAClB,GAAG,EAAE,GAAG;SACT,CAAC,CACH,CAAA;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAA;QACrD,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAA;IAC1C,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAc,EACd,MAA2B;IAE3B,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,MAAM,aAAa,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAA;IAEtD,0BAA0B;IAC1B,MAAM,YAAY,GAAmC,EAAE,CAAA;IACvD,MAAM,WAAW,GAAa,EAAE,CAAA;IAEhC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,CAAC;YAC3C,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACrB,SAAQ;QACV,CAAC;QAED,MAAM,GAAG,GAAG,iBAAiB,CAAC,aAAa,EAAE,GAAG,CAAC,CAAA;QACjD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACrB,SAAQ;QACV,CAAC;QAED,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;IACjC,CAAC;IAED,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;IACxC,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,cAAc,CAAC,CAAA;IAC1C,MAAM,UAAU,GAAG,aAAa,CAAC,cAAc,CAAC,CAAA;IAEhD,IAAI,CAAC;QACH,kCAAkC;QAClC,MAAM,MAAM,CAAC,IAAI,CACf,IAAI,oBAAoB,CAAC;YACvB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE;gBACN,OAAO,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;gBACtD,KAAK,EAAE,IAAI;aACZ;SACF,CAAC,CACH,CAAA;QAED,OAAO;YACL,SAAS,EAAE,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC;YAC7C,MAAM,EAAE,WAAW;SACpB,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAA;QACpD,uCAAuC;QACvC,MAAM,SAAS,GAAa,EAAE,CAAA;QAC9B,MAAM,MAAM,GAAa,CAAC,GAAG,WAAW,CAAC,CAAA;QAEzC,KAAK,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,YAAY,EAAE,CAAC;YACxC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,IAAI,CACf,IAAI,mBAAmB,CAAC;oBACtB,MAAM,EAAE,UAAU;oBAClB,GAAG,EAAE,GAAG;iBACT,CAAC,CACH,CAAA;gBACD,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;QAED,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAA;IAC9B,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,uCAAuC;AACvC,gFAAgF;AAEhF;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAW,EACX,MAA2B;IAE3B,IAAI,CAAC;QACH,OAAO,MAAM,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela-ai/sdk/media/server
|
|
3
|
+
*
|
|
4
|
+
* Server-side media utilities.
|
|
5
|
+
* Only import this in server components and API routes.
|
|
6
|
+
*/
|
|
7
|
+
export { getR2Client, clearR2ClientCache, getBucketName, getPublicUrlBase, getStoreId } from './r2-client';
|
|
8
|
+
export { generatePresignedUrl, generatePresignedUrls, isR2Url, validateStoreUrl, type GeneratePresignedUrlOptions, } from './presign';
|
|
9
|
+
export { deleteFile, deleteFileByKey, deleteFiles, safeDeleteFile } from './delete';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/media/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAG1G,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,OAAO,EACP,gBAAgB,EAChB,KAAK,2BAA2B,GACjC,MAAM,WAAW,CAAA;AAGlB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela-ai/sdk/media/server
|
|
3
|
+
*
|
|
4
|
+
* Server-side media utilities.
|
|
5
|
+
* Only import this in server components and API routes.
|
|
6
|
+
*/
|
|
7
|
+
// R2 Client
|
|
8
|
+
export { getR2Client, clearR2ClientCache, getBucketName, getPublicUrlBase, getStoreId } from './r2-client';
|
|
9
|
+
// Presigned URLs
|
|
10
|
+
export { generatePresignedUrl, generatePresignedUrls, isR2Url, validateStoreUrl, } from './presign';
|
|
11
|
+
// File Deletion
|
|
12
|
+
export { deleteFile, deleteFileByKey, deleteFiles, safeDeleteFile } from './delete';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/media/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY;AACZ,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE1G,iBAAiB;AACjB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,OAAO,EACP,gBAAgB,GAEjB,MAAM,WAAW,CAAA;AAElB,gBAAgB;AAChB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela-ai/sdk/media/server/presign
|
|
3
|
+
*
|
|
4
|
+
* Presigned URL generation for direct browser uploads to R2.
|
|
5
|
+
* Server-side only - do not import in client components.
|
|
6
|
+
*/
|
|
7
|
+
import type { PresignedUrlRequest, PresignedUrlResponse, MediaStorageConfig } from '../types';
|
|
8
|
+
/**
|
|
9
|
+
* Options for generating a presigned URL
|
|
10
|
+
*/
|
|
11
|
+
export interface GeneratePresignedUrlOptions {
|
|
12
|
+
/** URL expiration time in seconds (default: 300 = 5 minutes) */
|
|
13
|
+
expiresIn?: number;
|
|
14
|
+
/** Optional storage config override */
|
|
15
|
+
config?: MediaStorageConfig;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate a presigned URL for uploading a file to R2.
|
|
19
|
+
*
|
|
20
|
+
* The presigned URL allows the browser to upload directly to R2
|
|
21
|
+
* without the file passing through your server.
|
|
22
|
+
*
|
|
23
|
+
* @param request - Upload request details
|
|
24
|
+
* @param options - Optional configuration
|
|
25
|
+
* @returns Presigned URL response with upload URL and final public URL
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const result = await generatePresignedUrl({
|
|
30
|
+
* filename: 'product-image.jpg',
|
|
31
|
+
* contentType: 'image/jpeg',
|
|
32
|
+
* folder: 'products',
|
|
33
|
+
* entityId: 'prod_abc123',
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // Client uploads directly to result.uploadUrl
|
|
37
|
+
* // After upload, image is accessible at result.publicUrl
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function generatePresignedUrl(request: PresignedUrlRequest, options?: GeneratePresignedUrlOptions): Promise<PresignedUrlResponse>;
|
|
41
|
+
/**
|
|
42
|
+
* Generate presigned URLs for multiple files at once.
|
|
43
|
+
*
|
|
44
|
+
* @param requests - Array of upload request details
|
|
45
|
+
* @param options - Optional configuration
|
|
46
|
+
* @returns Array of presigned URL responses
|
|
47
|
+
*/
|
|
48
|
+
export declare function generatePresignedUrls(requests: PresignedUrlRequest[], options?: GeneratePresignedUrlOptions): Promise<PresignedUrlResponse[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Check if a URL is from this store's R2 storage
|
|
51
|
+
*/
|
|
52
|
+
export declare function isR2Url(url: string, config?: MediaStorageConfig): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Validate that a URL belongs to this store's storage prefix
|
|
55
|
+
*/
|
|
56
|
+
export declare function validateStoreUrl(url: string, config?: MediaStorageConfig): boolean;
|
|
57
|
+
//# sourceMappingURL=presign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presign.d.ts","sourceRoot":"","sources":["../../../src/media/server/presign.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,UAAU,CAAA;AAcjB;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uCAAuC;IACvC,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,oBAAoB,CAAC,CAwC/B;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAEjC;AAMD;;GAEG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAGzE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAiBlF"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela-ai/sdk/media/server/presign
|
|
3
|
+
*
|
|
4
|
+
* Presigned URL generation for direct browser uploads to R2.
|
|
5
|
+
* Server-side only - do not import in client components.
|
|
6
|
+
*/
|
|
7
|
+
import { PutObjectCommand } from '@aws-sdk/client-s3';
|
|
8
|
+
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
9
|
+
import { generateUniqueFilename } from '../types';
|
|
10
|
+
import { buildStorageKey, buildPublicUrl, requireMediaConfig } from '../config';
|
|
11
|
+
import { getR2Client, getBucketName, getPublicUrlBase, getStoreId } from './r2-client';
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// Presigned URL Generation
|
|
14
|
+
// =============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Default presigned URL expiration time in seconds (5 minutes)
|
|
17
|
+
*/
|
|
18
|
+
const DEFAULT_EXPIRES_IN = 5 * 60;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a presigned URL for uploading a file to R2.
|
|
21
|
+
*
|
|
22
|
+
* The presigned URL allows the browser to upload directly to R2
|
|
23
|
+
* without the file passing through your server.
|
|
24
|
+
*
|
|
25
|
+
* @param request - Upload request details
|
|
26
|
+
* @param options - Optional configuration
|
|
27
|
+
* @returns Presigned URL response with upload URL and final public URL
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const result = await generatePresignedUrl({
|
|
32
|
+
* filename: 'product-image.jpg',
|
|
33
|
+
* contentType: 'image/jpeg',
|
|
34
|
+
* folder: 'products',
|
|
35
|
+
* entityId: 'prod_abc123',
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Client uploads directly to result.uploadUrl
|
|
39
|
+
* // After upload, image is accessible at result.publicUrl
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export async function generatePresignedUrl(request, options = {}) {
|
|
43
|
+
const { expiresIn = DEFAULT_EXPIRES_IN, config } = options;
|
|
44
|
+
// Get configuration
|
|
45
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
46
|
+
const client = getR2Client(resolvedConfig);
|
|
47
|
+
const bucketName = getBucketName(resolvedConfig);
|
|
48
|
+
const publicUrlBase = getPublicUrlBase(resolvedConfig);
|
|
49
|
+
const storeId = getStoreId(resolvedConfig);
|
|
50
|
+
// Generate unique filename to prevent overwrites
|
|
51
|
+
const uniqueFilename = generateUniqueFilename(request.filename);
|
|
52
|
+
// Build the storage key (path)
|
|
53
|
+
const key = buildStorageKey(storeId, request.folder, uniqueFilename, request.entityId);
|
|
54
|
+
// Create the PutObject command
|
|
55
|
+
const command = new PutObjectCommand({
|
|
56
|
+
Bucket: bucketName,
|
|
57
|
+
Key: key,
|
|
58
|
+
ContentType: request.contentType,
|
|
59
|
+
});
|
|
60
|
+
// Generate presigned URL
|
|
61
|
+
const uploadUrl = await getSignedUrl(client, command, {
|
|
62
|
+
expiresIn,
|
|
63
|
+
});
|
|
64
|
+
// Calculate expiration time
|
|
65
|
+
const expiresAt = new Date(Date.now() + expiresIn * 1000);
|
|
66
|
+
// Build public URL
|
|
67
|
+
const publicUrl = buildPublicUrl(publicUrlBase, key);
|
|
68
|
+
return {
|
|
69
|
+
uploadUrl,
|
|
70
|
+
publicUrl,
|
|
71
|
+
key,
|
|
72
|
+
expiresAt,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate presigned URLs for multiple files at once.
|
|
77
|
+
*
|
|
78
|
+
* @param requests - Array of upload request details
|
|
79
|
+
* @param options - Optional configuration
|
|
80
|
+
* @returns Array of presigned URL responses
|
|
81
|
+
*/
|
|
82
|
+
export async function generatePresignedUrls(requests, options = {}) {
|
|
83
|
+
return Promise.all(requests.map((request) => generatePresignedUrl(request, options)));
|
|
84
|
+
}
|
|
85
|
+
// =============================================================================
|
|
86
|
+
// URL Validation
|
|
87
|
+
// =============================================================================
|
|
88
|
+
/**
|
|
89
|
+
* Check if a URL is from this store's R2 storage
|
|
90
|
+
*/
|
|
91
|
+
export function isR2Url(url, config) {
|
|
92
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
93
|
+
return url.startsWith(resolvedConfig.publicUrl);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Validate that a URL belongs to this store's storage prefix
|
|
97
|
+
*/
|
|
98
|
+
export function validateStoreUrl(url, config) {
|
|
99
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
100
|
+
if (!isR2Url(url, resolvedConfig)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
// Extract key from URL and check prefix
|
|
104
|
+
const baseUrl = resolvedConfig.publicUrl.endsWith('/')
|
|
105
|
+
? resolvedConfig.publicUrl.slice(0, -1)
|
|
106
|
+
: resolvedConfig.publicUrl;
|
|
107
|
+
const key = url.slice(baseUrl.length + 1);
|
|
108
|
+
// Key should start with stores/{storeId}/
|
|
109
|
+
const expectedPrefix = `stores/${resolvedConfig.storeId}/`;
|
|
110
|
+
return key.startsWith(expectedPrefix);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=presign.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presign.js","sourceRoot":"","sources":["../../../src/media/server/presign.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela-ai/sdk/media/server/r2-client
|
|
3
|
+
*
|
|
4
|
+
* S3-compatible client for Cloudflare R2 storage.
|
|
5
|
+
* Server-side only - do not import in client components.
|
|
6
|
+
*/
|
|
7
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
8
|
+
import type { MediaStorageConfig } from '../types';
|
|
9
|
+
/**
|
|
10
|
+
* Get or create the S3 client for R2.
|
|
11
|
+
* Caches the client instance for reuse.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getR2Client(config?: MediaStorageConfig): S3Client;
|
|
14
|
+
/**
|
|
15
|
+
* Clear the cached client (useful for testing)
|
|
16
|
+
*/
|
|
17
|
+
export declare function clearR2ClientCache(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Get the configured bucket name
|
|
20
|
+
*/
|
|
21
|
+
export declare function getBucketName(config?: MediaStorageConfig): string;
|
|
22
|
+
/**
|
|
23
|
+
* Get the public URL base for the bucket
|
|
24
|
+
*/
|
|
25
|
+
export declare function getPublicUrlBase(config?: MediaStorageConfig): string;
|
|
26
|
+
/**
|
|
27
|
+
* Get the store ID for path prefixing
|
|
28
|
+
*/
|
|
29
|
+
export declare function getStoreId(config?: MediaStorageConfig): string;
|
|
30
|
+
//# sourceMappingURL=r2-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-client.d.ts","sourceRoot":"","sources":["../../../src/media/server/r2-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAoBlD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,QAAQ,CAsBjE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAMD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAGjE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAGpE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAG9D"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela-ai/sdk/media/server/r2-client
|
|
3
|
+
*
|
|
4
|
+
* S3-compatible client for Cloudflare R2 storage.
|
|
5
|
+
* Server-side only - do not import in client components.
|
|
6
|
+
*/
|
|
7
|
+
import { S3Client } from '@aws-sdk/client-s3';
|
|
8
|
+
import { requireMediaConfig } from '../config';
|
|
9
|
+
// =============================================================================
|
|
10
|
+
// Client Instance Management
|
|
11
|
+
// =============================================================================
|
|
12
|
+
/** Cached S3 client instance */
|
|
13
|
+
let cachedClient = null;
|
|
14
|
+
/** Cached config used to create the client */
|
|
15
|
+
let cachedConfigHash = null;
|
|
16
|
+
/**
|
|
17
|
+
* Create a hash of the config for cache invalidation
|
|
18
|
+
*/
|
|
19
|
+
function hashConfig(config) {
|
|
20
|
+
return `${config.accountId}:${config.bucketName}:${config.accessKeyId.slice(0, 8)}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get or create the S3 client for R2.
|
|
24
|
+
* Caches the client instance for reuse.
|
|
25
|
+
*/
|
|
26
|
+
export function getR2Client(config) {
|
|
27
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
28
|
+
const configHash = hashConfig(resolvedConfig);
|
|
29
|
+
// Return cached client if config hasn't changed
|
|
30
|
+
if (cachedClient && cachedConfigHash === configHash) {
|
|
31
|
+
return cachedClient;
|
|
32
|
+
}
|
|
33
|
+
// Create new client
|
|
34
|
+
cachedClient = new S3Client({
|
|
35
|
+
region: 'auto',
|
|
36
|
+
endpoint: `https://${resolvedConfig.accountId}.r2.cloudflarestorage.com`,
|
|
37
|
+
credentials: {
|
|
38
|
+
accessKeyId: resolvedConfig.accessKeyId,
|
|
39
|
+
secretAccessKey: resolvedConfig.secretAccessKey,
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
cachedConfigHash = configHash;
|
|
43
|
+
return cachedClient;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Clear the cached client (useful for testing)
|
|
47
|
+
*/
|
|
48
|
+
export function clearR2ClientCache() {
|
|
49
|
+
cachedClient = null;
|
|
50
|
+
cachedConfigHash = null;
|
|
51
|
+
}
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// Bucket Operations
|
|
54
|
+
// =============================================================================
|
|
55
|
+
/**
|
|
56
|
+
* Get the configured bucket name
|
|
57
|
+
*/
|
|
58
|
+
export function getBucketName(config) {
|
|
59
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
60
|
+
return resolvedConfig.bucketName;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Get the public URL base for the bucket
|
|
64
|
+
*/
|
|
65
|
+
export function getPublicUrlBase(config) {
|
|
66
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
67
|
+
return resolvedConfig.publicUrl;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the store ID for path prefixing
|
|
71
|
+
*/
|
|
72
|
+
export function getStoreId(config) {
|
|
73
|
+
const resolvedConfig = config || requireMediaConfig();
|
|
74
|
+
return resolvedConfig.storeId;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=r2-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r2-client.js","sourceRoot":"","sources":["../../../src/media/server/r2-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAE9C,gFAAgF;AAChF,6BAA6B;AAC7B,gFAAgF;AAEhF,gCAAgC;AAChC,IAAI,YAAY,GAAoB,IAAI,CAAA;AAExC,8CAA8C;AAC9C,IAAI,gBAAgB,GAAkB,IAAI,CAAA;AAE1C;;GAEG;AACH,SAAS,UAAU,CAAC,MAA0B;IAC5C,OAAO,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;AACrF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,MAA2B;IACrD,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,MAAM,UAAU,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IAE7C,gDAAgD;IAChD,IAAI,YAAY,IAAI,gBAAgB,KAAK,UAAU,EAAE,CAAC;QACpD,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,oBAAoB;IACpB,YAAY,GAAG,IAAI,QAAQ,CAAC;QAC1B,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,WAAW,cAAc,CAAC,SAAS,2BAA2B;QACxE,WAAW,EAAE;YACX,WAAW,EAAE,cAAc,CAAC,WAAW;YACvC,eAAe,EAAE,cAAc,CAAC,eAAe;SAChD;KACF,CAAC,CAAA;IAEF,gBAAgB,GAAG,UAAU,CAAA;IAE7B,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,YAAY,GAAG,IAAI,CAAA;IACnB,gBAAgB,GAAG,IAAI,CAAA;AACzB,CAAC;AAED,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAA2B;IACvD,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,OAAO,cAAc,CAAC,UAAU,CAAA;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA2B;IAC1D,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,OAAO,cAAc,CAAC,SAAS,CAAA;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAA2B;IACpD,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,OAAO,cAAc,CAAC,OAAO,CAAA;AAC/B,CAAC"}
|