ckeditor5-livewire 1.9.0 → 1.11.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.
Files changed (46) hide show
  1. package/dist/hooks/editable.d.ts +3 -5
  2. package/dist/hooks/editable.d.ts.map +1 -1
  3. package/dist/hooks/editor/editor.d.ts +0 -4
  4. package/dist/hooks/editor/editor.d.ts.map +1 -1
  5. package/dist/hooks/editor/plugins/livewire-sync.d.ts.map +1 -1
  6. package/dist/hooks/editor/utils/cleanup-orphan-editor-elements.d.ts +8 -0
  7. package/dist/hooks/editor/utils/cleanup-orphan-editor-elements.d.ts.map +1 -0
  8. package/dist/hooks/editor/utils/create-editor-in-context.d.ts +6 -1
  9. package/dist/hooks/editor/utils/create-editor-in-context.d.ts.map +1 -1
  10. package/dist/hooks/editor/utils/index.d.ts +2 -0
  11. package/dist/hooks/editor/utils/index.d.ts.map +1 -1
  12. package/dist/hooks/editor/utils/is-multiroot-editor-instance.d.ts +6 -0
  13. package/dist/hooks/editor/utils/is-multiroot-editor-instance.d.ts.map +1 -0
  14. package/dist/hooks/editor/utils/wrap-with-watchdog.d.ts +7 -16
  15. package/dist/hooks/editor/utils/wrap-with-watchdog.d.ts.map +1 -1
  16. package/dist/hooks/ui-part.d.ts +2 -6
  17. package/dist/hooks/ui-part.d.ts.map +1 -1
  18. package/dist/index.cjs +2 -2
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.mjs +297 -220
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/shared/are-maps-equal.d.ts +11 -0
  23. package/dist/shared/are-maps-equal.d.ts.map +1 -0
  24. package/dist/shared/async-registry.d.ts +43 -10
  25. package/dist/shared/async-registry.d.ts.map +1 -1
  26. package/dist/shared/index.d.ts +1 -0
  27. package/dist/shared/index.d.ts.map +1 -1
  28. package/package.json +3 -3
  29. package/src/hooks/context/context.test.ts +3 -1
  30. package/src/hooks/editable.ts +73 -46
  31. package/src/hooks/editor/editor.test.ts +44 -9
  32. package/src/hooks/editor/editor.ts +159 -149
  33. package/src/hooks/editor/plugins/livewire-sync.ts +17 -8
  34. package/src/hooks/editor/utils/cleanup-orphan-editor-elements.test.ts +285 -0
  35. package/src/hooks/editor/utils/cleanup-orphan-editor-elements.ts +60 -0
  36. package/src/hooks/editor/utils/create-editor-in-context.ts +6 -2
  37. package/src/hooks/editor/utils/index.ts +2 -0
  38. package/src/hooks/editor/utils/is-multiroot-editor-instance.ts +8 -0
  39. package/src/hooks/editor/utils/wrap-with-watchdog.test.ts +34 -14
  40. package/src/hooks/editor/utils/wrap-with-watchdog.ts +16 -26
  41. package/src/hooks/ui-part.ts +10 -16
  42. package/src/shared/are-maps-equal.test.ts +56 -0
  43. package/src/shared/are-maps-equal.ts +22 -0
  44. package/src/shared/async-registry.test.ts +212 -31
  45. package/src/shared/async-registry.ts +178 -61
  46. package/src/shared/index.ts +1 -0
@@ -96,30 +96,6 @@ describe('async registry', () => {
96
96
  registry.unregister('item1');
97
97
  expect(registry.getItems()).not.toContain(item);
98
98
  });
99
-
100
- it('should throw an error if trying to unregister an item that is not registered', () => {
101
- expect(() => registry.unregister('nonexistent')).toThrow(
102
- 'Item with ID "nonexistent" is not registered.',
103
- );
104
- });
105
-
106
- it('should also unregister the default item if the unregistered item was the default one', async () => {
107
- const item1 = createMockItem('item1');
108
-
109
- registry.register('item1', item1); // This also registers it as default
110
-
111
- // Check it is the default
112
- const promise = registry.execute(null, item => item);
113
-
114
- await expect(promise).resolves.toBe(item1);
115
-
116
- registry.unregister('item1');
117
-
118
- // Now check that the default is also gone
119
- expect(() => registry.unregister(null)).toThrow(
120
- 'Item with ID "null" is not registered.',
121
- );
122
- });
123
99
  });
124
100
 
125
101
  describe('execute', () => {
@@ -234,7 +210,7 @@ describe('async registry', () => {
234
210
  });
235
211
  });
236
212
 
237
- describe('getitems', () => {
213
+ describe('getItems', () => {
238
214
  it('should return all registered items', () => {
239
215
  const item1 = createMockItem('item1');
240
216
  const item2 = createMockItem('item2');
@@ -244,7 +220,7 @@ describe('async registry', () => {
244
220
 
245
221
  const items = registry.getItems();
246
222
 
247
- expect(items).toHaveLength(3); // item1, item2, and default (which is item1)
223
+ expect(items).toHaveLength(3);
248
224
  expect(items).toContain(item1);
249
225
  expect(items).toContain(item2);
250
226
  });
@@ -252,16 +228,30 @@ describe('async registry', () => {
252
228
  it('should return unique items if some point to the same instance', () => {
253
229
  const item1 = createMockItem('item1');
254
230
 
255
- registry.register('item1', item1); // This also registers it as default
231
+ registry.register('item1', item1);
256
232
 
257
233
  const items = registry.getItems();
258
234
 
259
- expect(items).toHaveLength(2); // item1 and default (which is item1)
235
+ expect(items).toHaveLength(2);
260
236
  expect(items.filter(e => e === item1)).toHaveLength(2);
261
237
  });
262
238
  });
263
239
 
264
- describe('hasitem', () => {
240
+ describe('getItem', () => {
241
+ it('should return registered item', () => {
242
+ const item = createMockItem('item1');
243
+
244
+ registry.register('item1', item);
245
+
246
+ expect(registry.getItem('item1')).toBe(item);
247
+ });
248
+
249
+ it('should return undefined if item doesn\'t exist', () => {
250
+ expect(registry.getItem('item1')).toBeUndefined();
251
+ });
252
+ });
253
+
254
+ describe('hasItem', () => {
265
255
  it('should return true if an item with the given ID is registered', () => {
266
256
  const item = createMockItem('item1');
267
257
 
@@ -402,6 +392,197 @@ describe('async registry', () => {
402
392
  });
403
393
  });
404
394
 
395
+ describe('reset', () => {
396
+ it('should destroy all registered items', async () => {
397
+ const item1 = createMockItem('item1');
398
+ const item2 = createMockItem('item2');
399
+
400
+ registry.register('item1', item1);
401
+ registry.register('item2', item2);
402
+
403
+ await registry.reset();
404
+
405
+ expect(registry.getItems()).toHaveLength(0);
406
+ });
407
+
408
+ it('should call destroy on each unique item', async () => {
409
+ const destroyMock1 = vi.fn().mockResolvedValue(undefined);
410
+ const destroyMock2 = vi.fn().mockResolvedValue(undefined);
411
+
412
+ const item1 = { name: 'item1', destroy: destroyMock1 } as unknown as Mockitem;
413
+ const item2 = { name: 'item2', destroy: destroyMock2 } as unknown as Mockitem;
414
+
415
+ registry.register('item1', item1);
416
+ registry.register('item2', item2);
417
+
418
+ await registry.reset();
419
+
420
+ expect(destroyMock1).toHaveBeenCalledOnce();
421
+ expect(destroyMock2).toHaveBeenCalledOnce();
422
+ });
423
+
424
+ it('should clear watchers so they are no longer called after reset', async () => {
425
+ const watcher = vi.fn();
426
+ registry.watch(watcher);
427
+
428
+ await registry.reset();
429
+ watcher.mockClear();
430
+
431
+ const item = createMockItem('item1');
432
+ registry.register('item1', item);
433
+
434
+ expect(watcher).not.toHaveBeenCalled();
435
+ });
436
+ });
437
+
438
+ describe('mountEffect', () => {
439
+ it('should call onMount immediately when item is already registered', () => {
440
+ const item = createMockItem('item1');
441
+ registry.register('item1', item);
442
+
443
+ const onMount = vi.fn();
444
+ registry.mountEffect('item1', onMount);
445
+
446
+ expect(onMount).toHaveBeenCalledOnce();
447
+ expect(onMount).toHaveBeenCalledWith(item);
448
+ });
449
+
450
+ it('should call onMount when item registers later', () => {
451
+ const onMount = vi.fn();
452
+ registry.mountEffect('item1', onMount);
453
+
454
+ expect(onMount).not.toHaveBeenCalled();
455
+
456
+ const item = createMockItem('item1');
457
+ registry.register('item1', item);
458
+
459
+ expect(onMount).toHaveBeenCalledOnce();
460
+ expect(onMount).toHaveBeenCalledWith(item);
461
+ });
462
+
463
+ it('should call cleanup returned by onMount when item is unregistered', () => {
464
+ const item = createMockItem('item1');
465
+ registry.register('item1', item);
466
+
467
+ const cleanup = vi.fn();
468
+ registry.mountEffect('item1', () => cleanup);
469
+
470
+ registry.unregister('item1');
471
+
472
+ expect(cleanup).toHaveBeenCalledOnce();
473
+ });
474
+
475
+ it('should call cleanup and re-call onMount on re-registration', () => {
476
+ const item1a = createMockItem('item1');
477
+ registry.register('item1', item1a);
478
+
479
+ const cleanup = vi.fn();
480
+ const onMount = vi.fn(() => cleanup);
481
+ registry.mountEffect('item1', onMount);
482
+
483
+ expect(onMount).toHaveBeenCalledOnce();
484
+
485
+ registry.unregister('item1');
486
+ expect(cleanup).toHaveBeenCalledOnce();
487
+
488
+ const item1b = createMockItem('item1');
489
+ registry.register('item1', item1b);
490
+
491
+ expect(onMount).toHaveBeenCalledTimes(2);
492
+ expect(onMount).toHaveBeenLastCalledWith(item1b);
493
+ });
494
+
495
+ it('should not call onMount if item never registers', () => {
496
+ const onMount = vi.fn();
497
+ const stop = registry.mountEffect('item1', onMount);
498
+
499
+ stop();
500
+
501
+ expect(onMount).not.toHaveBeenCalled();
502
+ });
503
+
504
+ it('should run cleanup and stop watching when stop is called with item mounted', () => {
505
+ const item = createMockItem('item1');
506
+ registry.register('item1', item);
507
+
508
+ const cleanup = vi.fn();
509
+ const stop = registry.mountEffect('item1', () => cleanup);
510
+
511
+ stop();
512
+
513
+ expect(cleanup).toHaveBeenCalledOnce();
514
+
515
+ // Watcher should be gone — unregistering should not trigger cleanup again.
516
+ registry.unregister('item1');
517
+
518
+ expect(cleanup).toHaveBeenCalledOnce();
519
+ });
520
+
521
+ it('should not call cleanup when stop is called before item registers', () => {
522
+ const cleanup = vi.fn();
523
+ const onMount = vi.fn(() => cleanup);
524
+ const stop = registry.mountEffect('item1', onMount);
525
+
526
+ stop();
527
+
528
+ expect(onMount).not.toHaveBeenCalled();
529
+ expect(cleanup).not.toHaveBeenCalled();
530
+ });
531
+
532
+ it('should call onMount and immediately run cleanup when item registers after stop', () => {
533
+ const cleanup = vi.fn();
534
+ const onMount = vi.fn(() => cleanup);
535
+ const stop = registry.mountEffect('item1', onMount);
536
+
537
+ stop();
538
+
539
+ const item = createMockItem('item1');
540
+ registry.register('item1', item);
541
+
542
+ expect(onMount).toHaveBeenCalledOnce();
543
+ expect(onMount).toHaveBeenCalledWith(item);
544
+ expect(cleanup).toHaveBeenCalledOnce();
545
+ });
546
+
547
+ it('should stop watching after late cleanup fires', () => {
548
+ const onMount = vi.fn();
549
+ const stop = registry.mountEffect('item1', onMount);
550
+
551
+ stop();
552
+
553
+ const item1 = createMockItem('item1');
554
+ registry.register('item1', item1);
555
+
556
+ expect(onMount).toHaveBeenCalledOnce();
557
+
558
+ // Watcher should have been removed after late cleanup — further changes are ignored.
559
+ registry.unregister('item1');
560
+ registry.register('item1', createMockItem('item1'));
561
+
562
+ expect(onMount).toHaveBeenCalledOnce();
563
+ });
564
+
565
+ it('should not throw when onMount returns void and stop is called', () => {
566
+ const item = createMockItem('item1');
567
+ registry.register('item1', item);
568
+
569
+ const stop = registry.mountEffect('item1', () => {});
570
+
571
+ expect(() => stop()).not.toThrow();
572
+ });
573
+
574
+ it('should work with the default null ID', () => {
575
+ const item = createMockItem('item1');
576
+ registry.register('item1', item); // also registered as default
577
+
578
+ const onMount = vi.fn();
579
+ registry.mountEffect(null, onMount);
580
+
581
+ expect(onMount).toHaveBeenCalledOnce();
582
+ expect(onMount).toHaveBeenCalledWith(item);
583
+ });
584
+ });
585
+
405
586
  describe('waitFor', () => {
406
587
  it('should return a promise that resolves with the item instance', async () => {
407
588
  const item1 = createMockItem('item1');
@@ -518,7 +699,7 @@ describe('async registry', () => {
518
699
  const item1 = createMockItem('item1');
519
700
  registry.register('item1', item1);
520
701
 
521
- expect(watcher).toHaveBeenCalledTimes(3);
702
+ expect(watcher).toHaveBeenCalledTimes(2);
522
703
  });
523
704
 
524
705
  it('should call watcher when item is unregistered', () => {
@@ -531,7 +712,7 @@ describe('async registry', () => {
531
712
  watcher.mockClear();
532
713
  registry.unregister('item1');
533
714
 
534
- expect(watcher).toHaveBeenCalledTimes(2); // Unregister + default unregister
715
+ expect(watcher).toHaveBeenCalledTimes(1);
535
716
  });
536
717
 
537
718
  it('should call watcher when all items are destroyed', async () => {
@@ -1,3 +1,5 @@
1
+ import { areMapsEqual } from './are-maps-equal';
2
+
1
3
  /**
2
4
  * Generic async registry for objects with an async destroy method.
3
5
  * Provides a way to register, unregister, and execute callbacks on objects by ID.
@@ -23,6 +25,18 @@ export class AsyncRegistry<T extends Destructible> {
23
25
  */
24
26
  private readonly watchers = new Set<RegistryWatcher<T>>();
25
27
 
28
+ /**
29
+ * Batch nesting depth. When > 0, watcher notifications are deferred.
30
+ */
31
+ private batchDepth = 0;
32
+
33
+ /**
34
+ * Snapshot of the last state dispatched to watchers, used for change detection.
35
+ */
36
+ private lastNotifiedItems: Map<any, any> | null = null;
37
+
38
+ private lastNotifiedErrors: Map<any, any> | null = null;
39
+
26
40
  /**
27
41
  * Executes a function on an item.
28
42
  * If the item is not yet registered, it will wait for it to be registered.
@@ -68,6 +82,66 @@ export class AsyncRegistry<T extends Destructible> {
68
82
  });
69
83
  }
70
84
 
85
+ /**
86
+ * Reactively binds a mount/unmount lifecycle to a single registry item.
87
+ *
88
+ * @param id The ID of the item to observe.
89
+ * @param onMount Function executed when the item mounts.
90
+ * @returns A function that stops observing and immediately runs any pending cleanup.
91
+ */
92
+ mountEffect<E extends T = T>(
93
+ id: RegistryId | null,
94
+ onMount: (item: E) => (() => void) | void,
95
+ ): () => void {
96
+ let cleanup: VoidFunction | void;
97
+ let mountedItem: T | undefined;
98
+ let unmounted = false;
99
+
100
+ const unwatch = this.watch((items) => {
101
+ const item = items.get(id);
102
+
103
+ if (item === mountedItem) {
104
+ return;
105
+ }
106
+
107
+ cleanup?.();
108
+ cleanup = undefined;
109
+ mountedItem = item;
110
+
111
+ if (!item) {
112
+ return;
113
+ }
114
+
115
+ try {
116
+ const newCleanup = onMount(item as E);
117
+
118
+ if (unmounted) {
119
+ newCleanup?.();
120
+ unwatch();
121
+ }
122
+ else {
123
+ cleanup = newCleanup;
124
+ }
125
+ }
126
+ catch (err) {
127
+ /* v8 ignore start -- @preserve */
128
+ console.error(err);
129
+ throw err;
130
+ /* v8 ignore end */
131
+ }
132
+ });
133
+
134
+ return () => {
135
+ unmounted = true;
136
+
137
+ if (mountedItem) {
138
+ unwatch();
139
+ cleanup?.();
140
+ cleanup = undefined;
141
+ }
142
+ };
143
+ }
144
+
71
145
  /**
72
146
  * Registers an item.
73
147
  *
@@ -75,24 +149,27 @@ export class AsyncRegistry<T extends Destructible> {
75
149
  * @param item The item instance.
76
150
  */
77
151
  register(id: RegistryId | null, item: T): void {
78
- if (this.items.has(id)) {
79
- throw new Error(`Item with ID "${id}" is already registered.`);
80
- }
152
+ this.batch(() => {
153
+ if (this.items.has(id)) {
154
+ throw new Error(`Item with ID "${id}" is already registered.`);
155
+ }
81
156
 
82
- this.resetErrors(id);
83
- this.items.set(id, item);
157
+ this.resetErrors(id);
158
+ this.items.set(id, item);
84
159
 
85
- // Execute all pending callbacks for this item (synchronously).
86
- const pending = this.pendingCallbacks.get(id);
160
+ // Execute all pending callbacks for this item (synchronously).
161
+ const pending = this.pendingCallbacks.get(id);
87
162
 
88
- if (pending) {
89
- pending.success.forEach(callback => callback(item));
90
- this.pendingCallbacks.delete(id);
91
- }
163
+ if (pending) {
164
+ pending.success.forEach(callback => callback(item));
165
+ this.pendingCallbacks.delete(id);
166
+ }
92
167
 
93
- // Register the first item as the default item (null ID).
94
- this.registerAsDefault(id, item);
95
- this.notifyWatchers();
168
+ // Register the first item as the default item (null ID).
169
+ if (this.items.size === 1 && id !== null) {
170
+ this.register(null, item);
171
+ }
172
+ });
96
173
  }
97
174
 
98
175
  /**
@@ -102,24 +179,23 @@ export class AsyncRegistry<T extends Destructible> {
102
179
  * @param error The error to register.
103
180
  */
104
181
  error(id: RegistryId | null, error: any): void {
105
- this.items.delete(id);
106
- this.initializationErrors.set(id, error);
182
+ this.batch(() => {
183
+ this.items.delete(id);
184
+ this.initializationErrors.set(id, error);
107
185
 
108
- // Execute all pending error callbacks for this item.
109
- const pending = this.pendingCallbacks.get(id);
186
+ // Execute all pending error callbacks for this item.
187
+ const pending = this.pendingCallbacks.get(id);
110
188
 
111
- if (pending) {
112
- pending.error.forEach(callback => callback(error));
113
- this.pendingCallbacks.delete(id);
114
- }
115
-
116
- // Set as default error if this is the first error and no items exist.
117
- if (this.initializationErrors.size === 1 && !this.items.size) {
118
- this.error(null, error);
119
- }
189
+ if (pending) {
190
+ pending.error.forEach(callback => callback(error));
191
+ this.pendingCallbacks.delete(id);
192
+ }
120
193
 
121
- // Notify watchers about the error state.
122
- this.notifyWatchers();
194
+ // Set as default error if this is the first error and no items exist.
195
+ if (this.initializationErrors.size === 1 && !this.items.size) {
196
+ this.error(null, error);
197
+ }
198
+ });
123
199
  }
124
200
 
125
201
  /**
@@ -142,21 +218,23 @@ export class AsyncRegistry<T extends Destructible> {
142
218
  * Un-registers an item.
143
219
  *
144
220
  * @param id The ID of the item.
221
+ * @param resetPendingCallbacks If true resets pending callbacks.
145
222
  */
146
- unregister(id: RegistryId | null): void {
147
- if (!this.items.has(id)) {
148
- throw new Error(`Item with ID "${id}" is not registered.`);
149
- }
223
+ unregister(id: RegistryId | null, resetPendingCallbacks: boolean = true): void {
224
+ this.batch(() => {
225
+ // If unregistering the default item, clear it.
226
+ if (id && this.items.get(null) === this.items.get(id)) {
227
+ this.unregister(null, false);
228
+ }
150
229
 
151
- // If unregistering the default item, clear it.
152
- if (id && this.items.get(null) === this.items.get(id)) {
153
- this.unregister(null);
154
- }
230
+ this.items.delete(id);
155
231
 
156
- this.items.delete(id);
157
- this.pendingCallbacks.delete(id);
232
+ if (resetPendingCallbacks) {
233
+ this.pendingCallbacks.delete(id);
234
+ }
158
235
 
159
- this.notifyWatchers();
236
+ this.resetErrors(id);
237
+ });
160
238
  }
161
239
 
162
240
  /**
@@ -168,6 +246,15 @@ export class AsyncRegistry<T extends Destructible> {
168
246
  return Array.from(this.items.values());
169
247
  }
170
248
 
249
+ /**
250
+ * Returns single registered item.
251
+ *
252
+ * @returns Registered item.
253
+ */
254
+ getItem(id: RegistryId | null): T | undefined {
255
+ return this.items.get(id);
256
+ }
257
+
171
258
  /**
172
259
  * Checks if an item with the given ID is registered.
173
260
  *
@@ -207,7 +294,41 @@ export class AsyncRegistry<T extends Destructible> {
207
294
 
208
295
  await Promise.all(promises);
209
296
 
210
- this.notifyWatchers();
297
+ this.flushWatchers();
298
+ }
299
+
300
+ /**
301
+ * Destroys all registered editors and removes all watchers.
302
+ */
303
+ async reset() {
304
+ await this.destroyAll();
305
+ this.watchers.clear();
306
+ }
307
+
308
+ /**
309
+ * Executes a callback while deferring all watcher notifications.
310
+ * A single notification is fired synchronously after the callback returns,
311
+ * but only if the registry actually changed.
312
+ *
313
+ * Batches can be nested — watchers are notified only when the outermost
314
+ * batch completes.
315
+ *
316
+ * @param fn The callback to execute.
317
+ * @returns The return value of the callback.
318
+ */
319
+ batch<R>(fn: () => R): R {
320
+ this.batchDepth++;
321
+
322
+ try {
323
+ return fn();
324
+ }
325
+ finally {
326
+ this.batchDepth--;
327
+
328
+ if (this.batchDepth === 0) {
329
+ this.flushWatchers();
330
+ }
331
+ }
211
332
  }
212
333
 
213
334
  /**
@@ -238,15 +359,23 @@ export class AsyncRegistry<T extends Destructible> {
238
359
  }
239
360
 
240
361
  /**
241
- * Notifies all watchers about changes to the registry.
362
+ * Immediately dispatches the current state to all watchers if it changed.
242
363
  */
243
- private notifyWatchers(): void {
244
- this.watchers.forEach(
245
- watcher => watcher(
246
- new Map(this.items),
247
- new Map(this.initializationErrors),
248
- ),
249
- );
364
+ private flushWatchers(): void {
365
+ if (
366
+ areMapsEqual(this.lastNotifiedItems, this.items)
367
+ && areMapsEqual(this.lastNotifiedErrors, this.initializationErrors)
368
+ ) {
369
+ return;
370
+ }
371
+
372
+ this.lastNotifiedItems = new Map(this.items);
373
+ this.lastNotifiedErrors = new Map(this.initializationErrors);
374
+
375
+ this.watchers.forEach(watcher => watcher(
376
+ new Map(this.items),
377
+ new Map(this.initializationErrors),
378
+ ));
250
379
  }
251
380
 
252
381
  /**
@@ -265,18 +394,6 @@ export class AsyncRegistry<T extends Destructible> {
265
394
 
266
395
  return pending;
267
396
  }
268
-
269
- /**
270
- * Registers an item as the default (null ID) item if it's the first one.
271
- *
272
- * @param id The ID of the item being registered.
273
- * @param item The item instance.
274
- */
275
- private registerAsDefault(id: RegistryId | null, item: T): void {
276
- if (this.items.size === 1 && id !== null) {
277
- this.register(null, item);
278
- }
279
- }
280
397
  }
281
398
 
282
399
  /**
@@ -1,3 +1,4 @@
1
+ export * from './are-maps-equal';
1
2
  export * from './async-registry';
2
3
  export * from './camel-case';
3
4
  export * from './debounce';