gemi 0.74.0 → 0.75.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 (41) hide show
  1. package/dist/auth/AuthController.d.ts.map +1 -1
  2. package/dist/auth/config.d.ts +13 -0
  3. package/dist/auth/config.d.ts.map +1 -1
  4. package/dist/{chunk-43t1js09.js → chunk-hny56r47.js} +1 -1
  5. package/dist/{chunk-pjd63e8g.js → chunk-ppscsvv8.js} +1 -1
  6. package/dist/{chunk-msqr4d2d.js → chunk-ra1ccnd5.js} +2 -2
  7. package/dist/{chunk-msqr4d2d.js.map → chunk-ra1ccnd5.js.map} +1 -1
  8. package/dist/{chunk-w8jyvdgd.js → chunk-y3hmzww0.js} +1 -1
  9. package/dist/chunk-ytt205yt.js +19 -0
  10. package/dist/{chunk-smr9jn4z.js.map → chunk-ytt205yt.js.map} +6 -6
  11. package/dist/chunks/{ThemeProvider-D9v0VAbY.js → ThemeProvider-BiNFL6l7.js} +152 -15
  12. package/dist/chunks/ThemeProvider-BiNFL6l7.js.map +1 -0
  13. package/dist/client/QueryError.d.ts +6 -1
  14. package/dist/client/QueryError.d.ts.map +1 -1
  15. package/dist/client/QueryManagerContext.d.ts +32 -1
  16. package/dist/client/QueryManagerContext.d.ts.map +1 -1
  17. package/dist/client/QueryResource.d.ts +27 -0
  18. package/dist/client/QueryResource.d.ts.map +1 -1
  19. package/dist/client/auth/useUser.d.ts +7 -1
  20. package/dist/client/auth/useUser.d.ts.map +1 -1
  21. package/dist/client/index.d.ts +3 -1
  22. package/dist/client/index.d.ts.map +1 -1
  23. package/dist/client/index.js +34 -22
  24. package/dist/client/index.js.map +1 -1
  25. package/dist/client/retryPolicy.d.ts +49 -0
  26. package/dist/client/retryPolicy.d.ts.map +1 -0
  27. package/dist/client/useQuery.d.ts +21 -0
  28. package/dist/client/useQuery.d.ts.map +1 -1
  29. package/dist/facades/index.js +1 -1
  30. package/dist/http/index.js +1 -1
  31. package/dist/i18n/index.js +1 -1
  32. package/dist/kernel/index.js +1 -1
  33. package/dist/server/index.js +1 -1
  34. package/dist/services/index.js +1 -1
  35. package/dist/testing/index.js +1 -1
  36. package/package.json +1 -1
  37. package/dist/chunk-smr9jn4z.js +0 -19
  38. package/dist/chunks/ThemeProvider-D9v0VAbY.js.map +0 -1
  39. /package/dist/{chunk-43t1js09.js.map → chunk-hny56r47.js.map} +0 -0
  40. /package/dist/{chunk-pjd63e8g.js.map → chunk-ppscsvv8.js.map} +0 -0
  41. /package/dist/{chunk-w8jyvdgd.js.map → chunk-y3hmzww0.js.map} +0 -0
@@ -32,24 +32,72 @@ var Subject = class {
32
32
  * An HTTP failure from a query endpoint, in a shape an error boundary can
33
33
  * work with. `resolveVariant` used to store the parsed response body as the
34
34
  * error; boundaries expect an `Error`, so the body moves to a field.
35
+ *
36
+ * `body` is `null` when the error response was not JSON (a proxy's HTML 502
37
+ * page, a CDN's 404). `retryAfter` is the response's `Retry-After` header in
38
+ * ms, when it sent one — background retries wait that long.
35
39
  */
36
40
  var QueryError = class extends Error {
37
41
  path;
38
42
  variantKey;
39
43
  status;
40
44
  body;
41
- constructor(path, variantKey, status, body) {
45
+ retryAfter;
46
+ constructor(path, variantKey, status, body, retryAfter) {
42
47
  super(typeof body?.message === "string" ? body.message : `Request to /api${path} failed with status ${status}`);
43
48
  this.path = path;
44
49
  this.variantKey = variantKey;
45
50
  this.status = status;
46
51
  this.body = body;
52
+ this.retryAfter = retryAfter;
47
53
  this.name = "QueryError";
48
54
  }
49
55
  };
56
+ var DEFAULT_RETRY_BASE_DELAY = 1e3;
57
+ var MAX_RETRY_DELAY = 3e4;
58
+ /**
59
+ * A failure that another attempt might fix: no answer at all (offline, a
60
+ * dropped connection), a 2xx body that failed to parse, 408, 429 or any 5xx.
61
+ * Every other status — 400, 401, 403, 404, 410, 422, … — says the request
62
+ * itself is the problem, so sending it again gets the same answer.
63
+ */
64
+ function isRetryableQueryError(error) {
65
+ if (error instanceof QueryError) return error.status === 408 || error.status === 429 || error.status >= 500;
66
+ return error != null;
67
+ }
68
+ function shouldRetryQuery(failureCount, error, retry = 3) {
69
+ if (typeof retry === "function") return retry(failureCount, error);
70
+ if (retry === false || !isRetryableQueryError(error)) return false;
71
+ if (retry === true) return true;
72
+ return failureCount <= retry;
73
+ }
74
+ /**
75
+ * `baseDelay * 2^(failureCount - 1)`, capped at 30s: 1s, 2s, 4s, … by default.
76
+ * A `Retry-After` the server sent (a 429 or 503) is honoured as is.
77
+ */
78
+ function retryDelayFor(failureCount, error, retryDelay, baseDelay = DEFAULT_RETRY_BASE_DELAY) {
79
+ if (error instanceof QueryError && error.retryAfter !== void 0) return error.retryAfter;
80
+ if (typeof retryDelay === "function") return retryDelay(failureCount, error);
81
+ if (typeof retryDelay === "number") return retryDelay;
82
+ return Math.min(baseDelay * 2 ** Math.max(0, failureCount - 1), Math.max(MAX_RETRY_DELAY, baseDelay));
83
+ }
84
+ /**
85
+ * A `Retry-After` header value in ms: delta-seconds or an HTTP date.
86
+ * `undefined` when absent or unreadable.
87
+ */
88
+ function parseRetryAfter(value, now = Date.now()) {
89
+ if (value == null) return void 0;
90
+ const trimmed = value.trim();
91
+ if (trimmed === "") return void 0;
92
+ if (/^\d+$/.test(trimmed)) return Number(trimmed) * 1e3;
93
+ const date = Date.parse(trimmed);
94
+ if (Number.isNaN(date)) return void 0;
95
+ return Math.max(0, date - now);
96
+ }
50
97
  //#endregion
51
98
  //#region client/QueryResource.ts
52
99
  var DEFAULT_STALE_TIME = 5e3;
100
+ var MAX_TIMEOUT = 2 ** 31 - 1;
53
101
  var QueryResource = class {
54
102
  store;
55
103
  staleVariants = /* @__PURE__ */ new Set();
@@ -77,6 +125,18 @@ var QueryResource = class {
77
125
  * the wire at once.
78
126
  */
79
127
  watchers = /* @__PURE__ */ new Map();
128
+ /**
129
+ * Consecutive failed fetches per variant. A background retry adds to the
130
+ * count; any other fetch that fails starts it again at 1, and a success
131
+ * clears it. `useQuery`'s retry policy reads it to decide whether to go
132
+ * again and how long to wait.
133
+ */
134
+ failures = /* @__PURE__ */ new Map();
135
+ /**
136
+ * At most one scheduled retry per variant, however many mounted readers
137
+ * render it — each of them sees the same failure and asks for one.
138
+ */
139
+ retryTimers = /* @__PURE__ */ new Map();
80
140
  constructor(key, initialState) {
81
141
  this.key = key;
82
142
  this.store = new Subject(/* @__PURE__ */ new Map());
@@ -108,6 +168,8 @@ var QueryResource = class {
108
168
  });
109
169
  this.staleVariants.delete(variantKey);
110
170
  this.lastFetchRecord.set(variantKey, now);
171
+ this.failures.delete(variantKey);
172
+ this.cancelRetry(variantKey);
111
173
  this.settle(variantKey);
112
174
  changed = true;
113
175
  }
@@ -255,9 +317,53 @@ var QueryResource = class {
255
317
  released = true;
256
318
  const count = (this.watchers.get(variantKey) ?? 1) - 1;
257
319
  if (count > 0) this.watchers.set(variantKey, count);
258
- else this.watchers.delete(variantKey);
320
+ else {
321
+ this.watchers.delete(variantKey);
322
+ this.cancelRetry(variantKey);
323
+ }
259
324
  };
260
325
  }
326
+ /** How many fetches of this variant have failed in a row (0 after a success). */
327
+ failureCount(variantKey) {
328
+ return this.failures.get(variantKey) ?? 0;
329
+ }
330
+ /**
331
+ * Retry a failed variant after `delay` ms. A no-op while a retry is already
332
+ * scheduled or a request is on the wire. When the timer fires, the retry
333
+ * only goes out if the variant still sits on an error and is still
334
+ * rendered by someone.
335
+ */
336
+ scheduleRetry(variantKey, delay) {
337
+ if (typeof window === "undefined") return;
338
+ if (this.retryTimers.has(variantKey) || this.inflight.has(variantKey)) return;
339
+ const timer = setTimeout(() => {
340
+ this.retryTimers.delete(variantKey);
341
+ if (!this.watchers.has(variantKey)) return;
342
+ if (this.inflight.has(variantKey)) return;
343
+ const state = this.peek(variantKey);
344
+ if (!state?.error || state.loading) return;
345
+ this.resolveVariant(variantKey, state.hasData, true, true);
346
+ }, Math.min(Math.max(0, delay || 0), MAX_TIMEOUT));
347
+ this.retryTimers.set(variantKey, timer);
348
+ }
349
+ cancelRetry(variantKey) {
350
+ const timer = this.retryTimers.get(variantKey);
351
+ if (timer !== void 0) {
352
+ clearTimeout(timer);
353
+ this.retryTimers.delete(variantKey);
354
+ }
355
+ }
356
+ /**
357
+ * Refetch a failed variant now, silently — the browser came back online.
358
+ * Starts a fresh run of failures, so the retry policy applies in full again.
359
+ */
360
+ retryNow(variantKey) {
361
+ if (typeof window === "undefined") return;
362
+ if (this.inflight.has(variantKey)) return;
363
+ const state = this.peek(variantKey);
364
+ if (!state?.error || state.loading) return;
365
+ this.resolveVariant(variantKey, true);
366
+ }
261
367
  /** Every variant key this resource holds state for, in insertion order. */
262
368
  variantKeys() {
263
369
  return [...this.store.getValue().keys()];
@@ -312,9 +418,13 @@ var QueryResource = class {
312
418
  refetch(variantKey) {
313
419
  this.resolveVariant(variantKey, false, false);
314
420
  }
315
- async resolveVariant(variantKey, silent = false, cache = true) {
421
+ async resolveVariant(variantKey, silent = false, cache = true, isRetry = false) {
316
422
  if (typeof window === "undefined") return;
423
+ this.cancelRetry(variantKey);
317
424
  this.inflight.add(variantKey);
425
+ const recordFailure = () => {
426
+ this.failures.set(variantKey, isRetry ? (this.failures.get(variantKey) ?? 0) + 1 : 1);
427
+ };
318
428
  try {
319
429
  const store = this.store.getValue();
320
430
  const previousState = store.get(variantKey);
@@ -330,9 +440,15 @@ var QueryResource = class {
330
440
  const fullUrl = [this.key, variantKey].filter((s) => s.length).join("?");
331
441
  try {
332
442
  response = await fetch(`/api${fullUrl}`, { cache: cache && !this.staleVariants.has(variantKey) ? "default" : "reload" });
333
- data = await response.json();
443
+ try {
444
+ data = await response.json();
445
+ } catch (parseError) {
446
+ if (response.ok) throw parseError;
447
+ data = null;
448
+ }
334
449
  } catch (error) {
335
450
  console.error(`Error fetching url /api${fullUrl}`, error);
451
+ recordFailure();
336
452
  this.store.next(store.set(variantKey, {
337
453
  loading: false,
338
454
  data: previousState?.data,
@@ -352,13 +468,17 @@ var QueryResource = class {
352
468
  }));
353
469
  this.staleVariants.delete(variantKey);
354
470
  this.lastFetchRecord.set(variantKey, Date.now());
355
- } else this.store.next(store.set(variantKey, {
356
- loading: false,
357
- data: previousState?.data,
358
- hasData: previousState?.hasData ?? false,
359
- error: new QueryError(this.key, variantKey, response.status, data),
360
- version: previousState?.version
361
- }));
471
+ this.failures.delete(variantKey);
472
+ } else {
473
+ recordFailure();
474
+ this.store.next(store.set(variantKey, {
475
+ loading: false,
476
+ data: previousState?.data,
477
+ hasData: previousState?.hasData ?? false,
478
+ error: new QueryError(this.key, variantKey, response.status, data, parseRetryAfter(response.headers?.get?.("retry-after"))),
479
+ version: previousState?.version
480
+ }));
481
+ }
362
482
  } finally {
363
483
  this.inflight.delete(variantKey);
364
484
  this.settle(variantKey);
@@ -381,15 +501,32 @@ var APP_WIDE_QUERY_CONFIG_KEYS = [
381
501
  "suspense",
382
502
  "staleTime",
383
503
  "keepPreviousData",
504
+ "retry",
505
+ "retryDelay",
384
506
  "retryIntervalOnError",
385
507
  "refreshInterval",
386
508
  "revalidateOnFocus",
387
509
  "focusThrottleInterval"
388
510
  ];
511
+ var USER_QUERY_CONFIG_KEYS = [
512
+ "staleTime",
513
+ "retry",
514
+ "retryDelay",
515
+ "revalidateOnFocus",
516
+ "focusThrottleInterval",
517
+ "refreshInterval"
518
+ ];
519
+ /** Copy the listed keys whose value is defined. */
520
+ function pickDefined(source, keys) {
521
+ const picked = {};
522
+ if (!source) return picked;
523
+ for (const key of keys) if (source[key] !== void 0) picked[key] = source[key];
524
+ return picked;
525
+ }
389
526
  function pickAppWideQueryConfig(queryConfig) {
390
527
  if (!queryConfig) return null;
391
- const picked = {};
392
- for (const key of APP_WIDE_QUERY_CONFIG_KEYS) if (queryConfig[key] !== void 0) picked[key] = queryConfig[key];
528
+ const picked = pickDefined(queryConfig, APP_WIDE_QUERY_CONFIG_KEYS);
529
+ if (queryConfig.user) picked.user = pickDefined(queryConfig.user, USER_QUERY_CONFIG_KEYS);
393
530
  return picked;
394
531
  }
395
532
  var QueryManagerContext = createContext({
@@ -3109,6 +3246,6 @@ function useTheme() {
3109
3246
  return context;
3110
3247
  }
3111
3248
  //#endregion
3112
- export { isAbsoluteUrl as A, QueryError as B, useNavigate as C, ServerDataProvider as D, ServerDataContext as E, applyParams as F, QueryConfigContext as I, QueryManagerContext as L, Action as M, createMemoryHistory as N, useLocation as O, toVariantKey as P, QueryManagerProvider as R, useSearchParams as S, I18nProvider as T, Subject as V, registerDictionary as _, m as a, routeDataUrl as b, ClientRouterContext as c, ComponentsProvider as d, initialViewModulesReady as f, loadDictionaryForRender as g, loadDictionary as h, WebSocketContextProvider as i, ProgressManager as j, domainUrl as k, ClientRouterProvider as l, subscribeViewModules as m, useTheme as n, RouteTransitionProvider as o, loadViewModule as p, WebSocketContext as r, useRouteTransition as s, ThemeProvider as t, ComponentsContext as u, setActiveLocale as v, I18nContext as w, useRoute as x, readRoutePayload as y, DEFAULT_STALE_TIME as z };
3249
+ export { isAbsoluteUrl as A, pickDefined as B, useNavigate as C, ServerDataProvider as D, ServerDataContext as E, applyParams as F, QueryError as G, isRetryableQueryError as H, QueryConfigContext as I, Subject as K, QueryManagerContext as L, Action as M, createMemoryHistory as N, useLocation as O, toVariantKey as P, QueryManagerProvider as R, useSearchParams as S, I18nProvider as T, retryDelayFor as U, DEFAULT_STALE_TIME as V, shouldRetryQuery as W, registerDictionary as _, m as a, routeDataUrl as b, ClientRouterContext as c, ComponentsProvider as d, initialViewModulesReady as f, loadDictionaryForRender as g, loadDictionary as h, WebSocketContextProvider as i, ProgressManager as j, domainUrl as k, ClientRouterProvider as l, subscribeViewModules as m, useTheme as n, RouteTransitionProvider as o, loadViewModule as p, WebSocketContext as r, useRouteTransition as s, ThemeProvider as t, ComponentsContext as u, setActiveLocale as v, I18nContext as w, useRoute as x, readRoutePayload as y, USER_QUERY_CONFIG_KEYS as z };
3113
3250
 
3114
- //# sourceMappingURL=ThemeProvider-D9v0VAbY.js.map
3251
+ //# sourceMappingURL=ThemeProvider-BiNFL6l7.js.map