UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

199 lines (198 loc) 8.01 kB
"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;