@absolutejs/absolute 0.19.0-beta.851 → 0.19.0-beta.853

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 (45) hide show
  1. package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
  2. package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
  3. package/dist/angular/index.js +3 -1
  4. package/dist/angular/index.js.map +3 -3
  5. package/dist/angular/server.js +3 -1
  6. package/dist/angular/server.js.map +3 -3
  7. package/dist/build.js +1919 -284
  8. package/dist/build.js.map +17 -7
  9. package/dist/dev/client/handlers/angular.ts +17 -234
  10. package/dist/dev/client/handlers/angularHmrShim.ts +77 -0
  11. package/dist/dev/client/handlers/angularRuntime.ts +0 -476
  12. package/dist/dev/client/hmrClient.ts +21 -0
  13. package/dist/index.js +2006 -329
  14. package/dist/index.js.map +18 -8
  15. package/dist/react/browser.js.map +1 -1
  16. package/dist/react/index.js +3 -1
  17. package/dist/react/index.js.map +3 -3
  18. package/dist/react/server.js +3 -1
  19. package/dist/react/server.js.map +2 -2
  20. package/dist/src/core/prepare.d.ts +25 -0
  21. package/dist/src/dev/angular/fastHmrCompiler.d.ts +19 -0
  22. package/dist/src/dev/angular/hmrCompiler.d.ts +18 -0
  23. package/dist/src/dev/angular/hmrImportGenerator.d.ts +3 -0
  24. package/dist/src/dev/angular/hmrInjectionPlugin.d.ts +7 -0
  25. package/dist/src/dev/angular/resolveOwningComponents.d.ts +8 -0
  26. package/dist/src/dev/angular/vendor/translator/api/ast_factory.d.ts +363 -0
  27. package/dist/src/dev/angular/vendor/translator/api/import_generator.d.ts +49 -0
  28. package/dist/src/dev/angular/vendor/translator/context.d.ts +18 -0
  29. package/dist/src/dev/angular/vendor/translator/translator.d.ts +75 -0
  30. package/dist/src/dev/angular/vendor/translator/ts_util.d.ts +12 -0
  31. package/dist/src/dev/angular/vendor/translator/typescript_ast_factory.d.ts +66 -0
  32. package/dist/src/dev/angular/vendor/translator/typescript_translator.d.ts +13 -0
  33. package/dist/src/plugins/hmr.d.ts +25 -0
  34. package/dist/src/react/UniversalRouter.d.ts +3 -1
  35. package/dist/src/vue/components/Image.d.ts +1 -1
  36. package/dist/svelte/index.js +3 -1
  37. package/dist/svelte/index.js.map +2 -2
  38. package/dist/svelte/server.js +3 -1
  39. package/dist/svelte/server.js.map +2 -2
  40. package/dist/types/globals.d.ts +0 -12
  41. package/dist/vue/index.js +3 -1
  42. package/dist/vue/index.js.map +2 -2
  43. package/dist/vue/server.js +3 -1
  44. package/dist/vue/server.js.map +2 -2
  45. package/package.json +1 -1
@@ -26,21 +26,6 @@ type AngularComponentDefinition = {
26
26
  providers?: unknown;
27
27
  providersResolver?: unknown;
28
28
  selectors?: unknown[];
29
- styles?: string[];
30
- encapsulation?: number;
31
- template?: unknown;
32
- consts?: unknown;
33
- decls?: number;
34
- vars?: number;
35
- viewQuery?: unknown;
36
- contentQueries?: unknown;
37
- ngContentSelectors?: unknown;
38
- dependencies?: unknown;
39
- hostBindings?: unknown;
40
- hostVars?: number;
41
- hostAttrs?: unknown;
42
- inputs?: unknown;
43
- outputs?: unknown;
44
29
  };
45
30
 
46
31
  type ComponentCtor = (abstract new (...args: never[]) => unknown) & {
@@ -160,45 +145,6 @@ const hasProviderChanges = (oldCtor: ComponentCtor, newCtor: ComponentCtor) => {
160
145
  return false;
161
146
  };
162
147
 
163
- /* Style-update batch buffer.
164
- *
165
- * When a component-CSS edit triggers HMR, the rebuilt page chunk
166
- * re-evaluates with `__ANGULAR_HMR_STYLE_UPDATE_MODE__` set on the
167
- * window. Inside that mode, every `register(id, newCtor)` call from
168
- * the chunk's auto-registration block routes its newCtor straight
169
- * into `applyStyleUpdate(id, newCtor)` instead of being a no-op
170
- * (which is the default for already-registered IDs).
171
- *
172
- * This is the only way to reach CHILD-component classes — the page
173
- * chunk only `export *`s the page's own module, so a top-level
174
- * `Object.keys(newModule)` walk wouldn't find imported components.
175
- * The registration block runs once per compiled file (page + every
176
- * imported component), so it covers the whole subtree.
177
- *
178
- * The batch is consulted by `handleComponentStyleUpdate` after the
179
- * chunk import resolves: if any registration's update returned false,
180
- * the orchestrator falls through to a full reboot rather than leaving
181
- * the page partially restyled. */
182
-
183
- type StyleUpdateMode = typeof globalThis & {
184
- __ANGULAR_HMR_STYLE_UPDATE_MODE__?: boolean;
185
- };
186
-
187
- type StyleBatchEntry = { id: string; ok: boolean };
188
-
189
- const styleUpdateBatch: StyleBatchEntry[] = [];
190
-
191
- const beginStyleUpdateBatch = () => {
192
- styleUpdateBatch.length = 0;
193
- };
194
-
195
- const endStyleUpdateBatch = (): StyleBatchEntry[] => {
196
- const out = styleUpdateBatch.slice();
197
- styleUpdateBatch.length = 0;
198
-
199
- return out;
200
- };
201
-
202
148
  const register = (id: string, ctor: unknown) => {
203
149
  if (!id || !isComponentCtor(ctor)) return;
204
150
  if (!componentRegistry.has(id)) {
@@ -208,34 +154,6 @@ const register = (id: string, ctor: unknown) => {
208
154
  registeredAt: Date.now(),
209
155
  updateCount: 0
210
156
  });
211
-
212
- return;
213
- }
214
-
215
- // Already registered. If we're inside an HMR style-update or
216
- // template-update window, route this re-registration's new ctor
217
- // through the appropriate surgical patcher. The per-file
218
- // auto-registration block is the only place to intercept new ctors
219
- // for CHILD components — the page chunk's `export *` only re-exports
220
- // the page's own module.
221
- const styleScope = globalThis as StyleUpdateMode;
222
- if (styleScope.__ANGULAR_HMR_STYLE_UPDATE_MODE__) {
223
- const ok = applyStyleUpdate(id, ctor);
224
- styleUpdateBatch.push({ id, ok });
225
-
226
- return;
227
- }
228
- const tmplScope = globalThis as TemplateUpdateMode;
229
- if (tmplScope.__ANGULAR_HMR_TEMPLATE_UPDATE_MODE__) {
230
- const ok = applyTemplateUpdate(id, ctor);
231
- templateUpdateBatch.push({ id, ok });
232
-
233
- return;
234
- }
235
- const svcScope = globalThis as ServiceUpdateMode;
236
- if (svcScope.__ANGULAR_HMR_SERVICE_UPDATE_MODE__) {
237
- const ok = applyServiceUpdate(id, ctor);
238
- serviceUpdateBatch.push({ id, ok });
239
157
  }
240
158
  };
241
159
 
@@ -349,391 +267,6 @@ const markPatchedDirty = (ctor: ComponentCtor) => {
349
267
  }
350
268
  };
351
269
 
352
- /* Component-style HMR — swaps `ɵcmp.styles` and replaces matching
353
- * `<style>` tags in the document so the visible page reflects the new
354
- * CSS without a re-bootstrap.
355
- *
356
- * Why this is safe with Emulated encapsulation (the default): Angular's
357
- * compiler rewrites the CSS at build time, prefixing every selector
358
- * with `[_ngcontent-c<scopeId>]`. The scope ID is deterministic per
359
- * component def — the same source file produces the same scope ID
360
- * across rebuilds — so the rewritten DOM still matches the new CSS.
361
- * We only need to update the style *content*; the elements wearing
362
- * `_ngcontent-c<scopeId>` attributes are still on the page from the
363
- * initial bootstrap.
364
- *
365
- * ShadowDOM encapsulation (3) is not yet handled — each component
366
- * instance has its own shadow root with its own style tags, requiring
367
- * a per-instance walk. Falls through to reboot for now.
368
- *
369
- * The matching strategy: walk every `<style>` tag in `document.head`
370
- * and `document.body`, find ones whose `textContent` exactly matches a
371
- * string in the OLD `ɵcmp.styles` array, and replace it with the
372
- * corresponding string from the NEW array. Equal-length arrays only —
373
- * adding or removing a `styleUrl` entry triggers a reboot.
374
- *
375
- * Returns true on full success, false if we couldn't safely apply
376
- * (length mismatch, ShadowDOM, missing styles array, or any old
377
- * style had no DOM match — meaning we'd leave the page in a partially
378
- * updated state). */
379
-
380
- const SHADOW_DOM_ENCAPSULATION = 3;
381
-
382
- type StyleHost = {
383
- host: ParentNode;
384
- tags: HTMLStyleElement[];
385
- };
386
-
387
- const collectStyleHosts = (): StyleHost[] => {
388
- const hosts: StyleHost[] = [];
389
- const headTags = Array.from(
390
- document.head.querySelectorAll('style')
391
- ) as HTMLStyleElement[];
392
- const bodyTags = Array.from(
393
- document.body.querySelectorAll('style')
394
- ) as HTMLStyleElement[];
395
- if (headTags.length > 0)
396
- hosts.push({ host: document.head, tags: headTags });
397
- if (bodyTags.length > 0)
398
- hosts.push({ host: document.body, tags: bodyTags });
399
-
400
- return hosts;
401
- };
402
-
403
- const findStyleTagByContent = (
404
- hosts: StyleHost[],
405
- content: string,
406
- consumed: Set<HTMLStyleElement>
407
- ): HTMLStyleElement | null => {
408
- for (const { tags } of hosts) {
409
- for (const tag of tags) {
410
- if (consumed.has(tag)) continue;
411
- if (tag.textContent === content) return tag;
412
- }
413
- }
414
-
415
- return null;
416
- };
417
-
418
- const applyStyleUpdate = (id: string, newCtor: unknown) => {
419
- if (!isComponentCtor(newCtor)) return false;
420
-
421
- const entry = componentRegistry.get(id);
422
- if (!entry) {
423
- // First time we've seen this component — register it but no styles
424
- // to swap yet. The next edit will pick up the now-registered ctor.
425
- register(id, newCtor);
426
-
427
- return true;
428
- }
429
-
430
- const { liveCtor } = entry;
431
- if (liveCtor === newCtor) return true;
432
-
433
- const liveCmp = liveCtor.ɵcmp;
434
- const newCmp = newCtor.ɵcmp;
435
- if (!liveCmp || !newCmp) return false;
436
-
437
- if (
438
- liveCmp.encapsulation === SHADOW_DOM_ENCAPSULATION ||
439
- newCmp.encapsulation === SHADOW_DOM_ENCAPSULATION
440
- ) {
441
- // Shadow DOM scopes styles per-instance — out of scope for v1.
442
- return false;
443
- }
444
-
445
- const oldStyles = liveCmp.styles;
446
- const nextStyles = newCmp.styles;
447
- if (!Array.isArray(oldStyles) || !Array.isArray(nextStyles)) return false;
448
- if (oldStyles.length !== nextStyles.length) return false;
449
- if (oldStyles.length === 0) {
450
- // No styles to swap, no work to do — succeed trivially.
451
- liveCmp.styles = nextStyles;
452
-
453
- return true;
454
- }
455
-
456
- // Always update `ɵcmp.styles` first so future mounts of this
457
- // component pick up the new content. The DOM `<style>` swap below
458
- // handles currently-mounted instances; any component that isn't on
459
- // the page right now (an unopened modal, a route view that hasn't
460
- // been visited yet) simply has no `<style>` tag to find — and
461
- // that's fine. The next time it mounts, Angular reads from
462
- // `ɵcmp.styles` and injects the new content. Treating that case
463
- // as a failure was the source of the "Transition was skipped"
464
- // bug: the SPA at /portal/dashboard registered dozens of
465
- // components whose styles weren't injected (modals, sibling tabs,
466
- // etc.), and each missing live `<style>` tag forced fallthrough
467
- // to the reboot path, firing `startViewTransition` while the
468
- // previous one was still finishing.
469
- liveCmp.styles = nextStyles;
470
-
471
- const hosts = collectStyleHosts();
472
- const consumed = new Set<HTMLStyleElement>();
473
-
474
- for (let i = 0; i < oldStyles.length; i++) {
475
- const oldContent = oldStyles[i] ?? '';
476
- const nextContent = nextStyles[i] ?? '';
477
- if (oldContent === nextContent) continue;
478
- const tag = findStyleTagByContent(hosts, oldContent, consumed);
479
- if (!tag) {
480
- // Component not currently mounted (or already-swapped style).
481
- // Skip the DOM swap; `ɵcmp.styles` is already updated above
482
- // so the next mount picks up the new content.
483
- continue;
484
- }
485
- consumed.add(tag);
486
- tag.textContent = nextContent;
487
- }
488
-
489
- updateCounter.value++;
490
- entry.updateCount++;
491
- entry.registeredAt = Date.now();
492
-
493
- return true;
494
- };
495
-
496
- /* Template HMR — surgical swap of the template-related fields on a
497
- * registered component's `ɵcmp` so the live instance re-renders with
498
- * the new template WITHOUT re-instantiating. Inputs, outputs, host
499
- * bindings, providers, and lifecycle hooks live on the class
500
- * prototype + ɵcmp, and we leave those alone — only the template
501
- * factory and the slot counts/queries that depend on it are replaced.
502
- *
503
- * Why a defined list of fields and not a full `ɵcmp` swap: a wholesale
504
- * `Object.assign(liveCmp, newCmp)` would also overwrite `providers /
505
- * providersResolver` and other class-level metadata. Those changes
506
- * already require a full reboot (the existing fast-path handler in
507
- * `angular.ts` checks `hasProviderChanges` and bails). For a pure
508
- * template edit, restricting the patch to the template subgraph
509
- * keeps live instances on the same DI tokens, queryList references,
510
- * input bindings, etc. — only the rendered output changes.
511
- *
512
- * After the swap, the component's TView (the cached view layout) is
513
- * stale because slot counts may have changed. Angular regenerates the
514
- * TView lazily on the first re-render, but only if the existing one
515
- * is invalidated — which happens automatically when we walk the live
516
- * instances and call `applyChanges`. The same `markPatchedDirty`
517
- * helper used by `applyUpdate` covers OnPush views too. */
518
-
519
- const TEMPLATE_PATCH_FIELDS = [
520
- 'template',
521
- 'consts',
522
- 'decls',
523
- 'vars',
524
- 'viewQuery',
525
- 'contentQueries',
526
- 'ngContentSelectors',
527
- 'dependencies',
528
- 'hostBindings',
529
- 'hostVars',
530
- 'hostAttrs',
531
- 'inputs',
532
- 'outputs'
533
- ] as const;
534
-
535
- const applyTemplateUpdate = (id: string, newCtor: unknown) => {
536
- if (!isComponentCtor(newCtor)) return false;
537
-
538
- const entry = componentRegistry.get(id);
539
- if (!entry) {
540
- register(id, newCtor);
541
-
542
- return true;
543
- }
544
-
545
- const { liveCtor } = entry;
546
- if (liveCtor === newCtor) return true;
547
-
548
- const liveCmp = liveCtor.ɵcmp as Record<string, unknown> | undefined;
549
- const nextCmp = newCtor.ɵcmp as Record<string, unknown> | undefined;
550
- if (!liveCmp || !nextCmp) return false;
551
-
552
- // If providers changed, this isn't a pure template edit anymore —
553
- // fall back to reboot via the caller.
554
- if (hasProviderChanges(liveCtor, newCtor)) return false;
555
-
556
- for (const field of TEMPLATE_PATCH_FIELDS) {
557
- if (Object.prototype.hasOwnProperty.call(nextCmp, field)) {
558
- liveCmp[field] = nextCmp[field];
559
- }
560
- }
561
-
562
- pendingFastPatchRefresh.add(liveCtor);
563
- updateCounter.value++;
564
- entry.updateCount++;
565
- entry.registeredAt = Date.now();
566
-
567
- return true;
568
- };
569
-
570
- type TemplateUpdateMode = typeof globalThis & {
571
- __ANGULAR_HMR_TEMPLATE_UPDATE_MODE__?: boolean;
572
- };
573
-
574
- const templateUpdateBatch: StyleBatchEntry[] = [];
575
-
576
- const beginTemplateUpdateBatch = () => {
577
- templateUpdateBatch.length = 0;
578
- };
579
-
580
- const endTemplateUpdateBatch = (): StyleBatchEntry[] => {
581
- const out = templateUpdateBatch.slice();
582
- templateUpdateBatch.length = 0;
583
-
584
- return out;
585
- };
586
-
587
- /* Service HMR — Level 3 hybrid:
588
- * 1. Always swap prototype methods on the live ctor. Reaches every
589
- * live instance (singletons + transient injectees) because they
590
- * all share the same prototype.
591
- * 2. If the live singleton is reachable via the root injector,
592
- * attempt to instantiate a donor with the new ctor and copy any
593
- * OWN PROPERTIES that the live singleton is missing — this picks
594
- * up new class-field initializers without overwriting accumulated
595
- * runtime state. Donor instantiation is best-effort: services
596
- * using `inject()` outside of an injection context will throw,
597
- * and we just skip the field merge in that case (the prototype
598
- * swap still applies, so method changes take effect).
599
- * 3. The classifier only routes here for services with NO
600
- * side-effecting calls in the constructor / field initializers
601
- * (no `subscribe / setInterval / addEventListener / effect /
602
- * new Worker / new EventSource / etc.`). Anything that touches
603
- * external state at construction time falls through to reboot
604
- * via the server-side classification, never reaching this code
605
- * path. */
606
-
607
- type AppRefWithInjector = {
608
- injector?: { get?: (token: unknown, notFoundValue?: unknown) => unknown };
609
- };
610
-
611
- const getRootInjector = (): {
612
- get: (token: unknown, notFoundValue?: unknown) => unknown;
613
- } | null => {
614
- const app = window.__ANGULAR_APP__ as AppRefWithInjector | null;
615
- if (!app || !app.injector || typeof app.injector.get !== 'function') {
616
- return null;
617
- }
618
-
619
- return app.injector as {
620
- get: (token: unknown, notFoundValue?: unknown) => unknown;
621
- };
622
- };
623
-
624
- const swapPrototypeMethods = (
625
- liveCtor: ComponentCtor,
626
- newCtor: ComponentCtor
627
- ) => {
628
- const newProto = newCtor.prototype as Record<string, unknown>;
629
- const liveProto = liveCtor.prototype as Record<string, unknown>;
630
- Object.getOwnPropertyNames(newProto).forEach((prop) => {
631
- if (prop === 'constructor') return;
632
- try {
633
- const desc = Object.getOwnPropertyDescriptor(newProto, prop);
634
- if (desc) Object.defineProperty(liveProto, prop, desc);
635
- } catch {
636
- /* non-configurable property — skip */
637
- }
638
- });
639
- };
640
-
641
- const tryInstantiateServiceDonor = (newCtor: ComponentCtor): unknown | null => {
642
- try {
643
- // `new newCtor()` with no args. Works for services with no
644
- // constructor params and no `inject()` calls at field-init time.
645
- // Anything more sophisticated (services that use `inject()`
646
- // outside an injection context) throws here and we fall back to
647
- // prototype-only swap.
648
- return Reflect.construct(newCtor as unknown as new () => unknown, []);
649
- } catch {
650
- return null;
651
- }
652
- };
653
-
654
- const mergeMissingFields = (
655
- liveInstance: Record<string, unknown>,
656
- donor: Record<string, unknown>
657
- ) => {
658
- let merged = 0;
659
- Object.getOwnPropertyNames(donor).forEach((prop) => {
660
- if (Object.prototype.hasOwnProperty.call(liveInstance, prop)) return;
661
- try {
662
- const desc = Object.getOwnPropertyDescriptor(donor, prop);
663
- if (desc) {
664
- Object.defineProperty(liveInstance, prop, desc);
665
- merged++;
666
- }
667
- } catch {
668
- /* defining the property failed — skip */
669
- }
670
- });
671
-
672
- return merged;
673
- };
674
-
675
- const applyServiceUpdate = (id: string, newCtor: unknown) => {
676
- if (!isComponentCtor(newCtor)) return false;
677
-
678
- const entry = componentRegistry.get(id);
679
- if (!entry) {
680
- register(id, newCtor);
681
-
682
- return true;
683
- }
684
-
685
- const { liveCtor } = entry;
686
- if (liveCtor === newCtor) return true;
687
-
688
- // Method swap — reaches every live instance.
689
- swapPrototypeMethods(liveCtor, newCtor);
690
-
691
- // Best-effort field merge on the live singleton.
692
- const injector = getRootInjector();
693
- if (injector) {
694
- try {
695
- const liveInstance = injector.get(liveCtor, null) as Record<
696
- string,
697
- unknown
698
- > | null;
699
- if (liveInstance) {
700
- const donor = tryInstantiateServiceDonor(newCtor) as Record<
701
- string,
702
- unknown
703
- > | null;
704
- if (donor) mergeMissingFields(liveInstance, donor);
705
- }
706
- } catch {
707
- /* injector lookup failed — service may not be `providedIn:
708
- "root"`, or the type-token mismatched. Prototype swap is
709
- already applied, so methods take effect either way. */
710
- }
711
- }
712
-
713
- updateCounter.value++;
714
- entry.updateCount++;
715
- entry.registeredAt = Date.now();
716
-
717
- return true;
718
- };
719
-
720
- type ServiceUpdateMode = typeof globalThis & {
721
- __ANGULAR_HMR_SERVICE_UPDATE_MODE__?: boolean;
722
- };
723
-
724
- const serviceUpdateBatch: StyleBatchEntry[] = [];
725
-
726
- const beginServiceUpdateBatch = () => {
727
- serviceUpdateBatch.length = 0;
728
- };
729
-
730
- const endServiceUpdateBatch = (): StyleBatchEntry[] => {
731
- const out = serviceUpdateBatch.slice();
732
- serviceUpdateBatch.length = 0;
733
-
734
- return out;
735
- };
736
-
737
270
  const applyUpdate = (id: string, newCtor: unknown) => {
738
271
  if (!isComponentCtor(newCtor)) return false;
739
272
 
@@ -869,16 +402,7 @@ const hasPageExportsChanged = (sourceId: string): boolean => {
869
402
  export const installAngularHMRRuntime = () => {
870
403
  if (typeof window === 'undefined') return;
871
404
  window.__ANGULAR_HMR__ = {
872
- applyServiceUpdate,
873
- applyStyleUpdate,
874
- applyTemplateUpdate,
875
405
  applyUpdate,
876
- beginServiceUpdateBatch,
877
- beginStyleUpdateBatch,
878
- beginTemplateUpdateBatch,
879
- endServiceUpdateBatch,
880
- endStyleUpdateBatch,
881
- endTemplateUpdateBatch,
882
406
  getStats: getAngularHmrStats,
883
407
  hasPageExportsChanged,
884
408
  recordPageExports,
@@ -14,6 +14,7 @@ import {
14
14
  import { detectCurrentFramework } from './frameworkDetect';
15
15
  import { hideErrorOverlay, showErrorOverlay } from './errorOverlay';
16
16
  import { handleAngularUpdate } from './handlers/angular';
17
+ import { dispatchAngularComponentUpdate } from './handlers/angularHmrShim';
17
18
  import { handleReactUpdate } from './handlers/react';
18
19
  import { handleHTMLUpdate, handleScriptUpdate } from './handlers/html';
19
20
  import { handleHTMXUpdate } from './handlers/htmx';
@@ -152,6 +153,26 @@ const handleHMRMessage = (message: HMRMessage) => {
152
153
  hideErrorOverlay();
153
154
  handleAngularUpdate(message);
154
155
  break;
156
+ case 'angular:component-update': {
157
+ // Surgical-HMR fast path. Server resolved the changed
158
+ // file → owning component classes and emitted one
159
+ // message per affected component. Our injected
160
+ // `__ng_hmr_load` blocks (see hmrInjectionPlugin.ts)
161
+ // listen here and re-fetch the applyMetadata module.
162
+ const data = message.data as
163
+ | { id?: string; timestamp?: number }
164
+ | undefined;
165
+ if (data && typeof data.id === 'string') {
166
+ dispatchAngularComponentUpdate({
167
+ id: data.id,
168
+ timestamp:
169
+ typeof data.timestamp === 'number'
170
+ ? data.timestamp
171
+ : Date.now()
172
+ });
173
+ }
174
+ break;
175
+ }
155
176
  case 'rebuild-error':
156
177
  handleRebuildError(message);
157
178
  break;