@fedify/mysql 2.1.0-dev.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 +20 -0
- package/README.md +48 -0
- package/dist/_virtual/rolldown_runtime.cjs +33 -0
- package/dist/kv.cjs +235 -0
- package/dist/kv.d.cts +103 -0
- package/dist/kv.d.ts +104 -0
- package/dist/kv.js +234 -0
- package/dist/mod.cjs +8 -0
- package/dist/mod.d.cts +3 -0
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +7 -0
- package/dist/mq.cjs +395 -0
- package/dist/mq.d.cts +126 -0
- package/dist/mq.d.ts +127 -0
- package/dist/mq.js +394 -0
- package/package.json +95 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright 2024–2026 Hong Minhee
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
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, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- deno-fmt-ignore-file -->
|
|
2
|
+
|
|
3
|
+
@fedify/mysql: MySQL/MariaDB drivers for Fedify
|
|
4
|
+
===============================================
|
|
5
|
+
|
|
6
|
+
[![JSR][JSR badge]][JSR]
|
|
7
|
+
[![npm][npm badge]][npm]
|
|
8
|
+
|
|
9
|
+
This package provides [Fedify]'s [`KvStore`] and [`MessageQueue`]
|
|
10
|
+
implementations for MySQL/MariaDB:
|
|
11
|
+
|
|
12
|
+
- [`MysqlKvStore`]
|
|
13
|
+
- [`MysqlMessageQueue`]
|
|
14
|
+
|
|
15
|
+
~~~~ typescript
|
|
16
|
+
import { createFederation } from "@fedify/fedify";
|
|
17
|
+
import { MysqlKvStore, MysqlMessageQueue } from "@fedify/mysql";
|
|
18
|
+
import mysql from "mysql2/promise";
|
|
19
|
+
|
|
20
|
+
const pool = mysql.createPool("mysql://user:password@localhost/dbname");
|
|
21
|
+
|
|
22
|
+
const federation = createFederation({
|
|
23
|
+
kv: new MysqlKvStore(pool),
|
|
24
|
+
queue: new MysqlMessageQueue(pool),
|
|
25
|
+
});
|
|
26
|
+
~~~~
|
|
27
|
+
|
|
28
|
+
[JSR badge]: https://jsr.io/badges/@fedify/mysql
|
|
29
|
+
[JSR]: https://jsr.io/@fedify/mysql
|
|
30
|
+
[npm badge]: https://img.shields.io/npm/v/@fedify/mysql?logo=npm
|
|
31
|
+
[npm]: https://www.npmjs.com/package/@fedify/mysql
|
|
32
|
+
[Fedify]: https://fedify.dev/
|
|
33
|
+
[`KvStore`]: https://jsr.io/@fedify/fedify/doc/federation/~/KvStore
|
|
34
|
+
[`MessageQueue`]: https://jsr.io/@fedify/fedify/doc/federation/~/MessageQueue
|
|
35
|
+
[`MysqlKvStore`]: https://jsr.io/@fedify/mysql/doc/~/MysqlKvStore
|
|
36
|
+
[`MysqlMessageQueue`]: https://jsr.io/@fedify/mysql/doc/mq/~/MysqlMessageQueue
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
Installation
|
|
40
|
+
------------
|
|
41
|
+
|
|
42
|
+
~~~~ sh
|
|
43
|
+
deno add jsr:@fedify/mysql # Deno
|
|
44
|
+
npm add @fedify/mysql mysql2 # npm
|
|
45
|
+
pnpm add @fedify/mysql mysql2 # pnpm
|
|
46
|
+
yarn add @fedify/mysql mysql2 # Yarn
|
|
47
|
+
bun add @fedify/mysql mysql2 # Bun
|
|
48
|
+
~~~~
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
|
3
|
+
|
|
4
|
+
//#region rolldown:runtime
|
|
5
|
+
var __create = Object.create;
|
|
6
|
+
var __defProp = Object.defineProperty;
|
|
7
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
10
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
13
|
+
key = keys[i];
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
15
|
+
get: ((k) => from[k]).bind(null, key),
|
|
16
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
22
|
+
value: mod,
|
|
23
|
+
enumerable: true
|
|
24
|
+
}) : target, mod));
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
|
|
28
|
+
Object.defineProperty(exports, '__toESM', {
|
|
29
|
+
enumerable: true,
|
|
30
|
+
get: function () {
|
|
31
|
+
return __toESM;
|
|
32
|
+
}
|
|
33
|
+
});
|
package/dist/kv.cjs
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
|
|
2
|
+
const { Temporal } = require("@js-temporal/polyfill");
|
|
3
|
+
|
|
4
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
5
|
+
const es_toolkit = require_rolldown_runtime.__toESM(require("es-toolkit"));
|
|
6
|
+
const __logtape_logtape = require_rolldown_runtime.__toESM(require("@logtape/logtape"));
|
|
7
|
+
|
|
8
|
+
//#region src/kv.ts
|
|
9
|
+
const logger = (0, __logtape_logtape.getLogger)([
|
|
10
|
+
"fedify",
|
|
11
|
+
"mysql",
|
|
12
|
+
"kv"
|
|
13
|
+
]);
|
|
14
|
+
/**
|
|
15
|
+
* A key-value store that uses MySQL (or MariaDB) as the underlying storage.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { createFederation } from "@fedify/fedify";
|
|
20
|
+
* import { MysqlKvStore } from "@fedify/mysql";
|
|
21
|
+
* import mysql from "mysql2/promise";
|
|
22
|
+
*
|
|
23
|
+
* const pool = mysql.createPool("mysql://user:pass@localhost/db");
|
|
24
|
+
*
|
|
25
|
+
* const federation = createFederation({
|
|
26
|
+
* // ...
|
|
27
|
+
* kv: new MysqlKvStore(pool),
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @since 2.1.0
|
|
32
|
+
*/
|
|
33
|
+
var MysqlKvStore = class {
|
|
34
|
+
#pool;
|
|
35
|
+
#tableName;
|
|
36
|
+
#expireCleanupRate;
|
|
37
|
+
#initialized;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a new MySQL key-value store.
|
|
40
|
+
* @param pool The MySQL connection pool to use.
|
|
41
|
+
* @param options The options for the key-value store.
|
|
42
|
+
* @since 2.1.0
|
|
43
|
+
*/
|
|
44
|
+
constructor(pool, options = {}) {
|
|
45
|
+
this.#pool = pool;
|
|
46
|
+
const tableName = options.tableName ?? "fedify_kv";
|
|
47
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) throw new RangeError(`Invalid table name: ${JSON.stringify(tableName)}. Table names must start with a letter or underscore and contain only letters, digits, and underscores.`);
|
|
48
|
+
if (tableName.length > 50) throw new RangeError(`Invalid table name: ${JSON.stringify(tableName)}. Table names must be at most 50 characters long (MySQL identifier limit is 64 chars; the derived index "idx_<name>_expires" uses 12 more).`);
|
|
49
|
+
this.#tableName = tableName;
|
|
50
|
+
const expireCleanupRate = options.expireCleanupRate ?? 1;
|
|
51
|
+
if (expireCleanupRate < 0 || expireCleanupRate > 1) throw new RangeError(`Invalid expireCleanupRate: ${expireCleanupRate}. Must be a number between 0 and 1 inclusive.`);
|
|
52
|
+
this.#expireCleanupRate = expireCleanupRate;
|
|
53
|
+
this.#initialized = options.initialized ?? false;
|
|
54
|
+
}
|
|
55
|
+
async #expire() {
|
|
56
|
+
if (this.#expireCleanupRate <= 0) return;
|
|
57
|
+
if (this.#expireCleanupRate < 1 && Math.random() >= this.#expireCleanupRate) return;
|
|
58
|
+
await this.#pool.query(`DELETE FROM \`${this.#tableName}\`
|
|
59
|
+
WHERE \`expires\` IS NOT NULL AND \`expires\` < NOW(6)`);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* {@inheritDoc KvStore.get}
|
|
63
|
+
* @since 2.1.0
|
|
64
|
+
*/
|
|
65
|
+
async get(key) {
|
|
66
|
+
await this.initialize();
|
|
67
|
+
const serializedKey = JSON.stringify([...key]);
|
|
68
|
+
const [rows] = await this.#pool.query(`SELECT \`value\` FROM \`${this.#tableName}\`
|
|
69
|
+
WHERE \`key\` = ?
|
|
70
|
+
AND (\`expires\` IS NULL OR \`expires\` > NOW(6))`, [serializedKey]);
|
|
71
|
+
if (rows.length < 1) return void 0;
|
|
72
|
+
return rows[0].value;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* {@inheritDoc KvStore.set}
|
|
76
|
+
* @since 2.1.0
|
|
77
|
+
*/
|
|
78
|
+
async set(key, value, options) {
|
|
79
|
+
if (value === void 0) return;
|
|
80
|
+
await this.initialize();
|
|
81
|
+
const serializedKey = JSON.stringify([...key]);
|
|
82
|
+
const jsonValue = JSON.stringify(value);
|
|
83
|
+
if (options?.ttl != null) {
|
|
84
|
+
const ttlSeconds = durationToSeconds(options.ttl);
|
|
85
|
+
await this.#pool.query(`INSERT INTO \`${this.#tableName}\` (\`key\`, \`value\`, \`expires\`)
|
|
86
|
+
VALUES (?, CAST(? AS JSON),
|
|
87
|
+
DATE_ADD(NOW(6), INTERVAL ? SECOND))
|
|
88
|
+
ON DUPLICATE KEY UPDATE
|
|
89
|
+
\`value\` = VALUES(\`value\`),
|
|
90
|
+
\`expires\` = VALUES(\`expires\`)`, [
|
|
91
|
+
serializedKey,
|
|
92
|
+
jsonValue,
|
|
93
|
+
ttlSeconds
|
|
94
|
+
]);
|
|
95
|
+
} else await this.#pool.query(`INSERT INTO \`${this.#tableName}\` (\`key\`, \`value\`, \`expires\`)
|
|
96
|
+
VALUES (?, CAST(? AS JSON), NULL)
|
|
97
|
+
ON DUPLICATE KEY UPDATE
|
|
98
|
+
\`value\` = VALUES(\`value\`),
|
|
99
|
+
\`expires\` = NULL`, [serializedKey, jsonValue]);
|
|
100
|
+
await this.#expire();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* {@inheritDoc KvStore.delete}
|
|
104
|
+
* @since 2.1.0
|
|
105
|
+
*/
|
|
106
|
+
async delete(key) {
|
|
107
|
+
await this.initialize();
|
|
108
|
+
const serializedKey = JSON.stringify([...key]);
|
|
109
|
+
await this.#pool.query(`DELETE FROM \`${this.#tableName}\` WHERE \`key\` = ?`, [serializedKey]);
|
|
110
|
+
await this.#expire();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* {@inheritDoc KvStore.cas}
|
|
114
|
+
* @since 2.1.0
|
|
115
|
+
*/
|
|
116
|
+
async cas(key, expectedValue, newValue, options) {
|
|
117
|
+
await this.initialize();
|
|
118
|
+
const serializedKey = JSON.stringify([...key]);
|
|
119
|
+
let conn;
|
|
120
|
+
try {
|
|
121
|
+
conn = await this.#pool.getConnection();
|
|
122
|
+
await conn.beginTransaction();
|
|
123
|
+
const [rows] = await conn.query(`SELECT
|
|
124
|
+
\`value\`,
|
|
125
|
+
(\`expires\` IS NOT NULL AND \`expires\` <= NOW(6)) AS \`is_expired\`
|
|
126
|
+
FROM \`${this.#tableName}\`
|
|
127
|
+
WHERE \`key\` = ?
|
|
128
|
+
FOR UPDATE`, [serializedKey]);
|
|
129
|
+
const row = rows[0];
|
|
130
|
+
const currentValue = !row || row.is_expired ? void 0 : row.value;
|
|
131
|
+
if (!(0, es_toolkit.isEqual)(currentValue, expectedValue)) {
|
|
132
|
+
await conn.rollback();
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
if (newValue === void 0) await conn.query(`DELETE FROM \`${this.#tableName}\` WHERE \`key\` = ?`, [serializedKey]);
|
|
136
|
+
else {
|
|
137
|
+
const jsonValue = JSON.stringify(newValue);
|
|
138
|
+
if (options?.ttl != null) {
|
|
139
|
+
const ttlSeconds = durationToSeconds(options.ttl);
|
|
140
|
+
await conn.query(`INSERT INTO \`${this.#tableName}\`
|
|
141
|
+
(\`key\`, \`value\`, \`expires\`)
|
|
142
|
+
VALUES (?, CAST(? AS JSON),
|
|
143
|
+
DATE_ADD(NOW(6), INTERVAL ? SECOND))
|
|
144
|
+
ON DUPLICATE KEY UPDATE
|
|
145
|
+
\`value\` = VALUES(\`value\`),
|
|
146
|
+
\`expires\` = VALUES(\`expires\`)`, [
|
|
147
|
+
serializedKey,
|
|
148
|
+
jsonValue,
|
|
149
|
+
ttlSeconds
|
|
150
|
+
]);
|
|
151
|
+
} else await conn.query(`INSERT INTO \`${this.#tableName}\`
|
|
152
|
+
(\`key\`, \`value\`, \`expires\`)
|
|
153
|
+
VALUES (?, CAST(? AS JSON), NULL)
|
|
154
|
+
ON DUPLICATE KEY UPDATE
|
|
155
|
+
\`value\` = VALUES(\`value\`),
|
|
156
|
+
\`expires\` = NULL`, [serializedKey, jsonValue]);
|
|
157
|
+
}
|
|
158
|
+
await conn.commit();
|
|
159
|
+
await this.#expire();
|
|
160
|
+
return true;
|
|
161
|
+
} catch (e) {
|
|
162
|
+
if (conn) await conn.rollback();
|
|
163
|
+
throw e;
|
|
164
|
+
} finally {
|
|
165
|
+
if (conn) conn.release();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* {@inheritDoc KvStore.list}
|
|
170
|
+
* @since 2.1.0
|
|
171
|
+
*/
|
|
172
|
+
async *list(prefix) {
|
|
173
|
+
await this.initialize();
|
|
174
|
+
let rows;
|
|
175
|
+
if (prefix == null || prefix.length === 0) [rows] = await this.#pool.query(`SELECT \`key\`, \`value\` FROM \`${this.#tableName}\`
|
|
176
|
+
WHERE \`expires\` IS NULL OR \`expires\` > NOW(6)
|
|
177
|
+
ORDER BY \`key\``);
|
|
178
|
+
else {
|
|
179
|
+
const serializedPrefix = JSON.stringify([...prefix]);
|
|
180
|
+
const likePrefix = serializedPrefix.slice(0, -1).replace(/[%_\\]/g, "\\$&") + ",%";
|
|
181
|
+
[rows] = await this.#pool.query(`SELECT \`key\`, \`value\` FROM \`${this.#tableName}\`
|
|
182
|
+
WHERE (\`key\` = ? OR \`key\` LIKE ? ESCAPE '\\\\')
|
|
183
|
+
AND (\`expires\` IS NULL OR \`expires\` > NOW(6))
|
|
184
|
+
ORDER BY \`key\``, [serializedPrefix, likePrefix]);
|
|
185
|
+
}
|
|
186
|
+
for (const row of rows) yield {
|
|
187
|
+
key: JSON.parse(row.key),
|
|
188
|
+
value: row.value
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Creates the table used by the key-value store if it does not already exist.
|
|
193
|
+
* Does nothing if the table already exists.
|
|
194
|
+
*
|
|
195
|
+
* @since 2.1.0
|
|
196
|
+
*/
|
|
197
|
+
async initialize() {
|
|
198
|
+
if (this.#initialized) return;
|
|
199
|
+
logger.debug("Initializing the key-value store table {tableName}...", { tableName: this.#tableName });
|
|
200
|
+
await this.#pool.query(`CREATE TABLE IF NOT EXISTS \`${this.#tableName}\` (
|
|
201
|
+
\`key\` VARCHAR(768) NOT NULL,
|
|
202
|
+
\`value\` JSON NOT NULL,
|
|
203
|
+
\`expires\` DATETIME(6) NULL DEFAULT NULL,
|
|
204
|
+
PRIMARY KEY (\`key\`)
|
|
205
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`);
|
|
206
|
+
try {
|
|
207
|
+
await this.#pool.query(`CREATE INDEX \`idx_${this.#tableName}_expires\`
|
|
208
|
+
ON \`${this.#tableName}\` (\`expires\`)`);
|
|
209
|
+
} catch (e) {
|
|
210
|
+
if (e.code !== "ER_DUP_KEYNAME") throw e;
|
|
211
|
+
}
|
|
212
|
+
this.#initialized = true;
|
|
213
|
+
logger.debug("Initialized the key-value store table {tableName}.", { tableName: this.#tableName });
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Drops the table used by the key-value store. Does nothing if the table
|
|
217
|
+
* does not exist. Resets the initialized flag so that
|
|
218
|
+
* {@link MysqlKvStore.initialize} can recreate the table on the next call.
|
|
219
|
+
*
|
|
220
|
+
* @since 2.1.0
|
|
221
|
+
*/
|
|
222
|
+
async drop() {
|
|
223
|
+
await this.#pool.query(`DROP TABLE IF EXISTS \`${this.#tableName}\``);
|
|
224
|
+
this.#initialized = false;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
function durationToSeconds(duration) {
|
|
228
|
+
return duration.total({
|
|
229
|
+
unit: "second",
|
|
230
|
+
relativeTo: Temporal.Now.plainDateTimeISO()
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
//#endregion
|
|
235
|
+
exports.MysqlKvStore = MysqlKvStore;
|
package/dist/kv.d.cts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions } from "@fedify/fedify";
|
|
2
|
+
import { Pool } from "mysql2/promise";
|
|
3
|
+
|
|
4
|
+
//#region src/kv.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Options for the MySQL key-value store.
|
|
7
|
+
*
|
|
8
|
+
* @since 2.1.0
|
|
9
|
+
*/
|
|
10
|
+
interface MysqlKvStoreOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The table name to use for the key-value store.
|
|
13
|
+
* `"fedify_kv"` by default.
|
|
14
|
+
* @default `"fedify_kv"`
|
|
15
|
+
* @since 2.1.0
|
|
16
|
+
*/
|
|
17
|
+
readonly tableName?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the table has been initialized. `false` by default.
|
|
20
|
+
* @default `false`
|
|
21
|
+
* @since 2.1.0
|
|
22
|
+
*/
|
|
23
|
+
readonly initialized?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* The probability (between 0 and 1, inclusive) that expired entries are
|
|
26
|
+
* cleaned up on each mutation. Defaults to `1` (always clean up).
|
|
27
|
+
* Set to `0` to disable automatic expiry cleanup entirely.
|
|
28
|
+
* @default `1`
|
|
29
|
+
* @since 2.1.0
|
|
30
|
+
*/
|
|
31
|
+
readonly expireCleanupRate?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A key-value store that uses MySQL (or MariaDB) as the underlying storage.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { createFederation } from "@fedify/fedify";
|
|
39
|
+
* import { MysqlKvStore } from "@fedify/mysql";
|
|
40
|
+
* import mysql from "mysql2/promise";
|
|
41
|
+
*
|
|
42
|
+
* const pool = mysql.createPool("mysql://user:pass@localhost/db");
|
|
43
|
+
*
|
|
44
|
+
* const federation = createFederation({
|
|
45
|
+
* // ...
|
|
46
|
+
* kv: new MysqlKvStore(pool),
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @since 2.1.0
|
|
51
|
+
*/
|
|
52
|
+
declare class MysqlKvStore implements KvStore {
|
|
53
|
+
#private;
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new MySQL key-value store.
|
|
56
|
+
* @param pool The MySQL connection pool to use.
|
|
57
|
+
* @param options The options for the key-value store.
|
|
58
|
+
* @since 2.1.0
|
|
59
|
+
*/
|
|
60
|
+
constructor(pool: Pool, options?: MysqlKvStoreOptions);
|
|
61
|
+
/**
|
|
62
|
+
* {@inheritDoc KvStore.get}
|
|
63
|
+
* @since 2.1.0
|
|
64
|
+
*/
|
|
65
|
+
get<T = unknown>(key: KvKey): Promise<T | undefined>;
|
|
66
|
+
/**
|
|
67
|
+
* {@inheritDoc KvStore.set}
|
|
68
|
+
* @since 2.1.0
|
|
69
|
+
*/
|
|
70
|
+
set(key: KvKey, value: unknown, options?: KvStoreSetOptions | undefined): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* {@inheritDoc KvStore.delete}
|
|
73
|
+
* @since 2.1.0
|
|
74
|
+
*/
|
|
75
|
+
delete(key: KvKey): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* {@inheritDoc KvStore.cas}
|
|
78
|
+
* @since 2.1.0
|
|
79
|
+
*/
|
|
80
|
+
cas(key: KvKey, expectedValue: unknown, newValue: unknown, options?: KvStoreSetOptions): Promise<boolean>;
|
|
81
|
+
/**
|
|
82
|
+
* {@inheritDoc KvStore.list}
|
|
83
|
+
* @since 2.1.0
|
|
84
|
+
*/
|
|
85
|
+
list(prefix?: KvKey): AsyncIterable<KvStoreListEntry>;
|
|
86
|
+
/**
|
|
87
|
+
* Creates the table used by the key-value store if it does not already exist.
|
|
88
|
+
* Does nothing if the table already exists.
|
|
89
|
+
*
|
|
90
|
+
* @since 2.1.0
|
|
91
|
+
*/
|
|
92
|
+
initialize(): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Drops the table used by the key-value store. Does nothing if the table
|
|
95
|
+
* does not exist. Resets the initialized flag so that
|
|
96
|
+
* {@link MysqlKvStore.initialize} can recreate the table on the next call.
|
|
97
|
+
*
|
|
98
|
+
* @since 2.1.0
|
|
99
|
+
*/
|
|
100
|
+
drop(): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { MysqlKvStore, MysqlKvStoreOptions };
|
package/dist/kv.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Temporal } from "@js-temporal/polyfill";
|
|
2
|
+
import { KvKey, KvStore, KvStoreListEntry, KvStoreSetOptions } from "@fedify/fedify";
|
|
3
|
+
import { Pool } from "mysql2/promise";
|
|
4
|
+
|
|
5
|
+
//#region src/kv.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Options for the MySQL key-value store.
|
|
8
|
+
*
|
|
9
|
+
* @since 2.1.0
|
|
10
|
+
*/
|
|
11
|
+
interface MysqlKvStoreOptions {
|
|
12
|
+
/**
|
|
13
|
+
* The table name to use for the key-value store.
|
|
14
|
+
* `"fedify_kv"` by default.
|
|
15
|
+
* @default `"fedify_kv"`
|
|
16
|
+
* @since 2.1.0
|
|
17
|
+
*/
|
|
18
|
+
readonly tableName?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the table has been initialized. `false` by default.
|
|
21
|
+
* @default `false`
|
|
22
|
+
* @since 2.1.0
|
|
23
|
+
*/
|
|
24
|
+
readonly initialized?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* The probability (between 0 and 1, inclusive) that expired entries are
|
|
27
|
+
* cleaned up on each mutation. Defaults to `1` (always clean up).
|
|
28
|
+
* Set to `0` to disable automatic expiry cleanup entirely.
|
|
29
|
+
* @default `1`
|
|
30
|
+
* @since 2.1.0
|
|
31
|
+
*/
|
|
32
|
+
readonly expireCleanupRate?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A key-value store that uses MySQL (or MariaDB) as the underlying storage.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { createFederation } from "@fedify/fedify";
|
|
40
|
+
* import { MysqlKvStore } from "@fedify/mysql";
|
|
41
|
+
* import mysql from "mysql2/promise";
|
|
42
|
+
*
|
|
43
|
+
* const pool = mysql.createPool("mysql://user:pass@localhost/db");
|
|
44
|
+
*
|
|
45
|
+
* const federation = createFederation({
|
|
46
|
+
* // ...
|
|
47
|
+
* kv: new MysqlKvStore(pool),
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @since 2.1.0
|
|
52
|
+
*/
|
|
53
|
+
declare class MysqlKvStore implements KvStore {
|
|
54
|
+
#private;
|
|
55
|
+
/**
|
|
56
|
+
* Creates a new MySQL key-value store.
|
|
57
|
+
* @param pool The MySQL connection pool to use.
|
|
58
|
+
* @param options The options for the key-value store.
|
|
59
|
+
* @since 2.1.0
|
|
60
|
+
*/
|
|
61
|
+
constructor(pool: Pool, options?: MysqlKvStoreOptions);
|
|
62
|
+
/**
|
|
63
|
+
* {@inheritDoc KvStore.get}
|
|
64
|
+
* @since 2.1.0
|
|
65
|
+
*/
|
|
66
|
+
get<T = unknown>(key: KvKey): Promise<T | undefined>;
|
|
67
|
+
/**
|
|
68
|
+
* {@inheritDoc KvStore.set}
|
|
69
|
+
* @since 2.1.0
|
|
70
|
+
*/
|
|
71
|
+
set(key: KvKey, value: unknown, options?: KvStoreSetOptions | undefined): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* {@inheritDoc KvStore.delete}
|
|
74
|
+
* @since 2.1.0
|
|
75
|
+
*/
|
|
76
|
+
delete(key: KvKey): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* {@inheritDoc KvStore.cas}
|
|
79
|
+
* @since 2.1.0
|
|
80
|
+
*/
|
|
81
|
+
cas(key: KvKey, expectedValue: unknown, newValue: unknown, options?: KvStoreSetOptions): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* {@inheritDoc KvStore.list}
|
|
84
|
+
* @since 2.1.0
|
|
85
|
+
*/
|
|
86
|
+
list(prefix?: KvKey): AsyncIterable<KvStoreListEntry>;
|
|
87
|
+
/**
|
|
88
|
+
* Creates the table used by the key-value store if it does not already exist.
|
|
89
|
+
* Does nothing if the table already exists.
|
|
90
|
+
*
|
|
91
|
+
* @since 2.1.0
|
|
92
|
+
*/
|
|
93
|
+
initialize(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Drops the table used by the key-value store. Does nothing if the table
|
|
96
|
+
* does not exist. Resets the initialized flag so that
|
|
97
|
+
* {@link MysqlKvStore.initialize} can recreate the table on the next call.
|
|
98
|
+
*
|
|
99
|
+
* @since 2.1.0
|
|
100
|
+
*/
|
|
101
|
+
drop(): Promise<void>;
|
|
102
|
+
}
|
|
103
|
+
//#endregion
|
|
104
|
+
export { MysqlKvStore, MysqlKvStoreOptions };
|