@keyv/mysql
Version:
MySQL/MariaDB storage adapter for Keyv
238 lines (234 loc) • 7.96 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
KeyvMysql: () => KeyvMysql,
default: () => index_default
});
module.exports = __toCommonJS(index_exports);
var import_node_events = __toESM(require("events"), 1);
var import_mysql22 = __toESM(require("mysql2"), 1);
// src/pool.ts
var import_mysql2 = __toESM(require("mysql2"), 1);
var mysqlPool;
var globalUri;
var parseConnectionString = (connectionString) => {
connectionString = connectionString.replace(/#/g, "%23");
const url = new URL(connectionString);
const poolOptions = {
user: decodeURIComponent(url.username),
password: decodeURIComponent(url.password) || void 0,
host: url.hostname,
port: url.port ? Number.parseInt(url.port, 10) : void 0,
database: decodeURIComponent(url.pathname.slice(1))
// Remove the leading '/'
};
for (const key of Object.keys(poolOptions)) {
if (poolOptions[key] === void 0) {
delete poolOptions[key];
}
}
return poolOptions;
};
var pool = (uri, options = {}) => {
if (globalUri !== uri) {
mysqlPool = void 0;
globalUri = uri;
}
const connectObject = parseConnectionString(uri);
const poolOptions = { ...connectObject, ...options };
mysqlPool ??= import_mysql2.default.createPool(poolOptions);
return mysqlPool.promise();
};
var endPool = () => {
if (mysqlPool) {
mysqlPool.end();
}
globalUri = void 0;
};
// src/index.ts
var keyvMysqlKeys = /* @__PURE__ */ new Set([
"adapter",
"compression",
"connect",
"dialect",
"keySize",
"table",
"ttl",
"uri"
]);
var KeyvMysql = class extends import_node_events.default {
ttlSupport = false;
opts;
namespace;
query;
constructor(keyvOptions) {
super();
let options = {
dialect: "mysql",
uri: "mysql://localhost"
};
if (typeof keyvOptions === "string") {
options.uri = keyvOptions;
} else {
options = {
...options,
...keyvOptions
};
}
if (options.intervalExpiration !== void 0 && options.intervalExpiration > 0) {
this.ttlSupport = true;
}
const mysqlOptions = Object.fromEntries(
Object.entries(options).filter(([k]) => !keyvMysqlKeys.has(k))
);
delete mysqlOptions.namespace;
delete mysqlOptions.serialize;
delete mysqlOptions.deserialize;
const connection = async () => {
const conn = pool(options.uri, mysqlOptions);
return async (sql) => {
const data = await conn.query(sql);
return data[0];
};
};
this.opts = {
table: "keyv",
keySize: 255,
...options
};
const createTable = `CREATE TABLE IF NOT EXISTS ${this.opts.table}(id VARCHAR(${Number(this.opts.keySize)}) PRIMARY KEY, value TEXT)`;
const connected = connection().then(async (query) => {
await query(createTable);
if (this.opts.intervalExpiration !== void 0 && this.opts.intervalExpiration > 0) {
await query("SET GLOBAL event_scheduler = ON;");
await query("DROP EVENT IF EXISTS keyv_delete_expired_keys;");
await query(`CREATE EVENT IF NOT EXISTS keyv_delete_expired_keys ON SCHEDULE EVERY ${this.opts.intervalExpiration} SECOND
DO DELETE FROM ${this.opts.table}
WHERE CAST(value->'$.expires' AS UNSIGNED) BETWEEN 1 AND UNIX_TIMESTAMP(NOW(3)) * 1000;`);
}
return query;
}).catch((error) => this.emit("error", error));
this.query = async (sqlString) => {
const query = await connected;
return query(sqlString);
};
}
async get(key) {
const sql = `SELECT * FROM ${this.opts.table} WHERE id = ?`;
const select = import_mysql22.default.format(sql, [key]);
const rows = await this.query(select);
const row = rows[0];
return row?.value;
}
async getMany(keys) {
const sql = `SELECT * FROM ${this.opts.table} WHERE id IN (?)`;
const select = import_mysql22.default.format(sql, [keys]);
const rows = await this.query(select);
const results = [];
for (const key of keys) {
const rowIndex = rows.findIndex((row) => row.id === key);
results.push(
rowIndex === -1 ? void 0 : rows[rowIndex].value
);
}
return results;
}
// biome-ignore lint/suspicious/noExplicitAny: type format
async set(key, value) {
const sql = `INSERT INTO ${this.opts.table} (id, value)
VALUES(?, ?)
ON DUPLICATE KEY UPDATE value=?;`;
const insert = [key, value, value];
const upsert = import_mysql22.default.format(sql, insert);
return this.query(upsert);
}
async delete(key) {
const sql = `SELECT * FROM ${this.opts.table} WHERE id = ?`;
const select = import_mysql22.default.format(sql, [key]);
const delSql = `DELETE FROM ${this.opts.table} WHERE id = ?`;
const del = import_mysql22.default.format(delSql, [key]);
const rows = await this.query(select);
const row = rows[0];
if (row === void 0) {
return false;
}
await this.query(del);
return true;
}
async deleteMany(key) {
const sql = `DELETE FROM ${this.opts.table} WHERE id IN (?)`;
const del = import_mysql22.default.format(sql, [key]);
const result = await this.query(del);
return result.affectedRows !== 0;
}
async clear() {
const sql = `DELETE FROM ${this.opts.table} WHERE id LIKE ?`;
const del = import_mysql22.default.format(sql, [
this.namespace ? `${this.namespace}:%` : "%"
]);
await this.query(del);
}
async *iterator(namespace) {
const limit = Number.parseInt(this.opts.iterationLimit, 10) || 10;
async function* iterate(offset, options, query) {
const sql = `SELECT * FROM ${options.table} WHERE id LIKE ? LIMIT ? OFFSET ?`;
const select = import_mysql22.default.format(sql, [
// biome-ignore lint/style/useTemplate: need to fix
`${namespace ? namespace + ":" : ""}%`,
limit,
offset
]);
const entries = await query(select);
if (entries.length === 0) {
return;
}
for (const entry of entries) {
offset += 1;
yield [entry.id, entry.value];
}
yield* iterate(offset, options, query);
}
yield* iterate(0, this.opts, this.query);
}
async has(key) {
const exists = `SELECT EXISTS ( SELECT * FROM ${this.opts.table} WHERE id = '${key}' )`;
const rows = await this.query(exists);
return Object.values(rows[0])[0] === 1;
}
async disconnect() {
endPool();
}
};
var index_default = KeyvMysql;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
KeyvMysql
});