@yartsun/chat-widget-types 1.0.3 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +214 -115
- package/dist/config.types.d.ts +111 -46
- package/dist/config.types.d.ts.map +1 -1
- package/dist/config.types.js +2 -66
- package/dist/config.types.js.map +1 -1
- package/dist/default-config.d.ts +3 -0
- package/dist/default-config.d.ts.map +1 -0
- package/dist/default-config.js +3 -0
- package/dist/default-config.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/migration/commands.d.ts +59 -0
- package/dist/migration/commands.d.ts.map +1 -0
- package/dist/migration/commands.js +286 -0
- package/dist/migration/commands.js.map +1 -0
- package/dist/migration/examples.d.ts +198 -0
- package/dist/migration/examples.d.ts.map +1 -0
- package/dist/migration/examples.js +439 -0
- package/dist/migration/examples.js.map +1 -0
- package/dist/migration/facade.d.ts +85 -0
- package/dist/migration/facade.d.ts.map +1 -0
- package/dist/migration/facade.js +168 -0
- package/dist/migration/facade.js.map +1 -0
- package/dist/migration/migrator.d.ts +49 -0
- package/dist/migration/migrator.d.ts.map +1 -0
- package/dist/migration/migrator.js +245 -0
- package/dist/migration/migrator.js.map +1 -0
- package/dist/migration/strategies.d.ts +85 -0
- package/dist/migration/strategies.d.ts.map +1 -0
- package/dist/migration/strategies.js +217 -0
- package/dist/migration/strategies.js.map +1 -0
- package/dist/migration/types.d.ts +196 -0
- package/dist/migration/types.d.ts.map +1 -0
- package/dist/migration/types.js +5 -0
- package/dist/migration/types.js.map +1 -0
- package/dist/utils.d.ts +1 -11
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -129
- package/dist/utils.js.map +1 -1
- package/package.json +13 -4
- package/src/config.types.ts +131 -121
- package/src/default-config.ts +6 -0
- package/src/index.ts +26 -0
- package/src/migration/commands.ts +314 -0
- package/src/migration/examples.ts +471 -0
- package/src/migration/facade.ts +196 -0
- package/src/migration/migrator.ts +361 -0
- package/src/migration/strategies.ts +249 -0
- package/src/migration/types.ts +182 -0
- package/src/utils.ts +3 -145
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Команды для системы миграции конфигураций
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
MigrationCommand,
|
|
7
|
+
MigrationStepResult,
|
|
8
|
+
ConfigVersion,
|
|
9
|
+
VersionDetector,
|
|
10
|
+
ConfigValidator
|
|
11
|
+
} from './types'
|
|
12
|
+
// Команды для системы миграции конфигураций
|
|
13
|
+
|
|
14
|
+
/** Команда детекции версии конфигурации */
|
|
15
|
+
export class DetectVersionCommand implements MigrationCommand {
|
|
16
|
+
name = 'DetectVersion'
|
|
17
|
+
description = 'Определяет версию конфигурации'
|
|
18
|
+
|
|
19
|
+
execute(config: any): MigrationStepResult {
|
|
20
|
+
try {
|
|
21
|
+
const detector = new DefaultVersionDetector()
|
|
22
|
+
const version = detector.detect(config)
|
|
23
|
+
|
|
24
|
+
if (!version) {
|
|
25
|
+
return {
|
|
26
|
+
success: false,
|
|
27
|
+
errors: ['Не удалось определить версию конфигурации'],
|
|
28
|
+
warnings: [],
|
|
29
|
+
modified: false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
data: { version, config },
|
|
36
|
+
errors: [],
|
|
37
|
+
warnings: [],
|
|
38
|
+
modified: false
|
|
39
|
+
}
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return {
|
|
42
|
+
success: false,
|
|
43
|
+
errors: [`Ошибка при детекции версии: ${error}`],
|
|
44
|
+
warnings: [],
|
|
45
|
+
modified: false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Команда валидации конфигурации */
|
|
52
|
+
export class ValidateConfigCommand implements MigrationCommand {
|
|
53
|
+
name = 'ValidateConfig'
|
|
54
|
+
description = 'Валидирует конфигурацию для указанной версии'
|
|
55
|
+
|
|
56
|
+
execute(config: any, options: { version: ConfigVersion }): MigrationStepResult {
|
|
57
|
+
try {
|
|
58
|
+
const validator = new DefaultConfigValidator()
|
|
59
|
+
const result = validator.validate(config, options.version)
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
success: result.isValid,
|
|
63
|
+
data: { validationResult: result, config },
|
|
64
|
+
errors: result.errors,
|
|
65
|
+
warnings: result.warnings,
|
|
66
|
+
modified: false
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
errors: [`Ошибка при валидации: ${error}`],
|
|
72
|
+
warnings: [],
|
|
73
|
+
modified: false
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Команда очистки конфигурации от неизвестных полей */
|
|
80
|
+
export class CleanConfigCommand implements MigrationCommand {
|
|
81
|
+
name = 'CleanConfig'
|
|
82
|
+
description = 'Очищает конфигурацию от неизвестных полей для указанной версии'
|
|
83
|
+
|
|
84
|
+
execute(config: any, options: { version: ConfigVersion; preserveUnknown?: boolean }): MigrationStepResult {
|
|
85
|
+
try {
|
|
86
|
+
if (options.preserveUnknown) {
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
data: config,
|
|
90
|
+
errors: [],
|
|
91
|
+
warnings: ['Очистка пропущена - preserveUnknown: true'],
|
|
92
|
+
modified: false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Здесь можно реализовать логику очистки на основе схем
|
|
97
|
+
const cleanedConfig = this.cleanConfigForVersion(config, options.version)
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
data: cleanedConfig,
|
|
102
|
+
errors: [],
|
|
103
|
+
warnings: ['Конфигурация очищена от неизвестных полей'],
|
|
104
|
+
modified: true
|
|
105
|
+
}
|
|
106
|
+
} catch (error) {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
errors: [`Ошибка при очистке конфигурации: ${error}`],
|
|
110
|
+
warnings: [],
|
|
111
|
+
modified: false
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private cleanConfigForVersion(config: any, _version: ConfigVersion): any {
|
|
117
|
+
// Базовая реализация - возвращаем как есть
|
|
118
|
+
// В реальном проекте здесь была бы логика очистки на основе схем
|
|
119
|
+
return { ...config }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Команда создания резервной копии */
|
|
124
|
+
export class BackupConfigCommand implements MigrationCommand {
|
|
125
|
+
name = 'BackupConfig'
|
|
126
|
+
description = 'Создает резервную копию конфигурации'
|
|
127
|
+
|
|
128
|
+
execute(config: any, options?: { timestamp?: boolean }): MigrationStepResult {
|
|
129
|
+
try {
|
|
130
|
+
const timestamp = options?.timestamp ? Date.now() : undefined
|
|
131
|
+
const backup = {
|
|
132
|
+
original: JSON.parse(JSON.stringify(config)),
|
|
133
|
+
timestamp: timestamp || Date.now(),
|
|
134
|
+
version: new DefaultVersionDetector().detect(config)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
success: true,
|
|
139
|
+
data: { backup, config },
|
|
140
|
+
errors: [],
|
|
141
|
+
warnings: [],
|
|
142
|
+
modified: false
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
errors: [`Ошибка при создании резервной копии: ${error}`],
|
|
148
|
+
warnings: [],
|
|
149
|
+
modified: false
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Дефолтный детектор версий */
|
|
156
|
+
export class DefaultVersionDetector implements VersionDetector {
|
|
157
|
+
detect(config: any): ConfigVersion | null {
|
|
158
|
+
try {
|
|
159
|
+
// Проверяем структуру для определения версии
|
|
160
|
+
if (!config || !config.settings || !config.sections) {
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// V2 признаки: наличие новых полей
|
|
165
|
+
const hasV2Features = [
|
|
166
|
+
config.settings.loader !== undefined,
|
|
167
|
+
config.settings.buttonStyle !== undefined,
|
|
168
|
+
config.settings.buttonType !== undefined,
|
|
169
|
+
config.sections.top.params?.chipStyle !== undefined,
|
|
170
|
+
config.sections.inside.messageUser?.bgType !== undefined,
|
|
171
|
+
config.sections.inside.aprooveButton !== undefined,
|
|
172
|
+
config.sections.bottom.inputSend?.borderStyle !== undefined,
|
|
173
|
+
config.sections.bottom.warningAlert !== undefined,
|
|
174
|
+
config.sections.bottom.disclaimer !== undefined
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
// Если есть хотя бы 3 признака V2, считаем это V2
|
|
178
|
+
const v2FeaturesCount = hasV2Features.filter(Boolean).length
|
|
179
|
+
if (v2FeaturesCount >= 3) {
|
|
180
|
+
return '2.0'
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Базовая проверка на V1
|
|
184
|
+
const hasV1Structure = [
|
|
185
|
+
config.settings.widgetTitle !== undefined,
|
|
186
|
+
config.settings.bgChat !== undefined,
|
|
187
|
+
config.sections.top !== undefined,
|
|
188
|
+
config.sections.inside !== undefined,
|
|
189
|
+
config.sections.bottom !== undefined
|
|
190
|
+
].every(Boolean)
|
|
191
|
+
|
|
192
|
+
if (hasV1Structure) {
|
|
193
|
+
return '1.0'
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return null
|
|
197
|
+
} catch (error) {
|
|
198
|
+
console.error('Ошибка при детекции версии:', error)
|
|
199
|
+
return null
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Дефолтный валидатор конфигураций */
|
|
205
|
+
export class DefaultConfigValidator implements ConfigValidator {
|
|
206
|
+
validate(config: any, version: ConfigVersion): { isValid: boolean; errors: string[]; warnings: string[] } {
|
|
207
|
+
const errors: string[] = []
|
|
208
|
+
const warnings: string[] = []
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
if (!config) {
|
|
212
|
+
errors.push('Конфигурация не может быть пустой')
|
|
213
|
+
return { isValid: false, errors, warnings }
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Базовая валидация структуры
|
|
217
|
+
if (!config.settings) {
|
|
218
|
+
errors.push('Отсутствует секция settings')
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!config.sections) {
|
|
222
|
+
errors.push('Отсутствует секция sections')
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (config.sections) {
|
|
226
|
+
if (!config.sections.top) errors.push('Отсутствует секция sections.top')
|
|
227
|
+
if (!config.sections.inside) errors.push('Отсутствует секция sections.inside')
|
|
228
|
+
if (!config.sections.bottom) errors.push('Отсутствует секция sections.bottom')
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Версионная валидация
|
|
232
|
+
switch (version) {
|
|
233
|
+
case '1.0':
|
|
234
|
+
this.validateV1(config, errors, warnings)
|
|
235
|
+
break
|
|
236
|
+
case '2.0':
|
|
237
|
+
this.validateV2(config, errors, warnings)
|
|
238
|
+
break
|
|
239
|
+
default:
|
|
240
|
+
warnings.push(`Валидация для версии ${version} не реализована`)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
isValid: errors.length === 0,
|
|
245
|
+
errors,
|
|
246
|
+
warnings
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
errors.push(`Ошибка при валидации: ${error}`)
|
|
250
|
+
return { isValid: false, errors, warnings }
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
private validateV1(config: any, errors: string[], _warnings: string[]): void {
|
|
255
|
+
// Проверяем обязательные поля V1
|
|
256
|
+
const requiredV1Fields = [
|
|
257
|
+
'settings.widgetTitle',
|
|
258
|
+
'settings.welcomeMessage',
|
|
259
|
+
'settings.bgChat'
|
|
260
|
+
]
|
|
261
|
+
|
|
262
|
+
for (const field of requiredV1Fields) {
|
|
263
|
+
if (!this.getNestedValue(config, field)) {
|
|
264
|
+
errors.push(`Отсутствует обязательное поле: ${field}`)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private validateV2(config: any, errors: string[], warnings: string[]): void {
|
|
270
|
+
// Сначала валидируем как V1
|
|
271
|
+
this.validateV1(config, errors, warnings)
|
|
272
|
+
|
|
273
|
+
// Дополнительные проверки для V2
|
|
274
|
+
const expectedV2Fields = [
|
|
275
|
+
'settings.loader',
|
|
276
|
+
'settings.buttonStyle',
|
|
277
|
+
'sections.inside.aprooveButton',
|
|
278
|
+
'sections.bottom.warningAlert'
|
|
279
|
+
]
|
|
280
|
+
|
|
281
|
+
for (const field of expectedV2Fields) {
|
|
282
|
+
if (!this.getNestedValue(config, field)) {
|
|
283
|
+
warnings.push(`Рекомендуется добавить поле V2: ${field}`)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private getNestedValue(obj: any, path: string): any {
|
|
289
|
+
return path.split('.').reduce((current, key) => current?.[key], obj)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Фабрика команд */
|
|
294
|
+
export class CommandFactory {
|
|
295
|
+
private static commands = new Map<string, () => MigrationCommand>([
|
|
296
|
+
['DetectVersion', () => new DetectVersionCommand()],
|
|
297
|
+
['ValidateConfig', () => new ValidateConfigCommand()],
|
|
298
|
+
['CleanConfig', () => new CleanConfigCommand()],
|
|
299
|
+
['BackupConfig', () => new BackupConfigCommand()]
|
|
300
|
+
])
|
|
301
|
+
|
|
302
|
+
static createCommand(name: string): MigrationCommand | null {
|
|
303
|
+
const factory = this.commands.get(name)
|
|
304
|
+
return factory ? factory() : null
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static getAllCommands(): string[] {
|
|
308
|
+
return Array.from(this.commands.keys())
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
static registerCommand(name: string, factory: () => MigrationCommand): void {
|
|
312
|
+
this.commands.set(name, factory)
|
|
313
|
+
}
|
|
314
|
+
}
|