wisdom-sdk
Version:
Core business logic and data access layer for prediction markets
345 lines (343 loc) • 11 kB
JavaScript
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