@lakutata/cache 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +175 -0
- package/dist/cjs/cache/Cacher.d.ts +446 -0
- package/dist/cjs/cache/Cacher.js +523 -0
- package/dist/cjs/cache/exceptions/CacheDriverNotFoundException.d.ts +10 -0
- package/dist/cjs/cache/exceptions/CacheDriverNotFoundException.js +17 -0
- package/dist/cjs/cache/interfaces/CacherOptions.d.ts +42 -0
- package/dist/cjs/cache/interfaces/CacherOptions.js +2 -0
- package/dist/cjs/cache/lib/LikePrefix.d.ts +5 -0
- package/dist/cjs/cache/lib/LikePrefix.js +10 -0
- package/dist/cjs/cache/lib/LoadDriver.d.ts +6 -0
- package/dist/cjs/cache/lib/LoadDriver.js +66 -0
- package/dist/cjs/cache/lib/Serializer.d.ts +18 -0
- package/dist/cjs/cache/lib/Serializer.js +46 -0
- package/dist/cjs/cache/lib/WithTimeout.d.ts +7 -0
- package/dist/cjs/cache/lib/WithTimeout.js +23 -0
- package/dist/cjs/cache/options/FileCacheOptions.d.ts +54 -0
- package/dist/cjs/cache/options/FileCacheOptions.js +62 -0
- package/dist/cjs/cache/options/MemcacheCacheOptions.d.ts +57 -0
- package/dist/cjs/cache/options/MemcacheCacheOptions.js +66 -0
- package/dist/cjs/cache/options/MemoryCacheOptions.d.ts +39 -0
- package/dist/cjs/cache/options/MemoryCacheOptions.js +52 -0
- package/dist/cjs/cache/options/MongoCacheOptions.d.ts +72 -0
- package/dist/cjs/cache/options/MongoCacheOptions.js +81 -0
- package/dist/cjs/cache/options/MysqlCacheOptions.d.ts +73 -0
- package/dist/cjs/cache/options/MysqlCacheOptions.js +82 -0
- package/dist/cjs/cache/options/PostgresCacheOptions.d.ts +87 -0
- package/dist/cjs/cache/options/PostgresCacheOptions.js +92 -0
- package/dist/cjs/cache/options/RedisCacheOptions.d.ts +120 -0
- package/dist/cjs/cache/options/RedisCacheOptions.js +113 -0
- package/dist/cjs/cache/options/SqliteCacheOptions.d.ts +53 -0
- package/dist/cjs/cache/options/SqliteCacheOptions.js +61 -0
- package/dist/cjs/cache/stores/CacheStore.d.ts +44 -0
- package/dist/cjs/cache/stores/CacheStore.js +42 -0
- package/dist/cjs/cache/stores/CreateCacheStore.d.ts +7 -0
- package/dist/cjs/cache/stores/CreateCacheStore.js +37 -0
- package/dist/cjs/cache/stores/FileStore.d.ts +51 -0
- package/dist/cjs/cache/stores/FileStore.js +143 -0
- package/dist/cjs/cache/stores/MemcacheStore.d.ts +42 -0
- package/dist/cjs/cache/stores/MemcacheStore.js +69 -0
- package/dist/cjs/cache/stores/MemoryStore.d.ts +34 -0
- package/dist/cjs/cache/stores/MemoryStore.js +75 -0
- package/dist/cjs/cache/stores/MongoStore.d.ts +55 -0
- package/dist/cjs/cache/stores/MongoStore.js +72 -0
- package/dist/cjs/cache/stores/MysqlStore.d.ts +27 -0
- package/dist/cjs/cache/stores/MysqlStore.js +71 -0
- package/dist/cjs/cache/stores/PostgresStore.d.ts +30 -0
- package/dist/cjs/cache/stores/PostgresStore.js +85 -0
- package/dist/cjs/cache/stores/RedisStore.d.ts +58 -0
- package/dist/cjs/cache/stores/RedisStore.js +131 -0
- package/dist/cjs/cache/stores/SerializedCacheStore.d.ts +48 -0
- package/dist/cjs/cache/stores/SerializedCacheStore.js +80 -0
- package/dist/cjs/cache/stores/SqliteStore.d.ts +44 -0
- package/dist/cjs/cache/stores/SqliteStore.js +91 -0
- package/dist/cjs/cache/types/CacheStoreOptions.d.ts +15 -0
- package/dist/cjs/cache/types/CacheStoreOptions.js +2 -0
- package/dist/cjs/exports/Cache.d.ts +12 -0
- package/dist/cjs/exports/Cache.js +31 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/cache/Cacher.js +518 -0
- package/dist/esm/cache/exceptions/CacheDriverNotFoundException.js +13 -0
- package/dist/esm/cache/interfaces/CacherOptions.js +1 -0
- package/dist/esm/cache/lib/LikePrefix.js +7 -0
- package/dist/esm/cache/lib/LoadDriver.js +30 -0
- package/dist/esm/cache/lib/Serializer.js +42 -0
- package/dist/esm/cache/lib/WithTimeout.js +20 -0
- package/dist/esm/cache/options/FileCacheOptions.js +58 -0
- package/dist/esm/cache/options/MemcacheCacheOptions.js +62 -0
- package/dist/esm/cache/options/MemoryCacheOptions.js +48 -0
- package/dist/esm/cache/options/MongoCacheOptions.js +77 -0
- package/dist/esm/cache/options/MysqlCacheOptions.js +78 -0
- package/dist/esm/cache/options/PostgresCacheOptions.js +88 -0
- package/dist/esm/cache/options/RedisCacheOptions.js +109 -0
- package/dist/esm/cache/options/SqliteCacheOptions.js +57 -0
- package/dist/esm/cache/stores/CacheStore.js +36 -0
- package/dist/esm/cache/stores/CreateCacheStore.js +34 -0
- package/dist/esm/cache/stores/FileStore.js +136 -0
- package/dist/esm/cache/stores/MemcacheStore.js +65 -0
- package/dist/esm/cache/stores/MemoryStore.js +71 -0
- package/dist/esm/cache/stores/MongoStore.js +68 -0
- package/dist/esm/cache/stores/MysqlStore.js +67 -0
- package/dist/esm/cache/stores/PostgresStore.js +81 -0
- package/dist/esm/cache/stores/RedisStore.js +127 -0
- package/dist/esm/cache/stores/SerializedCacheStore.js +76 -0
- package/dist/esm/cache/stores/SqliteStore.js +87 -0
- package/dist/esm/cache/types/CacheStoreOptions.js +1 -0
- package/dist/esm/exports/Cache.js +12 -0
- package/dist/types/cache/Cacher.d.ts +446 -0
- package/dist/types/cache/exceptions/CacheDriverNotFoundException.d.ts +10 -0
- package/dist/types/cache/interfaces/CacherOptions.d.ts +42 -0
- package/dist/types/cache/lib/LikePrefix.d.ts +5 -0
- package/dist/types/cache/lib/LoadDriver.d.ts +6 -0
- package/dist/types/cache/lib/Serializer.d.ts +18 -0
- package/dist/types/cache/lib/WithTimeout.d.ts +7 -0
- package/dist/types/cache/options/FileCacheOptions.d.ts +54 -0
- package/dist/types/cache/options/MemcacheCacheOptions.d.ts +57 -0
- package/dist/types/cache/options/MemoryCacheOptions.d.ts +39 -0
- package/dist/types/cache/options/MongoCacheOptions.d.ts +72 -0
- package/dist/types/cache/options/MysqlCacheOptions.d.ts +73 -0
- package/dist/types/cache/options/PostgresCacheOptions.d.ts +87 -0
- package/dist/types/cache/options/RedisCacheOptions.d.ts +120 -0
- package/dist/types/cache/options/SqliteCacheOptions.d.ts +53 -0
- package/dist/types/cache/stores/CacheStore.d.ts +44 -0
- package/dist/types/cache/stores/CreateCacheStore.d.ts +7 -0
- package/dist/types/cache/stores/FileStore.d.ts +51 -0
- package/dist/types/cache/stores/MemcacheStore.d.ts +42 -0
- package/dist/types/cache/stores/MemoryStore.d.ts +34 -0
- package/dist/types/cache/stores/MongoStore.d.ts +55 -0
- package/dist/types/cache/stores/MysqlStore.d.ts +27 -0
- package/dist/types/cache/stores/PostgresStore.d.ts +30 -0
- package/dist/types/cache/stores/RedisStore.d.ts +58 -0
- package/dist/types/cache/stores/SerializedCacheStore.d.ts +48 -0
- package/dist/types/cache/stores/SqliteStore.d.ts +44 -0
- package/dist/types/cache/types/CacheStoreOptions.d.ts +15 -0
- package/dist/types/exports/Cache.d.ts +12 -0
- package/package.json +72 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the file store: the entries are kept in memory and saved to one JSON file (loaded when the cache
|
|
4
|
+
* starts, written in batches, flushed when the cache is destroyed), so they survive a restart. It needs no optional
|
|
5
|
+
* dependency.
|
|
6
|
+
*
|
|
7
|
+
* The file is rewritten whole on each save (through a temporary file renamed over it), so it suits small caches;
|
|
8
|
+
* the processes sharing one file overwrite each other's entries. An unreadable file (not JSON) fails the cache's
|
|
9
|
+
* creation; a failed save is emitted as an `'error'` event of the cache. The values are kept as JSON.
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import {Application} from 'lakutata'
|
|
13
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
14
|
+
*
|
|
15
|
+
* Application.run(() => ({
|
|
16
|
+
* id: 'file-cache.app',
|
|
17
|
+
* name: 'FileCache',
|
|
18
|
+
* components: {
|
|
19
|
+
* cache: buildCacherOptions({
|
|
20
|
+
* stores: {type: 'file', filename: './cache/data.json', namespace: 'app', writeDelay: 500},
|
|
21
|
+
* ttl: 3600000
|
|
22
|
+
* })
|
|
23
|
+
* }
|
|
24
|
+
* }))
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class FileCacheOptions extends DTO {
|
|
28
|
+
/**
|
|
29
|
+
* The store type: `'file'`.
|
|
30
|
+
*/
|
|
31
|
+
type: 'file';
|
|
32
|
+
/**
|
|
33
|
+
* The path of the file, relative to the working directory; created (with its directories) at the first save.
|
|
34
|
+
*/
|
|
35
|
+
filename: string;
|
|
36
|
+
/**
|
|
37
|
+
* The minimum interval, in milliseconds, between two purges of the expired entries from the file (done at a save).
|
|
38
|
+
* An expired entry is a miss anyway, and is removed when read.
|
|
39
|
+
* @default 3600000
|
|
40
|
+
*/
|
|
41
|
+
expiredCheckDelay?: number;
|
|
42
|
+
/**
|
|
43
|
+
* The delay, in milliseconds, between a change and the save of the file: the changes made meanwhile are saved
|
|
44
|
+
* together. `0` saves at the next turn of the event loop.
|
|
45
|
+
* @default 100
|
|
46
|
+
*/
|
|
47
|
+
writeDelay?: number;
|
|
48
|
+
/**
|
|
49
|
+
* The namespace of the entries: the keys are saved as `{namespace}:{key}`, and `clear()` deletes only the
|
|
50
|
+
* namespace's entries. Without it, `clear()` deletes all the file's entries.
|
|
51
|
+
* @default none
|
|
52
|
+
*/
|
|
53
|
+
namespace?: string;
|
|
54
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the Memcache store, a memcached server. It needs the optional peer dependency `memjs`
|
|
4
|
+
* (`npm install memjs`): without it, the cache's creation fails with `CacheDriverNotFoundException`.
|
|
5
|
+
*
|
|
6
|
+
* The client connects on its first command, so an unreachable server does not fail the cache's creation: its reads
|
|
7
|
+
* are misses (emitted as `'error'` events), its writes reject. The TTLs are rounded up to the second by memcached
|
|
8
|
+
* (an entry is a miss once its TTL in milliseconds has passed). memcached cannot list its keys: `clear()` flushes the
|
|
9
|
+
* whole server, the entries of the other namespaces and applications included. The values are kept as JSON.
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import {Application} from 'lakutata'
|
|
13
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
14
|
+
*
|
|
15
|
+
* Application.run(() => ({
|
|
16
|
+
* id: 'memcache.app',
|
|
17
|
+
* name: 'Memcache',
|
|
18
|
+
* components: {
|
|
19
|
+
* cache: buildCacherOptions({
|
|
20
|
+
* stores: {type: 'memcache', host: '127.0.0.1', port: 11211, namespace: 'app'},
|
|
21
|
+
* ttl: 60000
|
|
22
|
+
* })
|
|
23
|
+
* }
|
|
24
|
+
* }))
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class MemcacheCacheOptions extends DTO {
|
|
28
|
+
/**
|
|
29
|
+
* The store type: `'memcache'`.
|
|
30
|
+
*/
|
|
31
|
+
type: 'memcache';
|
|
32
|
+
/**
|
|
33
|
+
* The host name or IP address of the memcached server.
|
|
34
|
+
*/
|
|
35
|
+
host: string;
|
|
36
|
+
/**
|
|
37
|
+
* The port of the memcached server.
|
|
38
|
+
* @default 11211
|
|
39
|
+
*/
|
|
40
|
+
port?: number;
|
|
41
|
+
/**
|
|
42
|
+
* The SASL user name, for a server requiring authentication.
|
|
43
|
+
* @default none
|
|
44
|
+
*/
|
|
45
|
+
username?: string;
|
|
46
|
+
/**
|
|
47
|
+
* The SASL password, for a server requiring authentication.
|
|
48
|
+
* @default none
|
|
49
|
+
*/
|
|
50
|
+
password?: string;
|
|
51
|
+
/**
|
|
52
|
+
* The namespace of the entries: the keys are stored as `{namespace}:{namespace}:{key}` (the format of lakutata 2.x).
|
|
53
|
+
* It does not limit `clear()`, which flushes the whole server.
|
|
54
|
+
* @default none
|
|
55
|
+
*/
|
|
56
|
+
namespace?: string;
|
|
57
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the in-memory store: the entries are kept in the process (lost when it ends, not shared with the
|
|
4
|
+
* other processes), the values as they are (not copied, not converted to JSON). It needs no optional dependency.
|
|
5
|
+
*
|
|
6
|
+
* It is the store of a cache without `stores`; declared, it is a tier in front of a slower store (the first store of
|
|
7
|
+
* the list is read first, the hits of a later store are written back to the earlier ones).
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import {Application} from 'lakutata'
|
|
11
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
12
|
+
*
|
|
13
|
+
* Application.run(() => ({
|
|
14
|
+
* id: 'tiered-cache.app',
|
|
15
|
+
* name: 'TieredCache',
|
|
16
|
+
* components: {
|
|
17
|
+
* //A memory tier in front of Redis
|
|
18
|
+
* cache: buildCacherOptions({
|
|
19
|
+
* stores: [
|
|
20
|
+
* {type: 'memory', sweepInterval: 30000},
|
|
21
|
+
* {type: 'redis', host: '127.0.0.1', namespace: 'app'}
|
|
22
|
+
* ],
|
|
23
|
+
* ttl: 60000
|
|
24
|
+
* })
|
|
25
|
+
* }
|
|
26
|
+
* }))
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare class MemoryCacheOptions extends DTO {
|
|
30
|
+
/**
|
|
31
|
+
* The store type: `'memory'`.
|
|
32
|
+
*/
|
|
33
|
+
type: 'memory';
|
|
34
|
+
/**
|
|
35
|
+
* The interval, in milliseconds, between two removals of the expired entries (an expired entry is a miss anyway).
|
|
36
|
+
* @default 60000
|
|
37
|
+
*/
|
|
38
|
+
sweepInterval?: number;
|
|
39
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the MongoDB store: one document per entry in a collection. It needs the optional peer dependency
|
|
4
|
+
* `mongodb` (`npm install mongodb`): without it, the cache's creation fails with `CacheDriverNotFoundException`.
|
|
5
|
+
*
|
|
6
|
+
* When the cache starts, it connects to `mongodb://{username}:{password}@{host}:{port}` (the user authenticated
|
|
7
|
+
* against the `admin` database; `mongodb://{host}:{port}` without a username) and creates the collection's
|
|
8
|
+
* indexes: a unique one on `key`, and a TTL index on
|
|
9
|
+
* `expiresAt` by which MongoDB deletes the expired documents (its TTL monitor runs about once a minute; an expired
|
|
10
|
+
* document is a miss before). A failed connection fails the cache's creation. The values are kept as JSON.
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import {Application} from 'lakutata'
|
|
14
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
15
|
+
*
|
|
16
|
+
* Application.run(() => ({
|
|
17
|
+
* id: 'mongo-cache.app',
|
|
18
|
+
* name: 'MongoCache',
|
|
19
|
+
* components: {
|
|
20
|
+
* cache: buildCacherOptions({
|
|
21
|
+
* stores: {
|
|
22
|
+
* type: 'mongo',
|
|
23
|
+
* host: '127.0.0.1',
|
|
24
|
+
* database: 'app',
|
|
25
|
+
* collection: 'cache',
|
|
26
|
+
* username: 'app',
|
|
27
|
+
* password: process.env.MONGO_PASSWORD ?? ''
|
|
28
|
+
* }
|
|
29
|
+
* })
|
|
30
|
+
* }
|
|
31
|
+
* }))
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare class MongoCacheOptions extends DTO {
|
|
35
|
+
/**
|
|
36
|
+
* The store type: `'mongo'`.
|
|
37
|
+
*/
|
|
38
|
+
type: 'mongo';
|
|
39
|
+
/**
|
|
40
|
+
* The host name or IP address of the MongoDB server.
|
|
41
|
+
*/
|
|
42
|
+
host: string;
|
|
43
|
+
/**
|
|
44
|
+
* The database holding the collection.
|
|
45
|
+
*/
|
|
46
|
+
database: string;
|
|
47
|
+
/**
|
|
48
|
+
* The collection of the entries, created when missing.
|
|
49
|
+
*/
|
|
50
|
+
collection: string;
|
|
51
|
+
/**
|
|
52
|
+
* The port of the MongoDB server.
|
|
53
|
+
* @default 27017
|
|
54
|
+
*/
|
|
55
|
+
port?: number;
|
|
56
|
+
/**
|
|
57
|
+
* The user name; without it, no authentication.
|
|
58
|
+
* @default none (no authentication)
|
|
59
|
+
*/
|
|
60
|
+
username?: string;
|
|
61
|
+
/**
|
|
62
|
+
* The user's password; empty or omitted for a user without password (trust authentication).
|
|
63
|
+
* @default none
|
|
64
|
+
*/
|
|
65
|
+
password?: string;
|
|
66
|
+
/**
|
|
67
|
+
* The namespace of the entries: the keys are stored as `{namespace}:{key}`, and `clear()` deletes only the
|
|
68
|
+
* namespace's documents. Without it, `clear()` deletes all the collection's documents.
|
|
69
|
+
* @default none
|
|
70
|
+
*/
|
|
71
|
+
namespace?: string;
|
|
72
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the MySQL store (MySQL or MariaDB): one row per entry in a table. It needs the optional peer
|
|
4
|
+
* dependency `mysql2` (`npm install mysql2`): without it, the cache's creation fails with
|
|
5
|
+
* `CacheDriverNotFoundException`.
|
|
6
|
+
*
|
|
7
|
+
* When the cache starts, it creates the table when missing: `(id VARCHAR(255) PRIMARY KEY, value MEDIUMTEXT)`, the
|
|
8
|
+
* keys case sensitive (`utf8mb4_bin`); a failure (an unreachable server, a denied creation) fails the cache's
|
|
9
|
+
* creation. A key longer than 255 characters, its namespace prefix included, fails its write (in the strict SQL mode,
|
|
10
|
+
* the default), and a value is limited to 16 MB of JSON. An expired row is a miss; it stays in the table until it is read, overwritten or cleared. The
|
|
11
|
+
* values are kept as JSON.
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import {Application} from 'lakutata'
|
|
15
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
16
|
+
*
|
|
17
|
+
* Application.run(() => ({
|
|
18
|
+
* id: 'mysql-cache.app',
|
|
19
|
+
* name: 'MysqlCache',
|
|
20
|
+
* components: {
|
|
21
|
+
* cache: buildCacherOptions({
|
|
22
|
+
* stores: {
|
|
23
|
+
* type: 'mysql',
|
|
24
|
+
* host: '127.0.0.1',
|
|
25
|
+
* database: 'app',
|
|
26
|
+
* table: 'cache',
|
|
27
|
+
* username: 'app',
|
|
28
|
+
* password: process.env.MYSQL_PASSWORD ?? ''
|
|
29
|
+
* }
|
|
30
|
+
* })
|
|
31
|
+
* }
|
|
32
|
+
* }))
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare class MysqlCacheOptions extends DTO {
|
|
36
|
+
/**
|
|
37
|
+
* The store type: `'mysql'`.
|
|
38
|
+
*/
|
|
39
|
+
type: 'mysql';
|
|
40
|
+
/**
|
|
41
|
+
* The host name or IP address of the MySQL server.
|
|
42
|
+
*/
|
|
43
|
+
host: string;
|
|
44
|
+
/**
|
|
45
|
+
* The database holding the table.
|
|
46
|
+
*/
|
|
47
|
+
database: string;
|
|
48
|
+
/**
|
|
49
|
+
* The table of the entries, created when missing (the name is quoted: kept as written).
|
|
50
|
+
*/
|
|
51
|
+
table: string;
|
|
52
|
+
/**
|
|
53
|
+
* The port of the MySQL server.
|
|
54
|
+
* @default 3306
|
|
55
|
+
*/
|
|
56
|
+
port?: number;
|
|
57
|
+
/**
|
|
58
|
+
* The user name; without it, the driver's default.
|
|
59
|
+
* @default the driver's default
|
|
60
|
+
*/
|
|
61
|
+
username?: string;
|
|
62
|
+
/**
|
|
63
|
+
* The user's password; empty or omitted for a user without password (trust authentication).
|
|
64
|
+
* @default none
|
|
65
|
+
*/
|
|
66
|
+
password?: string;
|
|
67
|
+
/**
|
|
68
|
+
* The namespace of the entries: the keys are stored as `{namespace}:{key}`, and `clear()` deletes only the
|
|
69
|
+
* namespace's rows. Without it, `clear()` deletes all the table's rows.
|
|
70
|
+
* @default none
|
|
71
|
+
*/
|
|
72
|
+
namespace?: string;
|
|
73
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the PostgreSQL store: one row per entry in a table. It needs the optional peer dependency `pg`
|
|
4
|
+
* (`npm install pg`): without it, the cache's creation fails with `CacheDriverNotFoundException`.
|
|
5
|
+
*
|
|
6
|
+
* When the cache starts, it creates the schema (other than `public`) and the table when missing:
|
|
7
|
+
* `(key VARCHAR(255) PRIMARY KEY, value TEXT)`; a failure (an unreachable server, a denied creation) fails the cache's
|
|
8
|
+
* creation. A key longer than 255 characters, its namespace prefix included, fails its write. An expired row is a
|
|
9
|
+
* miss; it stays in the table until it is read, overwritten or cleared. The errors of the idle connections are emitted
|
|
10
|
+
* as `'error'` events of the cache. The values are kept as JSON.
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import {Application} from 'lakutata'
|
|
14
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
15
|
+
*
|
|
16
|
+
* Application.run(() => ({
|
|
17
|
+
* id: 'postgres-cache.app',
|
|
18
|
+
* name: 'PostgresCache',
|
|
19
|
+
* components: {
|
|
20
|
+
* cache: buildCacherOptions({
|
|
21
|
+
* stores: {
|
|
22
|
+
* type: 'postgres',
|
|
23
|
+
* host: '127.0.0.1',
|
|
24
|
+
* database: 'app',
|
|
25
|
+
* schema: 'cache',
|
|
26
|
+
* table: 'entries',
|
|
27
|
+
* username: 'app',
|
|
28
|
+
* password: process.env.PGPASSWORD ?? '',
|
|
29
|
+
* maxPoolSize: 5
|
|
30
|
+
* },
|
|
31
|
+
* ttl: 600000
|
|
32
|
+
* })
|
|
33
|
+
* }
|
|
34
|
+
* }))
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class PostgresCacheOptions extends DTO {
|
|
38
|
+
/**
|
|
39
|
+
* The store type: `'postgres'`.
|
|
40
|
+
*/
|
|
41
|
+
type: 'postgres';
|
|
42
|
+
/**
|
|
43
|
+
* The host name or IP address of the PostgreSQL server.
|
|
44
|
+
*/
|
|
45
|
+
host: string;
|
|
46
|
+
/**
|
|
47
|
+
* The database holding the table.
|
|
48
|
+
*/
|
|
49
|
+
database: string;
|
|
50
|
+
/**
|
|
51
|
+
* The table of the entries, created when missing. A plain name (letters, digits, `_`, `$`) is folded to lower
|
|
52
|
+
* case by PostgreSQL; any other name is quoted and kept as written.
|
|
53
|
+
*/
|
|
54
|
+
table: string;
|
|
55
|
+
/**
|
|
56
|
+
* The port of the PostgreSQL server.
|
|
57
|
+
* @default 5432
|
|
58
|
+
*/
|
|
59
|
+
port?: number;
|
|
60
|
+
/**
|
|
61
|
+
* The schema of the table, created when missing. Named as the table (a plain name folded to lower case).
|
|
62
|
+
* @default 'public'
|
|
63
|
+
*/
|
|
64
|
+
schema?: string;
|
|
65
|
+
/**
|
|
66
|
+
* The user name; without it, the driver's default: the `PGUSER` environment variable, else the user of the
|
|
67
|
+
* process.
|
|
68
|
+
* @default the driver's default
|
|
69
|
+
*/
|
|
70
|
+
username?: string;
|
|
71
|
+
/**
|
|
72
|
+
* The user's password; empty or omitted for a user without password (trust authentication).
|
|
73
|
+
* @default none
|
|
74
|
+
*/
|
|
75
|
+
password?: string;
|
|
76
|
+
/**
|
|
77
|
+
* The maximum number of connections of the pool (a positive integer).
|
|
78
|
+
* @default 10 (the default of `pg`)
|
|
79
|
+
*/
|
|
80
|
+
maxPoolSize?: number;
|
|
81
|
+
/**
|
|
82
|
+
* The namespace of the entries: the keys are stored as `{namespace}:{key}`, and `clear()` deletes only the
|
|
83
|
+
* namespace's rows. Without it, `clear()` deletes all the table's rows.
|
|
84
|
+
* @default none
|
|
85
|
+
*/
|
|
86
|
+
namespace?: string;
|
|
87
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the Redis store (Redis, Valkey or a compatible server). It needs the optional peer dependency
|
|
4
|
+
* `redis` (`npm install redis`, version 4 or 5; `@redis/client` is used when `redis` is not installed): without it,
|
|
5
|
+
* the cache's creation fails with `CacheDriverNotFoundException`.
|
|
6
|
+
*
|
|
7
|
+
* The store connects when the cache starts. While it is disconnected, its commands fail at once instead of waiting:
|
|
8
|
+
* the reads are misses (emitted as `'error'` events), the writes reject. The connection errors are emitted as
|
|
9
|
+
* `'error'` events of the cache. The entries expire in Redis (`PSETEX`). The values are kept as JSON.
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import {Application} from 'lakutata'
|
|
13
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
14
|
+
*
|
|
15
|
+
* Application.run(() => ({
|
|
16
|
+
* id: 'redis-cache.app',
|
|
17
|
+
* name: 'RedisCache',
|
|
18
|
+
* components: {
|
|
19
|
+
* cache: buildCacherOptions({
|
|
20
|
+
* stores: {
|
|
21
|
+
* type: 'redis',
|
|
22
|
+
* host: '127.0.0.1',
|
|
23
|
+
* password: process.env.REDIS_PASSWORD,
|
|
24
|
+
* namespace: 'app',
|
|
25
|
+
* //Start even when Redis is down, and keep reconnecting
|
|
26
|
+
* throwOnConnectError: false
|
|
27
|
+
* },
|
|
28
|
+
* ttl: 60000
|
|
29
|
+
* })
|
|
30
|
+
* }
|
|
31
|
+
* }))
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare class RedisCacheOptions extends DTO {
|
|
35
|
+
/**
|
|
36
|
+
* The store type: `'redis'`.
|
|
37
|
+
*/
|
|
38
|
+
type: 'redis';
|
|
39
|
+
/**
|
|
40
|
+
* The host name or IP address of the Redis server.
|
|
41
|
+
*/
|
|
42
|
+
host: string;
|
|
43
|
+
/**
|
|
44
|
+
* The port of the Redis server.
|
|
45
|
+
* @default 6379
|
|
46
|
+
*/
|
|
47
|
+
port?: number;
|
|
48
|
+
/**
|
|
49
|
+
* The index of the Redis database.
|
|
50
|
+
* @default 0
|
|
51
|
+
*/
|
|
52
|
+
database?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Whether to connect with TLS (with the system's certificate authorities).
|
|
55
|
+
* @default false
|
|
56
|
+
*/
|
|
57
|
+
tls?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* The delay, in milliseconds, before the first TCP keep-alive probe of the idle connection.
|
|
60
|
+
* @default the driver's default
|
|
61
|
+
*/
|
|
62
|
+
keepAlive?: number;
|
|
63
|
+
/**
|
|
64
|
+
* The user name (Redis 6 ACL).
|
|
65
|
+
* @default none (the default user)
|
|
66
|
+
*/
|
|
67
|
+
username?: string;
|
|
68
|
+
/**
|
|
69
|
+
* The password.
|
|
70
|
+
* @default none
|
|
71
|
+
*/
|
|
72
|
+
password?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Whether to reconnect after the connection is lost, after `100 ms × attempt` (3 seconds at most). When false, the
|
|
75
|
+
* store stays disconnected.
|
|
76
|
+
* @default true
|
|
77
|
+
*/
|
|
78
|
+
reconnect?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* The namespace of the entries: the keys are stored as `{namespace}{keyPrefixSeparator}{namespace}:{key}` (the
|
|
81
|
+
* format of lakutata 2.x), and `clear()` deletes only the namespace's keys. Without it, the keys are stored as
|
|
82
|
+
* they are, and `clear()` deletes all the database's string keys except the ones containing the
|
|
83
|
+
* `keyPrefixSeparator` (unless `noNamespaceAffectsAll`).
|
|
84
|
+
* @default none
|
|
85
|
+
*/
|
|
86
|
+
namespace?: string;
|
|
87
|
+
/**
|
|
88
|
+
* The separator between the namespace and the rest of a key.
|
|
89
|
+
* @default '::'
|
|
90
|
+
*/
|
|
91
|
+
keyPrefixSeparator?: string;
|
|
92
|
+
/**
|
|
93
|
+
* The number of keys `clear()` scans and deletes per batch; `0` for the default.
|
|
94
|
+
* @default 1000
|
|
95
|
+
*/
|
|
96
|
+
clearBatchSize?: number;
|
|
97
|
+
/**
|
|
98
|
+
* Whether to delete with `UNLINK` (the memory reclaimed in the background) rather than `DEL`.
|
|
99
|
+
* @default true
|
|
100
|
+
*/
|
|
101
|
+
useUnlink?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Whether `clear()` without namespace flushes the whole Redis database (`FLUSHDB`, every key of every type and
|
|
104
|
+
* namespace included), instead of scanning for its string keys outside the namespaces.
|
|
105
|
+
* @default false
|
|
106
|
+
*/
|
|
107
|
+
noNamespaceAffectsAll?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* How long, in milliseconds, the first connection may take before it fails.
|
|
110
|
+
* @default 5000
|
|
111
|
+
*/
|
|
112
|
+
connectTimeout?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Whether a failed first connection fails the cache's creation (and so the application's start). When false, the
|
|
115
|
+
* failure is emitted as an `'error'` event of the cache and the store keeps trying to connect in the background
|
|
116
|
+
* (as `reconnect` allows), its commands failing meanwhile.
|
|
117
|
+
* @default true
|
|
118
|
+
*/
|
|
119
|
+
throwOnConnectError?: boolean;
|
|
120
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { DTO } from '@lakutata/core';
|
|
2
|
+
/**
|
|
3
|
+
* The options of the SQLite store: one row per entry in a table of a SQLite database file. It needs the optional peer
|
|
4
|
+
* dependency `sqlite3` (`npm install sqlite3`): without it, the cache's creation fails with
|
|
5
|
+
* `CacheDriverNotFoundException`.
|
|
6
|
+
*
|
|
7
|
+
* When the cache starts, it opens the database (creating the file) and creates the table when missing:
|
|
8
|
+
* `(key VARCHAR(255) PRIMARY KEY, value TEXT)`. An expired row is a miss; it stays in the table until it is read,
|
|
9
|
+
* overwritten or cleared. The values are kept as JSON.
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import {Application} from 'lakutata'
|
|
13
|
+
* import {buildCacherOptions} from 'lakutata/com/cacher'
|
|
14
|
+
*
|
|
15
|
+
* Application.run(() => ({
|
|
16
|
+
* id: 'sqlite-cache.app',
|
|
17
|
+
* name: 'SqliteCache',
|
|
18
|
+
* components: {
|
|
19
|
+
* cache: buildCacherOptions({
|
|
20
|
+
* stores: {type: 'sqlite', database: './cache.db', table: 'cache', namespace: 'app'}
|
|
21
|
+
* })
|
|
22
|
+
* }
|
|
23
|
+
* }))
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare class SqliteCacheOptions extends DTO {
|
|
27
|
+
/**
|
|
28
|
+
* The store type: `'sqlite'`.
|
|
29
|
+
*/
|
|
30
|
+
type: 'sqlite';
|
|
31
|
+
/**
|
|
32
|
+
* The path of the database file, relative to the working directory (its directory must exist), or `':memory:'`
|
|
33
|
+
* for a database in memory.
|
|
34
|
+
*/
|
|
35
|
+
database: string;
|
|
36
|
+
/**
|
|
37
|
+
* The table of the entries, created when missing. The characters other than letters, digits and `_` are removed
|
|
38
|
+
* from the name, and `_` is prefixed when it does not start with a letter; a name left empty fails the cache's
|
|
39
|
+
* creation.
|
|
40
|
+
*/
|
|
41
|
+
table: string;
|
|
42
|
+
/**
|
|
43
|
+
* How long a statement waits for a database locked by another connection, in milliseconds, before failing.
|
|
44
|
+
* @default 10000
|
|
45
|
+
*/
|
|
46
|
+
busyTimeout?: number;
|
|
47
|
+
/**
|
|
48
|
+
* The namespace of the entries: the keys are stored as `{namespace}:{key}`, and `clear()` deletes only the
|
|
49
|
+
* namespace's rows. Without it, `clear()` deletes all the table's rows.
|
|
50
|
+
* @default none
|
|
51
|
+
*/
|
|
52
|
+
namespace?: string;
|
|
53
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CacheEntry } from '../lib/Serializer.js';
|
|
2
|
+
export type CacheStoreSetEntry = {
|
|
3
|
+
key: string;
|
|
4
|
+
value: unknown;
|
|
5
|
+
ttl?: number;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Whether an entry has expired
|
|
9
|
+
* @param entry
|
|
10
|
+
*/
|
|
11
|
+
export declare function isExpired(entry: CacheEntry): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* The expiration time of a ttl
|
|
14
|
+
* @param ttl
|
|
15
|
+
*/
|
|
16
|
+
export declare function expiresOf(ttl?: number): number | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* A cache store
|
|
19
|
+
* The ttls are in milliseconds, a missing or zero ttl never expires
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class CacheStore {
|
|
22
|
+
/**
|
|
23
|
+
* Report the errors of the background operations
|
|
24
|
+
*/
|
|
25
|
+
onError: (error: Error) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Connect to the backend
|
|
28
|
+
*/
|
|
29
|
+
connect(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Disconnect from the backend
|
|
32
|
+
*/
|
|
33
|
+
disconnect(): Promise<void>;
|
|
34
|
+
abstract get(key: string): Promise<CacheEntry | undefined>;
|
|
35
|
+
abstract getMany(keys: string[]): Promise<(CacheEntry | undefined)[]>;
|
|
36
|
+
abstract set(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
37
|
+
abstract setMany(entries: CacheStoreSetEntry[]): Promise<void>;
|
|
38
|
+
abstract delete(key: string): Promise<boolean>;
|
|
39
|
+
abstract deleteMany(keys: string[]): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Delete the entries of the namespace, or all the entries without namespace
|
|
42
|
+
*/
|
|
43
|
+
abstract clear(): Promise<void>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CacheStore } from './CacheStore.js';
|
|
2
|
+
import { CacheStoreOptions } from '../types/CacheStoreOptions.js';
|
|
3
|
+
/**
|
|
4
|
+
* Create the store of the options
|
|
5
|
+
* @param options
|
|
6
|
+
*/
|
|
7
|
+
export declare function createCacheStore(options: CacheStoreOptions): CacheStore | undefined;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SerializedCacheStore, SerializedEntry } from './SerializedCacheStore.js';
|
|
2
|
+
import { FileCacheOptions } from '../options/FileCacheOptions.js';
|
|
3
|
+
type FileRecord = {
|
|
4
|
+
expire?: number;
|
|
5
|
+
value: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* The file store, the entries are kept in memory and written to the file in batches
|
|
9
|
+
* File format: {"cache":[[key,{"expire":ms,"value":serialized entry}]],"lastExpire":ms}
|
|
10
|
+
*/
|
|
11
|
+
export declare class FileStore extends SerializedCacheStore {
|
|
12
|
+
protected readonly filename: string;
|
|
13
|
+
protected readonly expiredCheckDelay: number;
|
|
14
|
+
protected readonly writeDelay: number;
|
|
15
|
+
protected records: Map<string, FileRecord>;
|
|
16
|
+
protected lastExpire: number;
|
|
17
|
+
protected saveTimer?: NodeJS.Timeout;
|
|
18
|
+
protected saving: Promise<void>;
|
|
19
|
+
constructor(options: FileCacheOptions);
|
|
20
|
+
/**
|
|
21
|
+
* Whether a record has expired
|
|
22
|
+
* @param record
|
|
23
|
+
* @protected
|
|
24
|
+
*/
|
|
25
|
+
protected isExpired(record: FileRecord): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Remove the expired records, at most once in each expiredCheckDelay
|
|
28
|
+
* @protected
|
|
29
|
+
*/
|
|
30
|
+
protected removeExpired(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Save the records after the write delay
|
|
33
|
+
* @protected
|
|
34
|
+
*/
|
|
35
|
+
protected scheduleSave(): void;
|
|
36
|
+
/**
|
|
37
|
+
* Write the records to the file, through a temporary file
|
|
38
|
+
* @protected
|
|
39
|
+
*/
|
|
40
|
+
protected save(): Promise<void>;
|
|
41
|
+
connect(): Promise<void>;
|
|
42
|
+
disconnect(): Promise<void>;
|
|
43
|
+
protected read(key: string): Promise<string | undefined>;
|
|
44
|
+
protected readMany(keys: string[]): Promise<(string | undefined)[]>;
|
|
45
|
+
protected write(entry: SerializedEntry): Promise<void>;
|
|
46
|
+
protected writeMany(entries: SerializedEntry[]): Promise<void>;
|
|
47
|
+
protected remove(key: string): Promise<boolean>;
|
|
48
|
+
protected removeMany(keys: string[]): Promise<boolean>;
|
|
49
|
+
clear(): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
export {};
|