@pigment/auto-translate 1.2.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/LICENSE +22 -0
- package/README.md +405 -0
- package/dist/collections/translationExclusions.d.ts +2 -0
- package/dist/collections/translationExclusions.js +78 -0
- package/dist/collections/translationExclusions.js.map +1 -0
- package/dist/components/TranslationControl.css +92 -0
- package/dist/components/TranslationControl.d.ts +18 -0
- package/dist/components/TranslationControl.js +274 -0
- package/dist/components/TranslationControl.js.map +1 -0
- package/dist/exports/client.d.ts +5 -0
- package/dist/exports/client.js +5 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +5 -0
- package/dist/exports/rsc.js +5 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/globals/translationSettings.d.ts +2 -0
- package/dist/globals/translationSettings.js +78 -0
- package/dist/globals/translationSettings.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +254 -0
- package/dist/index.js.map +1 -0
- package/dist/services/translationService.d.ts +60 -0
- package/dist/services/translationService.js +533 -0
- package/dist/services/translationService.js.map +1 -0
- package/dist/types/index.d.ts +103 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utilities/fieldHelpers.d.ts +34 -0
- package/dist/utilities/fieldHelpers.js +180 -0
- package/dist/utilities/fieldHelpers.js.map +1 -0
- package/dist/utilities/injectTranslationControls.d.ts +5 -0
- package/dist/utilities/injectTranslationControls.js +92 -0
- package/dist/utilities/injectTranslationControls.js.map +1 -0
- package/package.json +114 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { filterExcludedPaths } from '../utilities/fieldHelpers.js';
|
|
3
|
+
export class TranslationService {
|
|
4
|
+
client;
|
|
5
|
+
config;
|
|
6
|
+
constructor(config){
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Extracts translatable text from lexical editor nodes
|
|
11
|
+
*/ extractFromLexicalNode(node, path, strings, deduplicationMap) {
|
|
12
|
+
const enableDeduplication = this.config.enableDeduplication !== false // Default to true
|
|
13
|
+
;
|
|
14
|
+
// Handle text nodes - skip whitespace-only or very short text
|
|
15
|
+
if (node.type === 'text' && node.text && typeof node.text === 'string') {
|
|
16
|
+
const trimmed = node.text.trim();
|
|
17
|
+
// Skip if empty, whitespace-only, or too short
|
|
18
|
+
if (trimmed.length === 0 || this.shouldSkipString(node.text, `${path}.text`)) {
|
|
19
|
+
return node;
|
|
20
|
+
}
|
|
21
|
+
const textPath = `${path}.text`;
|
|
22
|
+
if (enableDeduplication) {
|
|
23
|
+
// Check for deduplication
|
|
24
|
+
if (deduplicationMap.has(trimmed)) {
|
|
25
|
+
// This string already exists, just store the path mapping
|
|
26
|
+
const existingPaths = deduplicationMap.get(trimmed);
|
|
27
|
+
existingPaths.push(textPath);
|
|
28
|
+
return {
|
|
29
|
+
...node,
|
|
30
|
+
text: `__TRANSLATE_${textPath}__`
|
|
31
|
+
};
|
|
32
|
+
} else {
|
|
33
|
+
// New unique string
|
|
34
|
+
strings.set(textPath, node.text);
|
|
35
|
+
deduplicationMap.set(trimmed, [
|
|
36
|
+
textPath
|
|
37
|
+
]);
|
|
38
|
+
return {
|
|
39
|
+
...node,
|
|
40
|
+
text: `__TRANSLATE_${textPath}__`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
// No deduplication - add every string
|
|
45
|
+
strings.set(textPath, node.text);
|
|
46
|
+
deduplicationMap.set(trimmed, [
|
|
47
|
+
textPath
|
|
48
|
+
]);
|
|
49
|
+
return {
|
|
50
|
+
...node,
|
|
51
|
+
text: `__TRANSLATE_${textPath}__`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Handle nodes with children
|
|
56
|
+
if (node.children && Array.isArray(node.children)) {
|
|
57
|
+
return {
|
|
58
|
+
...node,
|
|
59
|
+
children: node.children.map((child, index)=>this.extractFromLexicalNode(child, `${path}.children[${index}]`, strings, deduplicationMap))
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return node;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Extracts translatable strings from data structure
|
|
66
|
+
* Returns a map of paths to translatable values and metadata for reconstruction
|
|
67
|
+
*/ extractTranslatableStrings(data, path = '') {
|
|
68
|
+
const strings = new Map();
|
|
69
|
+
const deduplicationMap = new Map() // value -> [paths]
|
|
70
|
+
;
|
|
71
|
+
const enableDeduplication = this.config.enableDeduplication !== false // Default to true
|
|
72
|
+
;
|
|
73
|
+
const extract = (obj, currentPath)=>{
|
|
74
|
+
if (obj === null || obj === undefined) {
|
|
75
|
+
return obj;
|
|
76
|
+
}
|
|
77
|
+
// Handle lexical editor format
|
|
78
|
+
if (this.isLexicalEditorNode(obj)) {
|
|
79
|
+
return this.extractFromLexicalNode(obj, currentPath, strings, deduplicationMap);
|
|
80
|
+
}
|
|
81
|
+
// Handle arrays
|
|
82
|
+
if (Array.isArray(obj)) {
|
|
83
|
+
return obj.map((item, index)=>extract(item, `${currentPath}[${index}]`));
|
|
84
|
+
}
|
|
85
|
+
// Handle objects
|
|
86
|
+
if (typeof obj === 'object') {
|
|
87
|
+
const result = {};
|
|
88
|
+
for (const [key, value] of Object.entries(obj)){
|
|
89
|
+
const newPath = currentPath ? `${currentPath}.${key}` : key;
|
|
90
|
+
result[key] = extract(value, newPath);
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
// Handle strings
|
|
95
|
+
if (typeof obj === 'string' && obj.trim().length > 0) {
|
|
96
|
+
// Skip IDs and other non-translatable strings
|
|
97
|
+
if (!this.shouldSkipString(obj, currentPath)) {
|
|
98
|
+
if (enableDeduplication) {
|
|
99
|
+
// Check for deduplication
|
|
100
|
+
const trimmedValue = obj.trim();
|
|
101
|
+
if (deduplicationMap.has(trimmedValue)) {
|
|
102
|
+
// This string already exists, just store the path mapping
|
|
103
|
+
const existingPaths = deduplicationMap.get(trimmedValue);
|
|
104
|
+
existingPaths.push(currentPath);
|
|
105
|
+
return `__TRANSLATE_${currentPath}__`;
|
|
106
|
+
} else {
|
|
107
|
+
// New unique string
|
|
108
|
+
strings.set(currentPath, obj);
|
|
109
|
+
deduplicationMap.set(trimmedValue, [
|
|
110
|
+
currentPath
|
|
111
|
+
]);
|
|
112
|
+
return `__TRANSLATE_${currentPath}__`;
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
// No deduplication - add every string
|
|
116
|
+
strings.set(currentPath, obj);
|
|
117
|
+
deduplicationMap.set(obj.trim(), [
|
|
118
|
+
currentPath
|
|
119
|
+
]);
|
|
120
|
+
return `__TRANSLATE_${currentPath}__`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return obj;
|
|
125
|
+
};
|
|
126
|
+
const metadata = extract(data, path);
|
|
127
|
+
return {
|
|
128
|
+
deduplicationMap,
|
|
129
|
+
metadata,
|
|
130
|
+
strings
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Lazily initialize OpenAI client only when needed
|
|
135
|
+
*/ getOpenAIClient() {
|
|
136
|
+
if (!this.client) {
|
|
137
|
+
const apiKey = this.config.provider?.apiKey || process.env.OPENAI_API_KEY;
|
|
138
|
+
if (!apiKey) {
|
|
139
|
+
throw new Error('OpenAI API key is required. Set OPENAI_API_KEY environment variable or provide it in plugin config.');
|
|
140
|
+
}
|
|
141
|
+
this.client = new OpenAI({
|
|
142
|
+
apiKey,
|
|
143
|
+
baseURL: this.config.provider?.baseURL || process.env.OPENAI_BASE_URL
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return this.client;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Gets the original value at a path in metadata (helper for deduplication)
|
|
150
|
+
*/ getOriginalValue(metadata, path) {
|
|
151
|
+
try {
|
|
152
|
+
const parts = path.split(/[.[\]]/).filter(Boolean);
|
|
153
|
+
let current = metadata;
|
|
154
|
+
for (const part of parts){
|
|
155
|
+
if (current === null || current === undefined) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
current = current[part];
|
|
159
|
+
}
|
|
160
|
+
return typeof current === 'string' ? current : null;
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Checks if an object is a lexical editor node
|
|
167
|
+
*/ isLexicalEditorNode(obj) {
|
|
168
|
+
return obj && typeof obj === 'object' && 'type' in obj && 'version' in obj && ('children' in obj || 'text' in obj);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Reconstructs data with translated strings, applying deduplicated translations
|
|
172
|
+
*/ reconstructWithTranslations(metadata, translations, deduplicationMap) {
|
|
173
|
+
// Build a comprehensive translation map including deduplicated paths
|
|
174
|
+
const fullTranslations = new Map();
|
|
175
|
+
// For each unique string that was translated
|
|
176
|
+
translations.forEach((translatedValue, originalPath)=>{
|
|
177
|
+
fullTranslations.set(originalPath, translatedValue);
|
|
178
|
+
// Find all paths that had the same original value
|
|
179
|
+
const originalValue = this.getOriginalValue(metadata, originalPath);
|
|
180
|
+
if (originalValue) {
|
|
181
|
+
const trimmed = originalValue.replace(/^__TRANSLATE_(.+)__$/, '$1');
|
|
182
|
+
// Look through deduplication map to find all paths with same value
|
|
183
|
+
for (const [value, paths] of deduplicationMap.entries()){
|
|
184
|
+
if (paths.includes(originalPath)) {
|
|
185
|
+
// Apply the same translation to all paths with this value
|
|
186
|
+
paths.forEach((path)=>{
|
|
187
|
+
fullTranslations.set(path, translatedValue);
|
|
188
|
+
});
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
const reconstruct = (obj)=>{
|
|
195
|
+
if (obj === null || obj === undefined) {
|
|
196
|
+
return obj;
|
|
197
|
+
}
|
|
198
|
+
// Handle arrays
|
|
199
|
+
if (Array.isArray(obj)) {
|
|
200
|
+
return obj.map((item)=>reconstruct(item));
|
|
201
|
+
}
|
|
202
|
+
// Handle objects
|
|
203
|
+
if (typeof obj === 'object') {
|
|
204
|
+
const result = {};
|
|
205
|
+
for (const [key, value] of Object.entries(obj)){
|
|
206
|
+
result[key] = reconstruct(value);
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
// Replace translation placeholders
|
|
211
|
+
if (typeof obj === 'string' && obj.startsWith('__TRANSLATE_')) {
|
|
212
|
+
const path = obj.slice(12, -2) // Remove __TRANSLATE_ prefix and __ suffix
|
|
213
|
+
;
|
|
214
|
+
return fullTranslations.get(path) || obj;
|
|
215
|
+
}
|
|
216
|
+
return obj;
|
|
217
|
+
};
|
|
218
|
+
return reconstruct(metadata);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Determines if a string should be skipped from translation
|
|
222
|
+
*/ shouldSkipString(str, path) {
|
|
223
|
+
// Skip IDs (MongoDB ObjectIds and similar)
|
|
224
|
+
if (/^[a-f0-9]{24}$/i.test(str)) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
// Skip URLs
|
|
228
|
+
if (/^https?:\/\//.test(str)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
// Skip file paths
|
|
232
|
+
if (/^\/\S*\.(jpg|jpeg|png|gif|webp|svg|pdf|mp4|webm|ogg|mp3|wav)$/i.test(str)) {
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
// Skip email addresses
|
|
236
|
+
if (/^[^\s@]+@[^\s@][^\s.@]*\.[^\s@]+$/.test(str)) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
// Skip ISO date strings
|
|
240
|
+
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(str)) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
// Skip date-time strings like "2019-01-31 12:05:04"
|
|
244
|
+
if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(str)) {
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
// Skip percentages like "100%"
|
|
248
|
+
if (/^\d+%$/.test(str)) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
// Skip pure numbers
|
|
252
|
+
if (/^\d+$/.test(str)) {
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
// Skip whitespace-only strings (including single spaces)
|
|
256
|
+
if (str.trim().length === 0) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
// Skip very short strings based on config (default: 3 characters)
|
|
260
|
+
const minLength = this.config.minStringLength !== undefined ? this.config.minStringLength : 3;
|
|
261
|
+
if (str.trim().length < minLength) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
// Skip status values
|
|
265
|
+
if ([
|
|
266
|
+
'archived',
|
|
267
|
+
'draft',
|
|
268
|
+
'pending',
|
|
269
|
+
'published'
|
|
270
|
+
].includes(str.toLowerCase())) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
// Skip paths ending with id, createdAt, updatedAt, etc.
|
|
274
|
+
const pathLower = path.toLowerCase();
|
|
275
|
+
if (pathLower.endsWith('id') || pathLower.endsWith('_id') || pathLower.includes('createdat') || pathLower.includes('updatedat')) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Translates using OpenAI API (optimized version)
|
|
282
|
+
*/ async translateWithOpenAI(data, fromLocale, toLocale) {
|
|
283
|
+
const client = this.getOpenAIClient();
|
|
284
|
+
const model = this.config.provider?.model || 'gpt-4o';
|
|
285
|
+
// Use optimization by default (can be disabled via config)
|
|
286
|
+
const useOptimization = this.config.optimizeTranslation !== false;
|
|
287
|
+
if (!useOptimization) {
|
|
288
|
+
// Use legacy approach: send entire structure
|
|
289
|
+
return this.translateWithOpenAILegacy(data, fromLocale, toLocale);
|
|
290
|
+
}
|
|
291
|
+
// Extract only translatable strings with deduplication
|
|
292
|
+
const { deduplicationMap, metadata, strings } = this.extractTranslatableStrings(data);
|
|
293
|
+
if (strings.size === 0) {
|
|
294
|
+
// Nothing to translate
|
|
295
|
+
return data;
|
|
296
|
+
}
|
|
297
|
+
// Create a simple object with just the strings to translate
|
|
298
|
+
const stringsToTranslate = {};
|
|
299
|
+
strings.forEach((value, key)=>{
|
|
300
|
+
stringsToTranslate[key] = value;
|
|
301
|
+
});
|
|
302
|
+
if (this.config.debugging) {
|
|
303
|
+
const originalSize = JSON.stringify(data).length;
|
|
304
|
+
const optimizedSize = JSON.stringify(stringsToTranslate).length;
|
|
305
|
+
const reduction = ((1 - optimizedSize / originalSize) * 100).toFixed(1);
|
|
306
|
+
// Calculate deduplication stats
|
|
307
|
+
let totalPaths = 0;
|
|
308
|
+
deduplicationMap.forEach((paths)=>{
|
|
309
|
+
totalPaths += paths.length;
|
|
310
|
+
});
|
|
311
|
+
const deduplicationSavings = totalPaths - strings.size;
|
|
312
|
+
const deduplicationPercent = totalPaths > 0 ? (deduplicationSavings / totalPaths * 100).toFixed(1) : '0';
|
|
313
|
+
console.log('[Auto-Translate] ✨ Optimization Stats:');
|
|
314
|
+
console.log(` 📊 Unique strings to translate: ${strings.size}`);
|
|
315
|
+
console.log(` 🔄 Total string instances: ${totalPaths}`);
|
|
316
|
+
console.log(` 💾 Deduplication savings: ${deduplicationSavings} strings (${deduplicationPercent}%)`);
|
|
317
|
+
console.log(` 📦 Original JSON size: ${originalSize.toLocaleString()} bytes`);
|
|
318
|
+
console.log(` 📦 Optimized JSON size: ${optimizedSize.toLocaleString()} bytes`);
|
|
319
|
+
console.log(` 🎯 Total size reduction: ${reduction}%`);
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
const response = await client.chat.completions.create({
|
|
323
|
+
messages: [
|
|
324
|
+
{
|
|
325
|
+
content: `You are a professional translator. Translate the values in this JSON object from ${fromLocale} to ${toLocale}.
|
|
326
|
+
Rules:
|
|
327
|
+
- Only translate the values, never the keys
|
|
328
|
+
- Preserve the exact JSON structure
|
|
329
|
+
- Maintain formatting, HTML tags, and special characters
|
|
330
|
+
- Return only valid JSON without any markdown formatting or code blocks
|
|
331
|
+
- If a value is already in the target language or is a proper noun, keep it as is`,
|
|
332
|
+
role: 'system'
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
content: JSON.stringify(stringsToTranslate, null, 2),
|
|
336
|
+
role: 'user'
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
model,
|
|
340
|
+
response_format: {
|
|
341
|
+
type: 'json_object'
|
|
342
|
+
},
|
|
343
|
+
temperature: 0.3
|
|
344
|
+
});
|
|
345
|
+
const translatedText = response.choices[0]?.message?.content;
|
|
346
|
+
if (!translatedText) {
|
|
347
|
+
throw new Error('No translation received from OpenAI');
|
|
348
|
+
}
|
|
349
|
+
const translatedStrings = JSON.parse(translatedText);
|
|
350
|
+
// Convert back to Map
|
|
351
|
+
const translationsMap = new Map();
|
|
352
|
+
for (const [key, value] of Object.entries(translatedStrings)){
|
|
353
|
+
if (typeof value === 'string') {
|
|
354
|
+
translationsMap.set(key, value);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Reconstruct the full data structure with translations, applying deduplication
|
|
358
|
+
return this.reconstructWithTranslations(metadata, translationsMap, deduplicationMap);
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.error('[Auto-Translate] Translation error:', error);
|
|
361
|
+
throw error;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Legacy translation method (sends entire structure)
|
|
366
|
+
*/ async translateWithOpenAILegacy(data, fromLocale, toLocale) {
|
|
367
|
+
const client = this.getOpenAIClient();
|
|
368
|
+
const model = this.config.provider?.model || 'gpt-4o';
|
|
369
|
+
try {
|
|
370
|
+
const response = await client.chat.completions.create({
|
|
371
|
+
messages: [
|
|
372
|
+
{
|
|
373
|
+
content: `You are a professional translator. Translate the JSON object values from ${fromLocale} to ${toLocale}.
|
|
374
|
+
Rules:
|
|
375
|
+
- Only translate the values, never the keys
|
|
376
|
+
- Preserve the exact JSON structure
|
|
377
|
+
- Do not translate field names like 'id', 'createdAt', 'updatedAt', etc.
|
|
378
|
+
- Maintain formatting, HTML tags, and special characters
|
|
379
|
+
- Return only valid JSON without any markdown formatting or code blocks
|
|
380
|
+
- If a value is already in the target language or is a proper noun, keep it as is`,
|
|
381
|
+
role: 'system'
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
content: JSON.stringify(data, null, 2),
|
|
385
|
+
role: 'user'
|
|
386
|
+
}
|
|
387
|
+
],
|
|
388
|
+
model,
|
|
389
|
+
response_format: {
|
|
390
|
+
type: 'json_object'
|
|
391
|
+
},
|
|
392
|
+
temperature: 0.3
|
|
393
|
+
});
|
|
394
|
+
const translatedText = response.choices[0]?.message?.content;
|
|
395
|
+
if (!translatedText) {
|
|
396
|
+
throw new Error('No translation received from OpenAI');
|
|
397
|
+
}
|
|
398
|
+
return JSON.parse(translatedText);
|
|
399
|
+
} catch (error) {
|
|
400
|
+
console.error('[Auto-Translate] Translation error:', error);
|
|
401
|
+
throw error;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Gets global and collection-specific excluded fields
|
|
406
|
+
*/ getConfigExcludedFields(collection) {
|
|
407
|
+
const globalExclusions = this.config.excludeFields || [];
|
|
408
|
+
const collectionConfig = this.config.collections?.[collection];
|
|
409
|
+
if (typeof collectionConfig === 'object' && collectionConfig.excludeFields) {
|
|
410
|
+
return [
|
|
411
|
+
...globalExclusions,
|
|
412
|
+
...collectionConfig.excludeFields
|
|
413
|
+
];
|
|
414
|
+
}
|
|
415
|
+
return globalExclusions;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Gets translation exclusions for a document
|
|
419
|
+
*/ async getExclusions(payload, collection, documentId, locale) {
|
|
420
|
+
const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions';
|
|
421
|
+
try {
|
|
422
|
+
const result = await payload.find({
|
|
423
|
+
collection: exclusionsSlug,
|
|
424
|
+
limit: 1,
|
|
425
|
+
where: {
|
|
426
|
+
and: [
|
|
427
|
+
{
|
|
428
|
+
collection: {
|
|
429
|
+
equals: collection
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
documentId: {
|
|
434
|
+
equals: documentId
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
locale: {
|
|
439
|
+
equals: locale
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
]
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
if (result.docs.length > 0) {
|
|
446
|
+
const exclusion = result.docs[0];
|
|
447
|
+
return exclusion.excludedPaths?.map((item)=>item.path) || [];
|
|
448
|
+
}
|
|
449
|
+
return [];
|
|
450
|
+
} catch (error) {
|
|
451
|
+
if (this.config.debugging) {
|
|
452
|
+
payload.logger.error(`[Auto-Translate] Error fetching exclusions: ${error}`);
|
|
453
|
+
}
|
|
454
|
+
return [];
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Main translation method
|
|
459
|
+
*/ async translate(options) {
|
|
460
|
+
const { collection, data, excludedPaths = [], fromLocale, payload, toLocale } = options;
|
|
461
|
+
// Filter out excluded paths before translation
|
|
462
|
+
const dataToTranslate = filterExcludedPaths(data, excludedPaths);
|
|
463
|
+
if (this.config.debugging) {
|
|
464
|
+
payload.logger.info(`[Auto-Translate] Translating from ${fromLocale} to ${toLocale} for collection ${collection}`);
|
|
465
|
+
payload.logger.info(`[Auto-Translate] Excluded paths: ${excludedPaths.join(', ')}`);
|
|
466
|
+
}
|
|
467
|
+
// Use custom translator if provided
|
|
468
|
+
if (this.config.provider?.customTranslate) {
|
|
469
|
+
return await this.config.provider.customTranslate(options);
|
|
470
|
+
}
|
|
471
|
+
// Use OpenAI by default
|
|
472
|
+
return await this.translateWithOpenAI(dataToTranslate, fromLocale, toLocale);
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Updates translation exclusions for a document
|
|
476
|
+
*/ async updateExclusions(payload, collection, documentId, locale, excludedPaths) {
|
|
477
|
+
const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions';
|
|
478
|
+
try {
|
|
479
|
+
const existing = await payload.find({
|
|
480
|
+
collection: exclusionsSlug,
|
|
481
|
+
limit: 1,
|
|
482
|
+
where: {
|
|
483
|
+
and: [
|
|
484
|
+
{
|
|
485
|
+
collection: {
|
|
486
|
+
equals: collection
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
documentId: {
|
|
491
|
+
equals: documentId
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
locale: {
|
|
496
|
+
equals: locale
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
]
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
const exclusionsData = {
|
|
503
|
+
collection,
|
|
504
|
+
documentId,
|
|
505
|
+
excludedPaths: excludedPaths.map((path)=>({
|
|
506
|
+
path
|
|
507
|
+
})),
|
|
508
|
+
locale
|
|
509
|
+
};
|
|
510
|
+
if (existing.docs.length > 0) {
|
|
511
|
+
await payload.update({
|
|
512
|
+
id: existing.docs[0].id,
|
|
513
|
+
collection: exclusionsSlug,
|
|
514
|
+
data: exclusionsData
|
|
515
|
+
});
|
|
516
|
+
} else {
|
|
517
|
+
await payload.create({
|
|
518
|
+
collection: exclusionsSlug,
|
|
519
|
+
data: exclusionsData
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (this.config.debugging) {
|
|
523
|
+
payload.logger.info(`[Auto-Translate] Updated exclusions for ${collection}:${documentId}:${locale}`);
|
|
524
|
+
}
|
|
525
|
+
} catch (error) {
|
|
526
|
+
if (this.config.debugging) {
|
|
527
|
+
payload.logger.error(`[Auto-Translate] Error updating exclusions: ${error}`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
//# sourceMappingURL=translationService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/translationService.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport OpenAI from 'openai'\n\nimport type { AutoTranslateConfig, TranslateOptions } from '../types/index.js'\n\nimport { filterExcludedPaths } from '../utilities/fieldHelpers.js'\n\nexport class TranslationService {\n private client?: OpenAI\n private config: AutoTranslateConfig\n\n constructor(config: AutoTranslateConfig) {\n this.config = config\n }\n\n /**\n * Extracts translatable text from lexical editor nodes\n */\n private extractFromLexicalNode(\n node: any,\n path: string,\n strings: Map<string, string>,\n deduplicationMap: Map<string, string[]>,\n ): any {\n const enableDeduplication = this.config.enableDeduplication !== false // Default to true\n\n // Handle text nodes - skip whitespace-only or very short text\n if (node.type === 'text' && node.text && typeof node.text === 'string') {\n const trimmed = node.text.trim()\n\n // Skip if empty, whitespace-only, or too short\n if (trimmed.length === 0 || this.shouldSkipString(node.text, `${path}.text`)) {\n return node\n }\n\n const textPath = `${path}.text`\n\n if (enableDeduplication) {\n // Check for deduplication\n if (deduplicationMap.has(trimmed)) {\n // This string already exists, just store the path mapping\n const existingPaths = deduplicationMap.get(trimmed)!\n existingPaths.push(textPath)\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n } else {\n // New unique string\n strings.set(textPath, node.text)\n deduplicationMap.set(trimmed, [textPath])\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n }\n } else {\n // No deduplication - add every string\n strings.set(textPath, node.text)\n deduplicationMap.set(trimmed, [textPath])\n return { ...node, text: `__TRANSLATE_${textPath}__` }\n }\n }\n\n // Handle nodes with children\n if (node.children && Array.isArray(node.children)) {\n return {\n ...node,\n children: node.children.map((child: any, index: number) =>\n this.extractFromLexicalNode(\n child,\n `${path}.children[${index}]`,\n strings,\n deduplicationMap,\n ),\n ),\n }\n }\n\n return node\n }\n\n /**\n * Extracts translatable strings from data structure\n * Returns a map of paths to translatable values and metadata for reconstruction\n */\n private extractTranslatableStrings(\n data: any,\n path: string = '',\n ): { deduplicationMap: Map<string, string[]>; metadata: any; strings: Map<string, string> } {\n const strings = new Map<string, string>()\n const deduplicationMap = new Map<string, string[]>() // value -> [paths]\n const enableDeduplication = this.config.enableDeduplication !== false // Default to true\n\n const extract = (obj: any, currentPath: string): any => {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle lexical editor format\n if (this.isLexicalEditorNode(obj)) {\n return this.extractFromLexicalNode(obj, currentPath, strings, deduplicationMap)\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item, index) => extract(item, `${currentPath}[${index}]`))\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {}\n for (const [key, value] of Object.entries(obj)) {\n const newPath = currentPath ? `${currentPath}.${key}` : key\n result[key] = extract(value, newPath)\n }\n return result\n }\n\n // Handle strings\n if (typeof obj === 'string' && obj.trim().length > 0) {\n // Skip IDs and other non-translatable strings\n if (!this.shouldSkipString(obj, currentPath)) {\n if (enableDeduplication) {\n // Check for deduplication\n const trimmedValue = obj.trim()\n if (deduplicationMap.has(trimmedValue)) {\n // This string already exists, just store the path mapping\n const existingPaths = deduplicationMap.get(trimmedValue)!\n existingPaths.push(currentPath)\n return `__TRANSLATE_${currentPath}__`\n } else {\n // New unique string\n strings.set(currentPath, obj)\n deduplicationMap.set(trimmedValue, [currentPath])\n return `__TRANSLATE_${currentPath}__`\n }\n } else {\n // No deduplication - add every string\n strings.set(currentPath, obj)\n deduplicationMap.set(obj.trim(), [currentPath])\n return `__TRANSLATE_${currentPath}__`\n }\n }\n }\n\n return obj\n }\n\n const metadata = extract(data, path)\n return { deduplicationMap, metadata, strings }\n }\n\n /**\n * Lazily initialize OpenAI client only when needed\n */\n private getOpenAIClient(): OpenAI {\n if (!this.client) {\n const apiKey = this.config.provider?.apiKey || process.env.OPENAI_API_KEY\n if (!apiKey) {\n throw new Error(\n 'OpenAI API key is required. Set OPENAI_API_KEY environment variable or provide it in plugin config.',\n )\n }\n\n this.client = new OpenAI({\n apiKey,\n baseURL: this.config.provider?.baseURL || process.env.OPENAI_BASE_URL,\n })\n }\n return this.client\n }\n\n /**\n * Gets the original value at a path in metadata (helper for deduplication)\n */\n private getOriginalValue(metadata: any, path: string): null | string {\n try {\n const parts = path.split(/[.[\\]]/).filter(Boolean)\n let current = metadata\n for (const part of parts) {\n if (current === null || current === undefined) {\n return null\n }\n current = current[part]\n }\n return typeof current === 'string' ? current : null\n } catch {\n return null\n }\n }\n\n /**\n * Checks if an object is a lexical editor node\n */\n private isLexicalEditorNode(obj: any): boolean {\n return (\n obj &&\n typeof obj === 'object' &&\n 'type' in obj &&\n 'version' in obj &&\n ('children' in obj || 'text' in obj)\n )\n }\n\n /**\n * Reconstructs data with translated strings, applying deduplicated translations\n */\n private reconstructWithTranslations(\n metadata: any,\n translations: Map<string, string>,\n deduplicationMap: Map<string, string[]>,\n ): any {\n // Build a comprehensive translation map including deduplicated paths\n const fullTranslations = new Map<string, string>()\n\n // For each unique string that was translated\n translations.forEach((translatedValue, originalPath) => {\n fullTranslations.set(originalPath, translatedValue)\n\n // Find all paths that had the same original value\n const originalValue = this.getOriginalValue(metadata, originalPath)\n if (originalValue) {\n const trimmed = originalValue.replace(/^__TRANSLATE_(.+)__$/, '$1')\n // Look through deduplication map to find all paths with same value\n for (const [value, paths] of deduplicationMap.entries()) {\n if (paths.includes(originalPath)) {\n // Apply the same translation to all paths with this value\n paths.forEach((path) => {\n fullTranslations.set(path, translatedValue)\n })\n break\n }\n }\n }\n })\n\n const reconstruct = (obj: any): any => {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => reconstruct(item))\n }\n\n // Handle objects\n if (typeof obj === 'object') {\n const result: any = {}\n for (const [key, value] of Object.entries(obj)) {\n result[key] = reconstruct(value)\n }\n return result\n }\n\n // Replace translation placeholders\n if (typeof obj === 'string' && obj.startsWith('__TRANSLATE_')) {\n const path = obj.slice(12, -2) // Remove __TRANSLATE_ prefix and __ suffix\n return fullTranslations.get(path) || obj\n }\n\n return obj\n }\n\n return reconstruct(metadata)\n }\n\n /**\n * Determines if a string should be skipped from translation\n */\n private shouldSkipString(str: string, path: string): boolean {\n // Skip IDs (MongoDB ObjectIds and similar)\n if (/^[a-f0-9]{24}$/i.test(str)) {\n return true\n }\n\n // Skip URLs\n if (/^https?:\\/\\//.test(str)) {\n return true\n }\n\n // Skip file paths\n if (/^\\/\\S*\\.(jpg|jpeg|png|gif|webp|svg|pdf|mp4|webm|ogg|mp3|wav)$/i.test(str)) {\n return true\n }\n\n // Skip email addresses\n if (/^[^\\s@]+@[^\\s@][^\\s.@]*\\.[^\\s@]+$/.test(str)) {\n return true\n }\n\n // Skip ISO date strings\n if (/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/.test(str)) {\n return true\n }\n\n // Skip date-time strings like \"2019-01-31 12:05:04\"\n if (/^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/.test(str)) {\n return true\n }\n\n // Skip percentages like \"100%\"\n if (/^\\d+%$/.test(str)) {\n return true\n }\n\n // Skip pure numbers\n if (/^\\d+$/.test(str)) {\n return true\n }\n\n // Skip whitespace-only strings (including single spaces)\n if (str.trim().length === 0) {\n return true\n }\n\n // Skip very short strings based on config (default: 3 characters)\n const minLength = this.config.minStringLength !== undefined ? this.config.minStringLength : 3\n if (str.trim().length < minLength) {\n return true\n }\n\n // Skip status values\n if (['archived', 'draft', 'pending', 'published'].includes(str.toLowerCase())) {\n return true\n }\n\n // Skip paths ending with id, createdAt, updatedAt, etc.\n const pathLower = path.toLowerCase()\n if (\n pathLower.endsWith('id') ||\n pathLower.endsWith('_id') ||\n pathLower.includes('createdat') ||\n pathLower.includes('updatedat')\n ) {\n return true\n }\n\n return false\n }\n\n /**\n * Translates using OpenAI API (optimized version)\n */\n private async translateWithOpenAI(data: any, fromLocale: string, toLocale: string): Promise<any> {\n const client = this.getOpenAIClient()\n const model = this.config.provider?.model || 'gpt-4o'\n\n // Use optimization by default (can be disabled via config)\n const useOptimization = this.config.optimizeTranslation !== false\n\n if (!useOptimization) {\n // Use legacy approach: send entire structure\n return this.translateWithOpenAILegacy(data, fromLocale, toLocale)\n }\n\n // Extract only translatable strings with deduplication\n const { deduplicationMap, metadata, strings } = this.extractTranslatableStrings(data)\n\n if (strings.size === 0) {\n // Nothing to translate\n return data\n }\n\n // Create a simple object with just the strings to translate\n const stringsToTranslate: Record<string, string> = {}\n strings.forEach((value, key) => {\n stringsToTranslate[key] = value\n })\n\n if (this.config.debugging) {\n const originalSize = JSON.stringify(data).length\n const optimizedSize = JSON.stringify(stringsToTranslate).length\n const reduction = ((1 - optimizedSize / originalSize) * 100).toFixed(1)\n\n // Calculate deduplication stats\n let totalPaths = 0\n deduplicationMap.forEach((paths) => {\n totalPaths += paths.length\n })\n const deduplicationSavings = totalPaths - strings.size\n const deduplicationPercent =\n totalPaths > 0 ? ((deduplicationSavings / totalPaths) * 100).toFixed(1) : '0'\n\n console.log('[Auto-Translate] ✨ Optimization Stats:')\n console.log(` 📊 Unique strings to translate: ${strings.size}`)\n console.log(` 🔄 Total string instances: ${totalPaths}`)\n console.log(\n ` 💾 Deduplication savings: ${deduplicationSavings} strings (${deduplicationPercent}%)`,\n )\n console.log(` 📦 Original JSON size: ${originalSize.toLocaleString()} bytes`)\n console.log(` 📦 Optimized JSON size: ${optimizedSize.toLocaleString()} bytes`)\n console.log(` 🎯 Total size reduction: ${reduction}%`)\n }\n\n try {\n const response = await client.chat.completions.create({\n messages: [\n {\n content: `You are a professional translator. Translate the values in this JSON object from ${fromLocale} to ${toLocale}. \n Rules:\n - Only translate the values, never the keys\n - Preserve the exact JSON structure\n - Maintain formatting, HTML tags, and special characters\n - Return only valid JSON without any markdown formatting or code blocks\n - If a value is already in the target language or is a proper noun, keep it as is`,\n role: 'system',\n },\n {\n content: JSON.stringify(stringsToTranslate, null, 2),\n role: 'user',\n },\n ],\n model,\n response_format: { type: 'json_object' },\n temperature: 0.3,\n })\n\n const translatedText = response.choices[0]?.message?.content\n\n if (!translatedText) {\n throw new Error('No translation received from OpenAI')\n }\n\n const translatedStrings = JSON.parse(translatedText)\n\n // Convert back to Map\n const translationsMap = new Map<string, string>()\n for (const [key, value] of Object.entries(translatedStrings)) {\n if (typeof value === 'string') {\n translationsMap.set(key, value)\n }\n }\n\n // Reconstruct the full data structure with translations, applying deduplication\n return this.reconstructWithTranslations(metadata, translationsMap, deduplicationMap)\n } catch (error) {\n console.error('[Auto-Translate] Translation error:', error)\n throw error\n }\n }\n\n /**\n * Legacy translation method (sends entire structure)\n */\n private async translateWithOpenAILegacy(\n data: any,\n fromLocale: string,\n toLocale: string,\n ): Promise<any> {\n const client = this.getOpenAIClient()\n const model = this.config.provider?.model || 'gpt-4o'\n\n try {\n const response = await client.chat.completions.create({\n messages: [\n {\n content: `You are a professional translator. Translate the JSON object values from ${fromLocale} to ${toLocale}. \n Rules:\n - Only translate the values, never the keys\n - Preserve the exact JSON structure\n - Do not translate field names like 'id', 'createdAt', 'updatedAt', etc.\n - Maintain formatting, HTML tags, and special characters\n - Return only valid JSON without any markdown formatting or code blocks\n - If a value is already in the target language or is a proper noun, keep it as is`,\n role: 'system',\n },\n {\n content: JSON.stringify(data, null, 2),\n role: 'user',\n },\n ],\n model,\n response_format: { type: 'json_object' },\n temperature: 0.3,\n })\n\n const translatedText = response.choices[0]?.message?.content\n\n if (!translatedText) {\n throw new Error('No translation received from OpenAI')\n }\n\n return JSON.parse(translatedText)\n } catch (error) {\n console.error('[Auto-Translate] Translation error:', error)\n throw error\n }\n }\n\n /**\n * Gets global and collection-specific excluded fields\n */\n getConfigExcludedFields(collection: string): string[] {\n const globalExclusions = this.config.excludeFields || []\n const collectionConfig = this.config.collections?.[collection]\n\n if (typeof collectionConfig === 'object' && collectionConfig.excludeFields) {\n return [...globalExclusions, ...collectionConfig.excludeFields]\n }\n\n return globalExclusions\n }\n\n /**\n * Gets translation exclusions for a document\n */\n async getExclusions(\n payload: Payload,\n collection: string,\n documentId: string,\n locale: string,\n ): Promise<string[]> {\n const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions'\n\n try {\n const result = await payload.find({\n collection: exclusionsSlug,\n limit: 1,\n where: {\n and: [\n { collection: { equals: collection } },\n { documentId: { equals: documentId } },\n { locale: { equals: locale } },\n ],\n },\n })\n\n if (result.docs.length > 0) {\n const exclusion = result.docs[0] as any\n return exclusion.excludedPaths?.map((item: any) => item.path) || []\n }\n\n return []\n } catch (error) {\n if (this.config.debugging) {\n payload.logger.error(`[Auto-Translate] Error fetching exclusions: ${error}`)\n }\n return []\n }\n }\n\n /**\n * Main translation method\n */\n async translate(options: TranslateOptions): Promise<any> {\n const { collection, data, excludedPaths = [], fromLocale, payload, toLocale } = options\n\n // Filter out excluded paths before translation\n const dataToTranslate = filterExcludedPaths(data, excludedPaths)\n\n if (this.config.debugging) {\n payload.logger.info(\n `[Auto-Translate] Translating from ${fromLocale} to ${toLocale} for collection ${collection}`,\n )\n payload.logger.info(`[Auto-Translate] Excluded paths: ${excludedPaths.join(', ')}`)\n }\n\n // Use custom translator if provided\n if (this.config.provider?.customTranslate) {\n return await this.config.provider.customTranslate(options)\n }\n\n // Use OpenAI by default\n return await this.translateWithOpenAI(dataToTranslate, fromLocale, toLocale)\n }\n\n /**\n * Updates translation exclusions for a document\n */\n async updateExclusions(\n payload: Payload,\n collection: string,\n documentId: string,\n locale: string,\n excludedPaths: string[],\n ): Promise<void> {\n const exclusionsSlug = this.config.translationExclusionsSlug || 'translation-exclusions'\n\n try {\n const existing = await payload.find({\n collection: exclusionsSlug,\n limit: 1,\n where: {\n and: [\n { collection: { equals: collection } },\n { documentId: { equals: documentId } },\n { locale: { equals: locale } },\n ],\n },\n })\n\n const exclusionsData = {\n collection,\n documentId,\n excludedPaths: excludedPaths.map((path) => ({ path })),\n locale,\n }\n\n if (existing.docs.length > 0) {\n await payload.update({\n id: existing.docs[0].id,\n collection: exclusionsSlug,\n data: exclusionsData,\n })\n } else {\n await payload.create({\n collection: exclusionsSlug,\n data: exclusionsData,\n })\n }\n\n if (this.config.debugging) {\n payload.logger.info(\n `[Auto-Translate] Updated exclusions for ${collection}:${documentId}:${locale}`,\n )\n }\n } catch (error) {\n if (this.config.debugging) {\n payload.logger.error(`[Auto-Translate] Error updating exclusions: ${error}`)\n }\n }\n }\n}\n"],"names":["OpenAI","filterExcludedPaths","TranslationService","client","config","extractFromLexicalNode","node","path","strings","deduplicationMap","enableDeduplication","type","text","trimmed","trim","length","shouldSkipString","textPath","has","existingPaths","get","push","set","children","Array","isArray","map","child","index","extractTranslatableStrings","data","Map","extract","obj","currentPath","undefined","isLexicalEditorNode","item","result","key","value","Object","entries","newPath","trimmedValue","metadata","getOpenAIClient","apiKey","provider","process","env","OPENAI_API_KEY","Error","baseURL","OPENAI_BASE_URL","getOriginalValue","parts","split","filter","Boolean","current","part","reconstructWithTranslations","translations","fullTranslations","forEach","translatedValue","originalPath","originalValue","replace","paths","includes","reconstruct","startsWith","slice","str","test","minLength","minStringLength","toLowerCase","pathLower","endsWith","translateWithOpenAI","fromLocale","toLocale","model","useOptimization","optimizeTranslation","translateWithOpenAILegacy","size","stringsToTranslate","debugging","originalSize","JSON","stringify","optimizedSize","reduction","toFixed","totalPaths","deduplicationSavings","deduplicationPercent","console","log","toLocaleString","response","chat","completions","create","messages","content","role","response_format","temperature","translatedText","choices","message","translatedStrings","parse","translationsMap","error","getConfigExcludedFields","collection","globalExclusions","excludeFields","collectionConfig","collections","getExclusions","payload","documentId","locale","exclusionsSlug","translationExclusionsSlug","find","limit","where","and","equals","docs","exclusion","excludedPaths","logger","translate","options","dataToTranslate","info","join","customTranslate","updateExclusions","existing","exclusionsData","update","id"],"mappings":"AAEA,OAAOA,YAAY,SAAQ;AAI3B,SAASC,mBAAmB,QAAQ,+BAA8B;AAElE,OAAO,MAAMC;IACHC,OAAe;IACfC,OAA2B;IAEnC,YAAYA,MAA2B,CAAE;QACvC,IAAI,CAACA,MAAM,GAAGA;IAChB;IAEA;;GAEC,GACD,AAAQC,uBACNC,IAAS,EACTC,IAAY,EACZC,OAA4B,EAC5BC,gBAAuC,EAClC;QACL,MAAMC,sBAAsB,IAAI,CAACN,MAAM,CAACM,mBAAmB,KAAK,MAAM,kBAAkB;;QAExF,8DAA8D;QAC9D,IAAIJ,KAAKK,IAAI,KAAK,UAAUL,KAAKM,IAAI,IAAI,OAAON,KAAKM,IAAI,KAAK,UAAU;YACtE,MAAMC,UAAUP,KAAKM,IAAI,CAACE,IAAI;YAE9B,+CAA+C;YAC/C,IAAID,QAAQE,MAAM,KAAK,KAAK,IAAI,CAACC,gBAAgB,CAACV,KAAKM,IAAI,EAAE,GAAGL,KAAK,KAAK,CAAC,GAAG;gBAC5E,OAAOD;YACT;YAEA,MAAMW,WAAW,GAAGV,KAAK,KAAK,CAAC;YAE/B,IAAIG,qBAAqB;gBACvB,0BAA0B;gBAC1B,IAAID,iBAAiBS,GAAG,CAACL,UAAU;oBACjC,0DAA0D;oBAC1D,MAAMM,gBAAgBV,iBAAiBW,GAAG,CAACP;oBAC3CM,cAAcE,IAAI,CAACJ;oBACnB,OAAO;wBAAE,GAAGX,IAAI;wBAAEM,MAAM,CAAC,YAAY,EAAEK,SAAS,EAAE,CAAC;oBAAC;gBACtD,OAAO;oBACL,oBAAoB;oBACpBT,QAAQc,GAAG,CAACL,UAAUX,KAAKM,IAAI;oBAC/BH,iBAAiBa,GAAG,CAACT,SAAS;wBAACI;qBAAS;oBACxC,OAAO;wBAAE,GAAGX,IAAI;wBAAEM,MAAM,CAAC,YAAY,EAAEK,SAAS,EAAE,CAAC;oBAAC;gBACtD;YACF,OAAO;gBACL,sCAAsC;gBACtCT,QAAQc,GAAG,CAACL,UAAUX,KAAKM,IAAI;gBAC/BH,iBAAiBa,GAAG,CAACT,SAAS;oBAACI;iBAAS;gBACxC,OAAO;oBAAE,GAAGX,IAAI;oBAAEM,MAAM,CAAC,YAAY,EAAEK,SAAS,EAAE,CAAC;gBAAC;YACtD;QACF;QAEA,6BAA6B;QAC7B,IAAIX,KAAKiB,QAAQ,IAAIC,MAAMC,OAAO,CAACnB,KAAKiB,QAAQ,GAAG;YACjD,OAAO;gBACL,GAAGjB,IAAI;gBACPiB,UAAUjB,KAAKiB,QAAQ,CAACG,GAAG,CAAC,CAACC,OAAYC,QACvC,IAAI,CAACvB,sBAAsB,CACzBsB,OACA,GAAGpB,KAAK,UAAU,EAAEqB,MAAM,CAAC,CAAC,EAC5BpB,SACAC;YAGN;QACF;QAEA,OAAOH;IACT;IAEA;;;GAGC,GACD,AAAQuB,2BACNC,IAAS,EACTvB,OAAe,EAAE,EACyE;QAC1F,MAAMC,UAAU,IAAIuB;QACpB,MAAMtB,mBAAmB,IAAIsB,MAAwB,mBAAmB;;QACxE,MAAMrB,sBAAsB,IAAI,CAACN,MAAM,CAACM,mBAAmB,KAAK,MAAM,kBAAkB;;QAExF,MAAMsB,UAAU,CAACC,KAAUC;YACzB,IAAID,QAAQ,QAAQA,QAAQE,WAAW;gBACrC,OAAOF;YACT;YAEA,+BAA+B;YAC/B,IAAI,IAAI,CAACG,mBAAmB,CAACH,MAAM;gBACjC,OAAO,IAAI,CAAC5B,sBAAsB,CAAC4B,KAAKC,aAAa1B,SAASC;YAChE;YAEA,gBAAgB;YAChB,IAAIe,MAAMC,OAAO,CAACQ,MAAM;gBACtB,OAAOA,IAAIP,GAAG,CAAC,CAACW,MAAMT,QAAUI,QAAQK,MAAM,GAAGH,YAAY,CAAC,EAAEN,MAAM,CAAC,CAAC;YAC1E;YAEA,iBAAiB;YACjB,IAAI,OAAOK,QAAQ,UAAU;gBAC3B,MAAMK,SAAc,CAAC;gBACrB,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACT,KAAM;oBAC9C,MAAMU,UAAUT,cAAc,GAAGA,YAAY,CAAC,EAAEK,KAAK,GAAGA;oBACxDD,MAAM,CAACC,IAAI,GAAGP,QAAQQ,OAAOG;gBAC/B;gBACA,OAAOL;YACT;YAEA,iBAAiB;YACjB,IAAI,OAAOL,QAAQ,YAAYA,IAAInB,IAAI,GAAGC,MAAM,GAAG,GAAG;gBACpD,8CAA8C;gBAC9C,IAAI,CAAC,IAAI,CAACC,gBAAgB,CAACiB,KAAKC,cAAc;oBAC5C,IAAIxB,qBAAqB;wBACvB,0BAA0B;wBAC1B,MAAMkC,eAAeX,IAAInB,IAAI;wBAC7B,IAAIL,iBAAiBS,GAAG,CAAC0B,eAAe;4BACtC,0DAA0D;4BAC1D,MAAMzB,gBAAgBV,iBAAiBW,GAAG,CAACwB;4BAC3CzB,cAAcE,IAAI,CAACa;4BACnB,OAAO,CAAC,YAAY,EAAEA,YAAY,EAAE,CAAC;wBACvC,OAAO;4BACL,oBAAoB;4BACpB1B,QAAQc,GAAG,CAACY,aAAaD;4BACzBxB,iBAAiBa,GAAG,CAACsB,cAAc;gCAACV;6BAAY;4BAChD,OAAO,CAAC,YAAY,EAAEA,YAAY,EAAE,CAAC;wBACvC;oBACF,OAAO;wBACL,sCAAsC;wBACtC1B,QAAQc,GAAG,CAACY,aAAaD;wBACzBxB,iBAAiBa,GAAG,CAACW,IAAInB,IAAI,IAAI;4BAACoB;yBAAY;wBAC9C,OAAO,CAAC,YAAY,EAAEA,YAAY,EAAE,CAAC;oBACvC;gBACF;YACF;YAEA,OAAOD;QACT;QAEA,MAAMY,WAAWb,QAAQF,MAAMvB;QAC/B,OAAO;YAAEE;YAAkBoC;YAAUrC;QAAQ;IAC/C;IAEA;;GAEC,GACD,AAAQsC,kBAA0B;QAChC,IAAI,CAAC,IAAI,CAAC3C,MAAM,EAAE;YAChB,MAAM4C,SAAS,IAAI,CAAC3C,MAAM,CAAC4C,QAAQ,EAAED,UAAUE,QAAQC,GAAG,CAACC,cAAc;YACzE,IAAI,CAACJ,QAAQ;gBACX,MAAM,IAAIK,MACR;YAEJ;YAEA,IAAI,CAACjD,MAAM,GAAG,IAAIH,OAAO;gBACvB+C;gBACAM,SAAS,IAAI,CAACjD,MAAM,CAAC4C,QAAQ,EAAEK,WAAWJ,QAAQC,GAAG,CAACI,eAAe;YACvE;QACF;QACA,OAAO,IAAI,CAACnD,MAAM;IACpB;IAEA;;GAEC,GACD,AAAQoD,iBAAiBV,QAAa,EAAEtC,IAAY,EAAiB;QACnE,IAAI;YACF,MAAMiD,QAAQjD,KAAKkD,KAAK,CAAC,UAAUC,MAAM,CAACC;YAC1C,IAAIC,UAAUf;YACd,KAAK,MAAMgB,QAAQL,MAAO;gBACxB,IAAII,YAAY,QAAQA,YAAYzB,WAAW;oBAC7C,OAAO;gBACT;gBACAyB,UAAUA,OAAO,CAACC,KAAK;YACzB;YACA,OAAO,OAAOD,YAAY,WAAWA,UAAU;QACjD,EAAE,OAAM;YACN,OAAO;QACT;IACF;IAEA;;GAEC,GACD,AAAQxB,oBAAoBH,GAAQ,EAAW;QAC7C,OACEA,OACA,OAAOA,QAAQ,YACf,UAAUA,OACV,aAAaA,OACZ,CAAA,cAAcA,OAAO,UAAUA,GAAE;IAEtC;IAEA;;GAEC,GACD,AAAQ6B,4BACNjB,QAAa,EACbkB,YAAiC,EACjCtD,gBAAuC,EAClC;QACL,qEAAqE;QACrE,MAAMuD,mBAAmB,IAAIjC;QAE7B,6CAA6C;QAC7CgC,aAAaE,OAAO,CAAC,CAACC,iBAAiBC;YACrCH,iBAAiB1C,GAAG,CAAC6C,cAAcD;YAEnC,kDAAkD;YAClD,MAAME,gBAAgB,IAAI,CAACb,gBAAgB,CAACV,UAAUsB;YACtD,IAAIC,eAAe;gBACjB,MAAMvD,UAAUuD,cAAcC,OAAO,CAAC,wBAAwB;gBAC9D,mEAAmE;gBACnE,KAAK,MAAM,CAAC7B,OAAO8B,MAAM,IAAI7D,iBAAiBiC,OAAO,GAAI;oBACvD,IAAI4B,MAAMC,QAAQ,CAACJ,eAAe;wBAChC,0DAA0D;wBAC1DG,MAAML,OAAO,CAAC,CAAC1D;4BACbyD,iBAAiB1C,GAAG,CAACf,MAAM2D;wBAC7B;wBACA;oBACF;gBACF;YACF;QACF;QAEA,MAAMM,cAAc,CAACvC;YACnB,IAAIA,QAAQ,QAAQA,QAAQE,WAAW;gBACrC,OAAOF;YACT;YAEA,gBAAgB;YAChB,IAAIT,MAAMC,OAAO,CAACQ,MAAM;gBACtB,OAAOA,IAAIP,GAAG,CAAC,CAACW,OAASmC,YAAYnC;YACvC;YAEA,iBAAiB;YACjB,IAAI,OAAOJ,QAAQ,UAAU;gBAC3B,MAAMK,SAAc,CAAC;gBACrB,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACT,KAAM;oBAC9CK,MAAM,CAACC,IAAI,GAAGiC,YAAYhC;gBAC5B;gBACA,OAAOF;YACT;YAEA,mCAAmC;YACnC,IAAI,OAAOL,QAAQ,YAAYA,IAAIwC,UAAU,CAAC,iBAAiB;gBAC7D,MAAMlE,OAAO0B,IAAIyC,KAAK,CAAC,IAAI,CAAC,GAAG,2CAA2C;;gBAC1E,OAAOV,iBAAiB5C,GAAG,CAACb,SAAS0B;YACvC;YAEA,OAAOA;QACT;QAEA,OAAOuC,YAAY3B;IACrB;IAEA;;GAEC,GACD,AAAQ7B,iBAAiB2D,GAAW,EAAEpE,IAAY,EAAW;QAC3D,2CAA2C;QAC3C,IAAI,kBAAkBqE,IAAI,CAACD,MAAM;YAC/B,OAAO;QACT;QAEA,YAAY;QACZ,IAAI,eAAeC,IAAI,CAACD,MAAM;YAC5B,OAAO;QACT;QAEA,kBAAkB;QAClB,IAAI,iEAAiEC,IAAI,CAACD,MAAM;YAC9E,OAAO;QACT;QAEA,uBAAuB;QACvB,IAAI,oCAAoCC,IAAI,CAACD,MAAM;YACjD,OAAO;QACT;QAEA,wBAAwB;QACxB,IAAI,uCAAuCC,IAAI,CAACD,MAAM;YACpD,OAAO;QACT;QAEA,oDAAoD;QACpD,IAAI,wCAAwCC,IAAI,CAACD,MAAM;YACrD,OAAO;QACT;QAEA,+BAA+B;QAC/B,IAAI,SAASC,IAAI,CAACD,MAAM;YACtB,OAAO;QACT;QAEA,oBAAoB;QACpB,IAAI,QAAQC,IAAI,CAACD,MAAM;YACrB,OAAO;QACT;QAEA,yDAAyD;QACzD,IAAIA,IAAI7D,IAAI,GAAGC,MAAM,KAAK,GAAG;YAC3B,OAAO;QACT;QAEA,kEAAkE;QAClE,MAAM8D,YAAY,IAAI,CAACzE,MAAM,CAAC0E,eAAe,KAAK3C,YAAY,IAAI,CAAC/B,MAAM,CAAC0E,eAAe,GAAG;QAC5F,IAAIH,IAAI7D,IAAI,GAAGC,MAAM,GAAG8D,WAAW;YACjC,OAAO;QACT;QAEA,qBAAqB;QACrB,IAAI;YAAC;YAAY;YAAS;YAAW;SAAY,CAACN,QAAQ,CAACI,IAAII,WAAW,KAAK;YAC7E,OAAO;QACT;QAEA,wDAAwD;QACxD,MAAMC,YAAYzE,KAAKwE,WAAW;QAClC,IACEC,UAAUC,QAAQ,CAAC,SACnBD,UAAUC,QAAQ,CAAC,UACnBD,UAAUT,QAAQ,CAAC,gBACnBS,UAAUT,QAAQ,CAAC,cACnB;YACA,OAAO;QACT;QAEA,OAAO;IACT;IAEA;;GAEC,GACD,MAAcW,oBAAoBpD,IAAS,EAAEqD,UAAkB,EAAEC,QAAgB,EAAgB;QAC/F,MAAMjF,SAAS,IAAI,CAAC2C,eAAe;QACnC,MAAMuC,QAAQ,IAAI,CAACjF,MAAM,CAAC4C,QAAQ,EAAEqC,SAAS;QAE7C,2DAA2D;QAC3D,MAAMC,kBAAkB,IAAI,CAAClF,MAAM,CAACmF,mBAAmB,KAAK;QAE5D,IAAI,CAACD,iBAAiB;YACpB,6CAA6C;YAC7C,OAAO,IAAI,CAACE,yBAAyB,CAAC1D,MAAMqD,YAAYC;QAC1D;QAEA,uDAAuD;QACvD,MAAM,EAAE3E,gBAAgB,EAAEoC,QAAQ,EAAErC,OAAO,EAAE,GAAG,IAAI,CAACqB,0BAA0B,CAACC;QAEhF,IAAItB,QAAQiF,IAAI,KAAK,GAAG;YACtB,uBAAuB;YACvB,OAAO3D;QACT;QAEA,4DAA4D;QAC5D,MAAM4D,qBAA6C,CAAC;QACpDlF,QAAQyD,OAAO,CAAC,CAACzB,OAAOD;YACtBmD,kBAAkB,CAACnD,IAAI,GAAGC;QAC5B;QAEA,IAAI,IAAI,CAACpC,MAAM,CAACuF,SAAS,EAAE;YACzB,MAAMC,eAAeC,KAAKC,SAAS,CAAChE,MAAMf,MAAM;YAChD,MAAMgF,gBAAgBF,KAAKC,SAAS,CAACJ,oBAAoB3E,MAAM;YAC/D,MAAMiF,YAAY,AAAC,CAAA,AAAC,CAAA,IAAID,gBAAgBH,YAAW,IAAK,GAAE,EAAGK,OAAO,CAAC;YAErE,gCAAgC;YAChC,IAAIC,aAAa;YACjBzF,iBAAiBwD,OAAO,CAAC,CAACK;gBACxB4B,cAAc5B,MAAMvD,MAAM;YAC5B;YACA,MAAMoF,uBAAuBD,aAAa1F,QAAQiF,IAAI;YACtD,MAAMW,uBACJF,aAAa,IAAI,AAAC,CAAA,AAACC,uBAAuBD,aAAc,GAAE,EAAGD,OAAO,CAAC,KAAK;YAE5EI,QAAQC,GAAG,CAAC;YACZD,QAAQC,GAAG,CAAC,CAAC,kCAAkC,EAAE9F,QAAQiF,IAAI,EAAE;YAC/DY,QAAQC,GAAG,CAAC,CAAC,6BAA6B,EAAEJ,YAAY;YACxDG,QAAQC,GAAG,CACT,CAAC,4BAA4B,EAAEH,qBAAqB,UAAU,EAAEC,qBAAqB,EAAE,CAAC;YAE1FC,QAAQC,GAAG,CAAC,CAAC,yBAAyB,EAAEV,aAAaW,cAAc,GAAG,MAAM,CAAC;YAC7EF,QAAQC,GAAG,CAAC,CAAC,0BAA0B,EAAEP,cAAcQ,cAAc,GAAG,MAAM,CAAC;YAC/EF,QAAQC,GAAG,CAAC,CAAC,2BAA2B,EAAEN,UAAU,CAAC,CAAC;QACxD;QAEA,IAAI;YACF,MAAMQ,WAAW,MAAMrG,OAAOsG,IAAI,CAACC,WAAW,CAACC,MAAM,CAAC;gBACpDC,UAAU;oBACR;wBACEC,SAAS,CAAC,iFAAiF,EAAE1B,WAAW,IAAI,EAAEC,SAAS;;;;;;6FAMtC,CAAC;wBAClF0B,MAAM;oBACR;oBACA;wBACED,SAAShB,KAAKC,SAAS,CAACJ,oBAAoB,MAAM;wBAClDoB,MAAM;oBACR;iBACD;gBACDzB;gBACA0B,iBAAiB;oBAAEpG,MAAM;gBAAc;gBACvCqG,aAAa;YACf;YAEA,MAAMC,iBAAiBT,SAASU,OAAO,CAAC,EAAE,EAAEC,SAASN;YAErD,IAAI,CAACI,gBAAgB;gBACnB,MAAM,IAAI7D,MAAM;YAClB;YAEA,MAAMgE,oBAAoBvB,KAAKwB,KAAK,CAACJ;YAErC,sBAAsB;YACtB,MAAMK,kBAAkB,IAAIvF;YAC5B,KAAK,MAAM,CAACQ,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAAC0E,mBAAoB;gBAC5D,IAAI,OAAO5E,UAAU,UAAU;oBAC7B8E,gBAAgBhG,GAAG,CAACiB,KAAKC;gBAC3B;YACF;YAEA,gFAAgF;YAChF,OAAO,IAAI,CAACsB,2BAA2B,CAACjB,UAAUyE,iBAAiB7G;QACrE,EAAE,OAAO8G,OAAO;YACdlB,QAAQkB,KAAK,CAAC,uCAAuCA;YACrD,MAAMA;QACR;IACF;IAEA;;GAEC,GACD,MAAc/B,0BACZ1D,IAAS,EACTqD,UAAkB,EAClBC,QAAgB,EACF;QACd,MAAMjF,SAAS,IAAI,CAAC2C,eAAe;QACnC,MAAMuC,QAAQ,IAAI,CAACjF,MAAM,CAAC4C,QAAQ,EAAEqC,SAAS;QAE7C,IAAI;YACF,MAAMmB,WAAW,MAAMrG,OAAOsG,IAAI,CAACC,WAAW,CAACC,MAAM,CAAC;gBACpDC,UAAU;oBACR;wBACEC,SAAS,CAAC,yEAAyE,EAAE1B,WAAW,IAAI,EAAEC,SAAS;;;;;;;6FAO9B,CAAC;wBAClF0B,MAAM;oBACR;oBACA;wBACED,SAAShB,KAAKC,SAAS,CAAChE,MAAM,MAAM;wBACpCgF,MAAM;oBACR;iBACD;gBACDzB;gBACA0B,iBAAiB;oBAAEpG,MAAM;gBAAc;gBACvCqG,aAAa;YACf;YAEA,MAAMC,iBAAiBT,SAASU,OAAO,CAAC,EAAE,EAAEC,SAASN;YAErD,IAAI,CAACI,gBAAgB;gBACnB,MAAM,IAAI7D,MAAM;YAClB;YAEA,OAAOyC,KAAKwB,KAAK,CAACJ;QACpB,EAAE,OAAOM,OAAO;YACdlB,QAAQkB,KAAK,CAAC,uCAAuCA;YACrD,MAAMA;QACR;IACF;IAEA;;GAEC,GACDC,wBAAwBC,UAAkB,EAAY;QACpD,MAAMC,mBAAmB,IAAI,CAACtH,MAAM,CAACuH,aAAa,IAAI,EAAE;QACxD,MAAMC,mBAAmB,IAAI,CAACxH,MAAM,CAACyH,WAAW,EAAE,CAACJ,WAAW;QAE9D,IAAI,OAAOG,qBAAqB,YAAYA,iBAAiBD,aAAa,EAAE;YAC1E,OAAO;mBAAID;mBAAqBE,iBAAiBD,aAAa;aAAC;QACjE;QAEA,OAAOD;IACT;IAEA;;GAEC,GACD,MAAMI,cACJC,OAAgB,EAChBN,UAAkB,EAClBO,UAAkB,EAClBC,MAAc,EACK;QACnB,MAAMC,iBAAiB,IAAI,CAAC9H,MAAM,CAAC+H,yBAAyB,IAAI;QAEhE,IAAI;YACF,MAAM7F,SAAS,MAAMyF,QAAQK,IAAI,CAAC;gBAChCX,YAAYS;gBACZG,OAAO;gBACPC,OAAO;oBACLC,KAAK;wBACH;4BAAEd,YAAY;gCAAEe,QAAQf;4BAAW;wBAAE;wBACrC;4BAAEO,YAAY;gCAAEQ,QAAQR;4BAAW;wBAAE;wBACrC;4BAAEC,QAAQ;gCAAEO,QAAQP;4BAAO;wBAAE;qBAC9B;gBACH;YACF;YAEA,IAAI3F,OAAOmG,IAAI,CAAC1H,MAAM,GAAG,GAAG;gBAC1B,MAAM2H,YAAYpG,OAAOmG,IAAI,CAAC,EAAE;gBAChC,OAAOC,UAAUC,aAAa,EAAEjH,IAAI,CAACW,OAAcA,KAAK9B,IAAI,KAAK,EAAE;YACrE;YAEA,OAAO,EAAE;QACX,EAAE,OAAOgH,OAAO;YACd,IAAI,IAAI,CAACnH,MAAM,CAACuF,SAAS,EAAE;gBACzBoC,QAAQa,MAAM,CAACrB,KAAK,CAAC,CAAC,4CAA4C,EAAEA,OAAO;YAC7E;YACA,OAAO,EAAE;QACX;IACF;IAEA;;GAEC,GACD,MAAMsB,UAAUC,OAAyB,EAAgB;QACvD,MAAM,EAAErB,UAAU,EAAE3F,IAAI,EAAE6G,gBAAgB,EAAE,EAAExD,UAAU,EAAE4C,OAAO,EAAE3C,QAAQ,EAAE,GAAG0D;QAEhF,+CAA+C;QAC/C,MAAMC,kBAAkB9I,oBAAoB6B,MAAM6G;QAElD,IAAI,IAAI,CAACvI,MAAM,CAACuF,SAAS,EAAE;YACzBoC,QAAQa,MAAM,CAACI,IAAI,CACjB,CAAC,kCAAkC,EAAE7D,WAAW,IAAI,EAAEC,SAAS,gBAAgB,EAAEqC,YAAY;YAE/FM,QAAQa,MAAM,CAACI,IAAI,CAAC,CAAC,iCAAiC,EAAEL,cAAcM,IAAI,CAAC,OAAO;QACpF;QAEA,oCAAoC;QACpC,IAAI,IAAI,CAAC7I,MAAM,CAAC4C,QAAQ,EAAEkG,iBAAiB;YACzC,OAAO,MAAM,IAAI,CAAC9I,MAAM,CAAC4C,QAAQ,CAACkG,eAAe,CAACJ;QACpD;QAEA,wBAAwB;QACxB,OAAO,MAAM,IAAI,CAAC5D,mBAAmB,CAAC6D,iBAAiB5D,YAAYC;IACrE;IAEA;;GAEC,GACD,MAAM+D,iBACJpB,OAAgB,EAChBN,UAAkB,EAClBO,UAAkB,EAClBC,MAAc,EACdU,aAAuB,EACR;QACf,MAAMT,iBAAiB,IAAI,CAAC9H,MAAM,CAAC+H,yBAAyB,IAAI;QAEhE,IAAI;YACF,MAAMiB,WAAW,MAAMrB,QAAQK,IAAI,CAAC;gBAClCX,YAAYS;gBACZG,OAAO;gBACPC,OAAO;oBACLC,KAAK;wBACH;4BAAEd,YAAY;gCAAEe,QAAQf;4BAAW;wBAAE;wBACrC;4BAAEO,YAAY;gCAAEQ,QAAQR;4BAAW;wBAAE;wBACrC;4BAAEC,QAAQ;gCAAEO,QAAQP;4BAAO;wBAAE;qBAC9B;gBACH;YACF;YAEA,MAAMoB,iBAAiB;gBACrB5B;gBACAO;gBACAW,eAAeA,cAAcjH,GAAG,CAAC,CAACnB,OAAU,CAAA;wBAAEA;oBAAK,CAAA;gBACnD0H;YACF;YAEA,IAAImB,SAASX,IAAI,CAAC1H,MAAM,GAAG,GAAG;gBAC5B,MAAMgH,QAAQuB,MAAM,CAAC;oBACnBC,IAAIH,SAASX,IAAI,CAAC,EAAE,CAACc,EAAE;oBACvB9B,YAAYS;oBACZpG,MAAMuH;gBACR;YACF,OAAO;gBACL,MAAMtB,QAAQpB,MAAM,CAAC;oBACnBc,YAAYS;oBACZpG,MAAMuH;gBACR;YACF;YAEA,IAAI,IAAI,CAACjJ,MAAM,CAACuF,SAAS,EAAE;gBACzBoC,QAAQa,MAAM,CAACI,IAAI,CACjB,CAAC,wCAAwC,EAAEvB,WAAW,CAAC,EAAEO,WAAW,CAAC,EAAEC,QAAQ;YAEnF;QACF,EAAE,OAAOV,OAAO;YACd,IAAI,IAAI,CAACnH,MAAM,CAACuF,SAAS,EAAE;gBACzBoC,QAAQa,MAAM,CAACrB,KAAK,CAAC,CAAC,4CAA4C,EAAEA,OAAO;YAC7E;QACF;IACF;AACF"}
|