@hotmeshio/hotmesh
Version:
Permanent-Memory Workflows & AI Agents
199 lines (198 loc) • 8.01 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.KVSQL = void 0;
const key_1 = require("../../../../modules/key");
const kvtransaction_1 = require("./kvtransaction");
const string_1 = require("./kvtypes/string");
const index_1 = require("./kvtypes/hash/index");
const list_1 = require("./kvtypes/list");
const zset_1 = require("./kvtypes/zset");
/**
* KVSQL is a class that provides a set of methods to interact with a Postgres database.
* It is used to interact with the database in a key-value manner.
*/
class KVSQL {
constructor(pgClient, namespace, appId) {
// String Commands
this.set = (...args) => this.string.set(...args);
this._set = (...args) => this.string._set(...args);
this.get = (...args) => this.string.get(...args);
this._get = (...args) => this.string._get(...args);
this.del = (...args) => this.string.del(...args);
this._del = (...args) => this.string._del(...args);
this.setnx = (...args) => this.string.setnx(...args);
this.setnxex = (...args) => this.string.setnxex(...args);
// Hash Commands
this.hset = (...args) => this.hash.hset(...args);
this._hset = (...args) => this.hash._hset(...args);
this.hsetnx = (...args) => this.hash.hsetnx(...args);
this.hget = (...args) => this.hash.hget(...args);
this._hget = (...args) => this.hash._hget(...args);
this.hdel = (...args) => this.hash.hdel(...args);
this._hdel = (...args) => this.hash._hdel(...args);
this.hmget = (...args) => this.hash.hmget(...args);
this._hmget = (...args) => this.hash._hmget(...args);
this.hgetall = (...args) => this.hash.hgetall(...args);
this.hincrbyfloat = (...args) => this.hash.hincrbyfloat(...args);
this._hincrbyfloat = (...args) => this.hash._hincrbyfloat(...args);
this.hscan = (...args) => this.hash.hscan(...args);
this._hscan = (...args) => this.hash._hscan(...args);
this.expire = (...args) => this.hash.expire(...args);
this._expire = (...args) => this.hash._expire(...args);
this.scan = (...args) => this.hash.scan(...args);
this._scan = (...args) => this.hash._scan(...args);
// List Commands
this.lrange = (...args) => this.list.lrange(...args);
this._lrange = (...args) => this.list._lrange(...args);
this.rpush = (...args) => this.list.rpush(...args);
this._rpush = (...args) => this.list._rpush(...args);
this.lpush = (...args) => this.list.lpush(...args);
this._lpush = (...args) => this.list._lpush(...args);
this.lpop = (...args) => this.list.lpop(...args);
this._lpop = (...args) => this.list._lpop(...args);
this.lmove = (...args) => this.list.lmove(...args);
this._lmove = (...args) => this.list._lmove(...args);
this.rename = (...args) => this.list.rename(...args);
this._rename = (...args) => this.list._rename(...args);
// Sorted Set Commands
this.zadd = (...args) => this.zset.zadd(...args);
this._zadd = (...args) => this.zset._zadd(...args);
this.zrange = (...args) => this.zset.zrange(...args);
this._zrange = (...args) => this.zset._zrange(...args);
this.zrangebyscore = (...args) => this.zset.zrangebyscore(...args);
this._zrangebyscore = (...args) => this.zset._zrangebyscore(...args);
this.zrangebyscore_withscores = (...args) => this.zset.zrangebyscore_withscores(...args);
this._zrangebyscore_withscores = (...args) => this.zset._zrangebyscore_withscores(...args);
this.zrem = (...args) => this.zset.zrem(...args);
this._zrem = (...args) => this.zset._zrem(...args);
this.zrank = (...args) => this.zset.zrank(...args);
this._zrank = (...args) => this.zset._zrank(...args);
this.zscore = (...args) => this.zset.zscore(...args);
this._zscore = (...args) => this.zset._zscore(...args);
this.pgClient = pgClient;
this.namespace = namespace;
this.appId = appId;
this.hash = (0, index_1.hashModule)(this);
this.list = (0, list_1.listModule)(this);
this.zset = (0, zset_1.zsetModule)(this);
this.string = (0, string_1.stringModule)(this);
}
isStatusOnly(fields) {
return fields.length === 1 && fields[0] === ':';
}
appendExpiryClause(baseQuery, tableAlias) {
return `
${baseQuery}
AND (${tableAlias}.expiry IS NULL OR ${tableAlias}.expiry > NOW())
`;
}
appendJobExpiryClause(baseQuery, tableAlias) {
return `
${baseQuery}
AND (${tableAlias}.expired_at IS NULL OR ${tableAlias}.expired_at > NOW())
`;
}
getMulti() {
return new kvtransaction_1.KVTransaction(this);
}
transact() {
return new kvtransaction_1.KVTransaction(this);
}
exec(...args) {
return Promise.resolve([]);
}
mintKey(type, params) {
//no-op
return '';
}
/**
* Resolves the table name when provided a key
*/
tableForKey(key, stats_type) {
if (key === key_1.HMNS) {
return 'public.hotmesh_connections';
}
const [_, appName, abbrev, ...rest] = key.split(':');
if (appName === 'a') {
return 'public.hotmesh_applications';
}
const id = rest?.length ? rest.join(':') : '';
const entity = key_1.KeyService.resolveEntityType(abbrev, id);
if (this.safeName(this.appId) !== this.safeName(appName)) {
throw new Error(`App ID mismatch: ${this.appId} !== ${appName}`);
}
const schemaName = this.safeName(appName);
if (entity === 'stats') {
let tableName;
if (stats_type === 'sorted_set') {
tableName = 'stats_ordered';
}
else if (stats_type === 'list' || key.endsWith(':processed')) {
tableName = 'stats_indexed';
}
else if (stats_type === 'hash' || /:\d$/.test(key)) {
tableName = 'stats_counted';
}
else {
throw new Error(`Unknown stats type [${stats_type}] for key [${key}]`);
}
return `${schemaName}.${tableName}`;
}
if (entity === 'unknown_entity') {
throw new Error(`Unknown entity type abbreviation: ${abbrev}`);
}
else if (entity === 'applications') {
return 'public.hotmesh_applications';
}
else {
return `${schemaName}.${entity}`;
}
}
safeName(input, prefix = '') {
if (!input) {
return 'connections';
}
let tableName = input.trim().toLowerCase();
tableName = tableName.replace(/[^a-z0-9]+/g, '_');
if (prefix) {
tableName = `${prefix}_${tableName}`;
}
if (tableName.length > 63) {
tableName = tableName.slice(0, 63);
}
tableName = tableName.replace(/_+$/g, '');
if (!tableName) {
tableName = 'connections';
}
return tableName;
}
async exists(key) {
const { sql, params } = this._exists(key);
const res = await this.pgClient.query(sql, params);
return res.rows.length ? res.rows[0].table_name : 0;
}
_exists(key) {
const tableName = this.tableForKey(key);
const isJobsTable = tableName.endsWith('jobs');
let sql;
if (isJobsTable) {
sql = `
SELECT FROM ${tableName}
WHERE id = $1
AND (expired_at IS NULL OR expired_at > NOW())
LIMIT 1;
`;
}
else {
sql = `
SELECT FROM ${tableName}
WHERE key = $1
AND (expiry IS NULL OR expiry > NOW())
LIMIT 1;
`;
}
const params = [key];
return { sql, params };
}
}
exports.KVSQL = KVSQL;