Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@lion/ajax

Package Overview
Dependencies
Maintainers
1
Versions
82
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@lion/ajax - npm Package Compare versions

Comparing version 0.6.0 to 0.7.0

docs/naga.json

6

CHANGELOG.md
# Change Log
## 0.7.0
### Minor Changes
- 2cd7993d: Set fromCache property on the Response, for user consumption. Allow setting cacheOptions on the AjaxClient upon instantiation. Create docs/demos.
## 0.6.0

@@ -4,0 +10,0 @@

10

custom-elements.json

@@ -192,3 +192,3 @@ {

"type": {
"text": "{\n addAcceptLanguage: boolean;\n xsrfCookieName: string | null;\n xsrfHeaderName: string | null;\n jsonPrefix: string;\n }"
"text": "Partial<AjaxClientConfig>"
}

@@ -1078,10 +1078,4 @@ },

"exports": []
},
{
"kind": "javascript-module",
"path": "./docs/cache-technical-docs.md",
"declarations": [],
"exports": []
}
]
}
}

10

package.json
{
"name": "@lion/ajax",
"version": "0.6.0",
"version": "0.7.0",
"description": "Thin wrapper around fetch with support for interceptors.",

@@ -27,3 +27,3 @@ "license": "MIT",

"scripts": {
"custom-elements-manifest": "custom-elements-manifest analyze --exclude 'docs/**/*'",
"custom-elements-manifest": "custom-elements-manifest analyze --litelement --exclude 'docs/**/*'",
"debug": "cd ../../ && npm run debug -- --group ajax",

@@ -45,4 +45,4 @@ "debug:firefox": "cd ../../ && npm run debug:firefox -- --group ajax",

},
"exports": "./index.js",
"customElementsManifest": "custom-elements.json"
}
"customElementsManifest": "custom-elements.json",
"exports": "./index.js"
}

@@ -5,2 +5,32 @@ [//]: # 'AUTO INSERT HEADER PREPUBLISH'

```js script
import { html } from '@lion/core';
import { renderLitAsNode } from '@lion/helpers';
import { ajax, AjaxClient, cacheRequestInterceptorFactory, cacheResponseInterceptorFactory } from '@lion/ajax';
import '@lion/helpers/sb-action-logger';
const getCacheIdentifier = () => {
let userId = localStorage.getItem('lion-ajax-cache-demo-user-id');
if (!userId) {
localStorage.setItem('lion-ajax-cache-demo-user-id', '1');
userId = '1';
}
return userId;
}
const cacheOptions = {
useCache: true,
timeToLive: 1000 * 60 * 10, // 10 minutes
};
ajax.addRequestInterceptor(cacheRequestInterceptorFactory(getCacheIdentifier, cacheOptions));
ajax.addResponseInterceptor(
cacheResponseInterceptorFactory(getCacheIdentifier, cacheOptions),
);
export default {
title: 'Ajax/Ajax',
};
```
`ajax` is a small wrapper around `fetch` which:

@@ -31,7 +61,23 @@

```js
import { ajax } from '@lion/ajax';
const response = await ajax.request('/api/users');
const users = await response.json();
```js preview-story
export const getRequest = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = (name) => {
ajax.request(`./packages/ajax/docs/${name}.json`)
.then(response => response.json())
.then(result => {
actionLogger.log(JSON.stringify(result, null, 2));
});
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${() => fetchHandler('pabu')}>Fetch Pabu</button>
<button @click=${() => fetchHandler('naga')}>Fetch Naga</button>
${actionLogger}
`;
}
```

@@ -53,10 +99,29 @@

We usually deal with JSON requests and responses. With `requestJson` you don't need to specifically stringify the request body or parse the response body:
We usually deal with JSON requests and responses. With `requestJson` you don't need to specifically stringify the request body or parse the response body.
The result will have the Response object on `.response` property, and the decoded json will be available on `.body`.
#### GET JSON request
```js
import { ajax } from '@lion/ajax';
const { response, body } = await ajax.requestJson('/api/users');
```js preview-story
export const getJsonRequest = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = (name) => {
ajax.requestJson(`./packages/ajax/docs/${name}.json`)
.then(result => {
console.log(result.response);
actionLogger.log(JSON.stringify(result.body, null, 2));
});
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${() => fetchHandler('pabu')}>Fetch Pabu</button>
<button @click=${() => fetchHandler('naga')}>Fetch Naga</button>
${actionLogger}
`;
}
```

@@ -79,29 +144,51 @@

```js
import { ajax } from '@lion/ajax';
try {
const users = await ajax.requestJson('/api/users');
} catch (error) {
if (error.response) {
if (error.response.status === 400) {
// handle a specific status code, for example 400 bad request
} else {
console.error(error);
```js preview-story
export const errorHandling = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = async () => {
try {
const users = await ajax.requestJson('/api/users');
} catch (error) {
if (error.response) {
if (error.response.status === 400) {
// handle a specific status code, for example 400 bad request
} else {
actionLogger.log(error);
}
} else {
// an error happened before receiving a response, ex. an incorrect request or network error
actionLogger.log(error);
}
}
} else {
// an error happened before receiving a response, ex. an incorrect request or network error
console.error(error);
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${fetchHandler}>Fetch</button>
${actionLogger}
`;
}
```
## Fetch Polyfill
For IE11 you will need a polyfill for fetch. You should add this on your top level layer, e.g. your application.
[This is the polyfill we recommend](https://github.com/github/fetch). It also has a [section for polyfilling AbortController](https://github.com/github/fetch#aborting-requests)
## Ajax Cache
A caching library that uses `lion-web/ajax` and adds cache interceptors to provide caching for use in
A caching library that uses `@lion/ajax` and adds cache interceptors to provide caching for use in
frontend `services`.
> Technical documentation and decisions can be found in
> [./docs/technical-docs.md](https://github.com/ing-bank/lion/blob/master/packages/ajax/docs/technical-docs.md)
The **request interceptor**'s main goal is to determine whether or not to
**return the cached object**. This is done based on the options that are being
passed.
The **response interceptor**'s goal is to determine **when to cache** the
requested response, based on the options that are being passed.
### Getting started

@@ -140,111 +227,245 @@

### Ajax cache example
Alternatively, most often for subclassers, you can extend or import `AjaxClient` yourself, and pass cacheOptions when instantiating the ajax singleton.
```js
import {
ajax,
cacheRequestInterceptorFactory,
cacheResponseInterceptorFactory,
} from '@lion-web/ajax';
import { AjaxClient } from '@lion/ajax';
const getCacheIdentifier = () => getActiveProfile().profileId;
export const ajax = new AjaxClient({
cacheOptions: {
useCache: true,
timeToLive: 1000 * 60 * 5, // 5 minutes
getCacheIdentifier: () => getActiveProfile().profileId,
},
})
```
const globalCacheOptions = {
useCache: false,
timeToLive: 50, // default: one hour (the cache instance will be replaced in 1 hour, regardless of this setting)
methods: ['get'], // default: ['get'] NOTE for now only 'get' is supported
// requestIdentificationFn: (requestConfig) => { }, // see docs below for more info
// invalidateUrls: [], see docs below for more info
// invalidateUrlsRegex: RegExp, // see docs below for more info
};
### Ajax cache example
// pass a function to the interceptorFactory that retrieves a cache identifier
// ajax.interceptors.request.use(cacheRequestInterceptorFactory(getCacheIdentifier, cacheOptions));
// ajax.interceptors.response.use(
// cacheResponseInterceptorFactory(getCacheIdentifier, cacheOptions),
// );
> Let's assume that we have a user session, for this demo purposes we already created an identifier function for this and set the cache interceptors.
class TodoService {
constructor() {
this.localAjaxConfig = {
cacheOptions: {
invalidateUrls: ['/api/todosbykeyword'], // default: []
},
};
}
We can see if a response is served from the cache by checking the `response.fromCache` property,
which is either undefined for normal requests, or set to true for responses that were served from cache.
/**
* Returns all todos from cache if not older than 5 minutes
*/
getTodos() {
return ajax.requestJson(`/api/todos`, this.localAjaxConfig);
```js preview-story
export const cache = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = (name) => {
ajax.requestJson(`./packages/ajax/docs/${name}.json`)
.then(result => {
actionLogger.log(`From cache: ${result.response.fromCache || false}`);
actionLogger.log(JSON.stringify(result.body, null, 2));
});
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${() => fetchHandler('pabu')}>Fetch Pabu</button>
<button @click=${() => fetchHandler('naga')}>Fetch Naga</button>
${actionLogger}
`;
}
```
/**
*
*/
getTodosByKeyword(keyword) {
return ajax.requestJson(`/api/todosbykeyword/${keyword}`, this.localAjaxConfig);
You can also change the cache options per request, which is handy if you don't want to remove and re-add the interceptors for a simple configuration change.
In this demo, when we fetch naga, we always pass `useCache: false` so the Response is never a cached one.
```js preview-story
export const cacheActionOptions = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = (name) => {
let actionCacheOptions;
if (name === 'naga') {
actionCacheOptions = {
useCache: false,
}
}
ajax.requestJson(`./packages/ajax/docs/${name}.json`, { cacheOptions: actionCacheOptions })
.then(result => {
actionLogger.log(`From cache: ${result.response.fromCache || false}`);
actionLogger.log(JSON.stringify(result.body, null, 2));
});
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${() => fetchHandler('pabu')}>Fetch Pabu</button>
<button @click=${() => fetchHandler('naga')}>Fetch Naga</button>
${actionLogger}
`;
}
```
/**
* Creates new todo and invalidates cache.
* `getTodos` will NOT take the response from cache
*/
saveTodo(todo) {
return ajax.requestJson(`/api/todos`, { method: 'POST', body: todo, ...this.localAjaxConfig });
### Invalidating cache
Invalidating the cache, or cache busting, can be done in multiple ways:
- Going past the `timeToLive` of the cache object
- Changing cache identifier (e.g. user session or active profile changes)
- Doing a non GET request to the cached endpoint
- Invalidates the cache of that endpoint
- Invalidates the cache of all other endpoints matching `invalidatesUrls` and `invalidateUrlsRegex`
#### Time to live
In this demo we pass a timeToLive of three seconds.
Try clicking the fetch button and watch fromCache change whenever TTL expires.
After TTL expires, the next request will set the cache again, and for the next 3 seconds you will get cached responses for subsequent requests.
```js preview-story
export const cacheTimeToLive = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = () => {
ajax.requestJson(`./packages/ajax/docs/pabu.json`, {
cacheOptions: {
timeToLive: 1000 * 3, // 3 seconds
}
})
.then(result => {
actionLogger.log(`From cache: ${result.response.fromCache || false}`);
actionLogger.log(JSON.stringify(result.body, null, 2));
});
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${fetchHandler}>Fetch Pabu</button>
${actionLogger}
`;
}
```
If a value returned by `cacheIdentifier` changes the cache is reset. We avoid situation of accessing old cache and proactively clean it, for instance when a user session is ended.
#### Changing cache identifier
### Ajax cache Options
For this demo we use localStorage to set a user id to `'1'`.
```js
const cacheOptions = {
// `useCache`: determines wether or not to use the cache
// can be boolean
// default: false
useCache: true,
Now we will allow you to change this identifier to invalidate the cache.
// `timeToLive`: is the time the cache should be kept in ms
// default: 0
// Note: regardless of this setting, the cache instance holding all the caches
// will be invalidated after one hour
timeToLive: 1000 * 60 * 5,
```js preview-story
export const changeCacheIdentifier = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = () => {
ajax.requestJson(`./packages/ajax/docs/pabu.json`)
.then(result => {
actionLogger.log(`From cache: ${result.response.fromCache || false}`);
actionLogger.log(JSON.stringify(result.body, null, 2));
});
}
// `methods`: an array of methods on which this configuration is applied
// Note: when `useCache` is `false` this will not be used
// NOTE: ONLY GET IS SUPPORTED
// default: ['get']
methods: ['get'],
const changeUserHandler = () => {
const currentUser = parseInt(localStorage.getItem('lion-ajax-cache-demo-user-id'), 10);
localStorage.setItem('lion-ajax-cache-demo-user-id', `${currentUser + 1}`);
}
// `invalidateUrls`: an array of strings that for each string that partially
// occurs as key in the cache, will be removed
// default: []
// Note: can be invalidated only by non-get request to the same url
invalidateUrls: ['/api/todosbykeyword'],
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${fetchHandler}>Fetch Pabu</button>
<button @click=${changeUserHandler}>Change user</button>
${actionLogger}
`;
}
```
// `invalidateUrlsRegex`: a RegExp object to match and delete
// each matched key in the cache
// Note: can be invalidated only by non-get request to the same url
invalidateUrlsRegex: /posts/
#### Non-GET request
// `requestIdentificationFn`: a function to provide a string that should be
// taken as a key in the cache.
// This can be used to cache post-requests.
// default: (requestConfig, searchParamsSerializer) => url + params
requestIdentificationFn: (request, serializer) => {
return `${request.url}?${serializer(request.params)}`;
},
};
In this demo we show that by doing a PATCH request, you invalidate the cache of the endpoint for subsequent GET requests.
Try clicking the GET pabu button twice so you see a cached response.
Then click the PATCH pabu button, followed by another GET, and you will see that this one is not served from cache, because the PATCH invalidated it.
The rationale is that if a user does a non-GET request to an endpoint, it will make the client-side caching of this endpoint outdated.
This is because non-GET requests usually in some way mutate the state of the database through interacting with this endpoint.
Therefore, we invalidate the cache, so the user gets the latest state from the database on the next GET request.
> Ignore the browser errors when clicking PATCH buttons, JSON files (our mock database) don't accept PATCH requests.
```js preview-story
export const nonGETRequest = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = (name, method) => {
ajax.requestJson(`./packages/ajax/docs/${name}.json`, { method })
.then(result => {
actionLogger.log(`From cache: ${result.response.fromCache || false}`);
actionLogger.log(JSON.stringify(result.body, null, 2));
});
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${() => fetchHandler('pabu', 'GET')}>GET Pabu</button>
<button @click=${() => fetchHandler('pabu', 'PATCH')}>PATCH Pabu</button>
<button @click=${() => fetchHandler('naga', 'GET')}>GET Naga</button>
<button @click=${() => fetchHandler('naga', 'PATCH')}>PATCH Naga</button>
${actionLogger}
`;
}
```
## Considerations
#### Invalidate Rules
## Fetch Polyfill
There are two kinds of invalidate rules:
For IE11 you will need a polyfill for fetch. You should add this on your top level layer, e.g. your application.
- `invalidateUrls` (array of URL like strings)
- `invalidateUrlsRegex` (RegExp)
[This is the polyfill we recommend](https://github.com/github/fetch). It also has a [section for polyfilling AbortController](https://github.com/github/fetch#aborting-requests)
If a non-GET method is fired, by default it only invalidates its own endpoint.
Invalidating `/api/users` cache by doing a PATCH, will not invalidate `/api/accounts` cache.
However, in the case of users and accounts, they may be very interconnected, so perhaps you do want to invalidate `/api/accounts` when invalidating `/api/users`.
This is what the invalidate rules are for.
In this demo, invalidating the `pabu` endpoint will invalidate `naga`, but not the other way around.
> For invalidateUrls you need the full URL e.g. `<protocol>://<domain>:<port>/<url>` so it's often easier to use invalidateUrlsRegex
```js preview-story
export const invalidateRules = () => {
const actionLogger = renderLitAsNode(html`<sb-action-logger></sb-action-logger>`);
const fetchHandler = (name, method) => {
const actionCacheOptions = {};
if (name === 'pabu') {
actionCacheOptions.invalidateUrlsRegex = /\/packages\/ajax\/docs\/naga.json/;
}
ajax.requestJson(`./packages/ajax/docs/${name}.json`, {
method,
cacheOptions: actionCacheOptions,
})
.then(result => {
actionLogger.log(`From cache: ${result.response.fromCache || false}`);
actionLogger.log(JSON.stringify(result.body, null, 2));
});
}
return html`
<style>
sb-action-logger {
--sb-action-logger-max-height: 300px;
}
</style>
<button @click=${() => fetchHandler('pabu', 'GET')}>GET Pabu</button>
<button @click=${() => fetchHandler('pabu', 'PATCH')}>PATCH Pabu</button>
<button @click=${() => fetchHandler('naga', 'GET')}>GET Naga</button>
<button @click=${() => fetchHandler('naga', 'PATCH')}>PATCH Naga</button>
${actionLogger}
`;
}
```

@@ -11,8 +11,4 @@ /**

constructor(config?: Partial<AjaxClientConfig>);
__config: {
addAcceptLanguage: boolean;
xsrfCookieName: string | null;
xsrfHeaderName: string | null;
jsonPrefix: string;
};
/** @type {Partial<AjaxClientConfig>} */
__config: Partial<AjaxClientConfig>;
/** @type {Array.<RequestInterceptor|CachedRequestInterceptor>} */

@@ -24,6 +20,6 @@ _requestInterceptors: Array<RequestInterceptor | CachedRequestInterceptor>;

* Sets the config for the instance
* @param {AjaxClientConfig} config configuration for the AjaxClass instance
* @param {Partial<AjaxClientConfig>} config configuration for the AjaxClass instance
*/
set options(arg: import("../types/types.js").AjaxClientConfig);
get options(): import("../types/types.js").AjaxClientConfig;
set options(arg: Partial<import("../types/types.js").AjaxClientConfig>);
get options(): Partial<import("../types/types.js").AjaxClientConfig>;
/** @param {RequestInterceptor} requestInterceptor */

@@ -30,0 +26,0 @@ addRequestInterceptor(requestInterceptor: RequestInterceptor): void;

/* eslint-disable consistent-return */
import {
cacheRequestInterceptorFactory,
cacheResponseInterceptorFactory,
} from './interceptors-cache.js';
import { acceptLanguageRequestInterceptor, createXSRFRequestInterceptor } from './interceptors.js';

@@ -17,2 +21,3 @@ import { AjaxClientFetchError } from './AjaxClientFetchError.js';

constructor(config = {}) {
/** @type {Partial<AjaxClientConfig>} */
this.__config = {

@@ -23,2 +28,6 @@ addAcceptLanguage: true,

jsonPrefix: '',
cacheOptions: {
getCacheIdentifier: () => '_default',
...config.cacheOptions,
},
...config,

@@ -41,2 +50,17 @@ };

}
if (this.__config.cacheOptions && this.__config.cacheOptions.useCache) {
this.addRequestInterceptor(
cacheRequestInterceptorFactory(
this.__config.cacheOptions.getCacheIdentifier,
this.__config.cacheOptions,
),
);
this.addResponseInterceptor(
cacheResponseInterceptorFactory(
this.__config.cacheOptions.getCacheIdentifier,
this.__config.cacheOptions,
),
);
}
}

@@ -46,3 +70,3 @@

* Sets the config for the instance
* @param {AjaxClientConfig} config configuration for the AjaxClass instance
* @param {Partial<AjaxClientConfig>} config configuration for the AjaxClass instance
*/

@@ -49,0 +73,0 @@ set options(config) {

@@ -207,18 +207,12 @@ /* eslint-disable consistent-return */

function composeCacheOptions(validatedInitialCacheOptions, configCacheOptions) {
/** @type {any} */
let actionCacheOptions = {};
let actionCacheOptions = validatedInitialCacheOptions;
actionCacheOptions =
configCacheOptions &&
validateOptions({
if (configCacheOptions) {
actionCacheOptions = validateOptions({
...validatedInitialCacheOptions,
...configCacheOptions,
});
}
const cacheOptions = {
...validatedInitialCacheOptions,
...actionCacheOptions,
};
return cacheOptions;
return actionCacheOptions;
}

@@ -253,3 +247,2 @@

const currentCache = getCache(getCacheIdentifier());
const cacheResponse = currentCache.get(cacheId, cacheOptions.timeToLive);

@@ -282,3 +275,2 @@

}
cacheRequest.cacheOptions.fromCache = true;

@@ -289,6 +281,8 @@ const init = /** @type {LionRequestInit} */ ({

headers,
request: cacheRequest,
});
return /** @type {CacheResponse} */ (new Response(cacheResponse, init));
const response = /** @type {CacheResponse} */ (new Response(cacheResponse, init));
response.request = cacheRequest;
response.fromCache = true;
return response;
}

@@ -322,3 +316,3 @@

const isAlreadyFromCache = !!cacheOptions.fromCache;
const isAlreadyFromCache = !!cacheResponse.fromCache;
// caching all responses with not default `timeToLive`

@@ -342,4 +336,5 @@ const isCacheActive = cacheOptions.timeToLive > 0;

const responseBody = await cacheResponse.clone().text();
// store the response data in the cache
getCache(getCacheIdentifier()).set(cacheId, cacheResponse.body);
getCache(getCacheIdentifier()).set(cacheId, responseBody);
} else {

@@ -346,0 +341,0 @@ // don't store in cache if the request method is not part of the configs methods

import { expect } from '@open-wc/testing';
import { stub } from 'sinon';
import { stub, useFakeTimers } from 'sinon';
import { AjaxClient, AjaxClientFetchError } from '@lion/ajax';

@@ -106,5 +106,3 @@

it('addRequestInterceptor() adds a function which intercepts the request', async () => {
ajax.addRequestInterceptor(async r => {
return new Request(`${r.url}/intercepted-1`);
});
ajax.addRequestInterceptor(async r => new Request(`${r.url}/intercepted-1`));
ajax.addRequestInterceptor(async r => new Request(`${r.url}/intercepted-2`));

@@ -214,2 +212,57 @@

describe('Caching', () => {
/** @type {number | undefined} */
let cacheId;
/** @type {() => string} */
let getCacheIdentifier;
const newCacheId = () => {
if (!cacheId) {
cacheId = 1;
} else {
cacheId += 1;
}
return cacheId;
};
beforeEach(() => {
getCacheIdentifier = () => String(cacheId);
});
it('allows configuring cache interceptors on the AjaxClient config', async () => {
newCacheId();
const customAjax = new AjaxClient({
cacheOptions: {
useCache: true,
timeToLive: 100,
getCacheIdentifier,
},
});
const clock = useFakeTimers({
shouldAdvanceTime: true,
});
// Smoke test 1: verify caching works
await customAjax.request('/foo');
expect(fetchStub.callCount).to.equal(1);
await customAjax.request('/foo');
expect(fetchStub.callCount).to.equal(1);
// Smoke test 2: verify caching is invalidated on non-get method
await customAjax.request('/foo', { method: 'POST' });
expect(fetchStub.callCount).to.equal(2);
await customAjax.request('/foo');
expect(fetchStub.callCount).to.equal(3);
// Smoke test 3: verify caching is invalidated after TTL has passed
await customAjax.request('/foo');
expect(fetchStub.callCount).to.equal(3);
clock.tick(101);
await customAjax.request('/foo');
expect(fetchStub.callCount).to.equal(4);
clock.restore();
});
});
describe('Abort', () => {

@@ -216,0 +269,0 @@ it('support aborting requests with AbortController', async () => {

@@ -7,3 +7,3 @@ import { expect } from '@open-wc/testing';

describe('ajax cache', function describeLibCache() {
describe('ajax cache', () => {
/** @type {number | undefined} */

@@ -10,0 +10,0 @@ let cacheId;

@@ -17,2 +17,3 @@ /**

xsrfHeaderName: string | null;
cacheOptions: CacheOptionsWithIdentifier;
jsonPrefix: string;

@@ -42,13 +43,13 @@ }

requestIdentificationFn?: RequestIdentificationFn;
fromCache?: boolean;
}
export interface ValidatedCacheOptions {
export interface CacheOptionsWithIdentifier extends CacheOptions {
getCacheIdentifier: () => string;
}
export interface ValidatedCacheOptions extends CacheOptions {
useCache: boolean;
methods: string[];
timeToLive: number;
invalidateUrls?: string[];
invalidateUrlsRegex?: RegExp;
requestIdentificationFn: RequestIdentificationFn;
fromCache?: boolean;
}

@@ -72,2 +73,3 @@

data: object | string;
fromCache?: boolean;
}

@@ -74,0 +76,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc