@bagelink/blox 1.5.17 → 1.5.20

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 (109) hide show
  1. package/dist/blox.css +720 -68
  2. package/dist/components/base/Button.vue.d.ts.map +1 -1
  3. package/dist/components/base/Container.vue.d.ts.map +1 -1
  4. package/dist/components/base/Image.vue.d.ts.map +1 -1
  5. package/dist/components/base/Spacer.vue.d.ts.map +1 -1
  6. package/dist/components/base/Text.vue.d.ts.map +1 -1
  7. package/dist/components/base/Title.vue.d.ts.map +1 -1
  8. package/dist/components/blocks/BigImage.vue.d.ts +12 -0
  9. package/dist/components/blocks/BigImage.vue.d.ts.map +1 -0
  10. package/dist/components/blocks/BigQuote.vue.d.ts +14 -0
  11. package/dist/components/blocks/BigQuote.vue.d.ts.map +1 -0
  12. package/dist/components/blocks/BlockFooter.vue.d.ts +17 -0
  13. package/dist/components/blocks/BlockFooter.vue.d.ts.map +1 -0
  14. package/dist/components/blocks/BlockNav.vue.d.ts +22 -0
  15. package/dist/components/blocks/BlockNav.vue.d.ts.map +1 -0
  16. package/dist/components/blocks/Cards.vue.d.ts +18 -0
  17. package/dist/components/blocks/Cards.vue.d.ts.map +1 -0
  18. package/dist/components/blocks/Contact.vue.d.ts +22 -0
  19. package/dist/components/blocks/Contact.vue.d.ts.map +1 -0
  20. package/dist/components/blocks/CountDown.vue.d.ts +76 -0
  21. package/dist/components/blocks/CountDown.vue.d.ts.map +1 -0
  22. package/dist/components/blocks/Cta.vue.d.ts +15 -0
  23. package/dist/components/blocks/Cta.vue.d.ts.map +1 -0
  24. package/dist/components/blocks/Faq.vue.d.ts +20 -0
  25. package/dist/components/blocks/Faq.vue.d.ts.map +1 -0
  26. package/dist/components/blocks/Grid.vue.d.ts +24 -0
  27. package/dist/components/blocks/Grid.vue.d.ts.map +1 -0
  28. package/dist/components/blocks/Icons.vue.d.ts +16 -0
  29. package/dist/components/blocks/Icons.vue.d.ts.map +1 -0
  30. package/dist/components/blocks/IconsList.vue.d.ts +22 -0
  31. package/dist/components/blocks/IconsList.vue.d.ts.map +1 -0
  32. package/dist/components/blocks/ImageCarousel.vue.d.ts +16 -0
  33. package/dist/components/blocks/ImageCarousel.vue.d.ts.map +1 -0
  34. package/dist/components/blocks/ImageLinkBoxes.vue.d.ts +14 -0
  35. package/dist/components/blocks/ImageLinkBoxes.vue.d.ts.map +1 -0
  36. package/dist/components/blocks/Logos.vue.d.ts +17 -0
  37. package/dist/components/blocks/Logos.vue.d.ts.map +1 -0
  38. package/dist/components/blocks/PopUp.vue.d.ts +23 -0
  39. package/dist/components/blocks/PopUp.vue.d.ts.map +1 -0
  40. package/dist/components/blocks/PriceTable.vue.d.ts +24 -0
  41. package/dist/components/blocks/PriceTable.vue.d.ts.map +1 -0
  42. package/dist/components/blocks/Space.vue.d.ts +25 -0
  43. package/dist/components/blocks/Space.vue.d.ts.map +1 -0
  44. package/dist/components/blocks/Tabs.vue.d.ts +15 -0
  45. package/dist/components/blocks/Tabs.vue.d.ts.map +1 -0
  46. package/dist/components/blocks/Team.vue.d.ts +19 -0
  47. package/dist/components/blocks/Team.vue.d.ts.map +1 -0
  48. package/dist/components/blocks/Testimonials.vue.d.ts +18 -0
  49. package/dist/components/blocks/Testimonials.vue.d.ts.map +1 -0
  50. package/dist/components/blocks/Text.vue.d.ts +10 -0
  51. package/dist/components/blocks/Text.vue.d.ts.map +1 -0
  52. package/dist/components/blocks/TextImage.vue.d.ts +20 -0
  53. package/dist/components/blocks/TextImage.vue.d.ts.map +1 -0
  54. package/dist/components/blocks/TextSide.vue.d.ts +19 -0
  55. package/dist/components/blocks/TextSide.vue.d.ts.map +1 -0
  56. package/dist/components/blocks/Title.vue.d.ts +16 -0
  57. package/dist/components/blocks/Title.vue.d.ts.map +1 -0
  58. package/dist/components/blocks/TitleSide.vue.d.ts +21 -0
  59. package/dist/components/blocks/TitleSide.vue.d.ts.map +1 -0
  60. package/dist/components/blocks/VideoBox.vue.d.ts +15 -0
  61. package/dist/components/blocks/VideoBox.vue.d.ts.map +1 -0
  62. package/dist/components/blocks/blocks.d.ts +27 -0
  63. package/dist/components/blocks/blocks.d.ts.map +1 -0
  64. package/dist/components/index.d.ts +4 -3
  65. package/dist/components/index.d.ts.map +1 -1
  66. package/dist/config/baseComponents.d.ts.map +1 -1
  67. package/dist/config/blockComponents.d.ts +41 -0
  68. package/dist/config/blockComponents.d.ts.map +1 -0
  69. package/dist/core/communication.d.ts.map +1 -1
  70. package/dist/core/registry.d.ts.map +1 -1
  71. package/dist/core/renderer.d.ts.map +1 -1
  72. package/dist/core/types.d.ts.map +1 -1
  73. package/dist/index.cjs +9223 -472
  74. package/dist/index.d.ts +2 -0
  75. package/dist/index.d.ts.map +1 -1
  76. package/dist/index.mjs +9124 -458
  77. package/dist/setup.d.ts +4 -4
  78. package/dist/setup.d.ts.map +1 -1
  79. package/dist/utils/componentPreviewGenerator.d.ts +113 -0
  80. package/dist/utils/componentPreviewGenerator.d.ts.map +1 -0
  81. package/dist/utils/normalizer.d.ts.map +1 -1
  82. package/dist/utils/styles.d.ts.map +1 -1
  83. package/dist/views/ExternalPreview.vue.d.ts.map +1 -1
  84. package/dist/views/RenderPage.vue.d.ts.map +1 -1
  85. package/package.json +3 -2
  86. package/components/base/Button.vue +0 -140
  87. package/components/base/Container.vue +0 -64
  88. package/components/base/Image.vue +0 -75
  89. package/components/base/Spacer.vue +0 -33
  90. package/components/base/Text.vue +0 -37
  91. package/components/base/Title.vue +0 -55
  92. package/components/index.ts +0 -20
  93. package/config/baseComponents.ts +0 -364
  94. package/core/communication.ts +0 -140
  95. package/core/registry.ts +0 -108
  96. package/core/renderer.ts +0 -217
  97. package/core/types.ts +0 -148
  98. package/dist/example-setup.d.ts +0 -1
  99. package/dist/example-setup.d.ts.map +0 -1
  100. package/dist/styles.d.ts +0 -8
  101. package/dist/styles.d.ts.map +0 -1
  102. package/dist/vite.config.d.ts +0 -3
  103. package/dist/vite.config.d.ts.map +0 -1
  104. package/index.ts +0 -43
  105. package/setup.ts +0 -472
  106. package/utils/normalizer.ts +0 -74
  107. package/utils/styles.ts +0 -228
  108. package/views/ExternalPreview.vue +0 -415
  109. package/views/RenderPage.vue +0 -127
package/setup.ts DELETED
@@ -1,472 +0,0 @@
1
- /**
2
- * Blox Setup and Registration
3
- *
4
- * Provides a simple, fluent API for setting up the Blox library
5
- */
6
-
7
- import type { App } from 'vue'
8
- import type { Router } from 'vue-router'
9
- import { baseComponentConfigs, type ComponentConfig } from './config/baseComponents'
10
- import { registerComponent, registerComponents as registerComponentsInRegistry } from './core/registry'
11
- import ExternalPreview from './views/ExternalPreview.vue'
12
- import RenderPage from './views/RenderPage.vue'
13
-
14
- export interface BloxInstance {
15
- /**
16
- * Register one or more components with the Blox library
17
- * Accepts either a single component config or an array of component configs
18
- */
19
- registerComponents: (
20
- components: ComponentConfig | ComponentConfig[],
21
- options?: RegisterOptions
22
- ) => BloxInstance
23
-
24
- /**
25
- * Register routes for preview pages with your Vue Router
26
- */
27
- registerRoutes: (router: Router, options?: RouteOptions) => BloxInstance
28
-
29
- /**
30
- * Get all registered component configurations
31
- */
32
- getRegisteredComponents: () => ComponentConfig[]
33
-
34
- /**
35
- * Get components by category
36
- */
37
- getComponentsByCategory: (category: string) => ComponentConfig[]
38
-
39
- /**
40
- * Get a list of registered component IDs
41
- */
42
- getComponentIds: () => string[]
43
-
44
- /**
45
- * Check if a component is registered
46
- */
47
- hasComponent: (id: string) => boolean
48
-
49
- /**
50
- * Install the Blox plugin into your Vue app
51
- */
52
- install: (app: App) => void
53
- }
54
-
55
- export interface RegisterOptions {
56
- /**
57
- * Category to assign to the components
58
- */
59
- category?: string
60
-
61
- /**
62
- * Skip validation for component configs
63
- * @default false
64
- */
65
- skipValidation?: boolean
66
- }
67
-
68
- export interface RouteOptions {
69
- /**
70
- * Base path for the preview routes
71
- * @default '/blox'
72
- */
73
- basePath?: string
74
-
75
- /**
76
- * Path for the external preview route
77
- * @default '/preview/:pageId?'
78
- */
79
- previewPath?: string
80
-
81
- /**
82
- * Path for the render page route
83
- * @default '/render/:pageId?'
84
- */
85
- renderPath?: string
86
-
87
- /**
88
- * Additional route meta properties
89
- */
90
- meta?: Record<string, any>
91
- }
92
-
93
- export interface BloxOptions {
94
- /**
95
- * Enable debug mode for additional logging
96
- * @default false
97
- */
98
- debug?: boolean
99
-
100
- /**
101
- * Automatically register base components on first registerComponents call
102
- * @default true
103
- */
104
- autoRegisterBaseComponents?: boolean
105
- }
106
-
107
- /**
108
- * Validation error class
109
- */
110
- export class ComponentValidationError extends Error {
111
- constructor(message: string, public componentId?: string) {
112
- super(message)
113
- this.name = 'ComponentValidationError'
114
- }
115
- }
116
-
117
- /**
118
- * Validate a component configuration
119
- */
120
- function validateComponentConfig(config: ComponentConfig): void {
121
- const errors: string[] = []
122
-
123
- if (!config.id || typeof config.id !== 'string') {
124
- errors.push('Component must have a valid "id" (string)')
125
- }
126
-
127
- if (!config.label || typeof config.label !== 'string') {
128
- errors.push('Component must have a valid "label" (string)')
129
- }
130
-
131
- if (!config.component) {
132
- errors.push('Component must have a "component" property (Vue component or async function)')
133
- }
134
-
135
- if (config.component && typeof config.component !== 'function' && typeof config.component !== 'object') {
136
- errors.push('Component "component" must be a Vue component or async function')
137
- }
138
-
139
- if (config.content && !Array.isArray(config.content)) {
140
- errors.push('Component "content" must be an array')
141
- }
142
-
143
- if (config.settings && !Array.isArray(config.settings)) {
144
- errors.push('Component "settings" must be an array')
145
- }
146
-
147
- if (errors.length > 0) {
148
- throw new ComponentValidationError(
149
- `Invalid component configuration for "${config.id || 'unknown'}":\n${errors.map(e => ` - ${e}`).join('\n')}`,
150
- config.id
151
- )
152
- }
153
- }
154
-
155
- /**
156
- * Calculate Levenshtein distance between two strings
157
- * Used for "did you mean" suggestions
158
- */
159
- function levenshteinDistance(a: string, b: string): number {
160
- const matrix: number[][] = []
161
-
162
- for (let i = 0; i <= b.length; i++) {
163
- matrix[i] = [i]
164
- }
165
-
166
- for (let j = 0; j <= a.length; j++) {
167
- matrix[0][j] = j
168
- }
169
-
170
- for (let i = 1; i <= b.length; i++) {
171
- for (let j = 1; j <= a.length; j++) {
172
- if (b.charAt(i - 1) === a.charAt(j - 1)) {
173
- matrix[i][j] = matrix[i - 1][j - 1]
174
- } else {
175
- matrix[i][j] = Math.min(
176
- matrix[i - 1][j - 1] + 1,
177
- matrix[i][j - 1] + 1,
178
- matrix[i - 1][j] + 1
179
- )
180
- }
181
- }
182
- }
183
-
184
- return matrix[b.length][a.length]
185
- }
186
-
187
- /**
188
- * Find similar component IDs (for "did you mean" suggestions)
189
- */
190
- function findSimilarIds(targetId: string, allIds: string[], maxSuggestions: number = 3): string[] {
191
- return allIds
192
- .map(id => ({ id, distance: levenshteinDistance(targetId.toLowerCase(), id.toLowerCase()) }))
193
- .filter(item => item.distance <= 3) // Only show suggestions within 3 edits
194
- .sort((a, b) => a.distance - b.distance)
195
- .slice(0, maxSuggestions)
196
- .map(item => item.id)
197
- }
198
-
199
- /**
200
- * Create better error message for missing component
201
- */
202
- function createMissingComponentError(id: string, registeredIds: string[]): Error {
203
- const suggestions = findSimilarIds(id, registeredIds)
204
- let message = `Component "${id}" is not registered.`
205
-
206
- if (suggestions.length > 0) {
207
- message += `\n\nDid you mean: ${suggestions.map(s => `"${s}"`).join(', ')}?`
208
- }
209
-
210
- message += `\n\nAvailable components: ${registeredIds.join(', ')}`
211
-
212
- return new Error(message)
213
- }
214
-
215
- /**
216
- * Create a new Blox instance with a fluent API
217
- *
218
- * @param options - Configuration options for the Blox instance
219
- *
220
- * @example
221
- * ```ts
222
- * import { createBlox, ButtonConfig, TextConfig } from '@bagelink/blox'
223
- * import { MyCustomComp } from './components'
224
- *
225
- * const blox = createBlox({ debug: true })
226
- * blox.registerComponents([ButtonConfig, TextConfig, MyCustomComp])
227
- * blox.registerRoutes(router)
228
- *
229
- * app.use(blox)
230
- * ```
231
- */
232
- export function createBlox(options: BloxOptions = {}): BloxInstance {
233
- const {
234
- debug = false,
235
- autoRegisterBaseComponents = true,
236
- } = options
237
-
238
- const registeredConfigs: ComponentConfig[] = []
239
- let hasRegisteredBaseComponents = false
240
-
241
- const log = (...args: any[]) => {
242
- if (debug) {
243
- console.log('[Blox]', ...args)
244
- }
245
- }
246
-
247
- const instance: BloxInstance = {
248
- registerComponents(
249
- components: ComponentConfig | ComponentConfig[],
250
- registerOptions: RegisterOptions = {}
251
- ) {
252
- const { category, skipValidation = false } = registerOptions
253
- const configs = Array.isArray(components) ? components : [components]
254
-
255
- // Auto-register base components on first call if enabled
256
- if (!hasRegisteredBaseComponents && autoRegisterBaseComponents) {
257
- const componentMap: Record<string, any> = {}
258
- baseComponentConfigs.forEach((config) => {
259
- componentMap[config.id] = config.component
260
- registeredConfigs.push(config)
261
- })
262
- registerComponentsInRegistry(componentMap)
263
- hasRegisteredBaseComponents = true
264
- log('Registered base components:', Object.keys(componentMap))
265
- if (!debug) {
266
- console.log('✅ Registered base components:', Object.keys(componentMap))
267
- }
268
- }
269
-
270
- // Register the provided components
271
- configs.forEach((config) => {
272
- // Validate config
273
- if (!skipValidation) {
274
- try {
275
- validateComponentConfig(config)
276
- } catch (error) {
277
- if (error instanceof ComponentValidationError) {
278
- console.error('❌ Component validation failed:')
279
- console.error(error.message)
280
- if (debug) {
281
- console.error('Component config:', config)
282
- }
283
- throw error
284
- }
285
- throw error
286
- }
287
- }
288
-
289
- // Apply category if provided in options
290
- const finalConfig = category ? { ...config, category } : config
291
-
292
- // Check for duplicate IDs
293
- if (registeredConfigs.some(c => c.id === finalConfig.id)) {
294
- const msg = `Component "${finalConfig.id}" is already registered. Overwriting...`
295
- console.warn(`⚠️ ${msg}`)
296
- const index = registeredConfigs.findIndex(c => c.id === finalConfig.id)
297
- registeredConfigs.splice(index, 1)
298
- }
299
-
300
- // For async components, store the factory function
301
- // They will be resolved when actually needed
302
- registerComponent(finalConfig.id, finalConfig.component)
303
- registeredConfigs.push(finalConfig)
304
-
305
- const categoryMsg = finalConfig.category ? ` (${finalConfig.category})` : ''
306
- log(`Registered component: ${finalConfig.id}${categoryMsg}`)
307
- if (!debug) {
308
- console.log(`✅ Registered component: ${finalConfig.id}${categoryMsg}`)
309
- }
310
- })
311
-
312
- return instance
313
- },
314
-
315
- registerRoutes(router: Router, routeOptions: RouteOptions = {}) {
316
- const {
317
- basePath = '/blox',
318
- previewPath = '/preview/:pageId?',
319
- renderPath = '/render/:pageId?',
320
- meta = {},
321
- } = routeOptions
322
-
323
- // Add the external preview route
324
- router.addRoute({
325
- path: `${basePath}${previewPath}`,
326
- name: 'blox-preview',
327
- component: ExternalPreview,
328
- meta: { blox: true, ...meta },
329
- })
330
-
331
- // Add the render page route
332
- router.addRoute({
333
- path: `${basePath}${renderPath}`,
334
- name: 'blox-render',
335
- component: RenderPage,
336
- meta: { blox: true, ...meta },
337
- })
338
-
339
- const routes = {
340
- preview: `${basePath}${previewPath}`,
341
- render: `${basePath}${renderPath}`,
342
- }
343
- log('Registered Blox routes:', routes)
344
- if (!debug) {
345
- console.log('✅ Registered Blox routes:', routes)
346
- }
347
-
348
- return instance
349
- },
350
-
351
- getRegisteredComponents() {
352
- return [...registeredConfigs]
353
- },
354
-
355
- getComponentsByCategory(category: string) {
356
- return registeredConfigs.filter(config => config.category === category)
357
- },
358
-
359
- getComponentIds() {
360
- return registeredConfigs.map(config => config.id)
361
- },
362
-
363
- hasComponent(id: string) {
364
- return registeredConfigs.some(config => config.id === id)
365
- },
366
-
367
- install(app: App) {
368
- // Make the registered configs available globally
369
- app.config.globalProperties.$blox = {
370
- configs: registeredConfigs,
371
- getComponent: (id: string) => {
372
- const config = registeredConfigs.find(c => c.id === id)
373
- if (!config && debug) {
374
- const error = createMissingComponentError(id, this.getComponentIds())
375
- console.error(error.message)
376
- }
377
- return config
378
- },
379
- getComponentsByCategory: (category: string) => this.getComponentsByCategory(category),
380
- hasComponent: (id: string) => registeredConfigs.some(c => c.id === id),
381
- }
382
-
383
- // Provide for composition API
384
- app.provide('blox', {
385
- configs: registeredConfigs,
386
- getComponent: (id: string) => {
387
- const config = registeredConfigs.find(c => c.id === id)
388
- if (!config && debug) {
389
- const error = createMissingComponentError(id, this.getComponentIds())
390
- console.error(error.message)
391
- }
392
- return config
393
- },
394
- getComponentsByCategory: (category: string) => this.getComponentsByCategory(category),
395
- hasComponent: (id: string) => registeredConfigs.some(c => c.id === id),
396
- })
397
-
398
- log('Blox plugin installed with', registeredConfigs.length, 'components')
399
- if (!debug) {
400
- console.log('✅ Blox plugin installed')
401
- }
402
-
403
- if (debug) {
404
- console.log('[Blox] Registered components:', this.getComponentIds())
405
- const categories = [
406
- ...new Set(
407
- registeredConfigs
408
- .map(c => c.category)
409
- .filter((cat): cat is string => Boolean(cat))
410
- )
411
- ]
412
- console.log('[Blox] Categories:', categories)
413
- console.log('[Blox] Debug mode enabled')
414
- }
415
- },
416
- }
417
-
418
- return instance
419
- }
420
-
421
- /**
422
- * Helper to create a custom component configuration with better TypeScript inference
423
- */
424
- export function createComponentConfig<T extends ComponentConfig>(config: T): T {
425
- return config
426
- }
427
-
428
- /**
429
- * Get all component configurations (base + custom)
430
- * This returns both the Vue components and their editor schemas
431
- */
432
- export function getAllComponentConfigs(customConfigs: ComponentConfig[] = []): ComponentConfig[] {
433
- return [...baseComponentConfigs, ...customConfigs]
434
- }
435
-
436
- /**
437
- * Register all base components from the library
438
- * Call this in your project's setup to make base components available
439
- *
440
- * @deprecated Use `createBlox().registerComponents()` instead for a simpler API
441
- */
442
- export function registerBaseComponents(): void {
443
- const componentMap: Record<string, any> = {}
444
-
445
- baseComponentConfigs.forEach((config) => {
446
- componentMap[config.id] = config.component
447
- })
448
-
449
- registerComponentsInRegistry(componentMap)
450
- console.log('✅ Registered base components:', Object.keys(componentMap))
451
- }
452
-
453
- /**
454
- * Register a single custom component
455
- *
456
- * @deprecated Use `createBlox().registerComponents()` instead for a simpler API
457
- */
458
- export function registerCustomComponent(config: ComponentConfig): void {
459
- registerComponent(config.id, config.component)
460
- console.log(`✅ Registered custom component: ${config.id}`)
461
- }
462
-
463
- /**
464
- * Register multiple custom components
465
- *
466
- * @deprecated Use `createBlox().registerComponents()` instead for a simpler API
467
- */
468
- export function registerCustomComponents(configs: ComponentConfig[]): void {
469
- configs.forEach((config) => {
470
- registerCustomComponent(config)
471
- })
472
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * Data Normalization Utilities
3
- *
4
- * Normalizes component data for proper rendering
5
- */
6
-
7
- /**
8
- * Convert string values to proper types for component props
9
- */
10
- export function normalizeComponentData(data: Record<string, any>): Record<string, any> {
11
- const normalized: Record<string, any> = {}
12
-
13
- // Fields that should remain as strings even if they look like numbers
14
- const stringFields = ['title', 'subTitle', 'btnTxt', 'tag', 'customId', 'height', 'url', 'href']
15
-
16
- for (const [key, value] of Object.entries(data)) {
17
- if (typeof value === 'string') {
18
- // Convert string booleans to actual booleans
19
- if (value === 'true') {
20
- normalized[key] = true
21
- } else if (value === 'false') {
22
- normalized[key] = false
23
- } else if (!Number.isNaN(Number(value)) && value !== '' && !stringFields.includes(key)) {
24
- // Convert string numbers to numbers (but not for text fields)
25
- normalized[key] = Number(value)
26
- } else {
27
- normalized[key] = value
28
- }
29
- } else {
30
- normalized[key] = value
31
- }
32
- }
33
-
34
- // Provide default props to avoid Vue warnings for missing required props
35
- if (!normalized.items) {
36
- normalized.items = []
37
- }
38
-
39
- // Ensure height is always a string if it exists
40
- if (normalized.height !== undefined && typeof normalized.height === 'number') {
41
- normalized.height = String(normalized.height)
42
- }
43
-
44
- if (normalized.height === 'auto') {
45
- // Convert auto height strings to valid numbers for components that expect numbers
46
- delete normalized.height
47
- }
48
-
49
- return normalized
50
- }
51
-
52
- /**
53
- * Deep clone an object
54
- */
55
- export function deepClone<T>(obj: T): T {
56
- return JSON.parse(JSON.stringify(obj))
57
- }
58
-
59
- /**
60
- * Merge objects deeply
61
- */
62
- export function deepMerge(target: any, source: any): any {
63
- const result = { ...target }
64
-
65
- for (const key in source) {
66
- if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
67
- result[key] = deepMerge(result[key] || {}, source[key])
68
- } else {
69
- result[key] = source[key]
70
- }
71
- }
72
-
73
- return result
74
- }