gemi 0.192.0 → 0.193.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 (25) hide show
  1. package/dist/{chunk-qnc840pb.js → chunk-7bbtre2j.js} +2 -2
  2. package/dist/{chunk-qnc840pb.js.map → chunk-7bbtre2j.js.map} +1 -1
  3. package/dist/chunk-8ysrmyer.js +24 -0
  4. package/dist/{chunk-mx9zcq7w.js.map → chunk-8ysrmyer.js.map} +3 -3
  5. package/dist/{chunk-qg60pgve.js → chunk-m0aq07nj.js} +1 -1
  6. package/dist/chunks/{RealtimeClient-CyGWZr6p.js → RealtimeClient-bPI28SVg.js} +2 -2
  7. package/dist/chunks/{RealtimeClient-CyGWZr6p.js.map → RealtimeClient-bPI28SVg.js.map} +1 -1
  8. package/dist/chunks/{ThemeProvider-BnmnRvEc.js → ThemeProvider-BstCYMfV.js} +104 -32
  9. package/dist/chunks/{ThemeProvider-BnmnRvEc.js.map → ThemeProvider-BstCYMfV.js.map} +1 -1
  10. package/dist/chunks/{fetchFileRequest-D4fAcY7D.js → fetchFileRequest-BD9pg9RL.js} +2 -2
  11. package/dist/chunks/{fetchFileRequest-D4fAcY7D.js.map → fetchFileRequest-BD9pg9RL.js.map} +1 -1
  12. package/dist/chunks/{live-B4RW02g2.js → live-DYLCi077.js} +3 -3
  13. package/dist/chunks/{live-B4RW02g2.js.map → live-DYLCi077.js.map} +1 -1
  14. package/dist/chunks/{mutationExtras-DgdYjcaa.js → mutationExtras-Bo8pvlEx.js} +2 -2
  15. package/dist/chunks/{mutationExtras-DgdYjcaa.js.map → mutationExtras-Bo8pvlEx.js.map} +1 -1
  16. package/dist/chunks/{subscriptionStream-Ca5V54ee.js → subscriptionStream-CtwEcda9.js} +2 -2
  17. package/dist/chunks/{subscriptionStream-Ca5V54ee.js.map → subscriptionStream-CtwEcda9.js.map} +1 -1
  18. package/dist/client/chunkLoadRecovery.d.ts +39 -5
  19. package/dist/client/chunkLoadRecovery.d.ts.map +1 -1
  20. package/dist/client/index.js +4 -4
  21. package/dist/server/index.js +1 -1
  22. package/dist/testing/index.js +2 -2
  23. package/package.json +1 -1
  24. package/dist/chunk-mx9zcq7w.js +0 -24
  25. /package/dist/{chunk-qg60pgve.js.map → chunk-m0aq07nj.js.map} +0 -0
@@ -1053,7 +1053,7 @@ function loadedRealtimeClient() {
1053
1053
  }
1054
1054
  var loading = null;
1055
1055
  /** The lazily loaded client. Replaceable in tests, to simulate a failed load. */
1056
- var lazyModules = { client: () => import("./RealtimeClient-CyGWZr6p.js") };
1056
+ var lazyModules = { client: () => import("./RealtimeClient-bPI28SVg.js") };
1057
1057
  /**
1058
1058
  * `./live`, loaded by the first live query. `<Page>` in `gemi/testing` sets
1059
1059
  * `current` up front, so a live query on a provided socket subscribes during
@@ -1061,7 +1061,7 @@ var lazyModules = { client: () => import("./RealtimeClient-CyGWZr6p.js") };
1061
1061
  */
1062
1062
  var liveModule = {
1063
1063
  current: null,
1064
- load: () => import("./live-B4RW02g2.js").then((n) => n.n)
1064
+ load: () => import("./live-DYLCi077.js").then((n) => n.n)
1065
1065
  };
1066
1066
  /**
1067
1067
  * The tab's client, loading its module and creating it on first use. `null`
@@ -3244,6 +3244,8 @@ function flattenComponentTree(componentTree) {
3244
3244
  * inside the cooldown is left to the route's error boundary. Per URL rather
3245
3245
  * than per tab (#1056): while two releases serve at once every navigation can
3246
3246
  * miss, and a tab-wide cooldown sent the second one to the error boundary.
3247
+ * Where `sessionStorage` cannot be used, the marker lives in `window.name`
3248
+ * instead (#1186); see `chunkReloadGuard`.
3247
3249
  *
3248
3250
  * The same guard covers a navigation whose data came from another build
3249
3251
  * (`recoverFromBuildMismatch`).
@@ -3389,46 +3391,116 @@ function cooldownMs() {
3389
3391
  }
3390
3392
  function reloadBlock(url) {
3391
3393
  if (typeof navigator !== "undefined" && navigator.onLine === false) return "offline";
3392
- let raw;
3393
- try {
3394
- raw = window.sessionStorage.getItem(CHUNK_RELOAD_MARKER);
3395
- } catch {
3396
- return "storage";
3397
- }
3398
- const at = parseMarker(raw)[url];
3399
- return at !== void 0 && withinCooldown(Date.now() - at, cooldownMs()) ? "cooldown" : null;
3394
+ const verdict = chunkReloadGuard(CHUNK_RELOAD_MARKER, url, cooldownMs(), false);
3395
+ return verdict === "ok" ? null : verdict;
3400
3396
  }
3401
3397
  function withinCooldown(elapsed, cooldownMs) {
3402
3398
  return elapsed >= 0 && elapsed < cooldownMs;
3403
3399
  }
3400
+ function markReload(url) {
3401
+ return chunkReloadGuard(CHUNK_RELOAD_MARKER, url, cooldownMs(), true) === "ok";
3402
+ }
3404
3403
  /**
3405
- * The marker: when each URL was last reloaded onto, `{ [url]: at }`. Anything
3406
- * else in it (a marker written by an earlier gemi as `{ url, at }`) is ignored.
3404
+ * The cross-document guard, shared by the client and the server's reload stub
3405
+ * (which embeds this function's source, so it uses nothing but globals and its
3406
+ * arguments). With `claim` false it only reads: `"cooldown"` when a reload
3407
+ * onto `url` was recorded inside `cooldownMs`, `"storage"` when no marker can
3408
+ * be read at all, otherwise `"ok"`. With `claim` true it also records the
3409
+ * reload, and answers `"ok"` only once the record was written and read back
3410
+ * unchanged; a caller navigates on nothing less.
3411
+ *
3412
+ * Two places can hold the marker:
3413
+ *
3414
+ * - `sessionStorage[key]`: `{ [url]: at }`, the format earlier releases read
3415
+ * and write, so a page from one and a stub from another share the guard.
3416
+ * - `window.name`, when `sessionStorage` throws or will not take the write
3417
+ * (#1186). It survives a load in the same tab, but it is shared state: the
3418
+ * page may use it, and another origin the tab goes to can read it. So it is
3419
+ * used only while it is empty or already holds gemi's marker — a value of
3420
+ * anyone else's is left exactly as it is, and the guard then fails closed —
3421
+ * and it holds no URL: `key + ":" + { o: hash(origin), m: { [hash(origin +
3422
+ * pathname)]: at } }`. A marker written by another origin (`o` differs) or
3423
+ * one that does not parse holds no reloads and is replaced on the next
3424
+ * write. Two paths whose hashes collide share one cooldown, which only ever
3425
+ * refuses a reload.
3426
+ *
3427
+ * A reload recorded in either place counts. Entries past the cooldown — and
3428
+ * from the future, after the clock was set back — are dropped as a new one is
3429
+ * written, so the marker holds only the reloads it still guards.
3407
3430
  */
3408
- function parseMarker(raw) {
3409
- let parsed;
3431
+ function chunkReloadGuard(key, url, cooldownMs, claim) {
3432
+ const now = Date.now();
3433
+ const fresh = function(at) {
3434
+ return typeof at === "number" && now - at >= 0 && now - at < cooldownMs;
3435
+ };
3436
+ const parse = function(raw) {
3437
+ try {
3438
+ const value = raw ? JSON.parse(raw) : null;
3439
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
3440
+ } catch {
3441
+ return {};
3442
+ }
3443
+ };
3444
+ const live = function(marker) {
3445
+ const kept = {};
3446
+ for (const k in marker) if (fresh(marker[k])) kept[k] = marker[k];
3447
+ return kept;
3448
+ };
3449
+ const hash = function(text) {
3450
+ let h = 2166136261;
3451
+ for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 16777619);
3452
+ return (h >>> 0).toString(36);
3453
+ };
3454
+ let storage = null;
3455
+ let stored = {};
3410
3456
  try {
3411
- parsed = raw ? JSON.parse(raw) : null;
3457
+ storage = window.sessionStorage;
3458
+ stored = parse(storage.getItem(key));
3412
3459
  } catch {
3413
- return {};
3460
+ storage = null;
3414
3461
  }
3415
- const marker = {};
3416
- if (parsed && typeof parsed === "object") {
3417
- for (const [url, at] of Object.entries(parsed)) if (typeof at === "number") marker[url] = at;
3418
- }
3419
- return marker;
3420
- }
3421
- function markReload(url) {
3462
+ const prefix = key + ":";
3463
+ let origin = "";
3464
+ let page = "";
3422
3465
  try {
3423
- const now = Date.now();
3424
- const marker = {};
3425
- for (const [other, at] of Object.entries(parseMarker(window.sessionStorage.getItem(CHUNK_RELOAD_MARKER)))) if (withinCooldown(now - at, cooldownMs())) marker[other] = at;
3426
- marker[url] = now;
3427
- window.sessionStorage.setItem(CHUNK_RELOAD_MARKER, JSON.stringify(marker));
3428
- return true;
3466
+ const target = new URL(url, window.location.href);
3467
+ origin = hash(new URL(window.location.href).origin);
3468
+ page = hash(target.origin + target.pathname);
3469
+ } catch {}
3470
+ let named = null;
3471
+ try {
3472
+ const name = window.name;
3473
+ if (!page) named = null;
3474
+ else if (name === "") named = {};
3475
+ else if (name.indexOf(prefix) === 0) {
3476
+ const held = parse(name.slice(prefix.length));
3477
+ const entries = held.m;
3478
+ named = held.o === origin && entries && typeof entries === "object" && !Array.isArray(entries) ? entries : {};
3479
+ }
3429
3480
  } catch {
3430
- return false;
3431
- }
3481
+ named = null;
3482
+ }
3483
+ if (fresh(stored[url]) || named && fresh(named[page])) return "cooldown";
3484
+ if (!storage && !named) return "storage";
3485
+ if (!claim) return "ok";
3486
+ if (storage) try {
3487
+ const nextStored = live(stored);
3488
+ nextStored[url] = now;
3489
+ const serialised = JSON.stringify(nextStored);
3490
+ storage.setItem(key, serialised);
3491
+ if (storage.getItem(key) === serialised) return "ok";
3492
+ } catch {}
3493
+ if (named) try {
3494
+ const nextNamed = live(named);
3495
+ nextNamed[page] = now;
3496
+ const value = prefix + JSON.stringify({
3497
+ o: origin,
3498
+ m: nextNamed
3499
+ });
3500
+ window.name = value;
3501
+ if (window.name === value) return "ok";
3502
+ } catch {}
3503
+ return "storage";
3432
3504
  }
3433
3505
  //#endregion
3434
3506
  //#region client/ComponentContext.tsx
@@ -4055,4 +4127,4 @@ function useTheme() {
4055
4127
  //#endregion
4056
4128
  export { QueryManagerProvider as $, useNavigate as A, createMemoryHistory as B, loadDictionaryForRender as C, routeDataUrl as D, readRoutePayload as E, useLocation as F, realtime as G, configureRealtime as H, domainUrl as I, loadedModule as J, reconnectRealtime as K, isAbsoluteUrl as L, I18nProvider as M, ServerDataContext as N, useRoute as O, ServerDataProvider as P, QueryManagerContext as Q, ProgressManager as R, loadDictionary as S, setActiveLocale as T, followLive as U, RealtimeContext as V, liveModule as W, applyParams as X, toVariantKey as Y, QueryConfigContext as Z, isChunkLoadError as _, Subject as _t, useRouteTransition as a, resetAuthenticationIncident as at, recoverFromBuildMismatch as b, ClientRouterContext as c, isExternalRedirect as ct, ComponentsProvider as d, equalJitter as dt, USER_QUERY_CONFIG_KEYS as et, initialViewModulesReady as f, isRetryableQueryError as ft, configureChunkLoadRecovery as g, QueryError as gt, CHUNK_RELOAD_MARKER as h, shouldRetryQuery as ht, RouteTransitionProvider as i, reportAuthenticationRefusal as it, I18nContext as j, useSearchParams as k, ClientRouterProvider as l, safeRedirectPath as lt, subscribeViewModules as m, retryDelayFor as mt, useTheme as n, DEFAULT_STALE_TIME as nt, useChannelSubscription as o, setAuthenticationNavigator as ot, loadViewModule as p, parseRetryAfter as pt, loadLazy as q, m as r, configureAuthenticationErrors as rt, useCoalesced as s, INTENDED_URL_PARAM as st, ThemeProvider as t, pickDefined as tt, ComponentsContext as u, readResponseBody as ut, navigationStarted as v, registerDictionary as w, recoverFromChunkLoadError as x, otherBuild as y, Action as z };
4057
4129
 
4058
- //# sourceMappingURL=ThemeProvider-BnmnRvEc.js.map
4130
+ //# sourceMappingURL=ThemeProvider-BstCYMfV.js.map