@naturalcycles/js-lib 15.73.0 → 15.74.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.
@@ -1,6 +1,7 @@
1
1
  import { _assert } from '../error/assert.js';
2
2
  import { localDate } from './localDate.js';
3
3
  import { _ms } from './time.util.js';
4
+ // oxlint-disable-next-line import/no-cycle -- intentional cycle
4
5
  import { WallTime } from './wallTime.js';
5
6
  export var ISODayOfWeek;
6
7
  (function (ISODayOfWeek) {
@@ -1,10 +1,10 @@
1
1
  import { MISS } from '../types.js';
2
- import { jsonMemoSerializer, MapMemoCache } from './memo.util.js';
2
+ import { jsonMemoSerializer } from './memo.util.js';
3
3
  /**
4
4
  * @experimental
5
5
  */
6
6
  export function _memoFnAsync(fn, opt) {
7
- const { logger = console, cacheFactory = () => new MapMemoCache(), cacheKeyFn = jsonMemoSerializer, } = opt;
7
+ const { logger = console, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt;
8
8
  const cache = cacheFactory();
9
9
  const memoizedFn = async function (...args) {
10
10
  const ctx = this;
@@ -2,6 +2,7 @@ import { isServerSide } from '../env.js';
2
2
  // oxlint-disable-next-line import/no-cycle -- intentional cycle
3
3
  import { _jsonParseIfPossible } from '../string/json.util.js';
4
4
  import { _truncate, _truncateMiddle } from '../string/string.util.js';
5
+ // oxlint-disable-next-line import/no-cycle -- intentional cycle
5
6
  import { _stringify } from '../string/stringify.js';
6
7
  /**
7
8
  * Useful to ensure that error in `catch (err) { ... }`
@@ -517,6 +517,9 @@ export class Fetcher {
517
517
  * statusCode of 0 (or absense of it) will BE retried.
518
518
  */
519
519
  shouldRetry(res) {
520
+ // Don't retry if the input AbortSignal was aborted
521
+ if (res.req.signal?.aborted)
522
+ return false;
520
523
  const { retryPost, retry3xx, retry4xx, retry5xx } = res.req;
521
524
  const { method } = res.req.init;
522
525
  if (method === 'POST' && !retryPost)
@@ -607,11 +610,12 @@ export class Fetcher {
607
610
  credentials: cfg.credentials,
608
611
  redirect: cfg.redirect,
609
612
  dispatcher: cfg.dispatcher,
613
+ keepalive: cfg.keepalive,
610
614
  },
611
615
  hooks: {},
612
616
  throwHttpErrors: true,
613
617
  errorData: {},
614
- }, _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger', 'name']));
618
+ }, _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger', 'name', 'keepalive']));
615
619
  norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase());
616
620
  return norm;
617
621
  }
@@ -660,6 +664,7 @@ export class Fetcher {
660
664
  method: opt.method || this.cfg.init.method,
661
665
  credentials: opt.credentials || this.cfg.init.credentials,
662
666
  redirect: opt.redirect || this.cfg.init.redirect || 'follow',
667
+ keepalive: opt.keepalive ?? this.cfg.init.keepalive,
663
668
  }, {
664
669
  headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
665
670
  }),
@@ -205,6 +205,12 @@ export interface FetcherOptions {
205
205
  * 'manual' will not throw, but return !ok response with 3xx status.
206
206
  */
207
207
  redirect?: RequestRedirect;
208
+ /**
209
+ * Default to false.
210
+ * When set to true, the request will not be aborted when the page is unloaded.
211
+ * Useful for sending analytics or tracking events that need to complete even if the user navigates away.
212
+ */
213
+ keepalive?: boolean;
208
214
  headers?: Record<string, any>;
209
215
  responseType?: FetcherResponseType;
210
216
  searchParams?: Record<string, any>;
@@ -287,6 +293,7 @@ export type RequestInitNormalized = Omit<RequestInit, 'method' | 'headers'> & {
287
293
  method: HttpMethod;
288
294
  headers: Record<string, any>;
289
295
  dispatcher?: Dispatcher;
296
+ keepalive?: boolean;
290
297
  };
291
298
  export interface FetcherSuccessResponse<BODY = unknown> {
292
299
  ok: true;
@@ -1,5 +1,6 @@
1
1
  // oxlint-disable-next-line import/no-cycle -- intentional cycle
2
2
  import { _isBackendErrorResponseObject, _isErrorLike, _isErrorObject } from '../error/error.util.js';
3
+ // oxlint-disable-next-line import/no-cycle -- intentional cycle
3
4
  import { _jsonParseIfPossible } from './json.util.js';
4
5
  import { _safeJsonStringify } from './safeJsonStringify.js';
5
6
  import { _truncateMiddle } from './string.util.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
3
  "type": "module",
4
- "version": "15.73.0",
4
+ "version": "15.74.0",
5
5
  "dependencies": {
6
6
  "tslib": "^2"
7
7
  },
@@ -16,6 +16,7 @@ import type {
16
16
  import type { LocalDate } from './localDate.js'
17
17
  import { localDate } from './localDate.js'
18
18
  import { _ms } from './time.util.js'
19
+ // oxlint-disable-next-line import/no-cycle -- intentional cycle
19
20
  import { WallTime } from './wallTime.js'
20
21
 
21
22
  export type LocalTimeUnit = 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second'
@@ -1,6 +1,7 @@
1
1
  import type { IsoDate, IsoDateTime } from '../types.js'
2
2
  // oxlint-disable-next-line import/no-cycle -- intentional cycle
3
3
  import { LocalDate } from './localDate.js'
4
+ // oxlint-disable-next-line import/no-cycle -- intentional cycle
4
5
  import type { DateTimeObject, LocalTime } from './localTime.js'
5
6
  import { localTime } from './localTime.js'
6
7
 
@@ -2,7 +2,7 @@ import type { AnyAsyncFunction, MaybeParameters } from '../types.js'
2
2
  import { MISS } from '../types.js'
3
3
  import type { AsyncMemoOptions } from './asyncMemo.decorator.js'
4
4
  import type { AsyncMemoCache } from './memo.util.js'
5
- import { jsonMemoSerializer, MapMemoCache } from './memo.util.js'
5
+ import { jsonMemoSerializer } from './memo.util.js'
6
6
 
7
7
  export interface MemoizedAsyncFunction {
8
8
  cache: AsyncMemoCache
@@ -15,11 +15,7 @@ export function _memoFnAsync<FN extends AnyAsyncFunction>(
15
15
  fn: FN,
16
16
  opt: AsyncMemoOptions<FN>,
17
17
  ): FN & MemoizedAsyncFunction {
18
- const {
19
- logger = console,
20
- cacheFactory = () => new MapMemoCache(),
21
- cacheKeyFn = jsonMemoSerializer,
22
- } = opt
18
+ const { logger = console, cacheFactory, cacheKeyFn = jsonMemoSerializer } = opt
23
19
 
24
20
  const cache = cacheFactory()
25
21
 
@@ -2,6 +2,7 @@ import { isServerSide } from '../env.js'
2
2
  // oxlint-disable-next-line import/no-cycle -- intentional cycle
3
3
  import { _jsonParseIfPossible } from '../string/json.util.js'
4
4
  import { _truncate, _truncateMiddle } from '../string/string.util.js'
5
+ // oxlint-disable-next-line import/no-cycle -- intentional cycle
5
6
  import { _stringify } from '../string/stringify.js'
6
7
  import type { Class } from '../types.js'
7
8
  import type {
@@ -263,6 +263,13 @@ export interface FetcherOptions {
263
263
  */
264
264
  redirect?: RequestRedirect
265
265
 
266
+ /**
267
+ * Default to false.
268
+ * When set to true, the request will not be aborted when the page is unloaded.
269
+ * Useful for sending analytics or tracking events that need to complete even if the user navigates away.
270
+ */
271
+ keepalive?: boolean
272
+
266
273
  // Removing RequestInit from options to simplify FetcherOptions interface.
267
274
  // Will instead only add hand-picked useful options, such as `credentials`.
268
275
  // init?: Partial<RequestInitNormalized>
@@ -361,6 +368,7 @@ export type RequestInitNormalized = Omit<RequestInit, 'method' | 'headers'> & {
361
368
  method: HttpMethod
362
369
  headers: Record<string, any>
363
370
  dispatcher?: Dispatcher
371
+ keepalive?: boolean
364
372
  }
365
373
 
366
374
  export interface FetcherSuccessResponse<BODY = unknown> {
@@ -646,6 +646,9 @@ export class Fetcher {
646
646
  * statusCode of 0 (or absense of it) will BE retried.
647
647
  */
648
648
  private shouldRetry(res: FetcherResponse): boolean {
649
+ // Don't retry if the input AbortSignal was aborted
650
+ if (res.req.signal?.aborted) return false
651
+
649
652
  const { retryPost, retry3xx, retry4xx, retry5xx } = res.req
650
653
  const { method } = res.req.init
651
654
  if (method === 'POST' && !retryPost) return false
@@ -741,12 +744,13 @@ export class Fetcher {
741
744
  credentials: cfg.credentials,
742
745
  redirect: cfg.redirect,
743
746
  dispatcher: cfg.dispatcher,
747
+ keepalive: cfg.keepalive,
744
748
  },
745
749
  hooks: {},
746
750
  throwHttpErrors: true,
747
751
  errorData: {},
748
752
  },
749
- _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger', 'name']),
753
+ _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger', 'name', 'keepalive']),
750
754
  )
751
755
 
752
756
  norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase())
@@ -801,6 +805,7 @@ export class Fetcher {
801
805
  method: opt.method || this.cfg.init.method,
802
806
  credentials: opt.credentials || this.cfg.init.credentials,
803
807
  redirect: opt.redirect || this.cfg.init.redirect || 'follow',
808
+ keepalive: opt.keepalive ?? this.cfg.init.keepalive,
804
809
  },
805
810
  {
806
811
  headers: _mapKeys(opt.headers || {}, k => k.toLowerCase()),
@@ -2,6 +2,7 @@
2
2
  import { _isBackendErrorResponseObject, _isErrorLike, _isErrorObject } from '../error/error.util.js'
3
3
  import type { ErrorLike } from '../error/index.js'
4
4
  import type { Reviver } from '../types.js'
5
+ // oxlint-disable-next-line import/no-cycle -- intentional cycle
5
6
  import { _jsonParseIfPossible } from './json.util.js'
6
7
  import { _safeJsonStringify } from './safeJsonStringify.js'
7
8
  import { _truncateMiddle } from './string.util.js'