UNPKG

@hotmeshio/hotmesh

Version:

Permanent-Memory Workflows & AI Agents

342 lines (341 loc) 10.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.arrayToHash = exports.isStreamMessage = exports.parseStreamMessage = exports.s = exports.isValidCron = exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.matchesStatus = exports.matchesStatusCode = exports.polyfill = exports.identifyProvider = exports.XSleepFor = exports.sleepImmediate = exports.sleepFor = exports.guid = exports.deterministicRandom = exports.deepCopy = exports.getSystemHealth = exports.hashOptions = void 0; const os_1 = __importDefault(require("os")); const crypto_1 = require("crypto"); const nanoid_1 = require("nanoid"); const ms_1 = __importDefault(require("ms")); const logger_1 = require("../services/logger"); const enums_1 = require("./enums"); const logger = new logger_1.LoggerService('hotmesh', 'utils'); /** * @private */ const hashOptions = (options) => { const str = JSON.stringify(options); return (0, crypto_1.createHash)('sha256').update(str).digest('hex'); }; exports.hashOptions = hashOptions; async function getSystemHealth() { const totalMemory = os_1.default.totalmem(); const freeMemory = os_1.default.freemem(); const usedMemory = totalMemory - freeMemory; const systemHealth = { TotalMemoryGB: `${(totalMemory / 1024 / 1024 / 1024).toFixed(2)} GB`, FreeMemoryGB: `${(freeMemory / 1024 / 1024 / 1024).toFixed(2)} GB`, UsedMemoryGB: `${(usedMemory / 1024 / 1024 / 1024).toFixed(2)} GB`, CPULoad: [], NetworkStats: [], }; return systemHealth; } exports.getSystemHealth = getSystemHealth; function deepCopy(obj) { return JSON.parse(JSON.stringify(obj)); } exports.deepCopy = deepCopy; function deterministicRandom(seed) { const x = Math.sin(seed) * 10000; return x - Math.floor(x); } exports.deterministicRandom = deterministicRandom; function guid(size = enums_1.HMSH_GUID_SIZE) { return `H` + (0, nanoid_1.nanoid)(size); } exports.guid = guid; async function sleepFor(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } exports.sleepFor = sleepFor; function sleepImmediate() { return new Promise((resolve) => setImmediate(resolve)); } exports.sleepImmediate = sleepImmediate; function XSleepFor(ms) { //can be interrupted with `clearTimeout` let timerId; const promise = new Promise((resolve) => { timerId = setTimeout(resolve, ms); }); return { promise, timerId }; } exports.XSleepFor = XSleepFor; /** * Identies the provider type based on the provider object. Customers may * explicitly set the provider type in the configuration. But this is a * convenience method to automatically identify the provider type. * @private */ function identifyProvider(provider) { const prototype = Object.getPrototypeOf(provider); if (provider.Query?.prototype || Object.keys(provider).includes('database') || prototype.name === 'Pool') { return 'postgres'; } else if (provider.toString().toLowerCase().includes('nats')) { return 'nats'; } let type = null; if (Object.keys(provider).includes('connection') || !isNaN(provider.totalCount) && !isNaN(provider.idleCount)) { type = 'postgres'; } else if (prototype.constructor.toString().includes('NatsConnectionImpl')) { type = 'nats'; } return type; } exports.identifyProvider = identifyProvider; /** * @private */ exports.polyfill = { /** * `connection` is the generic replacement */ providerConfig(obj) { return obj?.connection ?? obj?.connections; }, }; /** * @private */ function matchesStatusCode(code, pattern) { if (typeof pattern === 'string') { // Convert '*' wildcard to its regex equivalent (\d) const regexPattern = `^${pattern.replace(/\*/g, '\\d')}$`; return new RegExp(regexPattern).test(code.toString()); } return pattern.test(code.toString()); } exports.matchesStatusCode = matchesStatusCode; /** * @private */ function matchesStatus(status, targetStatus) { return status === targetStatus; } exports.matchesStatus = matchesStatus; /** * @private */ function findTopKey(obj, input) { for (const [key, value] of Object.entries(obj)) { if (value.hasOwnProperty(input)) { const parentKey = findTopKey(obj, key.replace(/^\./, '')); return (parentKey || key).replace(/^\./, ''); } } return null; } exports.findTopKey = findTopKey; /** * @private */ function findSubscriptionForTrigger(obj, value) { for (const [key, itemValue] of Object.entries(obj)) { if (itemValue === value) { return key; } } return null; } exports.findSubscriptionForTrigger = findSubscriptionForTrigger; /** * Get the subscription topic for the flow to which activityId belongs. * @private */ async function getSubscriptionTopic(activityId, store, appVID) { const appTransitions = await store.getTransitions(appVID); const appSubscriptions = await store.getSubscriptions(appVID); const triggerId = findTopKey(appTransitions, activityId); const topic = findSubscriptionForTrigger(appSubscriptions, triggerId); return topic; } exports.getSubscriptionTopic = getSubscriptionTopic; /** * returns the 12-digit format of the iso timestamp (e.g, 202101010000); returns * an empty string if overridden by the user to not segment by time (infinity). * @private */ function getTimeSeries(granularity) { if (granularity.toString() === 'infinity') { return '0'; } const now = new Date(); const granularityUnit = granularity.slice(-1); const granularityValue = parseInt(granularity.slice(0, -1), 10); if (granularityUnit === 'm') { const minute = Math.floor(now.getMinutes() / granularityValue) * granularityValue; now.setUTCMinutes(minute, 0, 0); } else if (granularityUnit === 'h') { now.setUTCMinutes(0, 0, 0); } return now .toISOString() .replace(/:\d\d\..+|-|T/g, '') .replace(':', ''); } exports.getTimeSeries = getTimeSeries; /** * @private */ function formatISODate(input) { const date = input instanceof Date ? input : new Date(input); return date.toISOString().replace(/[:TZ-]/g, ''); } exports.formatISODate = formatISODate; /** * @private */ function getSymKey(number) { const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; const base = alphabet.length; if (number < 0 || number >= Math.pow(base, 3)) { throw new Error('Number out of range'); } const [q1, r1] = divmod(number, base); const [q2, r2] = divmod(q1, base); return alphabet[q2] + alphabet[r1] + alphabet[r2]; } exports.getSymKey = getSymKey; /** * @private */ function getSymVal(number) { const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; const base = alphabet.length; if (number < 0 || number >= Math.pow(base, 2)) { throw new Error('Number out of range'); } const [q, r] = divmod(number, base); return alphabet[q] + alphabet[r]; } exports.getSymVal = getSymVal; /** * @private */ function divmod(m, n) { return [Math.floor(m / n), m % n]; } /** * @private */ function getIndexedHash(hash, target) { const index = hash[target] || 0; const newHash = { ...hash }; delete newHash[target]; return [index, newHash]; } exports.getIndexedHash = getIndexedHash; /** * @private */ function getValueByPath(obj, path) { const pathParts = path.split('/'); let currentValue = obj; for (const part of pathParts) { if (currentValue[part] !== undefined) { currentValue = currentValue[part]; } else { return undefined; } } return currentValue; } exports.getValueByPath = getValueByPath; /** * @private */ function restoreHierarchy(obj) { const result = {}; for (const key in obj) { if (obj[key] === undefined) continue; const keys = key.split('/'); let current = result; for (let i = 0; i < keys.length; i++) { if (i === keys.length - 1) { current[keys[i]] = obj[key]; } else { current[keys[i]] = current[keys[i]] || {}; current = current[keys[i]]; } } } return result; } exports.restoreHierarchy = restoreHierarchy; /** * @private */ function isValidCron(cronExpression) { const cronRegex = /^(\*|([0-5]?\d)) (\*|([01]?\d|2[0-3])) (\*|([12]?\d|3[01])) (\*|([1-9]|1[0-2])) (\*|([0-6](?:-[0-6])?(?:,[0-6])?))$/; return cronRegex.test(cronExpression); } exports.isValidCron = isValidCron; /** * Returns the number of seconds for a string using the milliseconds format * used by the `ms` npm package as the input. */ const s = (input) => { return (0, ms_1.default)(input) / 1000; }; exports.s = s; /** * @private */ const parseStreamMessage = (message) => { try { return JSON.parse(message); } catch (error) { logger.error('Error parsing Stream message', { error }); throw error; } }; exports.parseStreamMessage = parseStreamMessage; /** * @private */ const isStreamMessage = (result) => { return Array.isArray(result) && Array.isArray(result[0]); }; exports.isStreamMessage = isStreamMessage; /** * Transforms an array of arrays to an array of objects. */ const arrayToHash = (response) => { const results = []; let key; for (let i = 1; i < response.length; i++) { // ignore count const row = response[i]; const result = {}; if (Array.isArray(row)) { // Check if row is an array for (let j = 0; j < row.length; j += 2) { const key = row[j]; const value = row[j + 1]; result[key] = value; } if (key) { result.$ = key; } results.push(result); key = undefined; } else { key = row; } } return results; }; exports.arrayToHash = arrayToHash;