@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.
Files changed (116) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +175 -0
  3. package/dist/cjs/cache/Cacher.d.ts +446 -0
  4. package/dist/cjs/cache/Cacher.js +523 -0
  5. package/dist/cjs/cache/exceptions/CacheDriverNotFoundException.d.ts +10 -0
  6. package/dist/cjs/cache/exceptions/CacheDriverNotFoundException.js +17 -0
  7. package/dist/cjs/cache/interfaces/CacherOptions.d.ts +42 -0
  8. package/dist/cjs/cache/interfaces/CacherOptions.js +2 -0
  9. package/dist/cjs/cache/lib/LikePrefix.d.ts +5 -0
  10. package/dist/cjs/cache/lib/LikePrefix.js +10 -0
  11. package/dist/cjs/cache/lib/LoadDriver.d.ts +6 -0
  12. package/dist/cjs/cache/lib/LoadDriver.js +66 -0
  13. package/dist/cjs/cache/lib/Serializer.d.ts +18 -0
  14. package/dist/cjs/cache/lib/Serializer.js +46 -0
  15. package/dist/cjs/cache/lib/WithTimeout.d.ts +7 -0
  16. package/dist/cjs/cache/lib/WithTimeout.js +23 -0
  17. package/dist/cjs/cache/options/FileCacheOptions.d.ts +54 -0
  18. package/dist/cjs/cache/options/FileCacheOptions.js +62 -0
  19. package/dist/cjs/cache/options/MemcacheCacheOptions.d.ts +57 -0
  20. package/dist/cjs/cache/options/MemcacheCacheOptions.js +66 -0
  21. package/dist/cjs/cache/options/MemoryCacheOptions.d.ts +39 -0
  22. package/dist/cjs/cache/options/MemoryCacheOptions.js +52 -0
  23. package/dist/cjs/cache/options/MongoCacheOptions.d.ts +72 -0
  24. package/dist/cjs/cache/options/MongoCacheOptions.js +81 -0
  25. package/dist/cjs/cache/options/MysqlCacheOptions.d.ts +73 -0
  26. package/dist/cjs/cache/options/MysqlCacheOptions.js +82 -0
  27. package/dist/cjs/cache/options/PostgresCacheOptions.d.ts +87 -0
  28. package/dist/cjs/cache/options/PostgresCacheOptions.js +92 -0
  29. package/dist/cjs/cache/options/RedisCacheOptions.d.ts +120 -0
  30. package/dist/cjs/cache/options/RedisCacheOptions.js +113 -0
  31. package/dist/cjs/cache/options/SqliteCacheOptions.d.ts +53 -0
  32. package/dist/cjs/cache/options/SqliteCacheOptions.js +61 -0
  33. package/dist/cjs/cache/stores/CacheStore.d.ts +44 -0
  34. package/dist/cjs/cache/stores/CacheStore.js +42 -0
  35. package/dist/cjs/cache/stores/CreateCacheStore.d.ts +7 -0
  36. package/dist/cjs/cache/stores/CreateCacheStore.js +37 -0
  37. package/dist/cjs/cache/stores/FileStore.d.ts +51 -0
  38. package/dist/cjs/cache/stores/FileStore.js +143 -0
  39. package/dist/cjs/cache/stores/MemcacheStore.d.ts +42 -0
  40. package/dist/cjs/cache/stores/MemcacheStore.js +69 -0
  41. package/dist/cjs/cache/stores/MemoryStore.d.ts +34 -0
  42. package/dist/cjs/cache/stores/MemoryStore.js +75 -0
  43. package/dist/cjs/cache/stores/MongoStore.d.ts +55 -0
  44. package/dist/cjs/cache/stores/MongoStore.js +72 -0
  45. package/dist/cjs/cache/stores/MysqlStore.d.ts +27 -0
  46. package/dist/cjs/cache/stores/MysqlStore.js +71 -0
  47. package/dist/cjs/cache/stores/PostgresStore.d.ts +30 -0
  48. package/dist/cjs/cache/stores/PostgresStore.js +85 -0
  49. package/dist/cjs/cache/stores/RedisStore.d.ts +58 -0
  50. package/dist/cjs/cache/stores/RedisStore.js +131 -0
  51. package/dist/cjs/cache/stores/SerializedCacheStore.d.ts +48 -0
  52. package/dist/cjs/cache/stores/SerializedCacheStore.js +80 -0
  53. package/dist/cjs/cache/stores/SqliteStore.d.ts +44 -0
  54. package/dist/cjs/cache/stores/SqliteStore.js +91 -0
  55. package/dist/cjs/cache/types/CacheStoreOptions.d.ts +15 -0
  56. package/dist/cjs/cache/types/CacheStoreOptions.js +2 -0
  57. package/dist/cjs/exports/Cache.d.ts +12 -0
  58. package/dist/cjs/exports/Cache.js +31 -0
  59. package/dist/cjs/package.json +1 -0
  60. package/dist/esm/cache/Cacher.js +518 -0
  61. package/dist/esm/cache/exceptions/CacheDriverNotFoundException.js +13 -0
  62. package/dist/esm/cache/interfaces/CacherOptions.js +1 -0
  63. package/dist/esm/cache/lib/LikePrefix.js +7 -0
  64. package/dist/esm/cache/lib/LoadDriver.js +30 -0
  65. package/dist/esm/cache/lib/Serializer.js +42 -0
  66. package/dist/esm/cache/lib/WithTimeout.js +20 -0
  67. package/dist/esm/cache/options/FileCacheOptions.js +58 -0
  68. package/dist/esm/cache/options/MemcacheCacheOptions.js +62 -0
  69. package/dist/esm/cache/options/MemoryCacheOptions.js +48 -0
  70. package/dist/esm/cache/options/MongoCacheOptions.js +77 -0
  71. package/dist/esm/cache/options/MysqlCacheOptions.js +78 -0
  72. package/dist/esm/cache/options/PostgresCacheOptions.js +88 -0
  73. package/dist/esm/cache/options/RedisCacheOptions.js +109 -0
  74. package/dist/esm/cache/options/SqliteCacheOptions.js +57 -0
  75. package/dist/esm/cache/stores/CacheStore.js +36 -0
  76. package/dist/esm/cache/stores/CreateCacheStore.js +34 -0
  77. package/dist/esm/cache/stores/FileStore.js +136 -0
  78. package/dist/esm/cache/stores/MemcacheStore.js +65 -0
  79. package/dist/esm/cache/stores/MemoryStore.js +71 -0
  80. package/dist/esm/cache/stores/MongoStore.js +68 -0
  81. package/dist/esm/cache/stores/MysqlStore.js +67 -0
  82. package/dist/esm/cache/stores/PostgresStore.js +81 -0
  83. package/dist/esm/cache/stores/RedisStore.js +127 -0
  84. package/dist/esm/cache/stores/SerializedCacheStore.js +76 -0
  85. package/dist/esm/cache/stores/SqliteStore.js +87 -0
  86. package/dist/esm/cache/types/CacheStoreOptions.js +1 -0
  87. package/dist/esm/exports/Cache.js +12 -0
  88. package/dist/types/cache/Cacher.d.ts +446 -0
  89. package/dist/types/cache/exceptions/CacheDriverNotFoundException.d.ts +10 -0
  90. package/dist/types/cache/interfaces/CacherOptions.d.ts +42 -0
  91. package/dist/types/cache/lib/LikePrefix.d.ts +5 -0
  92. package/dist/types/cache/lib/LoadDriver.d.ts +6 -0
  93. package/dist/types/cache/lib/Serializer.d.ts +18 -0
  94. package/dist/types/cache/lib/WithTimeout.d.ts +7 -0
  95. package/dist/types/cache/options/FileCacheOptions.d.ts +54 -0
  96. package/dist/types/cache/options/MemcacheCacheOptions.d.ts +57 -0
  97. package/dist/types/cache/options/MemoryCacheOptions.d.ts +39 -0
  98. package/dist/types/cache/options/MongoCacheOptions.d.ts +72 -0
  99. package/dist/types/cache/options/MysqlCacheOptions.d.ts +73 -0
  100. package/dist/types/cache/options/PostgresCacheOptions.d.ts +87 -0
  101. package/dist/types/cache/options/RedisCacheOptions.d.ts +120 -0
  102. package/dist/types/cache/options/SqliteCacheOptions.d.ts +53 -0
  103. package/dist/types/cache/stores/CacheStore.d.ts +44 -0
  104. package/dist/types/cache/stores/CreateCacheStore.d.ts +7 -0
  105. package/dist/types/cache/stores/FileStore.d.ts +51 -0
  106. package/dist/types/cache/stores/MemcacheStore.d.ts +42 -0
  107. package/dist/types/cache/stores/MemoryStore.d.ts +34 -0
  108. package/dist/types/cache/stores/MongoStore.d.ts +55 -0
  109. package/dist/types/cache/stores/MysqlStore.d.ts +27 -0
  110. package/dist/types/cache/stores/PostgresStore.d.ts +30 -0
  111. package/dist/types/cache/stores/RedisStore.d.ts +58 -0
  112. package/dist/types/cache/stores/SerializedCacheStore.d.ts +48 -0
  113. package/dist/types/cache/stores/SqliteStore.d.ts +44 -0
  114. package/dist/types/cache/types/CacheStoreOptions.d.ts +15 -0
  115. package/dist/types/exports/Cache.d.ts +12 -0
  116. package/package.json +72 -0
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MongoStore = void 0;
4
+ const SerializedCacheStore_js_1 = require("./SerializedCacheStore.js");
5
+ const LoadDriver_js_1 = require("../lib/LoadDriver.js");
6
+ /**
7
+ * Escape the regular expression characters
8
+ * @param text
9
+ */
10
+ function escapeRegExp(text) {
11
+ return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
12
+ }
13
+ /**
14
+ * The MongoDB store
15
+ * Document format: {key: "{namespace}:{key}", value: serialized entry, expiresAt: Date | null}, removed by a TTL index
16
+ */
17
+ class MongoStore extends SerializedCacheStore_js_1.SerializedCacheStore {
18
+ constructor(options) {
19
+ super(options.namespace);
20
+ this.options = options;
21
+ }
22
+ async connect() {
23
+ const driver = await (0, LoadDriver_js_1.loadDriver)('MongoClient', 'mongodb');
24
+ const options = this.options;
25
+ const auth = options.username ? `${encodeURIComponent(options.username)}:${encodeURIComponent(options.password ?? '')}@` : '';
26
+ this.client = new driver.MongoClient(`mongodb://${auth}${options.host}:${options.port ?? 27017}`);
27
+ await this.client.connect();
28
+ this.collection = this.client.db(options.database).collection(options.collection);
29
+ await this.collection.createIndex({ key: 1 }, { unique: true, background: true });
30
+ await this.collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0, background: true });
31
+ }
32
+ async disconnect() {
33
+ await this.client?.close();
34
+ }
35
+ async read(key) {
36
+ const document = await this.collection.findOne({ key: { $eq: key } }, { projection: { _id: 0, value: 1 } });
37
+ return document ? document.value : undefined;
38
+ }
39
+ async readMany(keys) {
40
+ const documents = await this.collection.find({ key: { $in: keys } }, { projection: { _id: 0, key: 1, value: 1 } }).toArray();
41
+ const values = new Map(documents.map((document) => [document.key, document.value]));
42
+ return keys.map((key) => values.get(key));
43
+ }
44
+ /**
45
+ * The upsert of an entry
46
+ * @param entry
47
+ * @protected
48
+ */
49
+ update(entry) {
50
+ return [{ key: { $eq: entry.key } }, { $set: { key: entry.key, value: entry.data, expiresAt: entry.expires ? new Date(entry.expires) : null } }];
51
+ }
52
+ async write(entry) {
53
+ const [filter, update] = this.update(entry);
54
+ await this.collection.updateOne(filter, update, { upsert: true });
55
+ }
56
+ async writeMany(entries) {
57
+ await this.collection.bulkWrite(entries.map((entry) => {
58
+ const [filter, update] = this.update(entry);
59
+ return { updateOne: { filter: filter, update: update, upsert: true } };
60
+ }), { ordered: false });
61
+ }
62
+ async remove(key) {
63
+ return (await this.collection.deleteOne({ key: { $eq: key } })).deletedCount > 0;
64
+ }
65
+ async removeMany(keys) {
66
+ return (await this.collection.deleteMany({ key: { $in: keys } })).deletedCount > 0;
67
+ }
68
+ async clear() {
69
+ await this.collection.deleteMany(this.namespace ? { key: { $regex: `^${escapeRegExp(this.namespace)}:` } } : {});
70
+ }
71
+ }
72
+ exports.MongoStore = MongoStore;
@@ -0,0 +1,27 @@
1
+ import { SerializedCacheStore, SerializedEntry } from './SerializedCacheStore.js';
2
+ import { MysqlCacheOptions } from '../options/MysqlCacheOptions.js';
3
+ interface MysqlPool {
4
+ query(sql: string, values?: unknown[]): Promise<[any, unknown]>;
5
+ escapeId(identifier: string): string;
6
+ end(): Promise<void>;
7
+ }
8
+ /**
9
+ * The MySQL store
10
+ * Table format: (id VARCHAR(255) PRIMARY KEY, value TEXT), with "{namespace}:{key}" ids
11
+ */
12
+ export declare class MysqlStore extends SerializedCacheStore {
13
+ protected readonly options: MysqlCacheOptions;
14
+ protected pool: MysqlPool;
15
+ protected table: string;
16
+ constructor(options: MysqlCacheOptions);
17
+ connect(): Promise<void>;
18
+ disconnect(): Promise<void>;
19
+ protected read(key: string): Promise<string | undefined>;
20
+ protected readMany(keys: string[]): Promise<(string | undefined)[]>;
21
+ protected write(entry: SerializedEntry): Promise<void>;
22
+ protected writeMany(entries: SerializedEntry[]): Promise<void>;
23
+ protected remove(key: string): Promise<boolean>;
24
+ protected removeMany(keys: string[]): Promise<boolean>;
25
+ clear(): Promise<void>;
26
+ }
27
+ export {};
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MysqlStore = void 0;
4
+ const SerializedCacheStore_js_1 = require("./SerializedCacheStore.js");
5
+ const LoadDriver_js_1 = require("../lib/LoadDriver.js");
6
+ const LikePrefix_js_1 = require("../lib/LikePrefix.js");
7
+ /**
8
+ * The MySQL store
9
+ * Table format: (id VARCHAR(255) PRIMARY KEY, value TEXT), with "{namespace}:{key}" ids
10
+ */
11
+ class MysqlStore extends SerializedCacheStore_js_1.SerializedCacheStore {
12
+ constructor(options) {
13
+ super(options.namespace);
14
+ this.options = options;
15
+ }
16
+ async connect() {
17
+ const driver = await (0, LoadDriver_js_1.loadDriver)('createPool', 'mysql2');
18
+ const options = this.options;
19
+ this.pool = driver.createPool({
20
+ host: options.host,
21
+ port: options.port,
22
+ user: options.username,
23
+ password: options.password,
24
+ database: options.database
25
+ }).promise();
26
+ this.table = this.pool.escapeId(options.table);
27
+ try {
28
+ //The binary collation keeps the keys case sensitive, the tables of 2.x are used as they are
29
+ await this.pool.query(`CREATE TABLE IF NOT EXISTS ${this.table}(id VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin PRIMARY KEY, value MEDIUMTEXT)`);
30
+ }
31
+ catch (e) {
32
+ await this.pool.end().catch(() => undefined);
33
+ throw e;
34
+ }
35
+ }
36
+ async disconnect() {
37
+ await this.pool?.end();
38
+ }
39
+ async read(key) {
40
+ const [rows] = await this.pool.query(`SELECT value FROM ${this.table} WHERE id = ?`, [key]);
41
+ return rows[0]?.value;
42
+ }
43
+ async readMany(keys) {
44
+ const [rows] = await this.pool.query(`SELECT id, value FROM ${this.table} WHERE id IN (?)`, [keys]);
45
+ const values = new Map(rows.map((row) => [row.id, row.value]));
46
+ return keys.map((key) => values.get(key));
47
+ }
48
+ async write(entry) {
49
+ await this.pool.query(`INSERT INTO ${this.table} (id, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value = VALUES(value)`, [entry.key, entry.data]);
50
+ }
51
+ async writeMany(entries) {
52
+ await this.pool.query(`INSERT INTO ${this.table} (id, value) VALUES ? ON DUPLICATE KEY UPDATE value = VALUES(value)`, [entries.map((entry) => [entry.key, entry.data])]);
53
+ }
54
+ async remove(key) {
55
+ const [result] = await this.pool.query(`DELETE FROM ${this.table} WHERE id = ?`, [key]);
56
+ return result.affectedRows > 0;
57
+ }
58
+ async removeMany(keys) {
59
+ const [result] = await this.pool.query(`DELETE FROM ${this.table} WHERE id IN (?)`, [keys]);
60
+ return result.affectedRows > 0;
61
+ }
62
+ async clear() {
63
+ if (this.namespace) {
64
+ await this.pool.query(`DELETE FROM ${this.table} WHERE id LIKE ? ESCAPE '!'`, [(0, LikePrefix_js_1.likePrefix)(this.namespace)]);
65
+ }
66
+ else {
67
+ await this.pool.query(`DELETE FROM ${this.table}`);
68
+ }
69
+ }
70
+ }
71
+ exports.MysqlStore = MysqlStore;
@@ -0,0 +1,30 @@
1
+ import { SerializedCacheStore, SerializedEntry } from './SerializedCacheStore.js';
2
+ import { PostgresCacheOptions } from '../options/PostgresCacheOptions.js';
3
+ interface PostgresPool {
4
+ query(sql: string, values?: unknown[]): Promise<{
5
+ rows: any[];
6
+ rowCount: number | null;
7
+ }>;
8
+ on(event: string, listener: (...args: any[]) => void): unknown;
9
+ end(): Promise<void>;
10
+ }
11
+ /**
12
+ * The PostgreSQL store
13
+ * Table format: (key VARCHAR(255) PRIMARY KEY, value TEXT), with "{namespace}:{key}" keys
14
+ */
15
+ export declare class PostgresStore extends SerializedCacheStore {
16
+ protected readonly options: PostgresCacheOptions;
17
+ protected pool: PostgresPool;
18
+ protected table: string;
19
+ constructor(options: PostgresCacheOptions);
20
+ connect(): Promise<void>;
21
+ disconnect(): Promise<void>;
22
+ protected read(key: string): Promise<string | undefined>;
23
+ protected readMany(keys: string[]): Promise<(string | undefined)[]>;
24
+ protected write(entry: SerializedEntry): Promise<void>;
25
+ protected writeMany(entries: SerializedEntry[]): Promise<void>;
26
+ protected remove(key: string): Promise<boolean>;
27
+ protected removeMany(keys: string[]): Promise<boolean>;
28
+ clear(): Promise<void>;
29
+ }
30
+ export {};
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PostgresStore = void 0;
4
+ const SerializedCacheStore_js_1 = require("./SerializedCacheStore.js");
5
+ const LoadDriver_js_1 = require("../lib/LoadDriver.js");
6
+ const LikePrefix_js_1 = require("../lib/LikePrefix.js");
7
+ /**
8
+ * An identifier of the SQL text, the plain names are kept unquoted as in 2.x (and so folded to lower case)
9
+ * @param name
10
+ */
11
+ function identifier(name) {
12
+ return /^[A-Za-z_][A-Za-z0-9_$]*$/.test(name) ? name : `"${name.replace(/"/g, '""')}"`;
13
+ }
14
+ /**
15
+ * The PostgreSQL store
16
+ * Table format: (key VARCHAR(255) PRIMARY KEY, value TEXT), with "{namespace}:{key}" keys
17
+ */
18
+ class PostgresStore extends SerializedCacheStore_js_1.SerializedCacheStore {
19
+ constructor(options) {
20
+ super(options.namespace);
21
+ this.options = options;
22
+ }
23
+ async connect() {
24
+ const driver = await (0, LoadDriver_js_1.loadDriver)('Pool', 'pg');
25
+ const options = this.options;
26
+ this.pool = new driver.Pool({
27
+ host: options.host,
28
+ port: options.port,
29
+ user: options.username,
30
+ password: options.password,
31
+ database: options.database,
32
+ max: options.maxPoolSize
33
+ });
34
+ this.pool.on('error', (error) => this.onError(error));
35
+ const schema = identifier(options.schema ?? 'public');
36
+ this.table = `${schema}.${identifier(options.table)}`;
37
+ try {
38
+ if (schema !== 'public')
39
+ await this.pool.query(`CREATE SCHEMA IF NOT EXISTS ${schema}`);
40
+ await this.pool.query(`CREATE TABLE IF NOT EXISTS ${this.table}(key VARCHAR(255) PRIMARY KEY, value TEXT)`);
41
+ }
42
+ catch (e) {
43
+ //A concurrent creation of the same table
44
+ if (e.code !== '23505') {
45
+ await this.pool.end().catch(() => undefined);
46
+ throw e;
47
+ }
48
+ }
49
+ }
50
+ async disconnect() {
51
+ await this.pool?.end();
52
+ }
53
+ async read(key) {
54
+ const { rows } = await this.pool.query(`SELECT value FROM ${this.table} WHERE key = $1`, [key]);
55
+ return rows[0]?.value;
56
+ }
57
+ async readMany(keys) {
58
+ const { rows } = await this.pool.query(`SELECT key, value FROM ${this.table} WHERE key = ANY($1)`, [keys]);
59
+ const values = new Map(rows.map((row) => [row.key, row.value]));
60
+ return keys.map((key) => values.get(key));
61
+ }
62
+ async write(entry) {
63
+ await this.pool.query(`INSERT INTO ${this.table} (key, value) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET value = excluded.value`, [entry.key, entry.data]);
64
+ }
65
+ async writeMany(entries) {
66
+ //The last of the duplicate keys wins, one statement cannot update a row twice
67
+ const latest = new Map(entries.map((entry) => [entry.key, entry.data]));
68
+ await this.pool.query(`INSERT INTO ${this.table} (key, value) SELECT * FROM UNNEST($1::text[], $2::text[]) ON CONFLICT (key) DO UPDATE SET value = excluded.value`, [[...latest.keys()], [...latest.values()]]);
69
+ }
70
+ async remove(key) {
71
+ return !!(await this.pool.query(`DELETE FROM ${this.table} WHERE key = $1`, [key])).rowCount;
72
+ }
73
+ async removeMany(keys) {
74
+ return !!(await this.pool.query(`DELETE FROM ${this.table} WHERE key = ANY($1)`, [keys])).rowCount;
75
+ }
76
+ async clear() {
77
+ if (this.namespace) {
78
+ await this.pool.query(`DELETE FROM ${this.table} WHERE key LIKE $1 ESCAPE '!'`, [(0, LikePrefix_js_1.likePrefix)(this.namespace)]);
79
+ }
80
+ else {
81
+ await this.pool.query(`DELETE FROM ${this.table}`);
82
+ }
83
+ }
84
+ }
85
+ exports.PostgresStore = PostgresStore;
@@ -0,0 +1,58 @@
1
+ import { SerializedCacheStore, SerializedEntry } from './SerializedCacheStore.js';
2
+ import { RedisCacheOptions } from '../options/RedisCacheOptions.js';
3
+ interface RedisCommands<R> {
4
+ set(key: string, value: string): R;
5
+ pSetEx(key: string, milliseconds: number, value: string): R;
6
+ }
7
+ interface RedisMulti extends RedisCommands<RedisMulti> {
8
+ exec(): Promise<unknown[]>;
9
+ }
10
+ interface RedisClient extends RedisCommands<Promise<unknown>> {
11
+ isOpen: boolean;
12
+ isReady: boolean;
13
+ on(event: string, listener: (...args: any[]) => void): unknown;
14
+ connect(): Promise<unknown>;
15
+ disconnect(): Promise<unknown>;
16
+ quit?(): Promise<unknown>;
17
+ close?(): Promise<unknown>;
18
+ destroy?(): void;
19
+ get(key: string): Promise<string | null>;
20
+ mGet(keys: string[]): Promise<(string | null)[]>;
21
+ del(keys: string | string[]): Promise<number>;
22
+ unlink(keys: string | string[]): Promise<number>;
23
+ flushDb(): Promise<unknown>;
24
+ multi(): RedisMulti;
25
+ scanIterator(options: {
26
+ MATCH?: string;
27
+ COUNT?: number;
28
+ TYPE?: string;
29
+ }): AsyncIterable<string | string[]>;
30
+ }
31
+ /**
32
+ * The Redis store
33
+ * Key format: "{namespace}{keyPrefixSeparator}{namespace}:{key}", or "{key}" without namespace
34
+ */
35
+ export declare class RedisStore extends SerializedCacheStore {
36
+ protected readonly options: RedisCacheOptions;
37
+ protected readonly separator: string;
38
+ protected readonly clearBatchSize: number;
39
+ protected client: RedisClient;
40
+ constructor(options: RedisCacheOptions);
41
+ protected storeKey(key: string): string;
42
+ connect(): Promise<void>;
43
+ disconnect(): Promise<void>;
44
+ protected read(key: string): Promise<string | undefined>;
45
+ protected readMany(keys: string[]): Promise<(string | undefined)[]>;
46
+ protected write(entry: SerializedEntry): Promise<void>;
47
+ protected writeMany(entries: SerializedEntry[]): Promise<void>;
48
+ /**
49
+ * Delete keys
50
+ * @param keys
51
+ * @protected
52
+ */
53
+ protected unlink(keys: string | string[]): Promise<number>;
54
+ protected remove(key: string): Promise<boolean>;
55
+ protected removeMany(keys: string[]): Promise<boolean>;
56
+ clear(): Promise<void>;
57
+ }
58
+ export {};
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisStore = void 0;
4
+ const SerializedCacheStore_js_1 = require("./SerializedCacheStore.js");
5
+ const LoadDriver_js_1 = require("../lib/LoadDriver.js");
6
+ const WithTimeout_js_1 = require("../lib/WithTimeout.js");
7
+ /**
8
+ * Escape the glob pattern characters of a SCAN match
9
+ * @param text
10
+ */
11
+ function escapeGlob(text) {
12
+ return text.replace(/[*?[\]\\]/g, '\\$&');
13
+ }
14
+ /**
15
+ * The Redis store
16
+ * Key format: "{namespace}{keyPrefixSeparator}{namespace}:{key}", or "{key}" without namespace
17
+ */
18
+ class RedisStore extends SerializedCacheStore_js_1.SerializedCacheStore {
19
+ constructor(options) {
20
+ super(options.namespace);
21
+ this.options = options;
22
+ this.separator = options.keyPrefixSeparator ?? '::';
23
+ this.clearBatchSize = options.clearBatchSize || 1000;
24
+ }
25
+ storeKey(key) {
26
+ return this.namespace ? `${this.namespace}${this.separator}${this.namespace}:${key}` : key;
27
+ }
28
+ async connect() {
29
+ const driver = await (0, LoadDriver_js_1.loadDriver)('createClient', 'redis', '@redis/client');
30
+ const options = this.options;
31
+ const throwOnConnectError = options.throwOnConnectError !== false;
32
+ let connected = false;
33
+ this.client = driver.createClient({
34
+ username: options.username,
35
+ password: options.password,
36
+ database: options.database,
37
+ //Fail the commands while disconnected, a cache miss is better than a wait
38
+ disableOfflineQueue: true,
39
+ socket: {
40
+ host: options.host,
41
+ port: options.port,
42
+ tls: options.tls || undefined,
43
+ keepAlive: options.keepAlive,
44
+ keepAliveInitialDelay: options.keepAlive,
45
+ connectTimeout: options.connectTimeout,
46
+ reconnectStrategy: (retries, cause) => {
47
+ //The first connection fails at once when its errors are thrown
48
+ if (!connected && throwOnConnectError)
49
+ return cause;
50
+ return options.reconnect === false ? false : Math.min(retries * 100, 3000);
51
+ }
52
+ }
53
+ });
54
+ this.client.on('error', (error) => this.onError(error));
55
+ this.client.on('ready', () => {
56
+ connected = true;
57
+ });
58
+ try {
59
+ await (0, WithTimeout_js_1.withTimeout)(this.client.connect(), options.connectTimeout ?? 5000, `Redis connection timed out after ${options.connectTimeout ?? 5000}ms`);
60
+ }
61
+ catch (e) {
62
+ if (!throwOnConnectError)
63
+ return this.onError(e);
64
+ await this.disconnect();
65
+ throw e;
66
+ }
67
+ }
68
+ async disconnect() {
69
+ const client = this.client;
70
+ if (!client?.isOpen)
71
+ return;
72
+ //An offline client cannot quit gracefully
73
+ if (!client.isReady)
74
+ return client.destroy ? client.destroy() : void await client.disconnect().catch(() => undefined);
75
+ await (client.quit ? client.quit() : client.close());
76
+ }
77
+ async read(key) {
78
+ return (await this.client.get(key)) ?? undefined;
79
+ }
80
+ async readMany(keys) {
81
+ return (await this.client.mGet(keys)).map((data) => data ?? undefined);
82
+ }
83
+ async write(entry) {
84
+ entry.ttl ? await this.client.pSetEx(entry.key, Math.ceil(entry.ttl), entry.data) : await this.client.set(entry.key, entry.data);
85
+ }
86
+ async writeMany(entries) {
87
+ const multi = this.client.multi();
88
+ for (const entry of entries)
89
+ entry.ttl ? multi.pSetEx(entry.key, Math.ceil(entry.ttl), entry.data) : multi.set(entry.key, entry.data);
90
+ await multi.exec();
91
+ }
92
+ /**
93
+ * Delete keys
94
+ * @param keys
95
+ * @protected
96
+ */
97
+ async unlink(keys) {
98
+ return this.options.useUnlink === false ? await this.client.del(keys) : await this.client.unlink(keys);
99
+ }
100
+ async remove(key) {
101
+ return (await this.unlink(key)) > 0;
102
+ }
103
+ async removeMany(keys) {
104
+ return (await this.unlink(keys)) > 0;
105
+ }
106
+ async clear() {
107
+ if (!this.namespace && this.options.noNamespaceAffectsAll) {
108
+ await this.client.flushDb();
109
+ return;
110
+ }
111
+ const match = this.namespace ? `${escapeGlob(this.namespace + this.separator)}*` : '*';
112
+ let batch = [];
113
+ const deletions = [];
114
+ for await (const scanned of this.client.scanIterator({ MATCH: match, COUNT: this.clearBatchSize, TYPE: 'string' })) {
115
+ for (const key of Array.isArray(scanned) ? scanned : [scanned]) {
116
+ //Without namespace, the namespaced keys are kept
117
+ if (!this.namespace && key.includes(this.separator))
118
+ continue;
119
+ batch.push(key);
120
+ if (batch.length >= this.clearBatchSize) {
121
+ deletions.push(this.unlink(batch));
122
+ batch = [];
123
+ }
124
+ }
125
+ }
126
+ if (batch.length)
127
+ deletions.push(this.unlink(batch));
128
+ await Promise.all(deletions);
129
+ }
130
+ }
131
+ exports.RedisStore = RedisStore;
@@ -0,0 +1,48 @@
1
+ import { CacheStore, CacheStoreSetEntry } from './CacheStore.js';
2
+ import { CacheEntry } from '../lib/Serializer.js';
3
+ export type SerializedEntry = {
4
+ key: string;
5
+ data: string;
6
+ ttl?: number;
7
+ expires?: number;
8
+ };
9
+ /**
10
+ * A store keeping the entries serialized, under namespaced keys
11
+ */
12
+ export declare abstract class SerializedCacheStore extends CacheStore {
13
+ protected readonly namespace?: string;
14
+ protected constructor(namespace?: string);
15
+ /**
16
+ * The key of an entry in the backend
17
+ * @param key
18
+ * @protected
19
+ */
20
+ protected storeKey(key: string): string;
21
+ protected abstract read(key: string): Promise<string | undefined>;
22
+ protected abstract readMany(keys: string[]): Promise<(string | undefined)[]>;
23
+ protected abstract write(entry: SerializedEntry): Promise<void>;
24
+ protected abstract writeMany(entries: SerializedEntry[]): Promise<void>;
25
+ protected abstract remove(key: string): Promise<boolean>;
26
+ protected abstract removeMany(keys: string[]): Promise<boolean>;
27
+ /**
28
+ * Parse an entry read from the backend, the expired entries are removed
29
+ * @param storeKey
30
+ * @param data
31
+ * @protected
32
+ */
33
+ protected parse(storeKey: string, data: string | undefined): CacheEntry | undefined;
34
+ /**
35
+ * Serialize an entry
36
+ * @param key
37
+ * @param value
38
+ * @param ttl
39
+ * @protected
40
+ */
41
+ protected serialize(key: string, value: unknown, ttl?: number): SerializedEntry;
42
+ get(key: string): Promise<CacheEntry | undefined>;
43
+ getMany(keys: string[]): Promise<(CacheEntry | undefined)[]>;
44
+ set(key: string, value: unknown, ttl?: number): Promise<void>;
45
+ setMany(entries: CacheStoreSetEntry[]): Promise<void>;
46
+ delete(key: string): Promise<boolean>;
47
+ deleteMany(keys: string[]): Promise<boolean>;
48
+ }
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SerializedCacheStore = void 0;
4
+ const CacheStore_js_1 = require("./CacheStore.js");
5
+ const Serializer_js_1 = require("../lib/Serializer.js");
6
+ /**
7
+ * A store keeping the entries serialized, under namespaced keys
8
+ */
9
+ class SerializedCacheStore extends CacheStore_js_1.CacheStore {
10
+ constructor(namespace) {
11
+ super();
12
+ this.namespace = namespace || undefined;
13
+ }
14
+ /**
15
+ * The key of an entry in the backend
16
+ * @param key
17
+ * @protected
18
+ */
19
+ storeKey(key) {
20
+ return this.namespace ? `${this.namespace}:${key}` : key;
21
+ }
22
+ /**
23
+ * Parse an entry read from the backend, the expired entries are removed
24
+ * @param storeKey
25
+ * @param data
26
+ * @protected
27
+ */
28
+ parse(storeKey, data) {
29
+ if (data === undefined)
30
+ return undefined;
31
+ const entry = (0, Serializer_js_1.deserialize)(data);
32
+ if (entry === null || typeof entry !== 'object')
33
+ return undefined;
34
+ if ((0, CacheStore_js_1.isExpired)(entry)) {
35
+ this.remove(storeKey).catch(() => undefined);
36
+ return undefined;
37
+ }
38
+ return entry;
39
+ }
40
+ /**
41
+ * Serialize an entry
42
+ * @param key
43
+ * @param value
44
+ * @param ttl
45
+ * @protected
46
+ */
47
+ serialize(key, value, ttl) {
48
+ ttl = ttl || undefined;
49
+ const expires = (0, CacheStore_js_1.expiresOf)(ttl);
50
+ return { key: this.storeKey(key), data: (0, Serializer_js_1.serialize)({ value: value, expires: expires }), ttl: ttl, expires: expires };
51
+ }
52
+ async get(key) {
53
+ const storeKey = this.storeKey(key);
54
+ return this.parse(storeKey, await this.read(storeKey));
55
+ }
56
+ async getMany(keys) {
57
+ if (!keys.length)
58
+ return [];
59
+ const storeKeys = keys.map((key) => this.storeKey(key));
60
+ const data = await this.readMany(storeKeys);
61
+ return storeKeys.map((storeKey, index) => this.parse(storeKey, data[index]));
62
+ }
63
+ async set(key, value, ttl) {
64
+ await this.write(this.serialize(key, value, ttl));
65
+ }
66
+ async setMany(entries) {
67
+ if (!entries.length)
68
+ return;
69
+ await this.writeMany(entries.map((entry) => this.serialize(entry.key, entry.value, entry.ttl)));
70
+ }
71
+ async delete(key) {
72
+ return await this.remove(this.storeKey(key));
73
+ }
74
+ async deleteMany(keys) {
75
+ if (!keys.length)
76
+ return false;
77
+ return await this.removeMany(keys.map((key) => this.storeKey(key)));
78
+ }
79
+ }
80
+ exports.SerializedCacheStore = SerializedCacheStore;
@@ -0,0 +1,44 @@
1
+ import { SerializedCacheStore, SerializedEntry } from './SerializedCacheStore.js';
2
+ import { SqliteCacheOptions } from '../options/SqliteCacheOptions.js';
3
+ interface SqliteDatabase {
4
+ configure(option: 'busyTimeout', value: number): void;
5
+ all(sql: string, params: unknown[], callback: (error: Error | null, rows: any[]) => void): void;
6
+ run(sql: string, params: unknown[], callback: (this: {
7
+ changes: number;
8
+ }, error: Error | null) => void): void;
9
+ close(callback: (error: Error | null) => void): void;
10
+ }
11
+ /**
12
+ * The SQLite store
13
+ * Table format: (key VARCHAR(255) PRIMARY KEY, value TEXT), with "{namespace}:{key}" keys
14
+ */
15
+ export declare class SqliteStore extends SerializedCacheStore {
16
+ protected readonly options: SqliteCacheOptions;
17
+ protected database: SqliteDatabase;
18
+ protected readonly table: string;
19
+ constructor(options: SqliteCacheOptions);
20
+ /**
21
+ * Query the rows
22
+ * @param sql
23
+ * @param params
24
+ * @protected
25
+ */
26
+ protected all(sql: string, params: unknown[]): Promise<any[]>;
27
+ /**
28
+ * Run a statement, resolve the number of changed rows
29
+ * @param sql
30
+ * @param params
31
+ * @protected
32
+ */
33
+ protected run(sql: string, params?: unknown[]): Promise<number>;
34
+ connect(): Promise<void>;
35
+ disconnect(): Promise<void>;
36
+ protected read(key: string): Promise<string | undefined>;
37
+ protected readMany(keys: string[]): Promise<(string | undefined)[]>;
38
+ protected write(entry: SerializedEntry): Promise<void>;
39
+ protected writeMany(entries: SerializedEntry[]): Promise<void>;
40
+ protected remove(key: string): Promise<boolean>;
41
+ protected removeMany(keys: string[]): Promise<boolean>;
42
+ clear(): Promise<void>;
43
+ }
44
+ export {};