@algolia/client-common 5.0.0-alpha.92 → 5.0.0-alpha.99

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.
@@ -118,14 +118,35 @@ function createBrowserLocalStorageCache(options) {
118
118
  function getNamespace() {
119
119
  return JSON.parse(getStorage().getItem(namespaceKey) || '{}');
120
120
  }
121
+ function setNamespace(namespace) {
122
+ getStorage().setItem(namespaceKey, JSON.stringify(namespace));
123
+ }
124
+ function removeOutdatedCacheItems() {
125
+ const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null;
126
+ const namespace = getNamespace();
127
+ const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries(Object.entries(namespace).filter(([, cacheItem]) => {
128
+ return cacheItem.timestamp !== undefined;
129
+ }));
130
+ setNamespace(filteredNamespaceWithoutOldFormattedCacheItems);
131
+ if (!timeToLive) {
132
+ return;
133
+ }
134
+ const filteredNamespaceWithoutExpiredItems = Object.fromEntries(Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(([, cacheItem]) => {
135
+ const currentTimestamp = new Date().getTime();
136
+ const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp;
137
+ return !isExpired;
138
+ }));
139
+ setNamespace(filteredNamespaceWithoutExpiredItems);
140
+ }
121
141
  return {
122
142
  get(key, defaultValue, events = {
123
143
  miss: () => Promise.resolve()
124
144
  }) {
125
145
  return Promise.resolve().then(() => {
126
- const keyAsString = JSON.stringify(key);
127
- const value = getNamespace()[keyAsString];
128
- return Promise.all([value || defaultValue(), value !== undefined]);
146
+ removeOutdatedCacheItems();
147
+ return getNamespace()[JSON.stringify(key)];
148
+ }).then(value => {
149
+ return Promise.all([value ? value.value : defaultValue(), value !== undefined]);
129
150
  }).then(([value, exists]) => {
130
151
  return Promise.all([value, exists || events.miss(value)]);
131
152
  }).then(([value]) => value);
@@ -133,7 +154,10 @@ function createBrowserLocalStorageCache(options) {
133
154
  set(key, value) {
134
155
  return Promise.resolve().then(() => {
135
156
  const namespace = getNamespace();
136
- namespace[JSON.stringify(key)] = value;
157
+ namespace[JSON.stringify(key)] = {
158
+ timestamp: new Date().getTime(),
159
+ value
160
+ };
137
161
  getStorage().setItem(namespaceKey, JSON.stringify(namespace));
138
162
  return value;
139
163
  });
@@ -263,6 +287,20 @@ function createStatefulHost(host, status = 'up') {
263
287
  };
264
288
  }
265
289
 
290
+ function _toPrimitive(t, r) {
291
+ if ("object" != typeof t || !t) return t;
292
+ var e = t[Symbol.toPrimitive];
293
+ if (void 0 !== e) {
294
+ var i = e.call(t, r || "default");
295
+ if ("object" != typeof i) return i;
296
+ throw new TypeError("@@toPrimitive must return a primitive value.");
297
+ }
298
+ return ("string" === r ? String : Number)(t);
299
+ }
300
+ function _toPropertyKey(t) {
301
+ var i = _toPrimitive(t, "string");
302
+ return "symbol" == typeof i ? i : String(i);
303
+ }
266
304
  function _defineProperty(obj, key, value) {
267
305
  key = _toPropertyKey(key);
268
306
  if (key in obj) {
@@ -277,20 +315,6 @@ function _defineProperty(obj, key, value) {
277
315
  }
278
316
  return obj;
279
317
  }
280
- function _toPrimitive(input, hint) {
281
- if (typeof input !== "object" || input === null) return input;
282
- var prim = input[Symbol.toPrimitive];
283
- if (prim !== undefined) {
284
- var res = prim.call(input, hint || "default");
285
- if (typeof res !== "object") return res;
286
- throw new TypeError("@@toPrimitive must return a primitive value.");
287
- }
288
- return (hint === "string" ? String : Number)(input);
289
- }
290
- function _toPropertyKey(arg) {
291
- var key = _toPrimitive(arg, "string");
292
- return typeof key === "symbol" ? key : String(key);
293
- }
294
318
 
295
319
  class AlgoliaError extends Error {
296
320
  constructor(message, name) {
@@ -311,7 +335,7 @@ class ErrorWithStackTrace extends AlgoliaError {
311
335
  }
312
336
  class RetryError extends ErrorWithStackTrace {
313
337
  constructor(stackTrace) {
314
- super('Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.', stackTrace, 'RetryError');
338
+ super('Unreachable hosts - your application id may be incorrect. If the error persists, please create a ticket at https://support.algolia.com/ sharing steps we can use to reproduce the issue.', stackTrace, 'RetryError');
315
339
  }
316
340
  }
317
341
  class ApiError extends ErrorWithStackTrace {
@@ -116,14 +116,35 @@ function createBrowserLocalStorageCache(options) {
116
116
  function getNamespace() {
117
117
  return JSON.parse(getStorage().getItem(namespaceKey) || '{}');
118
118
  }
119
+ function setNamespace(namespace) {
120
+ getStorage().setItem(namespaceKey, JSON.stringify(namespace));
121
+ }
122
+ function removeOutdatedCacheItems() {
123
+ const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null;
124
+ const namespace = getNamespace();
125
+ const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries(Object.entries(namespace).filter(([, cacheItem]) => {
126
+ return cacheItem.timestamp !== undefined;
127
+ }));
128
+ setNamespace(filteredNamespaceWithoutOldFormattedCacheItems);
129
+ if (!timeToLive) {
130
+ return;
131
+ }
132
+ const filteredNamespaceWithoutExpiredItems = Object.fromEntries(Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(([, cacheItem]) => {
133
+ const currentTimestamp = new Date().getTime();
134
+ const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp;
135
+ return !isExpired;
136
+ }));
137
+ setNamespace(filteredNamespaceWithoutExpiredItems);
138
+ }
119
139
  return {
120
140
  get(key, defaultValue, events = {
121
141
  miss: () => Promise.resolve()
122
142
  }) {
123
143
  return Promise.resolve().then(() => {
124
- const keyAsString = JSON.stringify(key);
125
- const value = getNamespace()[keyAsString];
126
- return Promise.all([value || defaultValue(), value !== undefined]);
144
+ removeOutdatedCacheItems();
145
+ return getNamespace()[JSON.stringify(key)];
146
+ }).then(value => {
147
+ return Promise.all([value ? value.value : defaultValue(), value !== undefined]);
127
148
  }).then(([value, exists]) => {
128
149
  return Promise.all([value, exists || events.miss(value)]);
129
150
  }).then(([value]) => value);
@@ -131,7 +152,10 @@ function createBrowserLocalStorageCache(options) {
131
152
  set(key, value) {
132
153
  return Promise.resolve().then(() => {
133
154
  const namespace = getNamespace();
134
- namespace[JSON.stringify(key)] = value;
155
+ namespace[JSON.stringify(key)] = {
156
+ timestamp: new Date().getTime(),
157
+ value
158
+ };
135
159
  getStorage().setItem(namespaceKey, JSON.stringify(namespace));
136
160
  return value;
137
161
  });
@@ -261,6 +285,20 @@ function createStatefulHost(host, status = 'up') {
261
285
  };
262
286
  }
263
287
 
288
+ function _toPrimitive(t, r) {
289
+ if ("object" != typeof t || !t) return t;
290
+ var e = t[Symbol.toPrimitive];
291
+ if (void 0 !== e) {
292
+ var i = e.call(t, r || "default");
293
+ if ("object" != typeof i) return i;
294
+ throw new TypeError("@@toPrimitive must return a primitive value.");
295
+ }
296
+ return ("string" === r ? String : Number)(t);
297
+ }
298
+ function _toPropertyKey(t) {
299
+ var i = _toPrimitive(t, "string");
300
+ return "symbol" == typeof i ? i : String(i);
301
+ }
264
302
  function _defineProperty(obj, key, value) {
265
303
  key = _toPropertyKey(key);
266
304
  if (key in obj) {
@@ -275,20 +313,6 @@ function _defineProperty(obj, key, value) {
275
313
  }
276
314
  return obj;
277
315
  }
278
- function _toPrimitive(input, hint) {
279
- if (typeof input !== "object" || input === null) return input;
280
- var prim = input[Symbol.toPrimitive];
281
- if (prim !== undefined) {
282
- var res = prim.call(input, hint || "default");
283
- if (typeof res !== "object") return res;
284
- throw new TypeError("@@toPrimitive must return a primitive value.");
285
- }
286
- return (hint === "string" ? String : Number)(input);
287
- }
288
- function _toPropertyKey(arg) {
289
- var key = _toPrimitive(arg, "string");
290
- return typeof key === "symbol" ? key : String(key);
291
- }
292
316
 
293
317
  class AlgoliaError extends Error {
294
318
  constructor(message, name) {
@@ -309,7 +333,7 @@ class ErrorWithStackTrace extends AlgoliaError {
309
333
  }
310
334
  class RetryError extends ErrorWithStackTrace {
311
335
  constructor(stackTrace) {
312
- super('Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.', stackTrace, 'RetryError');
336
+ super('Unreachable hosts - your application id may be incorrect. If the error persists, please create a ticket at https://support.algolia.com/ sharing steps we can use to reproduce the issue.', stackTrace, 'RetryError');
313
337
  }
314
338
  }
315
339
  class ApiError extends ErrorWithStackTrace {
@@ -1 +1 @@
1
- {"version":3,"file":"createBrowserLocalStorageCache.d.ts","sourceRoot":"","sources":["../../../src/cache/createBrowserLocalStorageCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,KAAK,EAAe,MAAM,UAAU,CAAC;AAE/E,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,0BAA0B,GAClC,KAAK,CAqEP"}
1
+ {"version":3,"file":"createBrowserLocalStorageCache.d.ts","sourceRoot":"","sources":["../../../src/cache/createBrowserLocalStorageCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,0BAA0B,EAC1B,KAAK,EAEN,MAAM,UAAU,CAAC;AAElB,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,0BAA0B,GAClC,KAAK,CAiHP"}
@@ -33,11 +33,25 @@ export type BrowserLocalStorageOptions = {
33
33
  * The cache key.
34
34
  */
35
35
  key: string;
36
+ /**
37
+ * The time to live for each cached item in seconds.
38
+ */
39
+ timeToLive?: number;
36
40
  /**
37
41
  * The native local storage implementation.
38
42
  */
39
43
  localStorage?: Storage;
40
44
  };
45
+ export type BrowserLocalStorageCacheItem = {
46
+ /**
47
+ * The cache item creation timestamp.
48
+ */
49
+ timestamp: number;
50
+ /**
51
+ * The cache item value.
52
+ */
53
+ value: any;
54
+ };
41
55
  export type FallbackableCacheOptions = {
42
56
  /**
43
57
  * List of caches order by priority.
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/types/cache.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG;IAClB;;OAEG;IACH,GAAG,EAAE,CAAC,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EACjC,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EACnC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,KACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IAErB;;OAEG;IACH,GAAG,EAAE,CAAC,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EACjC,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,MAAM,CAAC,CAAC;IAErB;;OAEG;IACH,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;OAEG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,MAAM,IAAI;IAChC;;OAEG;IACH,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB,CAAC"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/types/cache.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,KAAK,GAAG;IAClB;;OAEG;IACH,GAAG,EAAE,CAAC,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EACjC,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EACnC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,KACzB,OAAO,CAAC,MAAM,CAAC,CAAC;IAErB;;OAEG;IACH,GAAG,EAAE,CAAC,MAAM,EACV,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EACjC,KAAK,EAAE,MAAM,KACV,OAAO,CAAC,MAAM,CAAC,CAAC;IAErB;;OAEG;IACH,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7D;;OAEG;IACH,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,MAAM,IAAI;IAChC;;OAEG;IACH,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/client-common",
3
- "version": "5.0.0-alpha.92",
3
+ "version": "5.0.0-alpha.99",
4
4
  "description": "Common package for the Algolia JavaScript API client.",
5
5
  "repository": "algolia/algoliasearch-client-javascript",
6
6
  "license": "MIT",
@@ -20,14 +20,14 @@
20
20
  "test": "jest"
21
21
  },
22
22
  "devDependencies": {
23
- "@babel/preset-env": "7.23.3",
23
+ "@babel/preset-env": "7.23.8",
24
24
  "@babel/preset-typescript": "7.23.3",
25
- "@types/jest": "29.5.8",
26
- "@types/node": "20.9.0",
25
+ "@types/jest": "29.5.11",
26
+ "@types/node": "20.11.5",
27
27
  "jest": "29.7.0",
28
28
  "jest-environment-jsdom": "29.7.0",
29
29
  "ts-jest": "29.1.1",
30
- "typescript": "5.2.2"
30
+ "typescript": "5.3.3"
31
31
  },
32
32
  "engines": {
33
33
  "node": ">= 14.0.0"
@@ -39,6 +39,24 @@ describe('browser local storage cache', () => {
39
39
  expect(missMock.mock.calls.length).toBe(1);
40
40
  });
41
41
 
42
+ it('reads unexpired timeToLive keys', async () => {
43
+ const cache = createBrowserLocalStorageCache({
44
+ key: version,
45
+ timeToLive: 5,
46
+ });
47
+ await cache.set({ key: 'foo' }, { bar: 1 });
48
+
49
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
50
+
51
+ expect(
52
+ await cache.get({ key: 'foo' }, defaultValue, {
53
+ miss: () => Promise.resolve(missMock()),
54
+ })
55
+ ).toMatchObject({ bar: 1 });
56
+
57
+ expect(missMock.mock.calls.length).toBe(0);
58
+ });
59
+
42
60
  it('deletes keys', async () => {
43
61
  const cache = createBrowserLocalStorageCache({ key: version });
44
62
 
@@ -53,19 +71,43 @@ describe('browser local storage cache', () => {
53
71
  expect(missMock.mock.calls.length).toBe(1);
54
72
  });
55
73
 
74
+ it('deletes expired keys', async () => {
75
+ const cache = createBrowserLocalStorageCache({
76
+ key: version,
77
+ timeToLive: -1,
78
+ });
79
+ await cache.set({ key: 'foo' }, { bar: 1 });
80
+
81
+ const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
82
+
83
+ expect(
84
+ await cache.get({ key: 'foo' }, defaultValue, {
85
+ miss: () => Promise.resolve(missMock()),
86
+ })
87
+ ).toMatchObject({ bar: 2 });
88
+
89
+ expect(missMock.mock.calls.length).toBe(1);
90
+ });
91
+
56
92
  it('can be cleared', async () => {
57
93
  const cache = createBrowserLocalStorageCache({ key: version });
58
-
59
94
  await cache.set({ key: 'foo' }, { bar: 1 });
95
+
60
96
  await cache.clear();
61
97
 
62
- const defaultValue = (): DefaultValue => Promise.resolve({ bar: 2 });
98
+ const defaultValue = (): Promise<void> => Promise.resolve({ bar: 2 });
63
99
 
64
- expect(await cache.get({ key: 'foo' }, defaultValue, events)).toMatchObject(
65
- { bar: 2 }
66
- );
67
- expect(missMock.mock.calls.length).toBe(1);
68
100
  expect(localStorage.length).toBe(0);
101
+
102
+ expect(
103
+ await cache.get({ key: 'foo' }, defaultValue, {
104
+ miss: () => Promise.resolve(missMock()),
105
+ })
106
+ ).toMatchObject({ bar: 2 });
107
+
108
+ expect(missMock.mock.calls.length).toBe(1);
109
+
110
+ expect(localStorage.getItem(`algolia-client-js-${version}`)).toEqual('{}');
69
111
  });
70
112
 
71
113
  it('do throws localstorage exceptions on access', async () => {
@@ -122,13 +164,23 @@ describe('browser local storage cache', () => {
122
164
  });
123
165
  const key = { foo: 'bar' };
124
166
  const value = 'foo';
125
-
126
167
  expect(localStorage.getItem(`algolia-client-js-${version}`)).toBeNull();
127
168
 
128
169
  await cache.set(key, value);
129
170
 
130
- expect(localStorage.getItem(`algolia-client-js-${version}`)).toBe(
131
- '{"{\\"foo\\":\\"bar\\"}":"foo"}'
171
+ const expectedValue = expect.objectContaining({
172
+ [JSON.stringify(key)]: {
173
+ timestamp: expect.any(Number),
174
+ value,
175
+ },
176
+ });
177
+
178
+ const localStorageValue = localStorage.getItem(
179
+ `algolia-client-js-${version}`
180
+ );
181
+
182
+ expect(JSON.parse(localStorageValue ? localStorageValue : '{}')).toEqual(
183
+ expectedValue
132
184
  );
133
185
  });
134
186
  });
@@ -1,4 +1,9 @@
1
- import type { BrowserLocalStorageOptions, Cache, CacheEvents } from '../types';
1
+ import type {
2
+ BrowserLocalStorageCacheItem,
3
+ BrowserLocalStorageOptions,
4
+ Cache,
5
+ CacheEvents,
6
+ } from '../types';
2
7
 
3
8
  export function createBrowserLocalStorageCache(
4
9
  options: BrowserLocalStorageOptions
@@ -19,20 +24,61 @@ export function createBrowserLocalStorageCache(
19
24
  return JSON.parse(getStorage().getItem(namespaceKey) || '{}');
20
25
  }
21
26
 
27
+ function setNamespace(namespace: Record<string, any>): void {
28
+ getStorage().setItem(namespaceKey, JSON.stringify(namespace));
29
+ }
30
+
31
+ function removeOutdatedCacheItems(): void {
32
+ const timeToLive = options.timeToLive ? options.timeToLive * 1000 : null;
33
+ const namespace = getNamespace<BrowserLocalStorageCacheItem>();
34
+
35
+ const filteredNamespaceWithoutOldFormattedCacheItems = Object.fromEntries(
36
+ Object.entries(namespace).filter(([, cacheItem]) => {
37
+ return cacheItem.timestamp !== undefined;
38
+ })
39
+ );
40
+
41
+ setNamespace(filteredNamespaceWithoutOldFormattedCacheItems);
42
+
43
+ if (!timeToLive) {
44
+ return;
45
+ }
46
+
47
+ const filteredNamespaceWithoutExpiredItems = Object.fromEntries(
48
+ Object.entries(filteredNamespaceWithoutOldFormattedCacheItems).filter(
49
+ ([, cacheItem]) => {
50
+ const currentTimestamp = new Date().getTime();
51
+ const isExpired = cacheItem.timestamp + timeToLive < currentTimestamp;
52
+
53
+ return !isExpired;
54
+ }
55
+ )
56
+ );
57
+
58
+ setNamespace(filteredNamespaceWithoutExpiredItems);
59
+ }
60
+
22
61
  return {
23
62
  get<TValue>(
24
63
  key: Record<string, any> | string,
25
64
  defaultValue: () => Promise<TValue>,
26
65
  events: CacheEvents<TValue> = {
27
- miss: (): Promise<void> => Promise.resolve(),
66
+ miss: () => Promise.resolve(),
28
67
  }
29
68
  ): Promise<TValue> {
30
69
  return Promise.resolve()
31
70
  .then(() => {
32
- const keyAsString = JSON.stringify(key);
33
- const value = getNamespace<TValue>()[keyAsString];
71
+ removeOutdatedCacheItems();
34
72
 
35
- return Promise.all([value || defaultValue(), value !== undefined]);
73
+ return getNamespace<Promise<BrowserLocalStorageCacheItem>>()[
74
+ JSON.stringify(key)
75
+ ];
76
+ })
77
+ .then((value) => {
78
+ return Promise.all([
79
+ value ? value.value : defaultValue(),
80
+ value !== undefined,
81
+ ]);
36
82
  })
37
83
  .then(([value, exists]) => {
38
84
  return Promise.all([value, exists || events.miss(value)]);
@@ -47,7 +93,10 @@ export function createBrowserLocalStorageCache(
47
93
  return Promise.resolve().then(() => {
48
94
  const namespace = getNamespace();
49
95
 
50
- namespace[JSON.stringify(key)] = value;
96
+ namespace[JSON.stringify(key)] = {
97
+ timestamp: new Date().getTime(),
98
+ value,
99
+ };
51
100
 
52
101
  getStorage().setItem(namespaceKey, JSON.stringify(namespace));
53
102
 
@@ -25,7 +25,7 @@ export class ErrorWithStackTrace extends AlgoliaError {
25
25
  export class RetryError extends ErrorWithStackTrace {
26
26
  constructor(stackTrace: StackFrame[]) {
27
27
  super(
28
- 'Unreachable hosts - your application id may be incorrect. If the error persists, contact support@algolia.com.',
28
+ 'Unreachable hosts - your application id may be incorrect. If the error persists, please create a ticket at https://support.algolia.com/ sharing steps we can use to reproduce the issue.',
29
29
  stackTrace,
30
30
  'RetryError'
31
31
  );
@@ -47,12 +47,29 @@ export type BrowserLocalStorageOptions = {
47
47
  */
48
48
  key: string;
49
49
 
50
+ /**
51
+ * The time to live for each cached item in seconds.
52
+ */
53
+ timeToLive?: number;
54
+
50
55
  /**
51
56
  * The native local storage implementation.
52
57
  */
53
58
  localStorage?: Storage;
54
59
  };
55
60
 
61
+ export type BrowserLocalStorageCacheItem = {
62
+ /**
63
+ * The cache item creation timestamp.
64
+ */
65
+ timestamp: number;
66
+
67
+ /**
68
+ * The cache item value.
69
+ */
70
+ value: any;
71
+ };
72
+
56
73
  export type FallbackableCacheOptions = {
57
74
  /**
58
75
  * List of caches order by priority.