diff --git a/README.md b/README.md index 781f88b51..bdb53b076 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ -

- keyv -
-
-

+

keyv

> Simple key-value storage with support for multiple backends diff --git a/packages/compress-brotli/package.json b/packages/compress-brotli/package.json index e058854af..67abbdcea 100644 --- a/packages/compress-brotli/package.json +++ b/packages/compress-brotli/package.json @@ -55,7 +55,7 @@ "dependencies": { "@keyv/serialize": "*", "compress-brotli": "^1.3.12", - "keyv": "*" + "keyv": "^5.2.1" }, "devDependencies": { "@keyv/test-suite": "*", diff --git a/packages/keyv/README.md b/packages/keyv/README.md index 67c545489..266e2c911 100644 --- a/packages/keyv/README.md +++ b/packages/keyv/README.md @@ -1,4 +1,4 @@ -[keyv](https://github.com/jaredwra/keyv) +

keyv

> Simple key-value storage with support for multiple backends diff --git a/packages/keyv/package.json b/packages/keyv/package.json index 1408ed98a..0c7b5e0b7 100644 --- a/packages/keyv/package.json +++ b/packages/keyv/package.json @@ -1,6 +1,6 @@ { "name": "keyv", - "version": "5.2.0", + "version": "5.2.1", "description": "Simple key-value storage with support for multiple backends", "type": "module", "main": "dist/index.cjs", diff --git a/packages/keyv/src/index.ts b/packages/keyv/src/index.ts index 4d814b2d2..293b56d57 100644 --- a/packages/keyv/src/index.ts +++ b/packages/keyv/src/index.ts @@ -12,6 +12,8 @@ export type DeserializedData = { export interface CompressionAdapter { compress(value: any, options?: any): Promise; decompress(value: any, options?: any): Promise; + serialize(data: DeserializedData): Promise | string; + deserialize(data: string): Promise | undefined> | DeserializedData | undefined; } export type Serialize = (data: DeserializedData) => Promise | string; diff --git a/packages/tiered/LICENSE b/packages/tiered/LICENSE deleted file mode 100644 index a6bad1d22..000000000 --- a/packages/tiered/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -MIT License - -Copyright (c) 2017-2021 Luke Childs -Copyright (c) 2021-2022 Jared Wray - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/tiered/README.md b/packages/tiered/README.md deleted file mode 100644 index a1fd5575a..000000000 --- a/packages/tiered/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# @keyv/tiered [keyv](https://github.com/jaredwra/keyv) - -> Tiered storage adapter for Keyv to manage local and remote store as one for Keyv - -[![build](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml/badge.svg)](https://github.com/jaredwray/keyv/actions/workflows/tests.yaml) -[![codecov](https://codecov.io/gh/jaredwray/keyv/branch/main/graph/badge.svg?token=bRzR3RyOXZ)](https://codecov.io/gh/jaredwray/keyv) -[![npm](https://img.shields.io/npm/v/@keyv/tiered.svg)](https://www.npmjs.com/package/@keyv/tiered) -[![npm](https://img.shields.io/npm/dm/@keyv/tiered)](https://npmjs.com/package/@keyv/tiered) - -# Feature is Deprecated - -This feature is deprecated and will be removed in 2025 as it is no longer needed. - -`offline` and `tiered` mode for caching is built into the core [Cacheable](https://cacheable.org) library which uses Keyv under the hood. Please use the `Cacheable` library for `offline` and `tiered` caching. - -## Install - -```shell -npm install --save keyv @keyv/tiered -``` - -## Usage - -First, you need to provide your `local` and `remote` stores to be used, being possible to use any [Keyv storage adapter](https://github.com/jaredwray/keyv#storage-adapters): - -```js -import Keyv from 'keyv'; -import KeyvTiered from '@keyv/tiered'; -import KeyvSqlite from '@keyv/sqlite'; - -const remoteStore = () => new Keyv({ - store: new KeyvSqlite({ - uri: 'sqlite://test/testdb.sqlite', - busyTimeout: 30_000, - }), -}); -const localStore = () => new Keyv(); -const remote = remoteStore(); -const local = localStore(); -const store = new KeyvTiered({remote, local}); -const keyv = new Keyv({store}); -keyv.on('error', handleConnectionError); -``` - -## API - -### KeyvTiered(\[options]) - -#### options - -##### local - -Type: `Object`
-Default: `new Keyv()` - -A keyv instance to be used as local strategy. - -##### remote - -Type: `Object`
-Default: `new Keyv()` - -A keyv instance to be used as remote strategy. - -##### validator - -Type: `Function`
-Default: `() => true` - -The validator function is used as a precondition to determining is remote storage should be checked. - -## License - -[MIT © Jared Wray](LISCENCE) \ No newline at end of file diff --git a/packages/tiered/package.json b/packages/tiered/package.json deleted file mode 100644 index 96bf2053b..000000000 --- a/packages/tiered/package.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "@keyv/tiered", - "version": "2.0.2", - "description": "Tiered storage adapter for Keyv", - "type": "module", - "main": "dist/index.cjs", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "require": "./dist/index.cjs", - "import": "./dist/index.js" - } - }, - "scripts": { - "build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean", - "prepare": "pnpm build", - "test": "xo --fix && vitest run --coverage", - "test:ci": "xo && vitest --run --sequence.setupFiles=list", - "clean": "rimraf ./node_modules ./coverage ./test/testdb.sqlite ./dist" - }, - "xo": { - "rules": { - "import/no-named-as-default": "off", - "unicorn/prefer-module": "off", - "unicorn/prefer-event-target": "off", - "n/file-extension-in-import": "off", - "import/extensions": "off", - "import/no-extraneous-dependencies": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unnecessary-type-arguments": "off" - } - }, - "repository": { - "type": "git", - "url": "git+https://github.com/jaredwray/keyv.git" - }, - "keywords": [ - "tiered", - "mulit", - "keyv", - "storage", - "adapter", - "key", - "value", - "store", - "cache", - "ttl" - ], - "author": "Jared Wray (http://jaredwray.com)", - "license": "MIT", - "bugs": { - "url": "https://github.com/jaredwray/keyv/issues" - }, - "homepage": "https://github.com/jaredwray/keyv", - "devDependencies": { - "@keyv/sqlite": "*", - "@keyv/test-suite": "*", - "c8": "^10.1.2", - "eslint": "^9.12.0", - "keyv": "^5.0.3", - "rimraf": "^6.0.1", - "tsd": "^0.31.2", - "typescript": "^5.6.2", - "xo": "^0.59.3" - }, - "tsd": { - "directory": "test" - }, - "engines": { - "node": ">= 18" - }, - "files": [ - "dist", - "LICENSE" - ] -} diff --git a/packages/tiered/src/index.ts b/packages/tiered/src/index.ts deleted file mode 100644 index b2052146d..000000000 --- a/packages/tiered/src/index.ts +++ /dev/null @@ -1,119 +0,0 @@ -import EventEmitter from 'node:events'; -import Keyv from 'keyv'; -import type {Options, Options_} from './types'; - -type KeyvTieredIndex = 'local' | 'remote'; - -export class KeyvTiered extends EventEmitter { - opts: Options_; - remote: Keyv; - local: Keyv; - iterationLimit?: string | number; - - constructor({remote = new Keyv(), local = new Keyv(), ...options}: Options) { - super(); - this.opts = { - validator: () => true, - dialect: 'tiered', - ...options, - }; - this.remote = remote; - this.local = local; - } - - async get(key: string): Promise { - const localResult: unknown = await this.local.get(key); - - if (localResult === undefined || !this.opts.validator(localResult, key)) { - const remoteResult: unknown = await this.remote.get(key); - - if (remoteResult === localResult) { - return remoteResult; - } - - await this.local.set(key, remoteResult); - - return remoteResult; - } - - return localResult; - } - - async getMany(keys: string[]): Promise { - const promises = []; - for (const key of keys) { - promises.push(this.get(key)); - } - - const values = await Promise.all(promises); - const data: unknown[] = []; - for (const value of values) { - data.push(value); - } - - return data; - } - - async set(key: string, value: any, ttl?: number) { - const toSet: KeyvTieredIndex[] = ['local', 'remote']; - return Promise.all(toSet.map(async store => this[store].set(key, value, ttl)), - ); - } - - async clear(): Promise { - const toClear: KeyvTieredIndex[] = ['local']; - if (!this.opts.localOnly) { - toClear.push('remote'); - } - - await Promise.all(toClear - .map(async store => this[store].clear()), - ); - - return undefined; - } - - async delete(key: string): Promise { - const toDelete: KeyvTieredIndex[] = ['local']; - if (!this.opts.localOnly) { - toDelete.push('remote'); - } - - const deleted = await Promise.all(toDelete - .map(async store => this[store].delete(key)), - ); - - return deleted.every(Boolean); - } - - async deleteMany(keys: string[]): Promise { - const promises = []; - for (const key of keys) { - promises.push(this.delete(key)); - } - - const values = await Promise.all(promises); - - return values.every(Boolean); - } - - async has(key: string): Promise { - const response = await this.local.has(key); - - if (!response || !this.opts.validator(response, key)) { - return this.remote.has(key); - } - - return response; - } - - async * iterator(namespace?: string): AsyncGenerator { - // @ts-expect-error - iterationLimit doesn't exist on Keyv - this.remote.opts.iterationLimit = Number.parseInt(this.iterationLimit as string, 10) || 10; - for await (const entries of this.remote.iterator!(namespace)) { - yield entries; - } - } -} - -export default KeyvTiered; diff --git a/packages/tiered/src/types.ts b/packages/tiered/src/types.ts deleted file mode 100644 index f09ae1505..000000000 --- a/packages/tiered/src/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type Keyv from 'keyv'; - -export type Options = { - local: Keyv; - remote: Keyv; - localOnly?: boolean; - iterationLimit?: number | string; -}; - -// eslint-disable-next-line @typescript-eslint/naming-convention -export type Options_ = { - validator: (value: any, key: string) => boolean; - dialect: string; - iterationLimit?: number | string; - localOnly?: boolean; -}; diff --git a/packages/tiered/test/test.ts b/packages/tiered/test/test.ts deleted file mode 100644 index 047a9fb5b..000000000 --- a/packages/tiered/test/test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import * as test from 'vitest'; -import Keyv from 'keyv'; -import KeyvSqlite from '@keyv/sqlite'; -import keyvTestSuite, {delay, keyvIteratorTests} from '@keyv/test-suite'; -import KeyvTiered from '../src/index'; - -const remoteStore = () => new Keyv({ - store: new KeyvSqlite({ - uri: 'sqlite://test/testdb.sqlite', - busyTimeout: 30_000, - }), -}); - -const localStore = () => new Keyv(); -const store = () => new KeyvTiered({remote: remoteStore(), local: localStore()}); - -// @ts-expect-error - Store -keyvTestSuite(test, Keyv, store); - -// @ts-expect-error - Store -keyvIteratorTests(test, Keyv, store); - -test.beforeEach(async () => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local}); - await store.clear(); -}); - -test.it('constructor on default', t => { - // @ts-expect-error - KeyvTiered needs constructor options - const store = new KeyvTiered({}); - t.expect(store.local.opts.store).toBeTruthy(); - t.expect(store.remote.opts.store).toBeTruthy(); -}); - -test.it('.set() sets to both stores', async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local}); - - await store.set('foo', 'bar'); - - const [remoteResult, localResult, storeResult] = await Promise.all([ - remote.get('foo'), - store.get('foo'), - local.get('foo'), - ]); - const result = remoteResult === localResult && storeResult === localResult; // Check equality as 'bar' is just a string - t.expect(result).toBeTruthy(); -}); - -test.it('.has() returns boolean', async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local}); - - await store.set('foo', 'bar'); - - t.expect(await store.has('foo')).toBeTruthy(); -}); - -test.it('.has() checks both stores', async t => { - const remote = remoteStore(); - // @ts-expect-error - KeyvTiered needs local - const store = new KeyvTiered({remote}); - - await remote.set('fizz', 'buzz'); - - t.expect(await store.has('fizz')).toBeTruthy(); -}); - -test.it('.delete() deletes both stores', async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local}); - - await store.set('fizz', 'buzz'); - await store.delete('fizz'); - - t.expect(await store.get('fizz')).toBeUndefined(); - t.expect(await local.get('fizz')).toBeUndefined(); - t.expect(await remote.get('fizz')).toBeUndefined(); -}); - -test.it('.deleteMany() deletes both stores', async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local}); - - await store.set('fizz', 'buzz'); - await store.set('fizz1', 'buzz1'); - const value = await store.deleteMany(['fizz', 'fizz1']); - - t.expect(value).toBeTruthy(); - t.expect(await store.get('fizz')).toBeUndefined(); - t.expect(await local.get('fizz')).toBeUndefined(); - t.expect(await remote.get('fizz')).toBeUndefined(); - t.expect(await store.get('fizz1')).toBeUndefined(); - t.expect(await local.get('fizz1')).toBeUndefined(); - t.expect(await remote.get('fizz1')).toBeUndefined(); -}); - -test.it('.getMany() deletes both stores', async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local}); - - await store.set('fizz', 'buzz'); - await store.set('fizz1', 'buzz1'); - let value = await store.getMany(['fizz', 'fizz1']); - t.expect(value).toStrictEqual(['buzz', 'buzz1']); - - value = await store.getMany(['fizz3', 'fizz4']); - t.expect(value).toStrictEqual([undefined, undefined]); -}); - -test.it( - '.delete({ localOnly: true }) deletes only local store', - async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local, localOnly: true}); - - await store.set('fizz', 'buzz'); - await store.delete('fizz'); - - t.expect(await local.get('fizz')).toBeUndefined(); - t.expect(await remote.get('fizz')).toBeTruthy(); - }, -); - -test.it('.clear() clears both stores', async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local}); - - await store.set('fizz', 'buzz'); - await store.clear(); - - t.expect(await store.get('fizz')).toBeUndefined(); -}); - -test.it('.clear({ localOnly: true }) clears local store alone', async t => { - const remote = remoteStore(); - const local = localStore(); - const store = new KeyvTiered({remote, local, localOnly: true}); - - await store.set('fizz', 'buzz'); - await store.clear(); - - t.expect(await local.get('fizz')).toBeUndefined(); - t.expect(await remote.get('fizz')).toBeTruthy(); -}); - -test.it('ttl is valid', async t => { - const remote = remoteStore(); - const local = new Keyv({ttl: 100}); // Set local ttl - const store = new KeyvTiered({remote, local}); - - await store.set('foo', 'bar'); - await remote.set('foo', 'notbar'); - - await delay(2000); - t.expect(await store.get('foo')).toBe('notbar'); -}); - -test.it('copy locally when is possible', async t => { - const remote = remoteStore(); - const local = new Keyv(); - const store = new KeyvTiered({remote, local}); - - await remote.set('foo', 'bar'); - - t.expect(await store.get('foo')).toBe('bar'); - t.expect(await local.get('foo')).toBe('bar'); -}); - -test.it('custom validator', async t => { - const remote = remoteStore(); - const local = new Keyv(); - const store = new KeyvTiered({ - remote, - local, - // @ts-expect-error - Validator not need params - validator(value: {timeSensitiveData: any}) { - if (value.timeSensitiveData) { - return false; - } // Fetch from remote store only - - return true; - }, - }); - - await store.set('1', {timeSensitiveData: 'bar'}); - await store.set('2', {timeSensitiveData: false}); - - t.expect(await store.get('1')).toStrictEqual({timeSensitiveData: 'bar'}); // Fetched from remote - t.expect(await store.get('2')).toStrictEqual({timeSensitiveData: false}); - - await remote.set('1', {timeSensitiveData: 'foo1'}); - await remote.set('2', {timeSensitiveData: 'foo2'}); // Set to remote so local has not been updated - - t.expect(await store.get('1')).toStrictEqual({timeSensitiveData: 'foo1'}); - t.expect(await store.get('2')).toStrictEqual({timeSensitiveData: false}); -}); diff --git a/packages/tiered/tsconfig.json b/packages/tiered/tsconfig.json deleted file mode 100644 index 7e9400d2a..000000000 --- a/packages/tiered/tsconfig.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ - "baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */ - - /* Emit */ - "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - "outDir": "./dist", /* Specify an output folder for all emitted files. */ - - /* Interop Constraints */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - - /* Completeness */ - "skipLibCheck": true, /* Skip type checking all .d.ts files. */ - "lib": [ - "ESNext", "DOM" - ] - } -} diff --git a/packages/tiered/vitest.config.ts b/packages/tiered/vitest.config.ts deleted file mode 100644 index 1ee8dcc07..000000000 --- a/packages/tiered/vitest.config.ts +++ /dev/null @@ -1,4 +0,0 @@ -import {defineConfig, mergeConfig} from 'vitest/config'; -import vitestConfig from '../../vitest.general.config'; - -export default mergeConfig(vitestConfig, defineConfig({})); diff --git a/packages/website/src/docs.ts b/packages/website/src/docs.ts index fc96b71c1..dd9eab018 100644 --- a/packages/website/src/docs.ts +++ b/packages/website/src/docs.ts @@ -69,7 +69,7 @@ async function copyCompressionDocs() { } function cleanDocumentFromImage(document: string) { - document = document.replace(`[keyv](https://github.com/jaredwra/keyv)`, ""); + document = document.replace(`

keyv

`, ""); document = document.replace(`[keyv](https://github.com/jaredwra/keyv)`, ""); document = document.replace(`[keyv](https://github.com/jaredwra/keyv)`, ""); return document;