@yartsun/chat-widget-types 1.0.2 → 1.0.5

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.
Files changed (47) hide show
  1. package/README.md +214 -115
  2. package/dist/config.types.d.ts +111 -44
  3. package/dist/config.types.d.ts.map +1 -1
  4. package/dist/config.types.js +2 -67
  5. package/dist/config.types.js.map +1 -1
  6. package/dist/index.d.ts +6 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +5 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/migration/commands.d.ts +59 -0
  11. package/dist/migration/commands.d.ts.map +1 -0
  12. package/dist/migration/commands.js +286 -0
  13. package/dist/migration/commands.js.map +1 -0
  14. package/dist/migration/examples.d.ts +198 -0
  15. package/dist/migration/examples.d.ts.map +1 -0
  16. package/dist/migration/examples.js +439 -0
  17. package/dist/migration/examples.js.map +1 -0
  18. package/dist/migration/facade.d.ts +85 -0
  19. package/dist/migration/facade.d.ts.map +1 -0
  20. package/dist/migration/facade.js +168 -0
  21. package/dist/migration/facade.js.map +1 -0
  22. package/dist/migration/migrator.d.ts +49 -0
  23. package/dist/migration/migrator.d.ts.map +1 -0
  24. package/dist/migration/migrator.js +245 -0
  25. package/dist/migration/migrator.js.map +1 -0
  26. package/dist/migration/strategies.d.ts +85 -0
  27. package/dist/migration/strategies.d.ts.map +1 -0
  28. package/dist/migration/strategies.js +217 -0
  29. package/dist/migration/strategies.js.map +1 -0
  30. package/dist/migration/types.d.ts +196 -0
  31. package/dist/migration/types.d.ts.map +1 -0
  32. package/dist/migration/types.js +5 -0
  33. package/dist/migration/types.js.map +1 -0
  34. package/dist/utils.d.ts +1 -11
  35. package/dist/utils.d.ts.map +1 -1
  36. package/dist/utils.js +3 -127
  37. package/dist/utils.js.map +1 -1
  38. package/package.json +13 -4
  39. package/src/config.types.ts +132 -118
  40. package/src/index.ts +26 -0
  41. package/src/migration/commands.ts +314 -0
  42. package/src/migration/examples.ts +471 -0
  43. package/src/migration/facade.ts +196 -0
  44. package/src/migration/migrator.ts +361 -0
  45. package/src/migration/strategies.ts +249 -0
  46. package/src/migration/types.ts +182 -0
  47. package/src/utils.ts +3 -143
@@ -0,0 +1,471 @@
1
+ /**
2
+ * Примеры использования системы миграции конфигураций
3
+ */
4
+
5
+ import {
6
+ quickMigrateV1toV2,
7
+ MigrationFacade,
8
+ MigrationPresets,
9
+ ConfigHelpers
10
+ } from './facade'
11
+ import { ConfigV1, ConfigV2 } from './types'
12
+
13
+ /** Пример простой миграции V1 -> V2 */
14
+ export async function exampleSimpleMigration() {
15
+ const configV1: ConfigV1 = {
16
+ settings: {
17
+ widgetTitle: "HyperShadow",
18
+ welcomeMessage: "🖖 Hi there — I'm your assistant for finding documents, tracking access, and making sense of your files. \n\nHow can I help?",
19
+ bgChat: "rgba(47, 47, 49, 0.90)",
20
+ gapMessageLine: 12,
21
+ paddingChat: 8,
22
+ fontFamily: "MacPaw Fixel",
23
+ borderRadius: "lg",
24
+ launchView: "closed",
25
+ letterSpacing: 0,
26
+ logo: "",
27
+ fontWeight: 400
28
+ },
29
+ sections: {
30
+ top: {
31
+ params: { size: "md" },
32
+ chipWidgetTitle: { color: "#BEB6E9", bgColor: "#5E4AC6" },
33
+ btnClose: { color: "#BBBBBD", bgColor: "#2F2F31" }
34
+ },
35
+ inside: {
36
+ params: { size: "md" },
37
+ messageUser: { color: "#F9F8F8", bgColor: "#F8F8F933" },
38
+ messageBot: { color: "#fff", bgColor: "#5E4AC6" },
39
+ welcomeMessage: { color: "#fff" }
40
+ },
41
+ bottom: {
42
+ params: { size: "sm" },
43
+ inputSend: { color: "#EEECEC", bgColor: "#1E1E1E" },
44
+ btnSend: { color: "#1E1E1E", bgColor: "rgba(255, 255, 255, 0.50)", type: "both" },
45
+ activeBtn: { color: "#fff", bgColor: "#F8F8F933" }
46
+ }
47
+ }
48
+ }
49
+
50
+ console.log('🔄 Миграция V1 -> V2...')
51
+
52
+ // Быстрая миграция
53
+ const migratedConfig = await quickMigrateV1toV2(configV1)
54
+
55
+ if (migratedConfig) {
56
+ console.log('✅ Миграция успешна!')
57
+ console.log('📋 Добавленные поля V2:')
58
+ console.log('- settings.loader:', migratedConfig.settings.loader)
59
+ console.log('- sections.top.params.buttonStyle:', migratedConfig.sections.top.params.buttonStyle)
60
+ console.log('- sections.top.params.buttonType:', migratedConfig.sections.top.params.buttonType)
61
+ console.log('- sections.top.params.chipStyle:', migratedConfig.sections.top.params.chipStyle)
62
+ console.log('- sections.bottom.warningAlert:', migratedConfig.sections.bottom.warningAlert)
63
+ console.log('- texts.disclaimer:', migratedConfig.texts.disclaimer)
64
+ } else {
65
+ console.log('❌ Ошибка миграции')
66
+ }
67
+
68
+ return migratedConfig
69
+ }
70
+
71
+ /** Пример детальной миграции с отчетами */
72
+ export async function exampleDetailedMigration() {
73
+ const facade = new MigrationFacade(true) // включаем подробные логи
74
+
75
+ const configV1: ConfigV1 = {
76
+ settings: {
77
+ widgetTitle: "Test Widget",
78
+ welcomeMessage: "Welcome!",
79
+ bgChat: "#000000",
80
+ gapMessageLine: 10,
81
+ paddingChat: 5,
82
+ fontFamily: "Arial",
83
+ borderRadius: "md",
84
+ launchView: "compact",
85
+ letterSpacing: 1,
86
+ logo: "logo.png",
87
+ fontWeight: 300
88
+ },
89
+ sections: {
90
+ top: {
91
+ params: { size: "lg" },
92
+ chipWidgetTitle: { color: "#111", bgColor: "#222" },
93
+ btnClose: { color: "#333", bgColor: "#444" }
94
+ },
95
+ inside: {
96
+ params: { size: "sm" },
97
+ messageUser: { color: "#555", bgColor: "#666" },
98
+ messageBot: { color: "#777", bgColor: "#888" },
99
+ welcomeMessage: { color: "#999" }
100
+ },
101
+ bottom: {
102
+ params: { size: "md" },
103
+ inputSend: { color: "#AAA", bgColor: "#BBB" },
104
+ btnSend: { color: "#CCC", bgColor: "#DDD", type: "icon" },
105
+ activeBtn: { color: "#EEE", bgColor: "#FFF" }
106
+ }
107
+ }
108
+ }
109
+
110
+ console.log('\n🔍 Предварительный просмотр миграции...')
111
+
112
+ // Предварительный просмотр
113
+ const preview = await facade.preview(configV1, '2.0', MigrationPresets.DEBUG)
114
+ console.log('📊 Отчет о планируемой миграции:')
115
+ console.log(`- Исходная версия: ${preview.fromVersion}`)
116
+ console.log(`- Целевая версия: ${preview.toVersion}`)
117
+ console.log(`- Стратегий будет применено: ${preview.appliedStrategies.length}`)
118
+ console.log(`- Ожидаемое время выполнения: ${preview.duration}ms`)
119
+ console.log(`- Описание: ${preview.summary}`)
120
+
121
+ console.log('\n🔄 Выполняем миграцию...')
122
+
123
+ // Полная миграция
124
+ const result = await facade.migrateV1toV2(configV1, MigrationPresets.DEBUG)
125
+
126
+ if (result.success) {
127
+ console.log('✅ Миграция завершена успешно!')
128
+ console.log(`📈 Применено стратегий: ${result.appliedStrategies.length}`)
129
+ console.log(`⚠️ Предупреждений: ${result.warnings.length}`)
130
+
131
+ if (result.warnings.length > 0) {
132
+ console.log('⚠️ Предупреждения:')
133
+ result.warnings.forEach(warning => console.log(` - ${warning}`))
134
+ }
135
+
136
+ return result.data
137
+ } else {
138
+ console.log('❌ Ошибки при миграции:')
139
+ result.errors.forEach(error => console.log(` - ${error}`))
140
+ return null
141
+ }
142
+ }
143
+
144
+ /** Пример пакетной миграции нескольких конфигураций */
145
+ export async function exampleBatchMigration() {
146
+ const configs = [
147
+ // Конфигурация 1 - V1
148
+ {
149
+ id: 'config1',
150
+ data: {
151
+ settings: {
152
+ widgetTitle: "Config 1",
153
+ welcomeMessage: "Hello from config 1",
154
+ bgChat: "#111",
155
+ gapMessageLine: 5,
156
+ paddingChat: 10,
157
+ fontFamily: "Roboto",
158
+ borderRadius: "sm",
159
+ launchView: "closed",
160
+ letterSpacing: 0,
161
+ logo: "",
162
+ fontWeight: 400
163
+ },
164
+ sections: {
165
+ top: {
166
+ params: { size: "md" },
167
+ chipWidgetTitle: { color: "#AAA", bgColor: "#BBB" },
168
+ btnClose: { color: "#CCC", bgColor: "#DDD" }
169
+ },
170
+ inside: {
171
+ params: { size: "md" },
172
+ messageUser: { color: "#EEE", bgColor: "#FFF" },
173
+ messageBot: { color: "#000", bgColor: "#111" },
174
+ welcomeMessage: { color: "#222" }
175
+ },
176
+ bottom: {
177
+ params: { size: "sm" },
178
+ inputSend: { color: "#333", bgColor: "#444" },
179
+ btnSend: { color: "#555", bgColor: "#666", type: "text" },
180
+ activeBtn: { color: "#777", bgColor: "#888" }
181
+ }
182
+ }
183
+ }
184
+ },
185
+ // Конфигурация 2 - уже V2
186
+ {
187
+ id: 'config2',
188
+ data: {
189
+ settings: {
190
+ widgetTitle: "Config 2",
191
+ welcomeMessage: "Hello from config 2",
192
+ bgChat: "#222",
193
+ gapMessageLine: 8,
194
+ paddingChat: 12,
195
+ fontFamily: "Inter",
196
+ borderRadius: "lg",
197
+ launchView: "compact",
198
+ letterSpacing: 1,
199
+ logo: "logo2.png",
200
+ fontWeight: 500,
201
+ loader: "circle-pulse",
202
+ buttonStyle: "outlined",
203
+ buttonType: "icon"
204
+ },
205
+ sections: {
206
+ top: {
207
+ params: { size: "lg", chipStyle: "outlined" },
208
+ chipWidgetTitle: { color: "#999", bgColor: "#AAA" },
209
+ btnClose: { color: "#BBB", bgColor: "#CCC" }
210
+ },
211
+ inside: {
212
+ params: { size: "lg" },
213
+ messageUser: { color: "#DDD", bgColor: "#EEE", bgType: "bubble" },
214
+ messageBot: { color: "#FFF", bgColor: "#000", bgType: "plain" },
215
+ welcomeMessage: { color: "#111" },
216
+ aprooveButton: { color: "#222", bgColor: "#333" },
217
+ rejectButton: { color: "#444", bgColor: "#555" }
218
+ },
219
+ bottom: {
220
+ params: { size: "md" },
221
+ inputSend: {
222
+ color: "#666",
223
+ bgColor: "#777",
224
+ borderStyle: { borderColor: "#888", borderWidth: 2 },
225
+ inputStyle: "stacked",
226
+ bgType: "bubble"
227
+ },
228
+ btnSend: { color: "#999", bgColor: "#AAA", type: "both" },
229
+ activeBtn: { color: "#BBB", bgColor: "#CCC" },
230
+ warningAlert: { color: "#DDD", bgColor: "#EEE" },
231
+ disclaimer: "Custom disclaimer"
232
+ }
233
+ }
234
+ }
235
+ }
236
+ ]
237
+
238
+ console.log('\n🔄 Пакетная миграция нескольких конфигураций...')
239
+
240
+ const results = []
241
+
242
+ for (const config of configs) {
243
+ console.log(`\n📋 Обрабатываем ${config.id}...`)
244
+
245
+ // Определяем версию
246
+ const version = ConfigHelpers.getConfigVersion(config.data)
247
+ console.log(` Версия: ${version || 'неизвестна'}`)
248
+
249
+ if (version === '1.0') {
250
+ console.log(' 🔄 Миграция V1 -> V2...')
251
+ const migrated = await quickMigrateV1toV2(config.data as ConfigV1)
252
+ results.push({
253
+ id: config.id,
254
+ originalVersion: version,
255
+ migrated: !!migrated,
256
+ result: migrated
257
+ })
258
+ } else if (version === '2.0') {
259
+ console.log(' ✅ Уже V2, пропускаем')
260
+ results.push({
261
+ id: config.id,
262
+ originalVersion: version,
263
+ migrated: false,
264
+ result: config.data
265
+ })
266
+ } else {
267
+ console.log(' ❌ Неизвестная версия')
268
+ results.push({
269
+ id: config.id,
270
+ originalVersion: version,
271
+ migrated: false,
272
+ result: null
273
+ })
274
+ }
275
+ }
276
+
277
+ console.log('\n📊 Результаты пакетной миграции:')
278
+ results.forEach(result => {
279
+ console.log(`- ${result.id}: ${result.originalVersion} -> ${result.migrated ? 'V2 (мигрировано)' : 'без изменений'}`)
280
+ })
281
+
282
+ return results
283
+ }
284
+
285
+ /** Пример с обработкой ошибок */
286
+ export async function exampleErrorHandling() {
287
+ console.log('\n🧪 Тестирование обработки ошибок...')
288
+
289
+ // Некорректная конфигурация
290
+ const brokenConfig = {
291
+ settings: {
292
+ // Отсутствуют обязательные поля
293
+ fontFamily: "Arial"
294
+ },
295
+ // Отсутствует sections
296
+ }
297
+
298
+ const facade = new MigrationFacade(true)
299
+
300
+ try {
301
+ console.log('🔄 Попытка миграции некорректной конфигурации...')
302
+ const result = await facade.migrate(brokenConfig, '2.0', MigrationPresets.STRICT)
303
+
304
+ if (result.success) {
305
+ console.log('✅ Неожиданно успешно')
306
+ } else {
307
+ console.log('❌ Ожидаемые ошибки:')
308
+ result.errors.forEach(error => console.log(` - ${error}`))
309
+ }
310
+ } catch (error) {
311
+ console.log('💥 Критическая ошибка:', error)
312
+ }
313
+
314
+ // Тестируем мягкий режим
315
+ console.log('\n🔄 Повторная попытка в мягком режиме...')
316
+ const softResult = await facade.migrate(brokenConfig, '2.0', MigrationPresets.SOFT)
317
+
318
+ if (softResult.success) {
319
+ console.log('✅ Успешно в мягком режиме')
320
+ } else {
321
+ console.log('❌ Ошибки даже в мягком режиме:')
322
+ softResult.errors.forEach(error => console.log(` - ${error}`))
323
+ }
324
+ }
325
+
326
+ /** Демонстрация всех примеров */
327
+ export async function runAllExamples() {
328
+ console.log('🚀 Запуск всех примеров миграции...\n')
329
+
330
+ try {
331
+ await exampleSimpleMigration()
332
+ await exampleDetailedMigration()
333
+ await exampleBatchMigration()
334
+ await exampleErrorHandling()
335
+
336
+ console.log('\n🎉 Все примеры выполнены!')
337
+ } catch (error) {
338
+ console.error('💥 Критическая ошибка в примерах:', error)
339
+ }
340
+ }
341
+
342
+ // Экспортируем готовые конфигурации для тестирования
343
+ export const SAMPLE_CONFIGS = {
344
+ V1_BASIC: {
345
+ settings: {
346
+ widgetTitle: "Sample V1",
347
+ welcomeMessage: "Welcome to V1",
348
+ bgChat: "#000",
349
+ gapMessageLine: 10,
350
+ paddingChat: 8,
351
+ fontFamily: "Arial",
352
+ borderRadius: "md",
353
+ launchView: "closed",
354
+ letterSpacing: 0,
355
+ logo: "",
356
+ fontWeight: 400
357
+ },
358
+ sections: {
359
+ top: {
360
+ params: { size: "md" },
361
+ chipWidgetTitle: { color: "#FFF", bgColor: "#000" },
362
+ btnClose: { color: "#000", bgColor: "#FFF" }
363
+ },
364
+ inside: {
365
+ params: { size: "md" },
366
+ messageUser: { color: "#333", bgColor: "#EEE" },
367
+ messageBot: { color: "#FFF", bgColor: "#007AFF" },
368
+ welcomeMessage: { color: "#333" }
369
+ },
370
+ bottom: {
371
+ params: { size: "sm" },
372
+ inputSend: { color: "#333", bgColor: "#FFF" },
373
+ btnSend: { color: "#FFF", bgColor: "#007AFF", type: "both" },
374
+ activeBtn: { color: "#007AFF", bgColor: "#EEE" }
375
+ }
376
+ }
377
+ } as ConfigV1,
378
+
379
+ V2_COMPLETE: {
380
+ schema: {
381
+ version: "2.0",
382
+ required: ["settings", "texts", "sections"]
383
+ },
384
+ settings: {
385
+ bgChat: "#111",
386
+ gapMessageLine: 12,
387
+ fontFamily: "Inter",
388
+ borderRadius: "lg",
389
+ launchView: "compact",
390
+ letterSpacing: 1,
391
+ logo: "logo.svg",
392
+ fontWeight: 500,
393
+ loader: "dots-pulse"
394
+ },
395
+ texts: {
396
+ widgetTitle: "Sample V2",
397
+ welcomeMessage: "Welcome to V2",
398
+ launchIssueTitle: "Launch Issue",
399
+ launchIssueText: "Please restart",
400
+ issueText: "Something went wrong",
401
+ reconnectText: "Reconnecting...",
402
+ inputPlaceholder: "Type your message",
403
+ disableInputText: "Please wait",
404
+ disclaimer: "This is a sample V2 configuration"
405
+ },
406
+ sections: {
407
+ warnings: {
408
+ launchIssue: {
409
+ params: { warningStyle: "gradient", showIcon: true, icon: "⚠️" },
410
+ iconColor: "#FFF",
411
+ bgColor: "#FF3B30",
412
+ headlineColor: "#FFF",
413
+ color: "#FFF",
414
+ resetButton: { color: "#FFF", bgColor: "#FF3B30" }
415
+ },
416
+ connectionIssue: {
417
+ params: { warningStyle: "gradient", showIcon: true, icon: "📡" },
418
+ iconColor: "#FFF",
419
+ bgColor: "#FF9500",
420
+ color: "#FFF",
421
+ resetButton: { color: "#FFF", bgColor: "#FF9500" }
422
+ },
423
+ reconnectIssue: {
424
+ params: { warningStyle: "gradient", showIcon: true, icon: "🔄" },
425
+ iconColor: "#FFF",
426
+ bgColor: "#007AFF",
427
+ color: "#FFF"
428
+ },
429
+ disableInputIssue: {
430
+ iconColor: "#FFF",
431
+ bgColor: "#8E8E93",
432
+ color: "#FFF"
433
+ }
434
+ },
435
+ loader: {
436
+ completeStep: "#34C759",
437
+ activeStep: "#007AFF"
438
+ },
439
+ top: {
440
+ params: {
441
+ size: "lg",
442
+ chipStyle: "filled",
443
+ chipType: "both",
444
+ buttonStyle: "filled",
445
+ buttonType: "both"
446
+ },
447
+ chipWidgetTitle: { color: "#FFF", bgColor: "#007AFF" },
448
+ buttons: { color: "#666", bgColor: "#EEE" }
449
+ },
450
+ inside: {
451
+ params: { size: "md" },
452
+ messageUser: { color: "#333", bgColor: "#F0F0F0", bgType: "bubble" },
453
+ messageBot: { color: "#FFF", bgColor: "#007AFF", bgType: "plain" },
454
+ welcomeMessage: { color: "#333" }
455
+ },
456
+ bottom: {
457
+ params: { size: "md", disclaimerShow: true },
458
+ inputSend: {
459
+ color: "#333",
460
+ bgColor: "#FFF",
461
+ borderStyle: { borderColor: "#DDD", borderWidth: 1 },
462
+ inputStyle: "inside",
463
+ bgType: "plain"
464
+ },
465
+ btnSend: { color: "#FFF", bgColor: "#007AFF", type: "both" },
466
+ activeBtn: { color: "#007AFF", bgColor: "#E3F2FD" },
467
+ warningAlert: { color: "#FFF", bgColor: "#FF9500" }
468
+ }
469
+ }
470
+ } as ConfigV2
471
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Фасад для удобного использования системы миграции
3
+ */
4
+
5
+ import {
6
+ ConfigVersion,
7
+ MigrationResult,
8
+ MigrationOptions,
9
+ MigrationReport,
10
+ ConfigV1,
11
+ ConfigV2
12
+ } from './types'
13
+ import { ConfigMigrator, DefaultMigrationLogger } from './migrator'
14
+ import { WidgetConfig } from '../config.types'
15
+
16
+ /** Простой API для миграции конфигураций */
17
+ export class MigrationFacade {
18
+ private migrator: ConfigMigrator
19
+
20
+ constructor(verbose: boolean = false) {
21
+ const logger = new DefaultMigrationLogger(verbose)
22
+ this.migrator = new ConfigMigrator(logger)
23
+ }
24
+
25
+ /**
26
+ * Мигрировать конфигурацию V1 в V2
27
+ */
28
+ async migrateV1toV2(config: ConfigV1, options?: MigrationOptions): Promise<MigrationResult<ConfigV2>> {
29
+ return this.migrator.migrate<ConfigV2>(config, '2.0', options)
30
+ }
31
+
32
+ /**
33
+ * Мигрировать конфигурацию в последнюю версию
34
+ */
35
+ async migrateToLatest(config: any, options?: MigrationOptions): Promise<MigrationResult<WidgetConfig>> {
36
+ return this.migrator.migrate<WidgetConfig>(config, '2.0', options) // пока V2 - последняя
37
+ }
38
+
39
+ /**
40
+ * Автоматически определить версию и мигрировать в указанную
41
+ */
42
+ async migrate(config: any, targetVersion: ConfigVersion, options?: MigrationOptions): Promise<MigrationResult> {
43
+ return this.migrator.migrate(config, targetVersion, options)
44
+ }
45
+
46
+ /**
47
+ * Предварительный просмотр миграции без её выполнения
48
+ */
49
+ async preview(config: any, targetVersion: ConfigVersion, options?: MigrationOptions): Promise<MigrationReport> {
50
+ return this.migrator.dryRun(config, targetVersion, options)
51
+ }
52
+
53
+ /**
54
+ * Быстрая проверка - можно ли мигрировать конфигурацию
55
+ */
56
+ async canMigrate(config: any, targetVersion: ConfigVersion): Promise<boolean> {
57
+ try {
58
+ const report = await this.preview(config, targetVersion)
59
+ return report.success
60
+ } catch {
61
+ return false
62
+ }
63
+ }
64
+ }
65
+
66
+ /** Глобальный экземпляр фасада */
67
+ export const migrationFacade = new MigrationFacade()
68
+
69
+ /** Утилитарные функции для быстрого использования */
70
+
71
+ /**
72
+ * Быстрая миграция V1 -> V2
73
+ */
74
+ export async function quickMigrateV1toV2(config: ConfigV1, options?: MigrationOptions): Promise<ConfigV2 | null> {
75
+ try {
76
+ const result = await migrationFacade.migrateV1toV2(config, options)
77
+ return result.success ? result.data! : null
78
+ } catch {
79
+ return null
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Быстрая миграция в последнюю версию
85
+ */
86
+ export async function quickMigrateToLatest(config: any, options?: MigrationOptions): Promise<WidgetConfig | null> {
87
+ try {
88
+ const result = await migrationFacade.migrateToLatest(config, options)
89
+ return result.success ? result.data! : null
90
+ } catch {
91
+ return null
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Синхронная функция миграции V1 -> V2 (без async/await)
97
+ * Использует только стратегии без побочных эффектов
98
+ */
99
+ export function syncMigrateV1toV2(_config: ConfigV1): ConfigV2 | null {
100
+ // Синхронная миграция пока не поддерживается, используйте async версию
101
+ console.warn('syncMigrateV1toV2 не поддерживается, используйте quickMigrateV1toV2')
102
+ return null
103
+ }
104
+
105
+ /** Набор предустановленных опций миграции */
106
+ export const MigrationPresets = {
107
+ /** Строгий режим - останавливается на любой ошибке */
108
+ STRICT: {
109
+ strict: true,
110
+ preserveUnknown: false,
111
+ verbose: false
112
+ } as MigrationOptions,
113
+
114
+ /** Мягкий режим - продолжает при ошибках, сохраняет неизвестные поля */
115
+ SOFT: {
116
+ strict: false,
117
+ preserveUnknown: true,
118
+ verbose: false
119
+ } as MigrationOptions,
120
+
121
+ /** Режим отладки - подробные логи */
122
+ DEBUG: {
123
+ strict: false,
124
+ preserveUnknown: true,
125
+ verbose: true
126
+ } as MigrationOptions,
127
+
128
+ /** Производственный режим - без логов, строгий */
129
+ PRODUCTION: {
130
+ strict: true,
131
+ preserveUnknown: false,
132
+ verbose: false
133
+ } as MigrationOptions
134
+ }
135
+
136
+ /** Хелперы для работы с конфигурациями */
137
+ export class ConfigHelpers {
138
+ /**
139
+ * Проверить является ли конфигурация V1
140
+ */
141
+ static isV1Config(config: any): config is ConfigV1 {
142
+ return config &&
143
+ config.settings &&
144
+ config.sections &&
145
+ config.settings.loader === undefined && // V1 не имеет loader
146
+ config.sections.inside?.aprooveButton === undefined // V1 не имеет approve кнопок
147
+ }
148
+
149
+ /**
150
+ * Проверить является ли конфигурация V2
151
+ */
152
+ static isV2Config(config: any): config is ConfigV2 {
153
+ return config &&
154
+ config.settings &&
155
+ config.sections &&
156
+ config.settings.loader !== undefined && // V2 имеет loader
157
+ config.sections.inside?.aprooveButton !== undefined // V2 имеет approve кнопки
158
+ }
159
+
160
+ /**
161
+ * Безопасно получить версию конфигурации
162
+ */
163
+ static getConfigVersion(config: any): ConfigVersion | null {
164
+ if (ConfigHelpers.isV2Config(config)) return '2.0'
165
+ if (ConfigHelpers.isV1Config(config)) return '1.0'
166
+ return null
167
+ }
168
+
169
+ /**
170
+ * Создать резервную копию конфигурации
171
+ */
172
+ static backup(config: any): { config: any; timestamp: number; version: ConfigVersion | null } {
173
+ return {
174
+ config: JSON.parse(JSON.stringify(config)),
175
+ timestamp: Date.now(),
176
+ version: ConfigHelpers.getConfigVersion(config)
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Валидировать основную структуру конфигурации
182
+ */
183
+ static isValidStructure(config: any): boolean {
184
+ return !!(
185
+ config &&
186
+ typeof config === 'object' &&
187
+ config.settings &&
188
+ typeof config.settings === 'object' &&
189
+ config.sections &&
190
+ typeof config.sections === 'object' &&
191
+ config.sections.top &&
192
+ config.sections.inside &&
193
+ config.sections.bottom
194
+ )
195
+ }
196
+ }