UNPKG

wisdom-sdk

Version:

Core business logic and data access layer for prediction markets

345 lines (343 loc) 11 kB
import { logger, AppError } from './chunk-2OHF4QSJ.js'; import { kv } from '@vercel/kv'; var kvLogger = logger.child({ context: "kv-store" }); var KV_PREFIXES = { MARKET: "market", MARKET_IDS: "market_ids", USER_MARKETS: "user_markets", MARKET_PARTICIPANTS: "market_participants", MARKET_CATEGORY: "market_category", // Index for markets by category MARKET_STATUS: "market_status", // Index for markets by status PREDICTION: "prediction", USER_PREDICTIONS: "user_predictions", MARKET_PREDICTIONS: "market_predictions", PREDICTION_NFT: "prediction_nft", USER_BALANCE: "user_balance", USER_STATS: "user_stats", LEADERBOARD: "leaderboard", LEADERBOARD_EARNINGS: "leaderboard_earnings", LEADERBOARD_ACCURACY: "leaderboard_accuracy", BUG_REPORT: "bug_report", BUG_REPORT_IDS: "bug_report_ids", USER_BUG_REPORTS: "user_bug_reports", // Transaction custody-related prefixes CUSTODY_TRANSACTION: "custody_transaction", CUSTODY_TRANSACTION_IDS: "custody_transaction_ids", USER_TRANSACTIONS: "user_transactions", SIGNER_TRANSACTIONS: "signer_transactions", MARKET_TRANSACTIONS: "market_transactions", CUSTODY_NFT_RECEIPT: "custody_nft_receipt", // Claim reward transaction prefixes CLAIM_REWARD_TRANSACTION: "claim_reward_transaction", USER_CLAIM_REWARDS: "user_claim_rewards" }; function getKey(entityType, id) { const prefix = KV_PREFIXES[entityType]; if (entityType === "MARKET_IDS" && !id) { return prefix; } return id ? `${prefix}:${id}` : prefix; } async function storeEntity(entityType, id, data) { try { const key = getKey(entityType, id); await kv.set(key, JSON.stringify(data)); return data; } catch (error) { throw new AppError({ message: `Failed to store ${entityType} with ID ${id}`, context: "kv-store", code: "KV_STORE_ERROR", originalError: error instanceof Error ? error : new Error(String(error)), data: { entityType, id, operation: "store" } }).log(); } } async function getEntity(entityType, id) { try { const key = getKey(entityType, id); let data = await kv.get(key); if (!data && entityType === "MARKET") { data = await kv.get(`markets:${id}`); } if (!data) { kvLogger.debug({ entityType, id }, `Entity not found: ${entityType}:${id}`); return null; } if (typeof data !== "string") { return data; } try { return JSON.parse(data); } catch (e) { throw new AppError({ message: `Error parsing JSON for ${entityType} with ID ${id}`, context: "kv-store", code: "KV_JSON_PARSE_ERROR", originalError: e instanceof Error ? e : new Error(String(e)), data: { entityType, id, operation: "parse" } }).log(); } } catch (error) { if (error instanceof AppError) { throw error; } throw new AppError({ message: `Failed to retrieve ${entityType} with ID ${id}`, context: "kv-store", code: "KV_RETRIEVE_ERROR", originalError: error instanceof Error ? error : new Error(String(error)), data: { entityType, id, operation: "get" } }).log(); } } async function deleteEntity(entityType, id) { try { const key = getKey(entityType, id); await kv.del(key); if (entityType === "MARKET") { await kv.del(`markets:${id}`); } return true; } catch (error) { new AppError({ message: `Failed to delete ${entityType} with ID ${id}`, context: "kv-store", code: "KV_DELETE_ERROR", originalError: error instanceof Error ? error : new Error(String(error)), data: { entityType, id, operation: "delete" } }).log(); return false; } } async function addToSet(setType, id, memberId) { try { const key = getKey(setType, id); await kv.sadd(key, memberId); if (setType === "MARKET_IDS") { await kv.sadd("market_ids", memberId); } return true; } catch (error) { new AppError({ message: `Failed to add member ${memberId} to set ${setType}:${id}`, context: "kv-store", code: "KV_SET_ADD_ERROR", originalError: error instanceof Error ? error : new Error(String(error)), data: { setType, id, memberId, operation: "sadd" } }).log(); return false; } } async function removeFromSet(setType, id, memberId) { try { const key = getKey(setType, id); await kv.srem(key, memberId); if (setType === "MARKET_IDS") { await kv.srem("market_ids", memberId); } return true; } catch (error) { new AppError({ message: `Failed to remove member ${memberId} from set ${setType}:${id}`, context: "kv-store", code: "KV_SET_REMOVE_ERROR", originalError: error instanceof Error ? error : new Error(String(error)), data: { setType, id, memberId, operation: "srem" } }).log(); return false; } } async function getSetMembers(setType, id) { try { const key = getKey(setType, id); let members = await kv.smembers(key); if (setType === "MARKET_IDS" && members.length === 0) { const legacyMembers = await kv.smembers("market_ids"); if (legacyMembers.length > 0) { for (const marketId of legacyMembers) { await addToSet("MARKET_IDS", "", marketId); } members = legacyMembers; } } return members; } catch (error) { new AppError({ message: `Failed to get members from set ${setType}:${id}`, context: "kv-store", code: "KV_SET_MEMBERS_ERROR", originalError: error instanceof Error ? error : new Error(String(error)), data: { setType, id, operation: "smembers" } }).log(); return []; } } async function isSetMember(setType, id, memberId) { try { const key = getKey(setType, id); const result = await kv.sismember(key, memberId); return !!result; } catch (error) { console.error(`Error checking set membership for ${setType} with ID ${id}:`, error); return false; } } async function addToSortedSet(setType, memberId, score) { try { const key = getKey(setType); await kv.zadd(key, { score, member: memberId }); return true; } catch (error) { console.error(`Error adding to sorted set ${setType}:`, error); return false; } } async function getTopFromSortedSet(setType, limit = 10, reverse = true) { try { const key = getKey(setType); return await kv.zrange(key, 0, limit - 1, { rev: reverse }); } catch (error) { console.error(`Error getting top members from sorted set ${setType}:`, error); return []; } } async function getScoresFromSortedSet(setType, memberIds) { try { const key = getKey(setType); const result = {}; const batchSize = 50; for (let i = 0; i < memberIds.length; i += batchSize) { const batch = memberIds.slice(i, i + batchSize); const batchScores = await Promise.all( batch.map(async (memberId) => { const score = await kv.zscore(key, memberId); return { memberId, score: score ? Number(score) : null }; }) ); batchScores.forEach(({ memberId, score }) => { if (score !== null) { result[memberId] = score; } }); } return result; } catch (error) { console.error(`Error getting scores from sorted set ${setType}:`, error); return {}; } } async function getKeys(pattern) { try { return await kv.keys(pattern); } catch (error) { console.error(`Error getting keys with pattern ${pattern}:`, error); return []; } } async function keyExists(entityType, id) { try { const key = getKey(entityType, id); const result = await kv.exists(key); return result === 1; } catch (error) { console.error(`Error checking if key exists for ${entityType} with ID ${id}:`, error); return false; } } async function getDebugInfo() { try { const allKeys = await kv.keys("*"); const patterns = [ "market:*", "markets:*", "market_ids", "prediction:*", "predictions:*", "user_predictions:*", "market_predictions:*", "prediction_nft:*", "prediction_nfts:*" ]; const result = { totalKeys: allKeys.length, keysByPattern: {} }; const keysByPattern = result.keysByPattern; for (const pattern of patterns) { const keys = await kv.keys(pattern); keysByPattern[pattern] = { count: keys.length, sample: keys.slice(0, 5) }; } return result; } catch (error) { console.error("Error getting debug info:", error); return { error: String(error) }; } } async function startTransaction() { try { const transaction = kv.multi(); const operations = []; const txObject = { operations, // Add entity to transaction async addEntity(entityType, id, data) { const key = getKey(entityType, id); transaction.set(key, JSON.stringify(data)); operations.push({ type: "entity", entityType, id, data }); }, // Add to set in transaction async addToSetInTransaction(setType, id, memberId) { const key = getKey(setType, id); transaction.sadd(key, memberId); operations.push({ type: "set", entityType: setType, id: memberId }); if (setType === "MARKET_IDS") { transaction.sadd("market_ids", memberId); } }, // Add to sorted set in transaction async addToSortedSetInTransaction(setType, memberId, score) { const key = getKey(setType); transaction.zadd(key, { score, member: memberId }); operations.push({ type: "sortedSet", entityType: setType, id: memberId, data: score }); }, // Execute all queued commands atomically async execute() { try { kvLogger.debug( { operationCount: operations.length }, `Executing transaction with ${operations.length} operations` ); await transaction.exec(); return true; } catch (error) { const appError = new AppError({ message: "Transaction execution failed", context: "kv-store", code: "TRANSACTION_FAILED", originalError: error instanceof Error ? error : new Error(String(error)), data: { operationCount: operations.length } }); appError.log(); return false; } } }; return txObject; } catch (error) { throw new AppError({ message: "Failed to start transaction", context: "kv-store", code: "TRANSACTION_START_ERROR", originalError: error instanceof Error ? error : new Error(String(error)) }).log(); } } export { KV_PREFIXES, addToSet, addToSortedSet, deleteEntity, getDebugInfo, getEntity, getKey, getKeys, getScoresFromSortedSet, getSetMembers, getTopFromSortedSet, isSetMember, keyExists, removeFromSet, startTransaction, storeEntity }; //# sourceMappingURL=chunk-FIJAO3BQ.js.map //# sourceMappingURL=chunk-FIJAO3BQ.js.map