UNPKG

@daiso-tech/core

Version:

The library offers flexible, framework-agnostic solutions for modern web applications, built on adaptable components that integrate seamlessly with popular frameworks like Next Js.

151 lines 4.88 kB
/** * @module Lock */ import { TimeSpan, } from "../../../../utilities/_module-exports.js"; /** * * IMPORT_PATH: `"@daiso-tech/core/lock/adapters"` * @group Adapters */ export class KyselyLockAdapter { kysely; expiredKeysRemovalInterval; shouldRemoveExpiredKeys; timeoutId = null; /** * @example * ```ts * import { KyselyLockAdapter } from "@daiso-tech/core/lock/adapters"; * import Sqlite from "better-sqlite3"; * * const lockAdapter = new KyselyLockAdapter({ * kysely: new Kysely({ * dialect: new SqliteDialect({ * database: new Sqlite("local.db"), * }), * }), * }); * // You need initialize the adapter once before using it. * await lockAdapter.init(); * ``` */ constructor(settings) { const { kysely, expiredKeysRemovalInterval = TimeSpan.fromMinutes(1), shouldRemoveExpiredKeys = true, } = settings; this.expiredKeysRemovalInterval = expiredKeysRemovalInterval; this.shouldRemoveExpiredKeys = shouldRemoveExpiredKeys; this.kysely = kysely; } async deInit() { if (this.shouldRemoveExpiredKeys && this.timeoutId !== null) { clearTimeout(this.timeoutId); } // Should not throw if the index does not exists thats why the try catch is used. try { await this.kysely.schema .dropIndex("lock_expiresAt") .on("lock") .execute(); } catch { /* EMPTY */ } // Should not throw if the table does not exists thats why the try catch is used. try { await this.kysely.schema.dropTable("lock").execute(); } catch { /* EMPTY */ } } async init() { // Should not throw if the table already exists thats why the try catch is used. try { await this.kysely.schema .createTable("lock") .ifNotExists() .addColumn("key", "varchar(255)", (col) => col.primaryKey()) .addColumn("owner", "varchar(255)") .addColumn("expiresAt", "bigint") .execute(); } catch { /* EMPTY */ } // Should not throw if the index already exists thats why the try catch is used. try { await this.kysely.schema .createIndex("lock_expiresAt") .on("lock") .columns(["expiresAt"]) .execute(); } catch { /* EMPTY */ } if (this.shouldRemoveExpiredKeys) { this.timeoutId = setTimeout(() => { void this.removeAllExpired(); }, this.expiredKeysRemovalInterval.toMilliseconds()); } } async removeAllExpired() { await this.kysely .deleteFrom("lock") .where("lock.expiresAt", "<=", new Date().getTime()) .execute(); } async insert(key, owner, expiration) { await this.kysely .insertInto("lock") .values({ key, owner, expiresAt: expiration?.getTime() ?? null, }) .execute(); } async update(key, owner, expiration) { const updateResult = await this.kysely .updateTable("lock") .where("lock.key", "=", key) // Has expired .where((eb) => eb.and([ eb("lock.expiresAt", "is not", null), eb("lock.expiresAt", "<=", Date.now()), ])) .set({ owner, expiresAt: expiration?.getTime() ?? null }) .executeTakeFirst(); return Number(updateResult.numUpdatedRows); // > 0; } async remove(key, owner) { await this.kysely .deleteFrom("lock") .where("lock.key", "=", key) .$if(owner !== null, (query) => query.where("lock.owner", "=", owner)) .execute(); } async refresh(key, owner, expiration) { const updateResult = await this.kysely .updateTable("lock") .where("lock.key", "=", key) .where("lock.owner", "=", owner) .set({ expiresAt: expiration.getTime() }) .executeTakeFirst(); return Number(updateResult.numUpdatedRows); // > 0; } async find(key) { const row = await this.kysely .selectFrom("lock") .where("lock.key", "=", key) .select(["lock.owner", "lock.expiresAt"]) .executeTakeFirst(); if (row === undefined) { return null; } return { expiration: row.expiresAt ? new Date(Number(row.expiresAt)) : null, owner: row.owner, }; } } //# sourceMappingURL=kysely-lock-adapter.js.map