@absolutejs/absolute 0.19.0-beta.744 → 0.19.0-beta.746

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.
@@ -511,12 +511,27 @@ const tickAngularApp = () => {
511
511
  }
512
512
  };
513
513
 
514
- const runWithViewTransition = (updateFn: () => Promise<void>) => {
514
+ /* HMR updates must be serialized.
515
+ If a second `startViewTransition` runs while a previous one is still in
516
+ flight, the browser aborts the old transition — but the old update
517
+ callback (destroyApp + async bootstrap) keeps running. Two updateFns
518
+ then race over the DOM and Angular state, corrupting HMR and forcing a
519
+ page refresh.
520
+ We avoid that by queuing later updates until the current one finishes.
521
+ If multiple updates queue up, only the latest matters (intermediate ones
522
+ are superseded), so we collapse to a single pending entry. */
523
+ let activeUpdate: Promise<void> | null = null;
524
+ let pendingUpdate: (() => Promise<void>) | null = null;
525
+
526
+ const runOneUpdate = async (updateFn: () => Promise<void>) => {
515
527
  const doc: ViewTransitionDocument = document;
528
+
516
529
  if (typeof doc.startViewTransition !== 'function') {
517
- updateFn().catch((err: unknown) => {
530
+ try {
531
+ await updateFn();
532
+ } catch (err) {
518
533
  console.warn('[HMR] Angular update failed (non-fatal):', err);
519
- });
534
+ }
520
535
 
521
536
  return;
522
537
  }
@@ -531,15 +546,56 @@ const runWithViewTransition = (updateFn: () => Promise<void>) => {
531
546
  /* ignored */
532
547
  }
533
548
 
534
- const removeStyle = () => {
549
+ let updatePromise: Promise<void> = Promise.resolve();
550
+ try {
551
+ const transition = doc.startViewTransition(() => {
552
+ updatePromise = updateFn();
553
+
554
+ return updatePromise;
555
+ });
556
+ // Wait for both the visual transition and the update callback.
557
+ // `transition.finished` rejects with AbortError when a new transition
558
+ // supersedes this one — swallow that since we serialize updates so
559
+ // it shouldn't happen, and even if it does we still want to wait
560
+ // for `updateFn` to complete before releasing the next update.
561
+ await Promise.all([
562
+ transition.finished.catch(() => {
563
+ /* skipped */
564
+ }),
565
+ updatePromise.catch((err) => {
566
+ console.warn('[HMR] Angular update failed (non-fatal):', err);
567
+ })
568
+ ]);
569
+ } catch (err) {
570
+ console.warn('[HMR] Angular update failed (non-fatal):', err);
571
+ // If startViewTransition itself threw, run the update directly so
572
+ // HMR still applies (loses the crossfade but preserves correctness).
573
+ try {
574
+ await updateFn();
575
+ } catch (innerErr) {
576
+ console.warn('[HMR] Angular update failed (non-fatal):', innerErr);
577
+ }
578
+ } finally {
535
579
  if (styleEl && styleEl.parentNode) styleEl.remove();
536
- };
580
+ }
581
+ };
537
582
 
538
- doc.startViewTransition(async () => {
539
- await updateFn();
540
- })
541
- .finished.then(removeStyle)
542
- .catch(removeStyle);
583
+ const runWithViewTransition = (updateFn: () => Promise<void>) => {
584
+ if (activeUpdate) {
585
+ // Supersede any earlier queued update — only the latest matters.
586
+ pendingUpdate = updateFn;
587
+
588
+ return;
589
+ }
590
+
591
+ activeUpdate = runOneUpdate(updateFn).finally(() => {
592
+ activeUpdate = null;
593
+ if (pendingUpdate) {
594
+ const next = pendingUpdate;
595
+ pendingUpdate = null;
596
+ runWithViewTransition(next);
597
+ }
598
+ });
543
599
  };
544
600
 
545
601
  const handleFullUpdate = (message: HMRMessage) => {
package/dist/index.js CHANGED
@@ -46932,14 +46932,11 @@ var mimeTypes, getMimeType = (filePath) => {
46932
46932
  continue;
46933
46933
  newIdentities.set(stripHash(webPath), webPath);
46934
46934
  }
46935
- const liveWebPaths = new Set(newIdentities.values());
46936
46935
  const staleKeys = [...store.keys()].filter((existingPath) => {
46937
46936
  if (existingPath.includes("/chunk-"))
46938
46937
  return false;
46939
46938
  const replacement = newIdentities.get(stripHash(existingPath));
46940
- if (replacement !== undefined)
46941
- return replacement !== existingPath;
46942
- return !liveWebPaths.has(existingPath);
46939
+ return replacement !== undefined && replacement !== existingPath;
46943
46940
  });
46944
46941
  staleKeys.forEach((key) => store.delete(key));
46945
46942
  for (const webPath of newIdentities.values()) {
@@ -50226,9 +50223,9 @@ var STORE_KEY = "__elysiaStore", getGlobalValue = (key) => Reflect.get(globalThi
50226
50223
  const pathname = resolveRequestPathname(request);
50227
50224
  if (moduleServerHandler) {
50228
50225
  const moduleResponse = await moduleServerHandler(pathname);
50229
- if (!moduleResponse)
50230
- return;
50231
- return resolveModuleResponse(moduleResponse, request.headers.get("If-None-Match"));
50226
+ if (moduleResponse) {
50227
+ return resolveModuleResponse(moduleResponse, request.headers.get("If-None-Match"));
50228
+ }
50232
50229
  }
50233
50230
  const bytes = lookupAsset(hmrState2.assetStore, pathname);
50234
50231
  if (!bytes) {
@@ -50242,7 +50239,7 @@ var STORE_KEY = "__elysiaStore", getGlobalValue = (key) => Reflect.get(globalThi
50242
50239
  });
50243
50240
  }, hmr = (hmrState2, manifest, moduleServerHandler) => new Elysia2({ name: "absolutejs-hmr" }).onStart(({ store }) => {
50244
50241
  restoreStore(store);
50245
- }).onBeforeHandle(async ({ request }) => {
50242
+ }).onRequest(async ({ request }) => {
50246
50243
  if (globalThis.__reactModuleRef) {
50247
50244
  await bridgeReactInternals();
50248
50245
  }
@@ -58541,5 +58538,5 @@ export {
58541
58538
  ANGULAR_INIT_TIMEOUT_MS
58542
58539
  };
58543
58540
 
58544
- //# debugId=758A53A3F7982E3F64756E2164756E21
58541
+ //# debugId=01DB8BB408B0E96D64756E2164756E21
58545
58542
  //# sourceMappingURL=index.js.map