gemi 0.192.0 → 0.193.1

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-qg60pgve.js → chunk-fhvyq67n.js} +1 -1
  2. package/dist/{chunk-qnc840pb.js → chunk-gsrf5c3c.js} +2 -2
  3. package/dist/{chunk-qnc840pb.js.map → chunk-gsrf5c3c.js.map} +1 -1
  4. package/dist/chunk-vese6sny.js +24 -0
  5. package/dist/{chunk-mx9zcq7w.js.map → chunk-vese6sny.js.map} +3 -3
  6. package/dist/chunks/{RealtimeClient-CyGWZr6p.js → RealtimeClient-BVQeh_yg.js} +2 -2
  7. package/dist/chunks/{RealtimeClient-CyGWZr6p.js.map → RealtimeClient-BVQeh_yg.js.map} +1 -1
  8. package/dist/chunks/{ThemeProvider-BnmnRvEc.js → ThemeProvider-G2g5Z1ZS.js} +142 -30
  9. package/dist/chunks/{ThemeProvider-BnmnRvEc.js.map → ThemeProvider-G2g5Z1ZS.js.map} +1 -1
  10. package/dist/chunks/{fetchFileRequest-D4fAcY7D.js → fetchFileRequest-Bzva9J59.js} +2 -2
  11. package/dist/chunks/{fetchFileRequest-D4fAcY7D.js.map → fetchFileRequest-Bzva9J59.js.map} +1 -1
  12. package/dist/chunks/{live-B4RW02g2.js → live-C2Sck5PO.js} +3 -3
  13. package/dist/chunks/{live-B4RW02g2.js.map → live-C2Sck5PO.js.map} +1 -1
  14. package/dist/chunks/{mutationExtras-DgdYjcaa.js → mutationExtras-HC7AC6zn.js} +2 -2
  15. package/dist/chunks/{mutationExtras-DgdYjcaa.js.map → mutationExtras-HC7AC6zn.js.map} +1 -1
  16. package/dist/chunks/{subscriptionStream-Ca5V54ee.js → subscriptionStream-CaTWWJwt.js} +2 -2
  17. package/dist/chunks/{subscriptionStream-Ca5V54ee.js.map → subscriptionStream-CaTWWJwt.js.map} +1 -1
  18. package/dist/client/chunkLoadRecovery.d.ts +42 -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-fhvyq67n.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-BVQeh_yg.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-C2Sck5PO.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`).
@@ -3283,6 +3285,7 @@ var pendingLoad = null;
3283
3285
  function configureChunkLoadRecovery(next) {
3284
3286
  enabled = next !== false;
3285
3287
  options = next || {};
3288
+ tidyWindowName();
3286
3289
  }
3287
3290
  /**
3288
3291
  * The router calls this as each client-side navigation starts. A full load
@@ -3389,46 +3392,155 @@ function cooldownMs() {
3389
3392
  }
3390
3393
  function reloadBlock(url) {
3391
3394
  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;
3395
+ const verdict = chunkReloadGuard(CHUNK_RELOAD_MARKER, url, cooldownMs(), false);
3396
+ return verdict === "ok" ? null : verdict;
3400
3397
  }
3401
3398
  function withinCooldown(elapsed, cooldownMs) {
3402
3399
  return elapsed >= 0 && elapsed < cooldownMs;
3403
3400
  }
3401
+ function markReload(url) {
3402
+ if (chunkReloadGuard("gemi:chunk-reload", url, cooldownMs(), true) !== "ok") return false;
3403
+ tidyWindowName();
3404
+ return true;
3405
+ }
3406
+ var tidyTimer;
3404
3407
  /**
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.
3408
+ * Takes gemi's marker out of `window.name` (back to `""`) once it guards
3409
+ * nothing (#1188): the page may want the name for itself, under the same
3410
+ * "only while it is empty" rule gemi follows. Now when nothing in it is live,
3411
+ * otherwise once the newest entry's cooldown has passed, checked again then
3412
+ * since a reload may have been recorded in the meantime. Only a name that
3413
+ * holds gemi's marker is ever cleared; see `chunkReloadGuard`'s `"tidy"`.
3407
3414
  */
3408
- function parseMarker(raw) {
3409
- let parsed;
3415
+ function tidyWindowName() {
3416
+ clearTimeout(tidyTimer);
3417
+ tidyTimer = void 0;
3418
+ if (typeof window === "undefined") return;
3419
+ let delay;
3410
3420
  try {
3411
- parsed = raw ? JSON.parse(raw) : null;
3421
+ if (chunkReloadGuard("gemi:chunk-reload", window.location.href, cooldownMs(), "tidy") !== "cooldown") return;
3422
+ const held = JSON.parse(window.name.slice(18));
3423
+ const now = Date.now();
3424
+ delay = Math.max(...Object.values(held.m).filter((at) => typeof at === "number" && at <= now)) + cooldownMs() - now;
3412
3425
  } catch {
3413
- return {};
3414
- }
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;
3426
+ return;
3418
3427
  }
3419
- return marker;
3428
+ if (!Number.isFinite(delay)) return;
3429
+ tidyTimer = setTimeout(tidyWindowName, Math.max(0, delay) + 1);
3420
3430
  }
3421
- function markReload(url) {
3431
+ /**
3432
+ * The cross-document guard, shared by the client and the server's reload stub
3433
+ * (which embeds this function's source, so it uses nothing but globals and its
3434
+ * arguments). With `claim` false it only reads: `"cooldown"` when a reload
3435
+ * onto `url` was recorded inside `cooldownMs`, `"storage"` when no marker can
3436
+ * be read at all, otherwise `"ok"`. With `claim` true it also records the
3437
+ * reload, and answers `"ok"` only once the record was written and read back
3438
+ * unchanged; a caller navigates on nothing less. With `"tidy"` it only looks
3439
+ * at `window.name`: a gemi marker with no live entry for this origin (expired,
3440
+ * from the future, another origin's, or one that does not parse) is cleared
3441
+ * to `""` and `"ok"` answered; `"cooldown"` while an entry is still live.
3442
+ *
3443
+ * Two places can hold the marker:
3444
+ *
3445
+ * - `sessionStorage[key]`: `{ [url]: at }`, the format earlier releases read
3446
+ * and write, so a page from one and a stub from another share the guard.
3447
+ * - `window.name`, when `sessionStorage` throws or will not take the write
3448
+ * (#1186). It survives a load in the same tab, but it is shared state: the
3449
+ * page may use it, and another origin the tab goes to can read it. So it is
3450
+ * used only while it is empty or already holds gemi's marker — a value of
3451
+ * anyone else's is left exactly as it is, and the guard then fails closed —
3452
+ * and it holds no URL: `key + ":" + { o: hash(origin), m: { [hash(origin +
3453
+ * pathname)]: at } }`. A marker written by another origin (`o` differs) or
3454
+ * one that does not parse holds no reloads and is replaced on the next
3455
+ * write. Two paths whose hashes collide share one cooldown, which only ever
3456
+ * refuses a reload.
3457
+ *
3458
+ * A reload recorded in either place counts. Entries past the cooldown — and
3459
+ * from the future, after the clock was set back — are dropped as a new one is
3460
+ * written, so the marker holds only the reloads it still guards.
3461
+ */
3462
+ function chunkReloadGuard(key, url, cooldownMs, claim) {
3463
+ const now = Date.now();
3464
+ const fresh = function(at) {
3465
+ return typeof at === "number" && now - at >= 0 && now - at < cooldownMs;
3466
+ };
3467
+ const parse = function(raw) {
3468
+ try {
3469
+ const value = raw ? JSON.parse(raw) : null;
3470
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
3471
+ } catch {
3472
+ return {};
3473
+ }
3474
+ };
3475
+ const live = function(marker) {
3476
+ const kept = {};
3477
+ for (const k in marker) if (fresh(marker[k])) kept[k] = marker[k];
3478
+ return kept;
3479
+ };
3480
+ const hash = function(text) {
3481
+ let h = 2166136261;
3482
+ for (let i = 0; i < text.length; i++) h = Math.imul(h ^ text.charCodeAt(i), 16777619);
3483
+ return (h >>> 0).toString(36);
3484
+ };
3485
+ let storage = null;
3486
+ let stored = {};
3422
3487
  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;
3488
+ storage = window.sessionStorage;
3489
+ stored = parse(storage.getItem(key));
3429
3490
  } catch {
3430
- return false;
3491
+ storage = null;
3431
3492
  }
3493
+ const prefix = key + ":";
3494
+ let origin = "";
3495
+ let page = "";
3496
+ try {
3497
+ const target = new URL(url, window.location.href);
3498
+ origin = hash(new URL(window.location.href).origin);
3499
+ page = hash(target.origin + target.pathname);
3500
+ } catch {}
3501
+ let named = null;
3502
+ try {
3503
+ const name = window.name;
3504
+ if (!page) named = null;
3505
+ else if (name === "") named = {};
3506
+ else if (name.indexOf(prefix) === 0) {
3507
+ const held = parse(name.slice(prefix.length));
3508
+ const entries = held.m;
3509
+ named = held.o === origin && entries && typeof entries === "object" && !Array.isArray(entries) ? entries : {};
3510
+ }
3511
+ } catch {
3512
+ named = null;
3513
+ }
3514
+ if (claim === "tidy") {
3515
+ try {
3516
+ const current = window.name;
3517
+ if (named === null || current.indexOf(prefix) !== 0) return "ok";
3518
+ for (const k in named) if (fresh(named[k])) return "cooldown";
3519
+ window.name = "";
3520
+ } catch {}
3521
+ return "ok";
3522
+ }
3523
+ if (fresh(stored[url]) || named && fresh(named[page])) return "cooldown";
3524
+ if (!storage && !named) return "storage";
3525
+ if (!claim) return "ok";
3526
+ if (storage) try {
3527
+ const nextStored = live(stored);
3528
+ nextStored[url] = now;
3529
+ const serialised = JSON.stringify(nextStored);
3530
+ storage.setItem(key, serialised);
3531
+ if (storage.getItem(key) === serialised) return "ok";
3532
+ } catch {}
3533
+ if (named) try {
3534
+ const nextNamed = live(named);
3535
+ nextNamed[page] = now;
3536
+ const value = prefix + JSON.stringify({
3537
+ o: origin,
3538
+ m: nextNamed
3539
+ });
3540
+ window.name = value;
3541
+ if (window.name === value) return "ok";
3542
+ } catch {}
3543
+ return "storage";
3432
3544
  }
3433
3545
  //#endregion
3434
3546
  //#region client/ComponentContext.tsx
@@ -4055,4 +4167,4 @@ function useTheme() {
4055
4167
  //#endregion
4056
4168
  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
4169
 
4058
- //# sourceMappingURL=ThemeProvider-BnmnRvEc.js.map
4170
+ //# sourceMappingURL=ThemeProvider-G2g5Z1ZS.js.map