akanjs 2.3.1-rc.1 → 2.3.1-rc.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.3.1-rc.1",
3
+ "version": "2.3.1-rc.2",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -1,4 +1,5 @@
1
- import { createElement, type ReactNode, startTransition, use, type Usable, useLayoutEffect, useState } from "react";
1
+ import { createElement, type ReactNode, startTransition, type Usable, use, useLayoutEffect, useState } from "react";
2
+ import { flushSync } from "react-dom";
2
3
  import { hydrateRoot } from "react-dom/client";
3
4
  import { createFromReadableStream } from "react-server-dom-webpack/client.browser";
4
5
  import {
@@ -80,6 +81,7 @@ type RscFetchResult =
80
81
  }
81
82
  | { type: "redirected"; status?: number };
82
83
  const MAX_RSC_CACHE_ENTRIES = 32;
84
+ const AKAN_RSC_NAVIGATION_COMMITTED_EVENT = "akan:rsc-navigation-committed";
83
85
  let documentNavigationFallbackInFlight = false;
84
86
 
85
87
  class RscRedirectNavigationStarted extends Error {
@@ -150,6 +152,10 @@ function hardNavigateAfterRscFailure(target: string, replace = false, error?: un
150
152
  else window.location.assign(target);
151
153
  }
152
154
 
155
+ function dispatchRscNavigationCommitted(href: string): void {
156
+ window.dispatchEvent(new CustomEvent(AKAN_RSC_NAVIGATION_COMMITTED_EVENT, { detail: { href } }));
157
+ }
158
+
153
159
  function navigateAfterRscRedirect(target: string, replace = true): void {
154
160
  const error = new RscRedirectNavigationStarted(target);
155
161
  const navigate = globalThis.__AKAN_RSC_NAVIGATE__;
@@ -168,12 +174,14 @@ function commitRscPatchNavigation({
168
174
  replace,
169
175
  scrollToTop,
170
176
  bumpScrollToTop,
177
+ onCommitted,
171
178
  }: {
172
179
  target: string;
173
180
  patch: Extract<RscFetchResult, { type: "patched" }>;
174
181
  replace?: boolean;
175
182
  scrollToTop?: boolean;
176
183
  bumpScrollToTop?: () => void;
184
+ onCommitted?: () => void;
177
185
  }): boolean {
178
186
  const patchThenable = patch.patchedNode.thenable;
179
187
  if (!patchThenable) throw new Error("[rscClient] validated RSC patch is missing a thenable");
@@ -182,7 +190,7 @@ function commitRscPatchNavigation({
182
190
 
183
191
  let outletCommitted = false;
184
192
  let headApplied = false;
185
- startTransition(() => {
193
+ flushSync(() => {
186
194
  try {
187
195
  headApplied = commitPreparedAkanHeadSnapshotPatch(preparedHeadPatch);
188
196
  if (!headApplied) {
@@ -203,6 +211,7 @@ function commitRscPatchNavigation({
203
211
  outletCommitted = false;
204
212
  }
205
213
  });
214
+ if (outletCommitted && headApplied) onCommitted?.();
206
215
  return outletCommitted && headApplied;
207
216
  }
208
217
 
@@ -375,9 +384,12 @@ function Root(): ReactNode {
375
384
  maxEntries: MAX_RSC_CACHE_ENTRIES,
376
385
  startTransition,
377
386
  commitThenable: (node) => {
378
- resetAkanSegmentOutletPatches();
379
- setThenable(node.thenable);
387
+ flushSync(() => {
388
+ resetAkanSegmentOutletPatches();
389
+ setThenable(node.thenable);
390
+ });
380
391
  },
392
+ onCommitted: () => dispatchRscNavigationCommitted(target),
381
393
  navId,
382
394
  getCurrentNavId: () => navigationSeq,
383
395
  });
@@ -420,6 +432,7 @@ function Root(): ReactNode {
420
432
  replace: options.replace,
421
433
  scrollToTop,
422
434
  bumpScrollToTop: () => setScrollToTopTick((tick) => tick + 1),
435
+ onCommitted: () => dispatchRscNavigationCommitted(target),
423
436
  })
424
437
  ) {
425
438
  rememberPatchedRouteState(patchResult.tree, patchResult.patchedNode);
@@ -444,6 +457,7 @@ function Root(): ReactNode {
444
457
  replace: options.replace,
445
458
  scrollToTop,
446
459
  bumpScrollToTop: () => setScrollToTopTick((tick) => tick + 1),
460
+ onCommitted: () => dispatchRscNavigationCommitted(target),
447
461
  })
448
462
  ) {
449
463
  rememberPatchedRouteState(fetched.tree, fetched.patchedNode);
@@ -490,8 +504,10 @@ function Root(): ReactNode {
490
504
  maxEntries: MAX_RSC_CACHE_ENTRIES,
491
505
  startTransition,
492
506
  commitThenable: (node) => {
493
- resetAkanSegmentOutletPatches();
494
- setThenable(node.thenable);
507
+ flushSync(() => {
508
+ resetAkanSegmentOutletPatches();
509
+ setThenable(node.thenable);
510
+ });
495
511
  },
496
512
  updateHistory: () => {
497
513
  if (options.replace) window.history.replaceState(null, "", target);
@@ -499,6 +515,7 @@ function Root(): ReactNode {
499
515
  },
500
516
  scrollToTop,
501
517
  bumpScrollToTop: () => setScrollToTopTick((tick) => tick + 1),
518
+ onCommitted: () => dispatchRscNavigationCommitted(target),
502
519
  navId,
503
520
  getCurrentNavId: () => navigationSeq,
504
521
  });
@@ -329,6 +329,7 @@ interface CommitRscNavigationInput<T> {
329
329
  updateHistory?: () => void;
330
330
  scrollToTop?: boolean;
331
331
  bumpScrollToTop?: () => void;
332
+ onCommitted?: () => void;
332
333
  }
333
334
 
334
335
  export function commitRscNavigation<T>({
@@ -341,6 +342,7 @@ export function commitRscNavigation<T>({
341
342
  updateHistory,
342
343
  scrollToTop,
343
344
  bumpScrollToTop,
345
+ onCommitted,
344
346
  }: CommitRscNavigationInput<T>): void {
345
347
  rememberRscCacheEntry(cache, href, thenable, maxEntries);
346
348
  startTransition(() => {
@@ -348,6 +350,7 @@ export function commitRscNavigation<T>({
348
350
  updateHistory?.();
349
351
  if (scrollToTop) bumpScrollToTop?.();
350
352
  });
353
+ onCommitted?.();
351
354
  }
352
355
 
353
356
  export function commitLatestRscNavigation<T>({
@@ -87,8 +87,9 @@ interface CommitRscNavigationInput<T> {
87
87
  updateHistory?: () => void;
88
88
  scrollToTop?: boolean;
89
89
  bumpScrollToTop?: () => void;
90
+ onCommitted?: () => void;
90
91
  }
91
- export declare function commitRscNavigation<T>({ cache, href, thenable, maxEntries, startTransition, commitThenable, updateHistory, scrollToTop, bumpScrollToTop, }: CommitRscNavigationInput<T>): void;
92
+ export declare function commitRscNavigation<T>({ cache, href, thenable, maxEntries, startTransition, commitThenable, updateHistory, scrollToTop, bumpScrollToTop, onCommitted, }: CommitRscNavigationInput<T>): void;
92
93
  export declare function commitLatestRscNavigation<T>({ navId, getCurrentNavId, ...input }: CommitRscNavigationInput<T> & {
93
94
  navId: number;
94
95
  getCurrentNavId: () => number;
@@ -244,6 +244,8 @@ function buildSearchParams(entries: Iterable<[string, string]>): Record<string,
244
244
  return params;
245
245
  }
246
246
 
247
+ const AKAN_RSC_NAVIGATION_COMMITTED_EVENT = "akan:rsc-navigation-committed";
248
+
247
249
  export const ClientInner = () => {
248
250
  const uiOperation = st.use.uiOperation();
249
251
  return (
@@ -290,11 +292,9 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
290
292
  prefix,
291
293
  router: {
292
294
  push: (href, routeOptions) => {
293
- syncHref(href);
294
295
  navigateRscWithFallback(href, routeOptions, () => window.location.assign(href));
295
296
  },
296
297
  replace: (href, routeOptions) => {
297
- syncHref(href);
298
298
  navigateRscWithFallback(href, { ...routeOptions, replace: true }, () => window.location.replace(href));
299
299
  },
300
300
  back: () => {
@@ -302,7 +302,6 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
302
302
  },
303
303
  refresh: () => {
304
304
  clearRscNavigationCache();
305
- syncHref(window.location.href);
306
305
  void navigateRsc(window.location.href, {
307
306
  replace: true,
308
307
  scrollToTop: false,
@@ -310,7 +309,13 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
310
309
  },
311
310
  },
312
311
  });
312
+ const syncCommittedNavigation = (event: Event) => {
313
+ const detail = (event as CustomEvent<{ href?: string }>).detail;
314
+ syncHref(detail?.href ?? window.location.href);
315
+ };
316
+ window.addEventListener(AKAN_RSC_NAVIGATION_COMMITTED_EVENT, syncCommittedNavigation);
313
317
  void Device.load({ lang });
318
+ return () => window.removeEventListener(AKAN_RSC_NAVIGATION_COMMITTED_EVENT, syncCommittedNavigation);
314
319
  }, [lang, prefix]);
315
320
 
316
321
  useEffect(() => {
@@ -322,8 +327,6 @@ export const ClientSsrBridge = ({ lang, prefix = "" }: ClientSsrBridgeProps) =>
322
327
  st.set({ pathname: window.location.pathname, path, searchParams });
323
328
  };
324
329
  sync();
325
- window.addEventListener("popstate", sync);
326
- return () => window.removeEventListener("popstate", sync);
327
330
  }, [lang, prefix]);
328
331
  return null;
329
332
  };