├── .nvmrc
├── .eslintignore
├── .github
├── CODEOWNERS
└── workflows
│ ├── build.yml
│ └── release.yml
├── .eslintrc.json
├── .gitignore
├── src
├── main.ts
├── utils.ts
├── __tests__
│ ├── utils.test.ts
│ └── index.test.ts
├── Cache.ts
└── index.ts
├── .npmignore
├── .editorconfig
├── demo
├── package.json
├── index.html
├── index.esm.js
├── index.js
└── package-lock.json
├── rollup.config.js
├── Makefile
├── tsconfig.json
├── LICENSE
├── .scripts
└── publish.sh
├── README.md
└── package.json
/.nvmrc:
--------------------------------------------------------------------------------
1 | 16.13.0
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build
2 | dist
3 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @raulanatol
2 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "@cowcoders/eslint-config/ts"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .dts/
4 | stats.html
5 | devs.sh
6 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | export * from './index';
2 | export * from './Cache';
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | src/
2 | test/
3 | config/
4 | .scripts/
5 | .idea/
6 | tsconfig.json
7 | .editorconfig
8 | .dts
9 | .eslintignore
10 | .eslintrc.json
11 | .nvmrc
12 | Makefile
13 | demo
14 | .github
15 | stats.html
16 | vite.config.js
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | end_of_line = lf
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on:
3 | push:
4 | jobs:
5 | build:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v2
9 | - name: Init project
10 | run: make init
11 | - name: Build and tests
12 | run: make
13 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | tags:
5 | - v[0-9]+.[0-9]+.*
6 | jobs:
7 | deploy:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/checkout@v2
11 | - name: Github Actions Releaser
12 | id: actions_releaser
13 | uses: raulanatol/github-actions-releaser@v2.1.0
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 |
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index.js",
8 | "start-browser": "webserver.local"
9 | },
10 | "type": "module",
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC",
14 | "dependencies": {
15 | "axios": "0.24.0",
16 | "axios-etag-cache": "1.2.1"
17 | },
18 | "devDependencies": {
19 | "vite": "2.9.16"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'rollup';
2 | import typescript from 'rollup-plugin-typescript2';
3 | import autoExternal from 'rollup-plugin-auto-external';
4 |
5 | export default defineConfig({
6 | input: 'src/main.ts',
7 | output: [
8 | {
9 | format: 'cjs',
10 | file: 'dist/index.js',
11 | sourcemap: true
12 | },
13 | {
14 | format: 'esm',
15 | file: 'dist/index.esm.js',
16 | sourcemap: true
17 | }
18 | ],
19 | plugins: [typescript(), autoExternal()]
20 | });
21 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | axios etag cache
6 |
7 |
8 |
9 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .DEFAULT_GOAL := build
2 |
3 | init:
4 | @echo "🏃♀️ Starting project..."
5 | @npm install
6 | @cd demo && npm install
7 |
8 | clean:
9 | @echo "🛁 Cleaning..."
10 | @npm run clean
11 |
12 | clean_all:
13 | @echo "🧨 Clean all"
14 | @rm -Rf node_modules package-lock.json
15 |
16 | test:
17 | @echo "Testing..."
18 | @npm run test
19 |
20 | .PHONY: demo
21 | demo:
22 | @cd demo && npm run start
23 |
24 | demo-browser:
25 | @cd demo && npm run start-browser
26 |
27 | build: clean test
28 | @echo "👩🏭 Building..."
29 | @npm run build
30 | @echo "✅"
31 |
32 | publish: build
33 | @echo "📦 Publish package..."
34 | @./.scripts/publish.sh
35 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "module": "es2015",
5 | "moduleResolution": "node",
6 | "declaration": true,
7 | "strict": true,
8 | "noImplicitAny": false,
9 | "strictNullChecks": true,
10 | "strictFunctionTypes": true,
11 | "noUnusedLocals": true,
12 | "noUnusedParameters": true,
13 | "noImplicitReturns": true,
14 | "noFallthroughCasesInSwitch": true,
15 | "importHelpers": true,
16 | "skipLibCheck": true,
17 | "esModuleInterop": true,
18 | "allowSyntheticDefaultImports": true,
19 | "experimentalDecorators": true,
20 | "sourceMap": true,
21 | "forceConsistentCasingInFileNames": true,
22 | "outDir": "./dist",
23 | "types": [
24 | "node",
25 | "jest"
26 | ],
27 | "lib": [
28 | "ES6",
29 | "DOM"
30 | ]
31 | },
32 | "include": [
33 | "src/**/*.ts"
34 | ],
35 | "exclude": [
36 | "node_modules",
37 | "**/*.test.ts"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { ConstructableCache } from './Cache';
2 |
3 |
4 | export interface axiosETAGCacheOptions {
5 | cacheClass?: ConstructableCache
6 | enablePost?: boolean
7 | }
8 |
9 | const byLowerCase = toFind => value => toLowerCase(value) === toFind;
10 | const toLowerCase = value => value.toLowerCase();
11 | const getKeys = headers => Object.keys(headers);
12 |
13 | export const getHeaderCaseInsensitive = (headerName, headers = {}) => {
14 | const key = getKeys(headers).find(byLowerCase(headerName));
15 | return key ? headers[key] : undefined;
16 | };
17 |
18 | export const cyrb53 = (str, seed = 0) => {
19 | let h1 = 0xdeadbeef ^ seed,
20 | h2 = 0x41c6ce57 ^ seed;
21 | for (let i = 0, ch; i < str.length; i++) {
22 | ch = str.charCodeAt(i);
23 | h1 = Math.imul(h1 ^ ch, 2654435761);
24 | h2 = Math.imul(h2 ^ ch, 1597334677);
25 | }
26 |
27 | h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
28 | h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
29 |
30 | return 4294967296 * (2097151 & h2) + (h1 >>> 0);
31 | };
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Raúl Anatol
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/__tests__/utils.test.ts:
--------------------------------------------------------------------------------
1 | import { getHeaderCaseInsensitive } from '../utils';
2 |
3 | describe('utils', () => {
4 | describe('getHeaderCaseInsensitive', () => {
5 | test('should return the header with no case sensitive', () => {
6 | const headers = { etag: 10 };
7 | expect(getHeaderCaseInsensitive('etag', headers)).toBe(10);
8 | });
9 |
10 | test('should return the header with no case sensitive', () => {
11 | const headers = { eTag: 10 };
12 | expect(getHeaderCaseInsensitive('etag', headers)).toBe(10);
13 | });
14 |
15 | test('should return the header with no case sensitive', () => {
16 | const headers = { ETAG: 10 };
17 | expect(getHeaderCaseInsensitive('etag', headers)).toBe(10);
18 | });
19 |
20 | test('should return undefined is not header found', () => {
21 | const headers = { ETAG: 10 };
22 | expect(getHeaderCaseInsensitive('etga', headers)).toBeUndefined();
23 | });
24 |
25 | test('should return undefined is not headers', () => {
26 | const headers = undefined;
27 | expect(getHeaderCaseInsensitive('etga', headers)).toBeUndefined();
28 | });
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/.scripts/publish.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | error() {
6 | echo "🚨 $1"
7 | exit 1
8 | }
9 |
10 | is_valid_version() {
11 | case $1 in
12 | patch) ;;
13 | minor) ;;
14 | major) ;;
15 | *) error "🚨 Invalid version >> $1" ;;
16 | esac
17 | }
18 |
19 | assert_ready_to_publish() {
20 | is_valid_version "$1"
21 | if [ ! -d dist ]; then
22 | error "Need build first"
23 | fi
24 | }
25 |
26 | publish() {
27 | echo "Publish $1"
28 | npm version "$1"
29 | npm publish --access public
30 | }
31 |
32 | NEW_VERSION=$1
33 |
34 | if [ -z "$NEW_VERSION" ]; then
35 | while true; do
36 | echo "Specify an version increase (patch minor major) "
37 | read -r answer
38 | case $answer in
39 | patch)
40 | NEW_VERSION="patch"
41 | break
42 | ;;
43 | minor)
44 | NEW_VERSION="minor"
45 | break
46 | ;;
47 | major)
48 | NEW_VERSION="major"
49 | break
50 | ;;
51 | *)
52 | echo "Only patch minor or major, please."
53 | ;;
54 | esac
55 | done
56 | fi
57 |
58 | assert_ready_to_publish $NEW_VERSION
59 | publish $NEW_VERSION
60 | git push --all
61 | git push --tags
62 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # axios-etag-cache
2 |
3 | Axios etag interceptor to enable If-None-Match request with ETAG support
4 |
5 | ## Basic use:
6 |
7 | ```js
8 | const axios = require('axios');
9 | const { axiosETAGCache } = require('axios-etag-cache');
10 |
11 | // Apply the axios ETAG interceptor
12 | const axiosWithETAGCache = axiosETAGCache(axios);
13 |
14 | axiosWithETAGCache
15 | .get('http://example.com')
16 | .then(console.log)
17 | .catch(console.error);
18 | ```
19 |
20 | ## Allow POST requests
21 |
22 | ```js
23 | const axiosWithETAGCache = axiosETAGCache(axios, {enablePost: true});
24 |
25 | ```
26 |
27 |
28 |
29 | ## External Storage
30 | Example implementation using RXDB
31 | ```js
32 | import { axiosETAGCache, BaseCache, CacheValue } from "axios-etag-cache";
33 |
34 | const cacheSchema = {
35 | title: "response cache schema",
36 | version: 4,
37 | primaryKey: {
38 | key: "url",
39 | fields: ["url"],
40 | separator: "|",
41 | },
42 | type: "object",
43 | properties: {
44 | url: {
45 | type: "string", maxLength: 100,
46 | },
47 | etag: {type: "string"},
48 | lastHitAt: {type: "number"},
49 | createdAt: {type: "number"},
50 | value: {type: "object"}
51 | },
52 | required: ["url", "etag", "value"],
53 | indexes: ["url"]
54 | }
55 |
56 | class RxDBStorageCache extends BaseCache {
57 | flushAll(): any {
58 | }
59 |
60 | async get(key: string): Promise {
61 | const cache = (
62 | await db["responseCache"].findOne({
63 | selector: {url: key},
64 | }).exec())?.toJSON();
65 | if (cache) {
66 | const payload: CacheValue = {
67 | value: cache.value,
68 | etag: cache.etag,
69 | createdAt: cache.createdAt,
70 | lastHitAt: cache.lastHitAt
71 | } as CacheValue;
72 | return payload;
73 | }
74 | return undefined;
75 | }
76 |
77 | set(key: string, value: CacheValue): any {
78 | db["responseCache"].upsert({
79 | url: key,
80 | value: value.value,
81 | etag: value.etag,
82 | createdAt: value.createdAt,
83 | lastHitAt: value.lastHitAt,
84 | });
85 | }
86 | }
87 |
88 | const axiosWithETAGCache = axiosETAGCache(axios, {cacheClass: RxDBStorageCache});
89 |
90 | ```
91 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "axios-etag-cache",
3 | "version": "1.4.0",
4 | "description": "Axios etag interceptor to enable If-None-Match request with ETAG support",
5 | "keywords": [
6 | "axios",
7 | "cache",
8 | "etag"
9 | ],
10 | "homepage": "https://github.com/raulanatol/axios-etag-cache#readme",
11 | "bugs": {
12 | "url": "https://github.com/raulanatol/axios-etag-cache/issues"
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "git+https://github.com/raulanatol/axios-etag-cache.git"
17 | },
18 | "license": "ISC",
19 | "author": {
20 | "name": "raulanatol",
21 | "email": "raul@natol.es"
22 | },
23 | "main": "dist/index.js",
24 | "module": "dist/index.esm.js",
25 | "types": "dist/main.d.ts",
26 | "directories": {
27 | "test": "test"
28 | },
29 | "scripts": {
30 | "build": "rollup -c",
31 | "clean": "rm -rf dist build",
32 | "lint": "eslint --max-warnings 0 . --ext .ts",
33 | "test": "jest",
34 | "prepare": "npm run build"
35 | },
36 | "jest": {
37 | "moduleFileExtensions": [
38 | "ts",
39 | "tsx",
40 | "js",
41 | "json"
42 | ],
43 | "moduleNameMapper": {
44 | "\\.(css|jpg|png|svg)$": "/node_modules/jest-css-modules"
45 | },
46 | "preset": "ts-jest",
47 | "testEnvironment": "node",
48 | "testPathIgnorePatterns": [
49 | "/node_modules/"
50 | ],
51 | "testRegex": "(/test/.*|\\.(test|spec))\\.(ts|tsx|js)$"
52 | },
53 | "devDependencies": {
54 | "@cowcoders/eslint-config": "1.5.5",
55 | "@rollup/plugin-babel": "5.3.1",
56 | "@rollup/plugin-node-resolve": "13.3.0",
57 | "@types/axios": "0.14.0",
58 | "@types/jest": "28.1.2",
59 | "@types/node": "18.0.0",
60 | "@typescript-eslint/eslint-plugin": "5.28.0",
61 | "@typescript-eslint/parser": "5.28.0",
62 | "axios": "0.27.2",
63 | "eslint": "8.18.0",
64 | "jest": "28.1.1",
65 | "nock": "13.2.7",
66 | "rollup": "2.75.7",
67 | "rollup-plugin-auto-external": "2.0.0",
68 | "rollup-plugin-typescript2": "0.32.1",
69 | "rollup-plugin-visualizer": "5.6.0",
70 | "shx": "0.3.4",
71 | "ts-jest": "28.0.5",
72 | "ts-loader": "9.3.0",
73 | "ts-node": "10.8.1",
74 | "typescript": "4.7.4",
75 | "webserver.local": "1.0.6"
76 | },
77 | "peerDependencies": {
78 | "axios": ">=0.24.0"
79 | },
80 | "pre-push": [
81 | "lint",
82 | "test",
83 | "build"
84 | ]
85 | }
86 |
--------------------------------------------------------------------------------
/src/Cache.ts:
--------------------------------------------------------------------------------
1 | export interface ConstructableCache {
2 | new(...args: any): T;
3 | }
4 |
5 | export abstract class BaseCache {
6 | abstract get(key: string): Promise
7 |
8 | abstract set(key: string, value: CacheValue)
9 |
10 | abstract flushAll()
11 |
12 | }
13 |
14 | export class DefaultCache extends BaseCache {
15 | private cache = {};
16 |
17 | get(key: string): Promise {
18 | return Promise.resolve(this.cache[key]);
19 | }
20 |
21 | set(key: string, value: CacheValue) {
22 | this.cache[key] = value;
23 | }
24 |
25 | flushAll() {
26 | this.cache = {};
27 | }
28 |
29 |
30 | }
31 |
32 | export class LocalStorageCache extends BaseCache {
33 | flushAll() {
34 | for (let i = 0; i < localStorage.length; i++){
35 | const key = localStorage.key(i);
36 | if (key !== null && key.startsWith('aec-')) {
37 | localStorage.removeItem(key);
38 | }
39 | }
40 | }
41 |
42 | get(key: string): Promise {
43 | return new Promise((resolve) => {
44 | try {
45 | const payload: string | null = localStorage.getItem('aec-' + key);
46 | if (payload !== null) {
47 | resolve(JSON.parse(payload));
48 | } else {
49 | resolve(undefined);
50 | }
51 | } catch (e) {
52 | resolve(undefined);
53 | }
54 | });
55 | }
56 |
57 | set(key: string, value: CacheValue) {
58 | try {
59 | const payload = JSON.stringify(value);
60 | localStorage.setItem('aec-' + key, payload);
61 | } catch (e) {
62 | console.log(e);
63 | }
64 | }
65 |
66 | }
67 |
68 | export interface CacheValue {
69 | etag: string;
70 | value: any;
71 | createdAt: number
72 | lastHitAt: number
73 | }
74 |
75 | let instance: ReturnType;
76 | let cache: BaseCache;
77 |
78 | const makeSingleton = (cacheClass: ConstructableCache) => {
79 | /** Closure of the singleton's value to keep it private */
80 | cache = new cacheClass();
81 | /** Only the accessors are returned */
82 | return {
83 | async get(uuid: string): Promise {
84 | let payload: CacheValue | undefined = await cache.get(uuid);
85 | if (payload) {
86 | payload = { ...payload, lastHitAt: Date.now() };
87 | cache.set(uuid, payload);
88 | }
89 | return payload;
90 | },
91 | set(uuid: string, etag: string, value: any) {
92 | return cache.set(uuid, { etag, value, createdAt: Date.now(), lastHitAt: 0 });
93 | },
94 | reset() {
95 | cache.flushAll();
96 | }
97 | };
98 | };
99 |
100 | export const getCacheInstance = (cacheClass: ConstructableCache) => {
101 | if (!instance) {
102 | instance = makeSingleton(cacheClass);
103 | return instance;
104 | }
105 | return instance;
106 | };
107 |
--------------------------------------------------------------------------------
/demo/index.esm.js:
--------------------------------------------------------------------------------
1 | class BaseCache {
2 | constructor() {
3 | this.cache = {};
4 | }
5 | get(key) {
6 | return this.cache[key];
7 | }
8 | set(key, value) {
9 | this.cache[key] = value;
10 | }
11 | flushAll() {
12 | this.cache = {};
13 | }
14 | }
15 | class DefaultCache extends BaseCache {
16 | }
17 | class Cache {
18 | constructor(cache) {
19 | this.cache = cache;
20 | }
21 | static getInstance() {
22 | if (!this.instance) {
23 | this.instance = new Cache(new DefaultCache());
24 | }
25 | return this.instance;
26 | }
27 | static get(uuid) {
28 | return this.getInstance().cache.get(uuid);
29 | }
30 | static set(uuid, etag, value) {
31 | return this.getInstance().cache.set(uuid, { etag, value });
32 | }
33 | static reset() {
34 | this.getInstance().cache.flushAll();
35 | }
36 | }
37 |
38 | const byLowerCase = toFind => value => toLowerCase(value) === toFind;
39 | const toLowerCase = value => value.toLowerCase();
40 | const getKeys = headers => Object.keys(headers);
41 | const getHeaderCaseInsensitive = (headerName, headers = {}) => {
42 | const key = getKeys(headers).find(byLowerCase(headerName));
43 | return key ? headers[key] : undefined;
44 | };
45 |
46 | function isCacheableMethod(config) {
47 | if (!config.method) {
48 | return false;
49 | }
50 | return ~['GET', 'HEAD'].indexOf(config.method.toUpperCase());
51 | }
52 | function getUrlByAxiosConfig(config) {
53 | return config.url;
54 | }
55 | const getCacheByAxiosConfig = (config) => {
56 | const url = getUrlByAxiosConfig(config);
57 | if (url) {
58 | return Cache.get(url);
59 | }
60 | return undefined;
61 | };
62 | function requestInterceptor(config) {
63 | if (isCacheableMethod(config)) {
64 | const url = getUrlByAxiosConfig(config);
65 | if (!url) {
66 | return undefined;
67 | }
68 | const lastCachedResult = Cache.get(url);
69 | if (lastCachedResult) {
70 | config.headers = Object.assign(Object.assign({}, config.headers), { 'If-None-Match': lastCachedResult.etag });
71 | }
72 | }
73 | return config;
74 | }
75 | function responseInterceptor(response) {
76 | if (isCacheableMethod(response.config)) {
77 | const responseETAG = getHeaderCaseInsensitive('etag', response.headers);
78 | if (responseETAG) {
79 | const url = getUrlByAxiosConfig(response.config);
80 | if (!url) {
81 | return null;
82 | }
83 | Cache.set(url, responseETAG, response.data);
84 | }
85 | }
86 | return response;
87 | }
88 | function responseErrorInterceptor(error) {
89 | if (error.response && error.response.status === 304) {
90 | const getCachedResult = getCacheByAxiosConfig(error.response.config);
91 | if (!getCachedResult) {
92 | return Promise.reject(error);
93 | }
94 | const newResponse = error.response;
95 | newResponse.status = 200;
96 | newResponse.data = getCachedResult.value;
97 | return Promise.resolve(newResponse);
98 | }
99 | return Promise.reject(error);
100 | }
101 | function resetCache() {
102 | Cache.reset();
103 | }
104 | function axiosETAGCache(axiosInstance) {
105 | axiosInstance.interceptors.request.use(requestInterceptor);
106 | axiosInstance.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
107 | return axiosInstance;
108 | }
109 |
110 | export { BaseCache, Cache, DefaultCache, axiosETAGCache, getCacheByAxiosConfig, resetCache };
111 | //# sourceMappingURL=index.esm.js.map
112 |
--------------------------------------------------------------------------------
/demo/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', { value: true });
4 |
5 | class BaseCache {
6 | constructor() {
7 | this.cache = {};
8 | }
9 | get(key) {
10 | return this.cache[key];
11 | }
12 | set(key, value) {
13 | this.cache[key] = value;
14 | }
15 | flushAll() {
16 | this.cache = {};
17 | }
18 | }
19 | class DefaultCache extends BaseCache {
20 | }
21 | class Cache {
22 | constructor(cache) {
23 | this.cache = cache;
24 | }
25 | static getInstance() {
26 | if (!this.instance) {
27 | this.instance = new Cache(new DefaultCache());
28 | }
29 | return this.instance;
30 | }
31 | static get(uuid) {
32 | return this.getInstance().cache.get(uuid);
33 | }
34 | static set(uuid, etag, value) {
35 | return this.getInstance().cache.set(uuid, { etag, value });
36 | }
37 | static reset() {
38 | this.getInstance().cache.flushAll();
39 | }
40 | }
41 |
42 | const byLowerCase = toFind => value => toLowerCase(value) === toFind;
43 | const toLowerCase = value => value.toLowerCase();
44 | const getKeys = headers => Object.keys(headers);
45 | const getHeaderCaseInsensitive = (headerName, headers = {}) => {
46 | const key = getKeys(headers).find(byLowerCase(headerName));
47 | return key ? headers[key] : undefined;
48 | };
49 |
50 | function isCacheableMethod(config) {
51 | if (!config.method) {
52 | return false;
53 | }
54 | return ~['GET', 'HEAD'].indexOf(config.method.toUpperCase());
55 | }
56 | function getUrlByAxiosConfig(config) {
57 | return config.url;
58 | }
59 | const getCacheByAxiosConfig = (config) => {
60 | const url = getUrlByAxiosConfig(config);
61 | if (url) {
62 | return Cache.get(url);
63 | }
64 | return undefined;
65 | };
66 | function requestInterceptor(config) {
67 | if (isCacheableMethod(config)) {
68 | const url = getUrlByAxiosConfig(config);
69 | if (!url) {
70 | return undefined;
71 | }
72 | const lastCachedResult = Cache.get(url);
73 | if (lastCachedResult) {
74 | config.headers = Object.assign(Object.assign({}, config.headers), { 'If-None-Match': lastCachedResult.etag });
75 | }
76 | }
77 | return config;
78 | }
79 | function responseInterceptor(response) {
80 | if (isCacheableMethod(response.config)) {
81 | const responseETAG = getHeaderCaseInsensitive('etag', response.headers);
82 | if (responseETAG) {
83 | const url = getUrlByAxiosConfig(response.config);
84 | if (!url) {
85 | return null;
86 | }
87 | Cache.set(url, responseETAG, response.data);
88 | }
89 | }
90 | return response;
91 | }
92 | function responseErrorInterceptor(error) {
93 | if (error.response && error.response.status === 304) {
94 | const getCachedResult = getCacheByAxiosConfig(error.response.config);
95 | if (!getCachedResult) {
96 | return Promise.reject(error);
97 | }
98 | const newResponse = error.response;
99 | newResponse.status = 200;
100 | newResponse.data = getCachedResult.value;
101 | return Promise.resolve(newResponse);
102 | }
103 | return Promise.reject(error);
104 | }
105 | function resetCache() {
106 | Cache.reset();
107 | }
108 | function axiosETAGCache(axiosInstance) {
109 | axiosInstance.interceptors.request.use(requestInterceptor);
110 | axiosInstance.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
111 | return axiosInstance;
112 | }
113 |
114 | exports.BaseCache = BaseCache;
115 | exports.Cache = Cache;
116 | exports.DefaultCache = DefaultCache;
117 | exports.axiosETAGCache = axiosETAGCache;
118 | exports.getCacheByAxiosConfig = getCacheByAxiosConfig;
119 | exports.resetCache = resetCache;
120 | //# sourceMappingURL=index.js.map
121 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import {axiosETAGCacheOptions, cyrb53, getHeaderCaseInsensitive} from './utils';
2 | import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
3 | import { DefaultCache, getCacheInstance } from './Cache';
4 |
5 | let Cache;
6 | let cacheableMethods = ['GET', 'HEAD'];
7 |
8 | function isCacheableMethod(config: AxiosRequestConfig) {
9 | if (!config.method) {
10 | return false;
11 | }
12 | return ~cacheableMethods.indexOf(config.method.toUpperCase());
13 | }
14 |
15 | function getUrlByAxiosConfig(config: AxiosRequestConfig) {
16 | return config.url;
17 | }
18 |
19 | export const getCacheByAxiosConfig = async (config: AxiosRequestConfig) => {
20 | const url = getUrlByAxiosConfig(config);
21 | if (url) {
22 | if (config.data) {
23 | const hash = cyrb53(config.data);
24 | return await Cache.get(hash + url);
25 | } else {
26 | return await Cache.get(url);
27 | }
28 | }
29 | return undefined;
30 | };
31 |
32 | function requestInterceptor(config: AxiosRequestConfig): Promise {
33 | return new Promise(async (resolve, reject) => {
34 | if (isCacheableMethod(config)) {
35 | const url = getUrlByAxiosConfig(config);
36 | if (!url) {
37 | reject(config);
38 | return;
39 | }
40 | let lastCachedResult;
41 | if (config.data) {
42 | try {
43 | const hash = cyrb53(JSON.stringify(config.data));
44 | lastCachedResult = await Cache.get(hash + url);
45 | } catch (e) {
46 | console.error(e);
47 | }
48 | } else {
49 | lastCachedResult = await Cache.get(url);
50 | }
51 |
52 | if (lastCachedResult) {
53 | config.headers = { ...config.headers, 'If-None-Match': lastCachedResult.etag };
54 | }
55 | }
56 | resolve(config);
57 | });
58 | }
59 |
60 | function responseInterceptor(response: AxiosResponse): Promise {
61 | return new Promise((resolve, reject) => {
62 | if (isCacheableMethod(response.config)) {
63 | const responseETAG = getHeaderCaseInsensitive('etag', response.headers);
64 | if (responseETAG) {
65 | const url = getUrlByAxiosConfig(response.config);
66 | if (!url) {
67 | reject(null);
68 | return;
69 | }
70 | if (response.config.data) {
71 | try {
72 | const hash = cyrb53(response.config.data);
73 | Cache.set(hash + url, responseETAG, response.data);
74 | } catch (e) {
75 | console.error(e);
76 | }
77 | } else {
78 | Cache.set(url, responseETAG, response.data);
79 | }
80 |
81 | }
82 | }
83 | resolve(response);
84 | });
85 | }
86 |
87 | function responseErrorInterceptor(error: AxiosError): Promise {
88 | return new Promise(async (resolve, reject) => {
89 | if (error.response && error.response.status === 304) {
90 | const getCachedResult = await getCacheByAxiosConfig(error.response.config);
91 | if (!getCachedResult) {
92 | reject(error);
93 | return;
94 | }
95 | const newResponse: AxiosResponse = error.response;
96 | newResponse.status = 200;
97 | newResponse.data = getCachedResult.value;
98 | resolve(newResponse);
99 | return;
100 | }
101 | reject(error);
102 | });
103 | }
104 |
105 | export async function resetCache() {
106 | await Cache.reset();
107 | }
108 |
109 | export function axiosETAGCache(axiosInstance: AxiosInstance, options?: axiosETAGCacheOptions): AxiosInstance {
110 | if (options?.cacheClass) {
111 | Cache = getCacheInstance(options.cacheClass);
112 | } else {
113 | Cache = getCacheInstance(DefaultCache);
114 | }
115 |
116 | if (options?.enablePost === true) {
117 | cacheableMethods.push('POST');
118 | }
119 |
120 | axiosInstance.interceptors.request.use(requestInterceptor);
121 | axiosInstance.interceptors.response.use(responseInterceptor, responseErrorInterceptor);
122 |
123 | return axiosInstance;
124 | }
125 |
--------------------------------------------------------------------------------
/src/__tests__/index.test.ts:
--------------------------------------------------------------------------------
1 | import { axiosETAGCache, getCacheByAxiosConfig, resetCache } from '../index';
2 | import nock from 'nock';
3 | import axios from 'axios';
4 | import { DefaultCache, getCacheInstance } from '../Cache';
5 |
6 | const Cache = getCacheInstance(DefaultCache);
7 |
8 |
9 | const USERS = [{ uuid: '123', name: 'John' }];
10 | const TEST_ETAG_0 = '123ABC';
11 | const TEST_ETAG_1 = '#123ABC';
12 | const BASE_PATH = 'http://api.example.com';
13 |
14 | function replyIfNoneMatchWithEtag(request, etag, results) {
15 | if (request.headers?.['if-none-match'] === etag) {
16 | return [200, results];
17 | }
18 | return [404, 'Invalid ETAG'];
19 | }
20 |
21 | function replyIfMatchEtag0(request) {
22 | replyIfNoneMatchWithEtag(request, TEST_ETAG_0, USERS);
23 | }
24 |
25 | function replyIfMatchEtag1(request) {
26 | replyIfNoneMatchWithEtag(request, TEST_ETAG_1, USERS);
27 | }
28 |
29 | function replyIfNotEtagHeaders(request) {
30 | if (!request.headers['if-none-match']) {
31 | return [200, USERS];
32 | }
33 | return [404, 'ETAG headers found'];
34 | }
35 |
36 | describe('Index', () => {
37 | describe('getCachedByAxiosConfig', () => {
38 | axiosETAGCache(axios);
39 | it('should returns undefined when no config url is registered', async () => {
40 | const config = {};
41 | Cache.get = jest.fn();
42 |
43 | const result = await getCacheByAxiosConfig(config);
44 |
45 | expect(result).toBeUndefined();
46 | expect(Cache.get).not.toBeCalled();
47 | });
48 |
49 | it('should call to the cache.get method if url is registered', async () => {
50 | const config = { url: 'defined' };
51 | Cache.get = jest.fn();
52 |
53 | const result = await getCacheByAxiosConfig(config);
54 |
55 | expect(result).toBeUndefined();
56 | expect(Cache.get).toBeCalled();
57 | });
58 | });
59 |
60 | it('should do the second request with a If-none-match header', done => {
61 | const call1 = nock(BASE_PATH).get('/users').reply(200, USERS, { Etag: TEST_ETAG_0 });
62 | const call2 = nock(BASE_PATH).get('/users').reply(200, function () {
63 | replyIfMatchEtag0(this.req);
64 | });
65 | axiosETAGCache(axios).get('http://api.example.com/users').then(() => {
66 | axiosETAGCache(axios).get('http://api.example.com/users').then(() => {
67 | expect(call1.isDone()).toBeTruthy();
68 | expect(call2.isDone()).toBeTruthy();
69 | done();
70 | }).catch(done);
71 | }).catch(done);
72 | });
73 |
74 | it('should works with normally when no etag is provided', done => {
75 | const call1 = nock(BASE_PATH).get('/actions').reply(200, USERS);
76 | const call2 = nock(BASE_PATH).get('/actions').reply(200, function () {
77 | replyIfNotEtagHeaders(this.req);
78 | });
79 | axiosETAGCache(axios).get('http://api.example.com/actions').then(() => {
80 | axiosETAGCache(axios).get('http://api.example.com/actions').then(() => {
81 | expect(call1.isDone()).toBeTruthy();
82 | expect(call2.isDone()).toBeTruthy();
83 | done();
84 | }).catch(done);
85 | }).catch(done);
86 | });
87 |
88 | it('should update the last ETAG value', done => {
89 | const call0 = nock(BASE_PATH).get('/actionsA').reply(200, USERS, { Etag: TEST_ETAG_0 });
90 | const call1 = nock(BASE_PATH).get('/actionsA').reply(200, USERS, { Etag: TEST_ETAG_1 });
91 | const call2 = nock(BASE_PATH).get('/actionsA').reply(200, replyIfMatchEtag1);
92 | axiosETAGCache(axios).get('http://api.example.com/actionsA').then(() => {
93 | axiosETAGCache(axios).get('http://api.example.com/actionsA').then(() => {
94 | axiosETAGCache(axios).get('http://api.example.com/actionsA').then(() => {
95 | expect(call0.isDone()).toBeTruthy();
96 | expect(call1.isDone()).toBeTruthy();
97 | expect(call2.isDone()).toBeTruthy();
98 | done();
99 | }).catch(done);
100 | }).catch(done);
101 | }).catch(done);
102 | });
103 |
104 | it('not cacheable methods should works with normally - POST', done => {
105 | const call1 = nock(BASE_PATH).post('/model').reply(200, USERS);
106 | axiosETAGCache(axios).post('http://api.example.com/model').then(() => {
107 | expect(call1.isDone()).toBeTruthy();
108 | done();
109 | }).catch(done);
110 | });
111 |
112 | it('should do second request without etag if cache was reset', done => {
113 | const call1 = nock(BASE_PATH).get('/users').reply(200, USERS, { Etag: TEST_ETAG_0 });
114 | const call2 = nock(BASE_PATH).get('/users').reply(200, function () {
115 | replyIfNotEtagHeaders(this.req);
116 | });
117 | axiosETAGCache(axios).get('http://api.example.com/users').then(() => {
118 | resetCache();
119 | axiosETAGCache(axios).get('http://api.example.com/users').then(() => {
120 | expect(call1.isDone()).toBeTruthy();
121 | expect(call2.isDone()).toBeTruthy();
122 | done();
123 | }).catch(done);
124 | }).catch(done);
125 | });
126 | });
127 |
--------------------------------------------------------------------------------
/demo/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "demo",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "axios": "0.24.0",
13 | "axios-etag-cache": "1.2.1"
14 | },
15 | "devDependencies": {
16 | "vite": "2.9.16"
17 | }
18 | },
19 | "node_modules/axios": {
20 | "version": "0.24.0",
21 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
22 | "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
23 | "dependencies": {
24 | "follow-redirects": "^1.14.4"
25 | }
26 | },
27 | "node_modules/axios-etag-cache": {
28 | "version": "1.2.1",
29 | "resolved": "https://registry.npmjs.org/axios-etag-cache/-/axios-etag-cache-1.2.1.tgz",
30 | "integrity": "sha512-PNUZuEdQfW75uGsQEdzTm1UnLr7qMFwS+X48vbVsKj1UCBR7T9DSdtn4gTu2clzOdave8tM3IP6xeqtpGOo17g==",
31 | "peerDependencies": {
32 | "axios": ">=0.24.0"
33 | }
34 | },
35 | "node_modules/esbuild": {
36 | "version": "0.14.46",
37 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.46.tgz",
38 | "integrity": "sha512-vdm5G1JdZBktva8dwQci/s44VbeBUg8g907xoZx77mqFZ4gU5GlMULNsdGeID+qXCXocsfYSGtE0LvqH3eiNQg==",
39 | "dev": true,
40 | "hasInstallScript": true,
41 | "bin": {
42 | "esbuild": "bin/esbuild"
43 | },
44 | "engines": {
45 | "node": ">=12"
46 | },
47 | "optionalDependencies": {
48 | "esbuild-android-64": "0.14.46",
49 | "esbuild-android-arm64": "0.14.46",
50 | "esbuild-darwin-64": "0.14.46",
51 | "esbuild-darwin-arm64": "0.14.46",
52 | "esbuild-freebsd-64": "0.14.46",
53 | "esbuild-freebsd-arm64": "0.14.46",
54 | "esbuild-linux-32": "0.14.46",
55 | "esbuild-linux-64": "0.14.46",
56 | "esbuild-linux-arm": "0.14.46",
57 | "esbuild-linux-arm64": "0.14.46",
58 | "esbuild-linux-mips64le": "0.14.46",
59 | "esbuild-linux-ppc64le": "0.14.46",
60 | "esbuild-linux-riscv64": "0.14.46",
61 | "esbuild-linux-s390x": "0.14.46",
62 | "esbuild-netbsd-64": "0.14.46",
63 | "esbuild-openbsd-64": "0.14.46",
64 | "esbuild-sunos-64": "0.14.46",
65 | "esbuild-windows-32": "0.14.46",
66 | "esbuild-windows-64": "0.14.46",
67 | "esbuild-windows-arm64": "0.14.46"
68 | }
69 | },
70 | "node_modules/esbuild-android-64": {
71 | "version": "0.14.46",
72 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.46.tgz",
73 | "integrity": "sha512-ZyJqwAcjNbZprs0ZAxnUAOhEhdE5kTKwz+CZuLmZYNLAPyRgBtaC8pT2PCuPifNvV8Cl3yLlrQPaOCjovoyb5g==",
74 | "cpu": [
75 | "x64"
76 | ],
77 | "dev": true,
78 | "optional": true,
79 | "os": [
80 | "android"
81 | ],
82 | "engines": {
83 | "node": ">=12"
84 | }
85 | },
86 | "node_modules/esbuild-android-arm64": {
87 | "version": "0.14.46",
88 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.46.tgz",
89 | "integrity": "sha512-BKcnUksvCijO9ONv6b4SikZE/OZftwJvX91XROODZGQmuwGVg97jmLDVu3lxuHdFlMNNzxh8taJ2mbCWZzH/Iw==",
90 | "cpu": [
91 | "arm64"
92 | ],
93 | "dev": true,
94 | "optional": true,
95 | "os": [
96 | "android"
97 | ],
98 | "engines": {
99 | "node": ">=12"
100 | }
101 | },
102 | "node_modules/esbuild-darwin-64": {
103 | "version": "0.14.46",
104 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.46.tgz",
105 | "integrity": "sha512-/ss2kO92sUJ9/1nHnMb3+oab8w6dyqKrMtPMvSYJ9KZIYGAZxz/WYxfFprY7Xk+ZxWnnlASSyZlG+If1nVmFYg==",
106 | "cpu": [
107 | "x64"
108 | ],
109 | "dev": true,
110 | "optional": true,
111 | "os": [
112 | "darwin"
113 | ],
114 | "engines": {
115 | "node": ">=12"
116 | }
117 | },
118 | "node_modules/esbuild-darwin-arm64": {
119 | "version": "0.14.46",
120 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.46.tgz",
121 | "integrity": "sha512-WX0JOaEFf6t+rIjXO6THsf/0fhQAt2Zb0/PSYlvXnuQQAmOmFAfPsuRNocp5ME0NGaUqZd4FxqqmLEVK3RzPAg==",
122 | "cpu": [
123 | "arm64"
124 | ],
125 | "dev": true,
126 | "optional": true,
127 | "os": [
128 | "darwin"
129 | ],
130 | "engines": {
131 | "node": ">=12"
132 | }
133 | },
134 | "node_modules/esbuild-freebsd-64": {
135 | "version": "0.14.46",
136 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.46.tgz",
137 | "integrity": "sha512-o+ozPFuHRCAGCVWU2bLurOUgVkT0jcPEu082VBUY2Q/yLf+B+/3nXzh4Fjp5O21tOvJRTn7hUVydG9j5+vYE6A==",
138 | "cpu": [
139 | "x64"
140 | ],
141 | "dev": true,
142 | "optional": true,
143 | "os": [
144 | "freebsd"
145 | ],
146 | "engines": {
147 | "node": ">=12"
148 | }
149 | },
150 | "node_modules/esbuild-freebsd-arm64": {
151 | "version": "0.14.46",
152 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.46.tgz",
153 | "integrity": "sha512-9zicZ0X43WDKz3sjNfcqYO38xbfJpSWYXB+FxvYYkmBwGA52K0SAu4oKuTTLi8od8X2IIo1x5C5TUNvKDSVJww==",
154 | "cpu": [
155 | "arm64"
156 | ],
157 | "dev": true,
158 | "optional": true,
159 | "os": [
160 | "freebsd"
161 | ],
162 | "engines": {
163 | "node": ">=12"
164 | }
165 | },
166 | "node_modules/esbuild-linux-32": {
167 | "version": "0.14.46",
168 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.46.tgz",
169 | "integrity": "sha512-ZnTpZMVb0VGvL99R5eh4OrJwbUyvpM6M88VAMuHP4LvFjuvZrhgefjKqEGuWZZW7JRnAjKqjXLjWdhdSjwMFnQ==",
170 | "cpu": [
171 | "ia32"
172 | ],
173 | "dev": true,
174 | "optional": true,
175 | "os": [
176 | "linux"
177 | ],
178 | "engines": {
179 | "node": ">=12"
180 | }
181 | },
182 | "node_modules/esbuild-linux-64": {
183 | "version": "0.14.46",
184 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.46.tgz",
185 | "integrity": "sha512-ECCRRZtX6l4ubeVhHhiVoK/uYAkvzNqfmR4gP4N/9H9RPu+b8YCcN4bQGp7xCuYIV6Xd41WpOMyO+xpcQvjtQQ==",
186 | "cpu": [
187 | "x64"
188 | ],
189 | "dev": true,
190 | "optional": true,
191 | "os": [
192 | "linux"
193 | ],
194 | "engines": {
195 | "node": ">=12"
196 | }
197 | },
198 | "node_modules/esbuild-linux-arm": {
199 | "version": "0.14.46",
200 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.46.tgz",
201 | "integrity": "sha512-RvTJEi4vj13c5FP9YPp+8Y6x6HK1E7uSqfy3y9UoeaNAzNZWA7fN1U3hQjTL/dy5zTJH5KE64mrt5k5+he+CQA==",
202 | "cpu": [
203 | "arm"
204 | ],
205 | "dev": true,
206 | "optional": true,
207 | "os": [
208 | "linux"
209 | ],
210 | "engines": {
211 | "node": ">=12"
212 | }
213 | },
214 | "node_modules/esbuild-linux-arm64": {
215 | "version": "0.14.46",
216 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.46.tgz",
217 | "integrity": "sha512-HX0TXCHyI0NEWG4jg8LlW1PbZQbnz+PUH56yjx996cgM5pC90u32drKs/tyJiyyQmNk9OXOogjKw7LEdp/Qc1w==",
218 | "cpu": [
219 | "arm64"
220 | ],
221 | "dev": true,
222 | "optional": true,
223 | "os": [
224 | "linux"
225 | ],
226 | "engines": {
227 | "node": ">=12"
228 | }
229 | },
230 | "node_modules/esbuild-linux-mips64le": {
231 | "version": "0.14.46",
232 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.46.tgz",
233 | "integrity": "sha512-jnb2NDwGqJUVmxn1v0f7seNdDm0nRNWHP9Z3MrWAGnBCdnnDlsjqRFDnbKoaQvWONEa+rOOr/giK+VL0hgQExA==",
234 | "cpu": [
235 | "mips64el"
236 | ],
237 | "dev": true,
238 | "optional": true,
239 | "os": [
240 | "linux"
241 | ],
242 | "engines": {
243 | "node": ">=12"
244 | }
245 | },
246 | "node_modules/esbuild-linux-ppc64le": {
247 | "version": "0.14.46",
248 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.46.tgz",
249 | "integrity": "sha512-uu3JTQUrwwauKY9z8yq5MnDyOlT3f2DNOzBcYz4dB78HqwEqilCsifoBGd0WcbED5n57dc59X+LZMTZ8Ose44w==",
250 | "cpu": [
251 | "ppc64"
252 | ],
253 | "dev": true,
254 | "optional": true,
255 | "os": [
256 | "linux"
257 | ],
258 | "engines": {
259 | "node": ">=12"
260 | }
261 | },
262 | "node_modules/esbuild-linux-riscv64": {
263 | "version": "0.14.46",
264 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.46.tgz",
265 | "integrity": "sha512-OB29r1EG44ZY34JnXCRERxo7k4pRKoQdaoRg2HIeCavatsXZwW4LCakpLnMQ72vXT1HtpBUABEjHkKkn5JyrUg==",
266 | "cpu": [
267 | "riscv64"
268 | ],
269 | "dev": true,
270 | "optional": true,
271 | "os": [
272 | "linux"
273 | ],
274 | "engines": {
275 | "node": ">=12"
276 | }
277 | },
278 | "node_modules/esbuild-linux-s390x": {
279 | "version": "0.14.46",
280 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.46.tgz",
281 | "integrity": "sha512-XQ/U9TueMSGYyPTKyZsJVraiuvxhwCDIMn/QwFXCRCJ6H/Cy/Rq33u9qhpeSziinHKfzJROGx5A8mQY6aYamdQ==",
282 | "cpu": [
283 | "s390x"
284 | ],
285 | "dev": true,
286 | "optional": true,
287 | "os": [
288 | "linux"
289 | ],
290 | "engines": {
291 | "node": ">=12"
292 | }
293 | },
294 | "node_modules/esbuild-netbsd-64": {
295 | "version": "0.14.46",
296 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.46.tgz",
297 | "integrity": "sha512-i15BwqHaAIFp1vBJkitAbHtwXcLk9TdHs/Ia1xGIAutQYXSJNPLM3Z4B4hyfHNEFl2yBqBIYpglMohv2ClNdOQ==",
298 | "cpu": [
299 | "x64"
300 | ],
301 | "dev": true,
302 | "optional": true,
303 | "os": [
304 | "netbsd"
305 | ],
306 | "engines": {
307 | "node": ">=12"
308 | }
309 | },
310 | "node_modules/esbuild-openbsd-64": {
311 | "version": "0.14.46",
312 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.46.tgz",
313 | "integrity": "sha512-XwOIFCE140Y/PvjrwjFfa/QLWBuvhR1mPCOa35mKx02jt++wPNgf0qhn6HfdVC3vQe7R46RwTp4q2cp99fepEg==",
314 | "cpu": [
315 | "x64"
316 | ],
317 | "dev": true,
318 | "optional": true,
319 | "os": [
320 | "openbsd"
321 | ],
322 | "engines": {
323 | "node": ">=12"
324 | }
325 | },
326 | "node_modules/esbuild-sunos-64": {
327 | "version": "0.14.46",
328 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.46.tgz",
329 | "integrity": "sha512-+kV3JnmfdxBVpHyFvuGXWtu6tXxXApOLPkSrVkMJf6+ns/3PLtPndpzwCzHjD+qYUEk8ln4MA+ufQ2qmjW5mZg==",
330 | "cpu": [
331 | "x64"
332 | ],
333 | "dev": true,
334 | "optional": true,
335 | "os": [
336 | "sunos"
337 | ],
338 | "engines": {
339 | "node": ">=12"
340 | }
341 | },
342 | "node_modules/esbuild-windows-32": {
343 | "version": "0.14.46",
344 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.46.tgz",
345 | "integrity": "sha512-gzGC1Q11B/Bo5A2EX4N22oigWmhL7Z0eDyc8kbSoJjqSrGQuRE7B0uMpluO+q0O/gZ1S3zdw+M4PCWlqOIeXLA==",
346 | "cpu": [
347 | "ia32"
348 | ],
349 | "dev": true,
350 | "optional": true,
351 | "os": [
352 | "win32"
353 | ],
354 | "engines": {
355 | "node": ">=12"
356 | }
357 | },
358 | "node_modules/esbuild-windows-64": {
359 | "version": "0.14.46",
360 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.46.tgz",
361 | "integrity": "sha512-Do2daaskfOjmCB7o3ygz6fD3K6SPjZLERiZLktzHz2oUCwsebKu/gmop0+j/XdrVIXC32wFzHzDS+9CTu9OShw==",
362 | "cpu": [
363 | "x64"
364 | ],
365 | "dev": true,
366 | "optional": true,
367 | "os": [
368 | "win32"
369 | ],
370 | "engines": {
371 | "node": ">=12"
372 | }
373 | },
374 | "node_modules/esbuild-windows-arm64": {
375 | "version": "0.14.46",
376 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.46.tgz",
377 | "integrity": "sha512-VEzMy6bM60/HT/URTDElyhfi2Pk0quCCrEhRlI4MRno/AIqYUGw0rZwkPl6PeoqVI6BgoBHGY576GWTiPmshCA==",
378 | "cpu": [
379 | "arm64"
380 | ],
381 | "dev": true,
382 | "optional": true,
383 | "os": [
384 | "win32"
385 | ],
386 | "engines": {
387 | "node": ">=12"
388 | }
389 | },
390 | "node_modules/follow-redirects": {
391 | "version": "1.14.8",
392 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
393 | "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
394 | "funding": [
395 | {
396 | "type": "individual",
397 | "url": "https://github.com/sponsors/RubenVerborgh"
398 | }
399 | ],
400 | "engines": {
401 | "node": ">=4.0"
402 | },
403 | "peerDependenciesMeta": {
404 | "debug": {
405 | "optional": true
406 | }
407 | }
408 | },
409 | "node_modules/fsevents": {
410 | "version": "2.3.2",
411 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
412 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
413 | "dev": true,
414 | "hasInstallScript": true,
415 | "optional": true,
416 | "os": [
417 | "darwin"
418 | ],
419 | "engines": {
420 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
421 | }
422 | },
423 | "node_modules/function-bind": {
424 | "version": "1.1.1",
425 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
426 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
427 | "dev": true
428 | },
429 | "node_modules/has": {
430 | "version": "1.0.3",
431 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
432 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
433 | "dev": true,
434 | "dependencies": {
435 | "function-bind": "^1.1.1"
436 | },
437 | "engines": {
438 | "node": ">= 0.4.0"
439 | }
440 | },
441 | "node_modules/is-core-module": {
442 | "version": "2.9.0",
443 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
444 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
445 | "dev": true,
446 | "dependencies": {
447 | "has": "^1.0.3"
448 | },
449 | "funding": {
450 | "url": "https://github.com/sponsors/ljharb"
451 | }
452 | },
453 | "node_modules/nanoid": {
454 | "version": "3.3.6",
455 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
456 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
457 | "dev": true,
458 | "funding": [
459 | {
460 | "type": "github",
461 | "url": "https://github.com/sponsors/ai"
462 | }
463 | ],
464 | "bin": {
465 | "nanoid": "bin/nanoid.cjs"
466 | },
467 | "engines": {
468 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
469 | }
470 | },
471 | "node_modules/path-parse": {
472 | "version": "1.0.7",
473 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
474 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
475 | "dev": true
476 | },
477 | "node_modules/picocolors": {
478 | "version": "1.0.0",
479 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
480 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
481 | "dev": true
482 | },
483 | "node_modules/postcss": {
484 | "version": "8.4.31",
485 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
486 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
487 | "dev": true,
488 | "funding": [
489 | {
490 | "type": "opencollective",
491 | "url": "https://opencollective.com/postcss/"
492 | },
493 | {
494 | "type": "tidelift",
495 | "url": "https://tidelift.com/funding/github/npm/postcss"
496 | },
497 | {
498 | "type": "github",
499 | "url": "https://github.com/sponsors/ai"
500 | }
501 | ],
502 | "dependencies": {
503 | "nanoid": "^3.3.6",
504 | "picocolors": "^1.0.0",
505 | "source-map-js": "^1.0.2"
506 | },
507 | "engines": {
508 | "node": "^10 || ^12 || >=14"
509 | }
510 | },
511 | "node_modules/resolve": {
512 | "version": "1.22.1",
513 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
514 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
515 | "dev": true,
516 | "dependencies": {
517 | "is-core-module": "^2.9.0",
518 | "path-parse": "^1.0.7",
519 | "supports-preserve-symlinks-flag": "^1.0.0"
520 | },
521 | "bin": {
522 | "resolve": "bin/resolve"
523 | },
524 | "funding": {
525 | "url": "https://github.com/sponsors/ljharb"
526 | }
527 | },
528 | "node_modules/rollup": {
529 | "version": "2.75.6",
530 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.6.tgz",
531 | "integrity": "sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==",
532 | "dev": true,
533 | "bin": {
534 | "rollup": "dist/bin/rollup"
535 | },
536 | "engines": {
537 | "node": ">=10.0.0"
538 | },
539 | "optionalDependencies": {
540 | "fsevents": "~2.3.2"
541 | }
542 | },
543 | "node_modules/source-map-js": {
544 | "version": "1.0.2",
545 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
546 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
547 | "dev": true,
548 | "engines": {
549 | "node": ">=0.10.0"
550 | }
551 | },
552 | "node_modules/supports-preserve-symlinks-flag": {
553 | "version": "1.0.0",
554 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
555 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
556 | "dev": true,
557 | "engines": {
558 | "node": ">= 0.4"
559 | },
560 | "funding": {
561 | "url": "https://github.com/sponsors/ljharb"
562 | }
563 | },
564 | "node_modules/vite": {
565 | "version": "2.9.16",
566 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.16.tgz",
567 | "integrity": "sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==",
568 | "dev": true,
569 | "dependencies": {
570 | "esbuild": "^0.14.27",
571 | "postcss": "^8.4.13",
572 | "resolve": "^1.22.0",
573 | "rollup": ">=2.59.0 <2.78.0"
574 | },
575 | "bin": {
576 | "vite": "bin/vite.js"
577 | },
578 | "engines": {
579 | "node": ">=12.2.0"
580 | },
581 | "optionalDependencies": {
582 | "fsevents": "~2.3.2"
583 | },
584 | "peerDependencies": {
585 | "less": "*",
586 | "sass": "*",
587 | "stylus": "*"
588 | },
589 | "peerDependenciesMeta": {
590 | "less": {
591 | "optional": true
592 | },
593 | "sass": {
594 | "optional": true
595 | },
596 | "stylus": {
597 | "optional": true
598 | }
599 | }
600 | }
601 | },
602 | "dependencies": {
603 | "axios": {
604 | "version": "0.24.0",
605 | "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
606 | "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
607 | "requires": {
608 | "follow-redirects": "^1.14.4"
609 | }
610 | },
611 | "axios-etag-cache": {
612 | "version": "1.2.1",
613 | "resolved": "https://registry.npmjs.org/axios-etag-cache/-/axios-etag-cache-1.2.1.tgz",
614 | "integrity": "sha512-PNUZuEdQfW75uGsQEdzTm1UnLr7qMFwS+X48vbVsKj1UCBR7T9DSdtn4gTu2clzOdave8tM3IP6xeqtpGOo17g==",
615 | "requires": {}
616 | },
617 | "esbuild": {
618 | "version": "0.14.46",
619 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.46.tgz",
620 | "integrity": "sha512-vdm5G1JdZBktva8dwQci/s44VbeBUg8g907xoZx77mqFZ4gU5GlMULNsdGeID+qXCXocsfYSGtE0LvqH3eiNQg==",
621 | "dev": true,
622 | "requires": {
623 | "esbuild-android-64": "0.14.46",
624 | "esbuild-android-arm64": "0.14.46",
625 | "esbuild-darwin-64": "0.14.46",
626 | "esbuild-darwin-arm64": "0.14.46",
627 | "esbuild-freebsd-64": "0.14.46",
628 | "esbuild-freebsd-arm64": "0.14.46",
629 | "esbuild-linux-32": "0.14.46",
630 | "esbuild-linux-64": "0.14.46",
631 | "esbuild-linux-arm": "0.14.46",
632 | "esbuild-linux-arm64": "0.14.46",
633 | "esbuild-linux-mips64le": "0.14.46",
634 | "esbuild-linux-ppc64le": "0.14.46",
635 | "esbuild-linux-riscv64": "0.14.46",
636 | "esbuild-linux-s390x": "0.14.46",
637 | "esbuild-netbsd-64": "0.14.46",
638 | "esbuild-openbsd-64": "0.14.46",
639 | "esbuild-sunos-64": "0.14.46",
640 | "esbuild-windows-32": "0.14.46",
641 | "esbuild-windows-64": "0.14.46",
642 | "esbuild-windows-arm64": "0.14.46"
643 | }
644 | },
645 | "esbuild-android-64": {
646 | "version": "0.14.46",
647 | "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.46.tgz",
648 | "integrity": "sha512-ZyJqwAcjNbZprs0ZAxnUAOhEhdE5kTKwz+CZuLmZYNLAPyRgBtaC8pT2PCuPifNvV8Cl3yLlrQPaOCjovoyb5g==",
649 | "dev": true,
650 | "optional": true
651 | },
652 | "esbuild-android-arm64": {
653 | "version": "0.14.46",
654 | "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.46.tgz",
655 | "integrity": "sha512-BKcnUksvCijO9ONv6b4SikZE/OZftwJvX91XROODZGQmuwGVg97jmLDVu3lxuHdFlMNNzxh8taJ2mbCWZzH/Iw==",
656 | "dev": true,
657 | "optional": true
658 | },
659 | "esbuild-darwin-64": {
660 | "version": "0.14.46",
661 | "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.46.tgz",
662 | "integrity": "sha512-/ss2kO92sUJ9/1nHnMb3+oab8w6dyqKrMtPMvSYJ9KZIYGAZxz/WYxfFprY7Xk+ZxWnnlASSyZlG+If1nVmFYg==",
663 | "dev": true,
664 | "optional": true
665 | },
666 | "esbuild-darwin-arm64": {
667 | "version": "0.14.46",
668 | "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.46.tgz",
669 | "integrity": "sha512-WX0JOaEFf6t+rIjXO6THsf/0fhQAt2Zb0/PSYlvXnuQQAmOmFAfPsuRNocp5ME0NGaUqZd4FxqqmLEVK3RzPAg==",
670 | "dev": true,
671 | "optional": true
672 | },
673 | "esbuild-freebsd-64": {
674 | "version": "0.14.46",
675 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.46.tgz",
676 | "integrity": "sha512-o+ozPFuHRCAGCVWU2bLurOUgVkT0jcPEu082VBUY2Q/yLf+B+/3nXzh4Fjp5O21tOvJRTn7hUVydG9j5+vYE6A==",
677 | "dev": true,
678 | "optional": true
679 | },
680 | "esbuild-freebsd-arm64": {
681 | "version": "0.14.46",
682 | "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.46.tgz",
683 | "integrity": "sha512-9zicZ0X43WDKz3sjNfcqYO38xbfJpSWYXB+FxvYYkmBwGA52K0SAu4oKuTTLi8od8X2IIo1x5C5TUNvKDSVJww==",
684 | "dev": true,
685 | "optional": true
686 | },
687 | "esbuild-linux-32": {
688 | "version": "0.14.46",
689 | "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.46.tgz",
690 | "integrity": "sha512-ZnTpZMVb0VGvL99R5eh4OrJwbUyvpM6M88VAMuHP4LvFjuvZrhgefjKqEGuWZZW7JRnAjKqjXLjWdhdSjwMFnQ==",
691 | "dev": true,
692 | "optional": true
693 | },
694 | "esbuild-linux-64": {
695 | "version": "0.14.46",
696 | "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.46.tgz",
697 | "integrity": "sha512-ECCRRZtX6l4ubeVhHhiVoK/uYAkvzNqfmR4gP4N/9H9RPu+b8YCcN4bQGp7xCuYIV6Xd41WpOMyO+xpcQvjtQQ==",
698 | "dev": true,
699 | "optional": true
700 | },
701 | "esbuild-linux-arm": {
702 | "version": "0.14.46",
703 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.46.tgz",
704 | "integrity": "sha512-RvTJEi4vj13c5FP9YPp+8Y6x6HK1E7uSqfy3y9UoeaNAzNZWA7fN1U3hQjTL/dy5zTJH5KE64mrt5k5+he+CQA==",
705 | "dev": true,
706 | "optional": true
707 | },
708 | "esbuild-linux-arm64": {
709 | "version": "0.14.46",
710 | "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.46.tgz",
711 | "integrity": "sha512-HX0TXCHyI0NEWG4jg8LlW1PbZQbnz+PUH56yjx996cgM5pC90u32drKs/tyJiyyQmNk9OXOogjKw7LEdp/Qc1w==",
712 | "dev": true,
713 | "optional": true
714 | },
715 | "esbuild-linux-mips64le": {
716 | "version": "0.14.46",
717 | "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.46.tgz",
718 | "integrity": "sha512-jnb2NDwGqJUVmxn1v0f7seNdDm0nRNWHP9Z3MrWAGnBCdnnDlsjqRFDnbKoaQvWONEa+rOOr/giK+VL0hgQExA==",
719 | "dev": true,
720 | "optional": true
721 | },
722 | "esbuild-linux-ppc64le": {
723 | "version": "0.14.46",
724 | "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.46.tgz",
725 | "integrity": "sha512-uu3JTQUrwwauKY9z8yq5MnDyOlT3f2DNOzBcYz4dB78HqwEqilCsifoBGd0WcbED5n57dc59X+LZMTZ8Ose44w==",
726 | "dev": true,
727 | "optional": true
728 | },
729 | "esbuild-linux-riscv64": {
730 | "version": "0.14.46",
731 | "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.46.tgz",
732 | "integrity": "sha512-OB29r1EG44ZY34JnXCRERxo7k4pRKoQdaoRg2HIeCavatsXZwW4LCakpLnMQ72vXT1HtpBUABEjHkKkn5JyrUg==",
733 | "dev": true,
734 | "optional": true
735 | },
736 | "esbuild-linux-s390x": {
737 | "version": "0.14.46",
738 | "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.46.tgz",
739 | "integrity": "sha512-XQ/U9TueMSGYyPTKyZsJVraiuvxhwCDIMn/QwFXCRCJ6H/Cy/Rq33u9qhpeSziinHKfzJROGx5A8mQY6aYamdQ==",
740 | "dev": true,
741 | "optional": true
742 | },
743 | "esbuild-netbsd-64": {
744 | "version": "0.14.46",
745 | "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.46.tgz",
746 | "integrity": "sha512-i15BwqHaAIFp1vBJkitAbHtwXcLk9TdHs/Ia1xGIAutQYXSJNPLM3Z4B4hyfHNEFl2yBqBIYpglMohv2ClNdOQ==",
747 | "dev": true,
748 | "optional": true
749 | },
750 | "esbuild-openbsd-64": {
751 | "version": "0.14.46",
752 | "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.46.tgz",
753 | "integrity": "sha512-XwOIFCE140Y/PvjrwjFfa/QLWBuvhR1mPCOa35mKx02jt++wPNgf0qhn6HfdVC3vQe7R46RwTp4q2cp99fepEg==",
754 | "dev": true,
755 | "optional": true
756 | },
757 | "esbuild-sunos-64": {
758 | "version": "0.14.46",
759 | "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.46.tgz",
760 | "integrity": "sha512-+kV3JnmfdxBVpHyFvuGXWtu6tXxXApOLPkSrVkMJf6+ns/3PLtPndpzwCzHjD+qYUEk8ln4MA+ufQ2qmjW5mZg==",
761 | "dev": true,
762 | "optional": true
763 | },
764 | "esbuild-windows-32": {
765 | "version": "0.14.46",
766 | "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.46.tgz",
767 | "integrity": "sha512-gzGC1Q11B/Bo5A2EX4N22oigWmhL7Z0eDyc8kbSoJjqSrGQuRE7B0uMpluO+q0O/gZ1S3zdw+M4PCWlqOIeXLA==",
768 | "dev": true,
769 | "optional": true
770 | },
771 | "esbuild-windows-64": {
772 | "version": "0.14.46",
773 | "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.46.tgz",
774 | "integrity": "sha512-Do2daaskfOjmCB7o3ygz6fD3K6SPjZLERiZLktzHz2oUCwsebKu/gmop0+j/XdrVIXC32wFzHzDS+9CTu9OShw==",
775 | "dev": true,
776 | "optional": true
777 | },
778 | "esbuild-windows-arm64": {
779 | "version": "0.14.46",
780 | "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.46.tgz",
781 | "integrity": "sha512-VEzMy6bM60/HT/URTDElyhfi2Pk0quCCrEhRlI4MRno/AIqYUGw0rZwkPl6PeoqVI6BgoBHGY576GWTiPmshCA==",
782 | "dev": true,
783 | "optional": true
784 | },
785 | "follow-redirects": {
786 | "version": "1.14.8",
787 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
788 | "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA=="
789 | },
790 | "fsevents": {
791 | "version": "2.3.2",
792 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
793 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
794 | "dev": true,
795 | "optional": true
796 | },
797 | "function-bind": {
798 | "version": "1.1.1",
799 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
800 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
801 | "dev": true
802 | },
803 | "has": {
804 | "version": "1.0.3",
805 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
806 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
807 | "dev": true,
808 | "requires": {
809 | "function-bind": "^1.1.1"
810 | }
811 | },
812 | "is-core-module": {
813 | "version": "2.9.0",
814 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
815 | "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
816 | "dev": true,
817 | "requires": {
818 | "has": "^1.0.3"
819 | }
820 | },
821 | "nanoid": {
822 | "version": "3.3.6",
823 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
824 | "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
825 | "dev": true
826 | },
827 | "path-parse": {
828 | "version": "1.0.7",
829 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
830 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
831 | "dev": true
832 | },
833 | "picocolors": {
834 | "version": "1.0.0",
835 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
836 | "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
837 | "dev": true
838 | },
839 | "postcss": {
840 | "version": "8.4.31",
841 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
842 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
843 | "dev": true,
844 | "requires": {
845 | "nanoid": "^3.3.6",
846 | "picocolors": "^1.0.0",
847 | "source-map-js": "^1.0.2"
848 | }
849 | },
850 | "resolve": {
851 | "version": "1.22.1",
852 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
853 | "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
854 | "dev": true,
855 | "requires": {
856 | "is-core-module": "^2.9.0",
857 | "path-parse": "^1.0.7",
858 | "supports-preserve-symlinks-flag": "^1.0.0"
859 | }
860 | },
861 | "rollup": {
862 | "version": "2.75.6",
863 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.75.6.tgz",
864 | "integrity": "sha512-OEf0TgpC9vU6WGROJIk1JA3LR5vk/yvqlzxqdrE2CzzXnqKXNzbAwlWUXis8RS3ZPe7LAq+YUxsRa0l3r27MLA==",
865 | "dev": true,
866 | "requires": {
867 | "fsevents": "~2.3.2"
868 | }
869 | },
870 | "source-map-js": {
871 | "version": "1.0.2",
872 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
873 | "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
874 | "dev": true
875 | },
876 | "supports-preserve-symlinks-flag": {
877 | "version": "1.0.0",
878 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
879 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
880 | "dev": true
881 | },
882 | "vite": {
883 | "version": "2.9.16",
884 | "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.16.tgz",
885 | "integrity": "sha512-X+6q8KPyeuBvTQV8AVSnKDvXoBMnTx8zxh54sOwmmuOdxkjMmEJXH2UEchA+vTMps1xw9vL64uwJOWryULg7nA==",
886 | "dev": true,
887 | "requires": {
888 | "esbuild": "^0.14.27",
889 | "fsevents": "~2.3.2",
890 | "postcss": "^8.4.13",
891 | "resolve": "^1.22.0",
892 | "rollup": ">=2.59.0 <2.78.0"
893 | }
894 | }
895 | }
896 | }
897 |
--------------------------------------------------------------------------------