'use strict'; var crypto = require('crypto'); // src/core/encoding.ts function encode(value, encoding) { switch (encoding) { case "plain": return value; case "base64": return encodeBase64(value); case "hex": return encodeHex(value); case "rot13": return encodeRot13(value); default: throw new Error(`Unknown encoding type: ${encoding}`); } } function decode(value, encoding) { switch (encoding) { case "plain": return value; case "base64": return decodeBase64(value); case "hex": return decodeHex(value); case "rot13": return decodeRot13(value); // ROT13 is symmetric default: throw new Error(`Unknown encoding type: ${encoding}`); } } function encodeBase64(value) { return Buffer.from(value, "utf-8").toString("base64"); } function decodeBase64(value) { return Buffer.from(value, "base64").toString("utf-8"); } function encodeHex(value) { return Buffer.from(value, "utf-8").toString("hex"); } function decodeHex(value) { return Buffer.from(value, "hex").toString("utf-8"); } function encodeRot13(value) { return value.replace(/[a-zA-Z]/g, (char) => { const base = char <= "Z" ? 65 : 97; return String.fromCharCode((char.charCodeAt(0) - base + 13) % 26 + base); }); } function decodeRot13(value) { return encodeRot13(value); } function encodeChain(value, encodings) { return encodings.reduce((acc, encoding) => encode(acc, encoding), value); } function decodeChain(value, encodings) { return [...encodings].reverse().reduce((acc, encoding) => decode(acc, encoding), value); } function generateId(length = 32) { return crypto.randomBytes(length).toString("hex"); } function signChallenge(data, secret) { return crypto.createHmac("sha256", secret).update(data).digest("hex"); } function safeCompare(a, b) { if (a.length !== b.length) { return false; } try { return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)); } catch { return false; } } function randomInt(min, max) { const range2 = max - min + 1; const bytesNeeded = Math.ceil(Math.log2(range2) / 8) || 1; const maxValid = Math.floor(256 ** bytesNeeded / range2) * range2 - 1; let randomValue; do { const bytes = crypto.randomBytes(bytesNeeded); randomValue = bytes.reduce((acc, byte, i) => acc + byte * 256 ** i, 0); } while (randomValue > maxValid); return min + randomValue % range2; } function randomElement(arr) { if (arr.length === 0) { throw new Error("Cannot select from empty array"); } return arr[randomInt(0, arr.length - 1)]; } // src/functions/math.ts function fibonacci(n) { if (n < 0) throw new Error("Fibonacci not defined for negative numbers"); if (n <= 1) return n; let a = 0, b = 1; for (let i = 2; i <= n; i++) { const temp = a + b; a = b; b = temp; } return b; } function isPrime(n) { if (n < 2) return false; if (n === 2) return true; if (n % 2 === 0) return false; for (let i = 3; i <= Math.sqrt(n); i += 2) { if (n % i === 0) return false; } return true; } function gcd(a, b) { a = Math.abs(a); b = Math.abs(b); while (b !== 0) { const temp = b; b = a % b; a = temp; } return a; } function lcm(a, b) { return Math.abs(a * b) / gcd(a, b); } function factorial(n) { if (n < 0) throw new Error("Factorial not defined for negative numbers"); if (n <= 1) return 1; let result = 1; for (let i = 2; i <= n; i++) { result *= i; } return result; } function modPow(base, exp, mod) { if (mod === 1) return 0; let result = 1; base = base % mod; while (exp > 0) { if (exp % 2 === 1) { result = result * base % mod; } exp = Math.floor(exp / 2); base = base * base % mod; } return result; } function digitSum(n) { n = Math.abs(n); let sum = 0; while (n > 0) { sum += n % 10; n = Math.floor(n / 10); } return sum; } function digitCount(n) { if (n === 0) return 1; return Math.floor(Math.log10(Math.abs(n))) + 1; } function isPerfectSquare(n) { if (n < 0) return false; const sqrt = Math.sqrt(n); return sqrt === Math.floor(sqrt); } function triangular(n) { return n * (n + 1) / 2; } function sumOfPrimes(n) { let count = 0; let sum = 0; let num = 2; while (count < n) { if (isPrime(num)) { sum += num; count++; } num++; } return sum; } var mathFunctions = [ { name: "fibonacci", fn: fibonacci, parameterTypes: ["number"], description: "Calculate the nth Fibonacci number", difficulty: "easy" }, { name: "isPrime", fn: isPrime, parameterTypes: ["number"], description: "Check if a number is prime", difficulty: "easy" }, { name: "gcd", fn: gcd, parameterTypes: ["number", "number"], description: "Calculate greatest common divisor of two numbers", difficulty: "easy" }, { name: "lcm", fn: lcm, parameterTypes: ["number", "number"], description: "Calculate least common multiple of two numbers", difficulty: "medium" }, { name: "factorial", fn: factorial, parameterTypes: ["number"], description: "Calculate factorial of a number", difficulty: "easy" }, { name: "modPow", fn: modPow, parameterTypes: ["number", "number", "number"], description: "Calculate modular exponentiation (base^exp mod mod)", difficulty: "hard" }, { name: "digitSum", fn: digitSum, parameterTypes: ["number"], description: "Calculate sum of digits in a number", difficulty: "easy" }, { name: "digitCount", fn: digitCount, parameterTypes: ["number"], description: "Count the number of digits", difficulty: "easy" }, { name: "isPerfectSquare", fn: isPerfectSquare, parameterTypes: ["number"], description: "Check if a number is a perfect square", difficulty: "easy" }, { name: "triangular", fn: triangular, parameterTypes: ["number"], description: "Calculate the nth triangular number", difficulty: "easy" }, { name: "sumOfPrimes", fn: sumOfPrimes, parameterTypes: ["number"], description: "Calculate sum of first n prime numbers", difficulty: "hard" } ]; // src/functions/string.ts function reverseWords(str) { return str.split(" ").reverse().join(" "); } function reverseString(str) { return str.split("").reverse().join(""); } function countVowels(str) { const vowels = "aeiouAEIOU"; let count = 0; for (const char of str) { if (vowels.includes(char)) count++; } return count; } function countConsonants(str) { const consonants = "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ"; let count = 0; for (const char of str) { if (consonants.includes(char)) count++; } return count; } function caesarCipher(str, shift) { shift = (shift % 26 + 26) % 26; return str.replace(/[a-zA-Z]/g, (char) => { const base = char <= "Z" ? 65 : 97; return String.fromCharCode((char.charCodeAt(0) - base + shift) % 26 + base); }); } function hammingDistance(a, b) { if (a.length !== b.length) { throw new Error("Strings must be of equal length"); } let distance = 0; for (let i = 0; i < a.length; i++) { if (a[i] !== b[i]) distance++; } return distance; } function countSubstring(str, sub) { if (sub.length === 0) return 0; let count = 0; let pos = 0; while ((pos = str.indexOf(sub, pos)) !== -1) { count++; pos += 1; } return count; } function charAtWrapped(str, index) { if (str.length === 0) return ""; index = (index % str.length + str.length) % str.length; return str[index]; } function asciiSum(str) { let sum = 0; for (const char of str) { sum += char.charCodeAt(0); } return sum; } function removeVowels(str) { return str.replace(/[aeiouAEIOU]/g, ""); } function alternatingCase(str) { return str.split("").map((char, i) => i % 2 === 0 ? char.toLowerCase() : char.toUpperCase()).join(""); } function wordCount(str) { return str.trim().split(/\s+/).filter((w) => w.length > 0).length; } function longestWord(str) { const words = str.split(/\s+/).filter((w) => w.length > 0); if (words.length === 0) return ""; return words.reduce((a, b) => a.length >= b.length ? a : b); } var stringFunctions = [ { name: "reverseWords", fn: reverseWords, parameterTypes: ["string"], description: "Reverse the order of words in a string", difficulty: "easy" }, { name: "reverseString", fn: reverseString, parameterTypes: ["string"], description: "Reverse a string character by character", difficulty: "easy" }, { name: "countVowels", fn: countVowels, parameterTypes: ["string"], description: "Count the number of vowels in a string", difficulty: "easy" }, { name: "countConsonants", fn: countConsonants, parameterTypes: ["string"], description: "Count the number of consonants in a string", difficulty: "easy" }, { name: "caesarCipher", fn: caesarCipher, parameterTypes: ["string", "number"], description: "Apply Caesar cipher with given shift", difficulty: "medium" }, { name: "hammingDistance", fn: hammingDistance, parameterTypes: ["string", "string"], description: "Calculate Hamming distance between two equal-length strings", difficulty: "medium" }, { name: "countSubstring", fn: countSubstring, parameterTypes: ["string", "string"], description: "Count occurrences of a substring", difficulty: "easy" }, { name: "charAtWrapped", fn: charAtWrapped, parameterTypes: ["string", "number"], description: "Get character at index with wrapping", difficulty: "easy" }, { name: "asciiSum", fn: asciiSum, parameterTypes: ["string"], description: "Calculate sum of ASCII values of all characters", difficulty: "medium" }, { name: "removeVowels", fn: removeVowels, parameterTypes: ["string"], description: "Remove all vowels from a string", difficulty: "easy" }, { name: "alternatingCase", fn: alternatingCase, parameterTypes: ["string"], description: "Convert to alternating case", difficulty: "easy" }, { name: "wordCount", fn: wordCount, parameterTypes: ["string"], description: "Count words in a string", difficulty: "easy" }, { name: "longestWord", fn: longestWord, parameterTypes: ["string"], description: "Get the longest word in a string", difficulty: "easy" } ]; // src/functions/array.ts function sumEvens(arr) { return arr.filter((n) => n % 2 === 0).reduce((a, b) => a + b, 0); } function sumOdds(arr) { return arr.filter((n) => n % 2 !== 0).reduce((a, b) => a + b, 0); } function product(arr) { return arr.reduce((a, b) => a * b, 1); } function rotateArray(arr, k) { if (arr.length === 0) return []; k = (k % arr.length + arr.length) % arr.length; return [...arr.slice(-k), ...arr.slice(0, -k)]; } function findMedian(arr) { if (arr.length === 0) throw new Error("Cannot find median of empty array"); const sorted = [...arr].sort((a, b) => a - b); const mid = Math.floor(sorted.length / 2); if (sorted.length % 2 === 0) { return (sorted[mid - 1] + sorted[mid]) / 2; } return sorted[mid]; } function findMode(arr) { if (arr.length === 0) throw new Error("Cannot find mode of empty array"); const counts = /* @__PURE__ */ new Map(); for (const n of arr) { counts.set(n, (counts.get(n) || 0) + 1); } let maxCount = 0; let mode = arr[0]; for (const [n, count] of counts) { if (count > maxCount) { maxCount = count; mode = n; } } return mode; } function range(arr) { if (arr.length === 0) return 0; return Math.max(...arr) - Math.min(...arr); } function countGreaterThan(arr, threshold) { return arr.filter((n) => n > threshold).length; } function countLessThan(arr, threshold) { return arr.filter((n) => n < threshold).length; } function secondLargest(arr) { if (arr.length < 2) throw new Error("Array must have at least 2 elements"); const sorted = [...new Set(arr)].sort((a, b) => b - a); if (sorted.length < 2) throw new Error("Array must have at least 2 distinct elements"); return sorted[1]; } function runningSum(arr) { const result = []; let sum = 0; for (const n of arr) { sum += n; result.push(sum); } return result; } function elementAtWrapped(arr, index) { if (arr.length === 0) throw new Error("Array is empty"); index = (index % arr.length + arr.length) % arr.length; return arr[index]; } function dotProduct(a, b) { if (a.length !== b.length) throw new Error("Arrays must have equal length"); let sum = 0; for (let i = 0; i < a.length; i++) { sum += a[i] * b[i]; } return sum; } function maxIndex(arr) { if (arr.length === 0) throw new Error("Array is empty"); let maxIdx = 0; for (let i = 1; i < arr.length; i++) { if (arr[i] > arr[maxIdx]) { maxIdx = i; } } return maxIdx; } var arrayFunctions = [ { name: "sumEvens", fn: sumEvens, parameterTypes: ["number[]"], description: "Sum all even numbers in an array", difficulty: "easy" }, { name: "sumOdds", fn: sumOdds, parameterTypes: ["number[]"], description: "Sum all odd numbers in an array", difficulty: "easy" }, { name: "product", fn: product, parameterTypes: ["number[]"], description: "Calculate the product of all elements", difficulty: "easy" }, { name: "rotateArray", fn: rotateArray, parameterTypes: ["number[]", "number"], description: "Rotate array by k positions to the right", difficulty: "medium" }, { name: "findMedian", fn: findMedian, parameterTypes: ["number[]"], description: "Find the median value of an array", difficulty: "medium" }, { name: "findMode", fn: findMode, parameterTypes: ["number[]"], description: "Find the mode (most frequent element)", difficulty: "medium" }, { name: "range", fn: range, parameterTypes: ["number[]"], description: "Calculate the range (max - min) of an array", difficulty: "easy" }, { name: "countGreaterThan", fn: countGreaterThan, parameterTypes: ["number[]", "number"], description: "Count elements greater than a threshold", difficulty: "easy" }, { name: "countLessThan", fn: countLessThan, parameterTypes: ["number[]", "number"], description: "Count elements less than a threshold", difficulty: "easy" }, { name: "secondLargest", fn: secondLargest, parameterTypes: ["number[]"], description: "Find the second largest element", difficulty: "medium" }, { name: "runningSum", fn: runningSum, parameterTypes: ["number[]"], description: "Calculate running sum array", difficulty: "easy" }, { name: "elementAtWrapped", fn: elementAtWrapped, parameterTypes: ["number[]", "number"], description: "Get element at index with wrapping", difficulty: "easy" }, { name: "dotProduct", fn: dotProduct, parameterTypes: ["number[]", "number[]"], description: "Calculate dot product of two arrays", difficulty: "medium" }, { name: "maxIndex", fn: maxIndex, parameterTypes: ["number[]"], description: "Find index of maximum element", difficulty: "easy" } ]; // src/functions/composite.ts function applyChainedOperations(initialValue, operations) { let result = initialValue; for (const op of operations) { switch (op.operation) { case "add": result += op.value ?? 0; break; case "subtract": result -= op.value ?? 0; break; case "multiply": result *= op.value ?? 1; break; case "divide": if (op.value === 0) throw new Error("Division by zero"); result /= op.value ?? 1; break; case "modulo": if (op.value === 0) throw new Error("Modulo by zero"); result %= op.value ?? 1; break; case "power": result = Math.pow(result, op.value ?? 1); break; case "floor": result = Math.floor(result); break; case "ceil": result = Math.ceil(result); break; case "abs": result = Math.abs(result); break; case "negate": result = -result; break; default: throw new Error(`Unknown operation: ${op.operation}`); } } return result; } function computeAndHash(a, b, c) { const step1 = a * b; const step2 = step1 + c; const step3 = step2 % 1e3; const step4 = step3 * (a % 10); return Math.abs(step4).toString(16).padStart(4, "0"); } function evaluatePolynomial(a, b, c, x) { return a * x * x + b * x + c; } function weightedSum(values, weights) { if (values.length !== weights.length) { throw new Error("Values and weights must have same length"); } let sum = 0; for (let i = 0; i < values.length; i++) { sum += values[i] * weights[i]; } return sum; } function checksum(values) { let result = 0; for (let i = 0; i < values.length; i++) { result = (result << 5) - result + values[i] | 0; } return Math.abs(result); } function evaluateExpression(expr) { if (typeof expr === "number") { return expr; } if (!Array.isArray(expr) || expr.length !== 3) { throw new Error("Invalid expression format"); } const [op, left, right] = expr; const leftVal = evaluateExpression(left); const rightVal = evaluateExpression(right); switch (op) { case "+": return leftVal + rightVal; case "-": return leftVal - rightVal; case "*": return leftVal * rightVal; case "/": if (rightVal === 0) throw new Error("Division by zero"); return leftVal / rightVal; case "%": if (rightVal === 0) throw new Error("Modulo by zero"); return leftVal % rightVal; case "^": return Math.pow(leftVal, rightVal); default: throw new Error(`Unknown operator: ${op}`); } } var compositeFunctions = [ { name: "applyChainedOperations", fn: applyChainedOperations, parameterTypes: ["number", "ChainedOperation[]"], description: "Apply a chain of arithmetic operations to a value", difficulty: "medium" }, { name: "computeAndHash", fn: computeAndHash, parameterTypes: ["number", "number", "number"], description: "Compute operations and return hex hash", difficulty: "hard" }, { name: "evaluatePolynomial", fn: evaluatePolynomial, parameterTypes: ["number", "number", "number", "number"], description: "Evaluate polynomial a*x^2 + b*x + c", difficulty: "medium" }, { name: "weightedSum", fn: weightedSum, parameterTypes: ["number[]", "number[]"], description: "Compute weighted sum of two arrays", difficulty: "medium" }, { name: "checksum", fn: checksum, parameterTypes: ["number[]"], description: "Compute a simple checksum from values", difficulty: "medium" }, { name: "evaluateExpression", fn: evaluateExpression, parameterTypes: ["expression"], description: "Evaluate a nested arithmetic expression", difficulty: "hard" } ]; // src/functions/index.ts var allFunctions = [ ...mathFunctions, ...stringFunctions, ...arrayFunctions, ...compositeFunctions ]; function getFunctionsByDifficulty(difficulty) { return allFunctions.filter((f) => f.difficulty === difficulty); } function getFunctionByName(name) { return allFunctions.find((f) => f.name === name); } function getRandomFunction(difficulty) { const pool = difficulty ? getFunctionsByDifficulty(difficulty) : allFunctions; if (pool.length === 0) { throw new Error(`No functions available for difficulty: ${difficulty}`); } const index = Math.floor(Math.random() * pool.length); return pool[index]; } var functionCategories = { math: mathFunctions, string: stringFunctions, array: arrayFunctions, composite: compositeFunctions }; function getFunctionsByCategory(category) { return functionCategories[category]; } // src/core/generator.ts var DEFAULT_CONFIG = { difficulty: "medium", challengeTypes: ["function_execution", "chained_operations", "encoded_instruction"], expirationMs: 3e4, // 30 seconds rateLimit: { maxAttempts: 10, windowMs: 6e4 } }; var ChallengeGenerator = class { config; constructor(config) { this.config = { ...DEFAULT_CONFIG, ...config }; } /** * Generate a new challenge */ generate(overrides) { const type = overrides?.type ?? randomElement(this.config.challengeTypes); const difficulty = overrides?.difficulty ?? this.config.difficulty; const { payload, expectedAnswer } = this.generatePayload(type, difficulty); const id = generateId(); const expiresAt = Date.now() + this.config.expirationMs; const signatureData = JSON.stringify({ id, type, payload, expiresAt, expectedAnswer }); const signature = signChallenge(signatureData, this.config.secret); const challenge = { id, type, difficulty, payload, expiresAt, signature }; return { challenge, expectedAnswer }; } /** * Generate payload for a specific challenge type */ generatePayload(type, difficulty) { switch (type) { case "function_execution": return this.generateFunctionExecution(difficulty); case "chained_operations": return this.generateChainedOperations(difficulty); case "encoded_instruction": return this.generateEncodedInstruction(difficulty); case "pattern_extraction": return this.generatePatternExtraction(difficulty); case "code_transform": return this.generateCodeTransform(difficulty); default: throw new Error(`Unknown challenge type: ${type}`); } } /** * Generate a function execution challenge */ generateFunctionExecution(difficulty) { const func = getRandomFunction(difficulty); const parameters = this.generateParameters(func.name, difficulty); const result = func.fn(...parameters); const responseEncoding = this.getResponseEncoding(difficulty); const expectedAnswer = encode(String(result), responseEncoding); const functionCode = this.getFunctionCodeString(func.name); const payload = { type: "function_execution", functionName: func.name, functionCode, parameters, responseEncoding }; return { payload, expectedAnswer }; } /** * Generate a chained operations challenge */ generateChainedOperations(difficulty) { const operationCount = difficulty === "easy" ? 3 : difficulty === "medium" ? 5 : 7; const initialValue = randomInt(10, 100); const operations = []; let currentValue = initialValue; const availableOps = [ "add", "subtract", "multiply", "modulo", "floor", "abs" ]; if (difficulty === "hard") { availableOps.push("power", "ceil", "negate"); } for (let i = 0; i < operationCount; i++) { const operation = randomElement(availableOps); let value; switch (operation) { case "add": case "subtract": value = randomInt(1, 50); break; case "multiply": value = randomInt(2, 10); break; case "divide": value = randomInt(2, 5); break; case "modulo": value = randomInt(10, 100); break; case "power": value = randomInt(1, 3); break; default: value = void 0; } operations.push({ operation, value }); switch (operation) { case "add": currentValue += value; break; case "subtract": currentValue -= value; break; case "multiply": currentValue *= value; break; case "divide": currentValue /= value; break; case "modulo": currentValue %= value; break; case "power": currentValue = Math.pow(currentValue, value); break; case "floor": currentValue = Math.floor(currentValue); break; case "ceil": currentValue = Math.ceil(currentValue); break; case "abs": currentValue = Math.abs(currentValue); break; case "negate": currentValue = -currentValue; break; } } const responseEncoding = this.getResponseEncoding(difficulty); const expectedAnswer = encode(String(currentValue), responseEncoding); const payload = { type: "chained_operations", initialValue, operations, responseEncoding }; return { payload, expectedAnswer }; } /** * Generate an encoded instruction challenge */ generateEncodedInstruction(difficulty) { const a = randomInt(10, 100); const b = randomInt(10, 100); const operations = ["+", "-", "*"]; const op = randomElement(operations); let result; switch (op) { case "+": result = a + b; break; case "-": result = a - b; break; case "*": result = a * b; break; default: result = a + b; } const instruction = `Calculate: ${a} ${op} ${b}`; const instructionEncoding = this.getInstructionEncoding(difficulty); const responseEncoding = this.getResponseEncoding(difficulty); const encodedInstruction = encode(instruction, instructionEncoding); const expectedAnswer = encode(String(result), responseEncoding); const payload = { type: "encoded_instruction", instruction: encodedInstruction, instructionEncoding, responseEncoding }; return { payload, expectedAnswer }; } /** * Generate a pattern extraction challenge */ generatePatternExtraction(difficulty) { const count = difficulty === "easy" ? 3 : difficulty === "medium" ? 5 : 7; const items = []; for (let i = 0; i < count; i++) { items.push({ id: i + 1, value: randomInt(10, 100) }); } const queries = [ { query: "sum(items[*].value)", fn: (data) => data.reduce((s, i) => s + i.value, 0) }, { query: "max(items[*].value)", fn: (data) => Math.max(...data.map((i) => i.value)) }, { query: "min(items[*].value)", fn: (data) => Math.min(...data.map((i) => i.value)) }, { query: "count(items)", fn: (data) => data.length } ]; const selected = randomElement(queries); const result = selected.fn(items); const responseEncoding = this.getResponseEncoding(difficulty); const expectedAnswer = encode(String(result), responseEncoding); return { payload: { type: "pattern_extraction", data: { items }, query: selected.query, responseEncoding }, expectedAnswer }; } /** * Generate a code transform challenge */ generateCodeTransform(difficulty) { const a = randomInt(1, 20); const b = randomInt(1, 20); const code = `const x = ${a}; const y = ${b}; return x + y;`; const result = a + b; const responseEncoding = this.getResponseEncoding(difficulty); const expectedAnswer = encode(String(result), responseEncoding); return { payload: { type: "code_transform", code, transform: "execute", responseEncoding }, expectedAnswer }; } /** * Get response encoding based on difficulty */ getResponseEncoding(difficulty) { switch (difficulty) { case "easy": return "plain"; case "medium": return randomElement(["plain", "base64"]); case "hard": return randomElement(["base64", "hex"]); } } /** * Get instruction encoding based on difficulty */ getInstructionEncoding(difficulty) { switch (difficulty) { case "easy": return "base64"; case "medium": return randomElement(["base64", "rot13"]); case "hard": return randomElement(["hex", "rot13"]); } } /** * Generate parameters for a function */ generateParameters(functionName, difficulty) { const range2 = difficulty === "easy" ? [1, 20] : difficulty === "medium" ? [10, 50] : [20, 100]; switch (functionName) { case "fibonacci": return [randomInt(5, 15)]; // Keep reasonable for performance case "isPrime": return [randomInt(range2[0], range2[1])]; case "gcd": case "lcm": return [randomInt(range2[0], range2[1]), randomInt(range2[0], range2[1])]; case "factorial": return [randomInt(3, 10)]; // Keep reasonable case "modPow": return [randomInt(2, 10), randomInt(2, 8), randomInt(10, 50)]; case "digitSum": case "digitCount": case "isPerfectSquare": case "triangular": return [randomInt(range2[0], range2[1])]; case "sumOfPrimes": return [randomInt(3, 8)]; // Keep reasonable // String functions case "reverseWords": case "reverseString": case "countVowels": case "countConsonants": case "removeVowels": case "alternatingCase": case "wordCount": case "longestWord": case "asciiSum": return [this.generateRandomWords(difficulty === "easy" ? 2 : difficulty === "medium" ? 4 : 6)]; case "caesarCipher": return [this.generateRandomWords(3), randomInt(1, 25)]; case "hammingDistance": const word = this.generateRandomWord(5); return [word, this.mutateWord(word, 2)]; case "countSubstring": return ["hello world hello", "hello"]; case "charAtWrapped": return ["alphabet", randomInt(0, 20)]; // Array functions case "sumEvens": case "sumOdds": case "product": case "findMedian": case "findMode": case "range": case "secondLargest": case "runningSum": case "maxIndex": return [this.generateRandomArray(difficulty === "easy" ? 4 : difficulty === "medium" ? 6 : 8)]; case "rotateArray": return [this.generateRandomArray(5), randomInt(1, 5)]; case "countGreaterThan": case "countLessThan": return [this.generateRandomArray(6), randomInt(20, 50)]; case "elementAtWrapped": return [this.generateRandomArray(5), randomInt(0, 10)]; case "dotProduct": { const len = difficulty === "easy" ? 3 : difficulty === "medium" ? 4 : 5; return [this.generateRandomArray(len), this.generateRandomArray(len)]; } // Composite functions case "evaluatePolynomial": return [randomInt(1, 5), randomInt(1, 10), randomInt(1, 10), randomInt(1, 5)]; case "weightedSum": { const wlen = difficulty === "easy" ? 3 : 4; return [this.generateRandomArray(wlen), this.generateRandomArray(wlen)]; } case "checksum": return [this.generateRandomArray(5)]; case "computeAndHash": return [randomInt(10, 50), randomInt(10, 50), randomInt(10, 50)]; default: return [randomInt(range2[0], range2[1])]; } } /** * Generate random words joined by spaces */ generateRandomWords(count) { const words = ["hello", "world", "test", "code", "data", "alpha", "beta", "gamma"]; const selected = []; for (let i = 0; i < count; i++) { selected.push(randomElement(words)); } return selected.join(" "); } /** * Generate a random word */ generateRandomWord(length) { const chars = "abcdefghijklmnopqrstuvwxyz"; let word = ""; for (let i = 0; i < length; i++) { word += chars[randomInt(0, chars.length - 1)]; } return word; } /** * Mutate a word by changing N characters */ mutateWord(word, changes) { const chars = word.split(""); const positions = /* @__PURE__ */ new Set(); while (positions.size < Math.min(changes, word.length)) { positions.add(randomInt(0, word.length - 1)); } for (const pos of positions) { let newChar; do { newChar = "abcdefghijklmnopqrstuvwxyz"[randomInt(0, 25)]; } while (newChar === chars[pos]); chars[pos] = newChar; } return chars.join(""); } /** * Generate a random array of numbers */ generateRandomArray(length) { const arr = []; for (let i = 0; i < length; i++) { arr.push(randomInt(1, 50)); } return arr; } /** * Get function code as string (for display) */ getFunctionCodeString(functionName) { const codeMap = { fibonacci: `function fibonacci(n) { if (n <= 1) return n; let a = 0, b = 1; for (let i = 2; i <= n; i++) { const temp = a + b; a = b; b = temp; } return b; }`, isPrime: `function isPrime(n) { if (n < 2) return false; if (n === 2) return true; if (n % 2 === 0) return false; for (let i = 3; i <= Math.sqrt(n); i += 2) { if (n % i === 0) return false; } return true; }`, gcd: `function gcd(a, b) { a = Math.abs(a); b = Math.abs(b); while (b !== 0) { const temp = b; b = a % b; a = temp; } return a; }`, digitSum: `function digitSum(n) { n = Math.abs(n); let sum = 0; while (n > 0) { sum += n % 10; n = Math.floor(n / 10); } return sum; }`, countVowels: `function countVowels(str) { const vowels = 'aeiouAEIOU'; let count = 0; for (const char of str) { if (vowels.includes(char)) count++; } return count; }`, sumEvens: `function sumEvens(arr) { return arr.filter(n => n % 2 === 0).reduce((a, b) => a + b, 0); }` }; return codeMap[functionName] || `function ${functionName}(...args) { /* implementation */ }`; } }; function createGenerator(config) { return new ChallengeGenerator(config); } // src/utils/rate-limiter.ts var RateLimiter = class { entries = /* @__PURE__ */ new Map(); config; cleanupInterval = null; constructor(config) { this.config = { maxAttempts: config.maxAttempts, windowMs: config.windowMs }; this.startCleanup(); } /** * Check if a key is rate limited */ isRateLimited(key) { const entry = this.entries.get(key); if (!entry) { return false; } if (Date.now() > entry.resetAt) { this.entries.delete(key); return false; } return entry.count >= this.config.maxAttempts; } /** * Record an attempt for a key */ recordAttempt(key) { const now = Date.now(); let entry = this.entries.get(key); if (!entry || now > entry.resetAt) { entry = { count: 0, resetAt: now + this.config.windowMs }; this.entries.set(key, entry); } if (entry.count >= this.config.maxAttempts) { return { allowed: false, remaining: 0, resetAt: entry.resetAt }; } entry.count++; return { allowed: true, remaining: this.config.maxAttempts - entry.count, resetAt: entry.resetAt }; } /** * Get remaining attempts for a key */ getRemainingAttempts(key) { const entry = this.entries.get(key); if (!entry || Date.now() > entry.resetAt) { return this.config.maxAttempts; } return Math.max(0, this.config.maxAttempts - entry.count); } /** * Reset rate limit for a key */ reset(key) { this.entries.delete(key); } /** * Clear all rate limit entries */ clear() { this.entries.clear(); } /** * Start periodic cleanup of expired entries */ startCleanup() { this.cleanupInterval = setInterval(() => { this.cleanup(); }, 6e4); if (this.cleanupInterval.unref) { this.cleanupInterval.unref(); } } /** * Stop the cleanup interval */ destroy() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } this.clear(); } /** * Remove expired entries */ cleanup() { const now = Date.now(); for (const [key, entry] of this.entries) { if (now > entry.resetAt) { this.entries.delete(key); } } } /** * Get current stats */ getStats() { let totalAttempts = 0; for (const entry of this.entries.values()) { totalAttempts += entry.count; } return { activeKeys: this.entries.size, totalAttempts }; } }; function createRateLimiter(config) { return new RateLimiter({ maxAttempts: config?.maxAttempts ?? 10, windowMs: config?.windowMs ?? 6e4 // 1 minute default }); } // src/core/verifier.ts var ChallengeVerifier = class { config; rateLimiter; challengeStore = /* @__PURE__ */ new Map(); cleanupInterval = null; constructor(config) { this.config = { difficulty: "medium", challengeTypes: ["function_execution", "chained_operations", "encoded_instruction"], expirationMs: 3e4, rateLimit: { maxAttempts: 10, windowMs: 6e4 }, ...config }; this.rateLimiter = createRateLimiter(this.config.rateLimit); this.startCleanup(); } /** * Store expected answer for a challenge */ storeChallenge(challengeId, expectedAnswer, expiresAt) { this.challengeStore.set(challengeId, { expectedAnswer, expiresAt }); } /** * Verify a challenge solution */ verify(challenge, solution, clientIdentifier) { const clientKey = clientIdentifier || "anonymous"; const rateLimitResult = this.rateLimiter.recordAttempt(clientKey); if (!rateLimitResult.allowed) { return { valid: false, error: `Rate limited. Try again in ${Math.ceil((rateLimitResult.resetAt - Date.now()) / 1e3)} seconds.`, errorCode: "RATE_LIMITED" }; } if (challenge.id !== solution.challengeId) { return { valid: false, error: "Challenge ID mismatch", errorCode: "CHALLENGE_NOT_FOUND" }; } if (Date.now() > challenge.expiresAt) { return { valid: false, error: "Challenge has expired", errorCode: "EXPIRED" }; } const stored = this.challengeStore.get(challenge.id); if (!stored) { return { valid: false, error: "Challenge not found or already used", errorCode: "CHALLENGE_NOT_FOUND" }; } if (Date.now() > stored.expiresAt) { this.challengeStore.delete(challenge.id); return { valid: false, error: "Challenge has expired", errorCode: "EXPIRED" }; } const signatureData = JSON.stringify({ id: challenge.id, type: challenge.type, payload: challenge.payload, expiresAt: challenge.expiresAt, expectedAnswer: stored.expectedAnswer }); const expectedSignature = signChallenge(signatureData, this.config.secret); if (!safeCompare(challenge.signature, expectedSignature)) { return { valid: false, error: "Invalid challenge signature", errorCode: "INVALID_SIGNATURE" }; } if (!safeCompare(solution.solution, stored.expectedAnswer)) { return { valid: false, error: "Incorrect solution", errorCode: "INVALID_SOLUTION" }; } this.challengeStore.delete(challenge.id); this.rateLimiter.reset(clientKey); return { valid: true }; } /** * Verify a solution using only the signature (stateless mode) * * In stateless mode, the expected answer is encoded in the signature * This is less secure but allows for distributed deployments */ verifyStateless(challenge, solution, clientIdentifier) { const clientKey = clientIdentifier || "anonymous"; const rateLimitResult = this.rateLimiter.recordAttempt(clientKey); if (!rateLimitResult.allowed) { return { valid: false, error: `Rate limited. Try again in ${Math.ceil((rateLimitResult.resetAt - Date.now()) / 1e3)} seconds.`, errorCode: "RATE_LIMITED" }; } if (challenge.id !== solution.challengeId) { return { valid: false, error: "Challenge ID mismatch", errorCode: "CHALLENGE_NOT_FOUND" }; } if (Date.now() > challenge.expiresAt) { return { valid: false, error: "Challenge has expired", errorCode: "EXPIRED" }; } const signatureData = JSON.stringify({ id: challenge.id, type: challenge.type, payload: challenge.payload, expiresAt: challenge.expiresAt, expectedAnswer: solution.solution }); const computedSignature = signChallenge(signatureData, this.config.secret); if (!safeCompare(challenge.signature, computedSignature)) { return { valid: false, error: "Incorrect solution", errorCode: "INVALID_SOLUTION" }; } return { valid: true }; } /** * Get rate limit status for a client */ getRateLimitStatus(clientIdentifier) { return { remaining: this.rateLimiter.getRemainingAttempts(clientIdentifier), isLimited: this.rateLimiter.isRateLimited(clientIdentifier) }; } /** * Start periodic cleanup of expired challenges */ startCleanup() { this.cleanupInterval = setInterval(() => { this.cleanup(); }, 6e4); if (this.cleanupInterval.unref) { this.cleanupInterval.unref(); } } /** * Clean up expired challenges */ cleanup() { const now = Date.now(); for (const [id, stored] of this.challengeStore) { if (now > stored.expiresAt) { this.challengeStore.delete(id); } } } /** * Destroy the verifier and clean up resources */ destroy() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } this.rateLimiter.destroy(); this.challengeStore.clear(); } /** * Get stats for monitoring */ getStats() { return { pendingChallenges: this.challengeStore.size, rateLimitStats: this.rateLimiter.getStats() }; } }; function createVerifier(config) { return new ChallengeVerifier(config); } // src/server/standalone.ts var CaptchaLM = class { generator; verifier; config; constructor(config) { this.config = config; this.generator = new ChallengeGenerator(config); this.verifier = new ChallengeVerifier(config); } /** * Generate a new challenge */ generate(options) { const result = this.generator.generate(options); this.verifier.storeChallenge( result.challenge.id, result.expectedAnswer, result.challenge.expiresAt ); return result; } /** * Verify a challenge solution */ verify(challenge, solution, clientIdentifier) { const challengeSolution = { challengeId: challenge.id, solution }; return this.verifier.verify(challenge, challengeSolution, clientIdentifier); } /** * Verify a challenge solution in stateless mode * (no server-side storage required) */ verifyStateless(challenge, solution, clientIdentifier) { const challengeSolution = { challengeId: challenge.id, solution }; return this.verifier.verifyStateless(challenge, challengeSolution, clientIdentifier); } /** * Get rate limit status for a client */ getRateLimitStatus(clientIdentifier) { return this.verifier.getRateLimitStatus(clientIdentifier); } /** * Get stats for monitoring */ getStats() { return this.verifier.getStats(); } /** * Get the current configuration */ getConfig() { return { ...this.config }; } /** * Destroy and clean up resources */ destroy() { this.verifier.destroy(); } }; function createCaptchaLM(config) { return new CaptchaLM(config); } // src/server/middleware.ts var DEFAULT_MIDDLEWARE_CONFIG = { challengeIdHeader: "x-captchalm-id", solutionHeader: "x-captchalm-solution", challengeEndpoint: "/_captchalm/challenge" }; function buildSolvingInstructions(challenge) { const payload = challenge.payload; const responseEncoding = payload.responseEncoding || "plain"; let instructions = ""; switch (challenge.type) { case "function_execution": instructions = `TASK: Execute the following function with the given parameters. FUNCTION: ${payload.functionCode} PARAMETERS: ${JSON.stringify(payload.parameters)} STEPS: 1. Execute ${payload.functionName}(${payload.parameters.join(", ")}) 2. Take the result and encode it as: ${responseEncoding} 3. Submit the encoded result as your answer`; break; case "chained_operations": const ops = payload.operations; const opList = ops.map( (op, i) => ` ${i + 1}. ${op.operation}${op.value !== void 0 ? ` ${op.value}` : ""}` ).join("\n"); instructions = `TASK: Apply these operations to the initial value in order. INITIAL VALUE: ${payload.initialValue} OPERATIONS: ${opList} STEPS: 1. Start with ${payload.initialValue} 2. Apply each operation in sequence 3. Encode the final result as: ${responseEncoding} 4. Submit the encoded result as your answer`; break; case "encoded_instruction": instructions = `TASK: Decode the instruction, compute the result, and encode your answer. ENCODED INSTRUCTION: ${payload.instruction} INSTRUCTION ENCODING: ${payload.instructionEncoding} STEPS: 1. Decode the instruction from ${payload.instructionEncoding} 2. The decoded text will be a math expression like "Calculate: X + Y" 3. Compute the result 4. Encode your answer as: ${responseEncoding} 5. Submit the encoded result`; break; case "pattern_extraction": instructions = `TASK: Query the data and compute the result. DATA: ${JSON.stringify(payload.data, null, 2)} QUERY: ${payload.query} STEPS: 1. Parse the query (e.g., "sum(items[*].value)" means sum all 'value' fields in 'items' array) 2. Execute the query on the data 3. Encode the result as: ${responseEncoding} 4. Submit the encoded result`; break; case "code_transform": instructions = `TASK: Execute the code and return the result. CODE: ${payload.code} TRANSFORM: ${payload.transform} STEPS: 1. Execute the JavaScript code (it uses 'return' to return a value) 2. Apply transform: ${payload.transform === "execute" ? "just use the result" : payload.transform} 3. Encode the result as: ${responseEncoding} 4. Submit the encoded result`; break; default: instructions = `TASK: Solve the challenge of type "${challenge.type}" using the payload data.`; } instructions += ` ENCODING REFERENCE: - plain: Return the value as a string (e.g., 42 \u2192 "42") - base64: Base64 encode (e.g., 42 \u2192 "NDI=") - hex: Hex encode each character (e.g., 42 \u2192 "3432") - rot13: Shift each letter by 13 (e.g., "abc" \u2192 "nop")`; return instructions; } function createExpressMiddleware(config) { const fullConfig = { ...DEFAULT_MIDDLEWARE_CONFIG, ...config }; const generator = new ChallengeGenerator(fullConfig); const verifier = new ChallengeVerifier(fullConfig); function getClientIdentifier(req) { const forwarded = req.headers["x-forwarded-for"]; if (forwarded) { const ip = Array.isArray(forwarded) ? forwarded[0] : forwarded.split(",")[0]; return ip.trim(); } return req.ip || req.socket.remoteAddress || "unknown"; } const challenge = (_req, res) => { try { const { challenge: challenge2, expectedAnswer } = generator.generate(); verifier.storeChallenge(challenge2.id, expectedAnswer, challenge2.expiresAt); res.json({ success: true, challenge: { id: challenge2.id, type: challenge2.type, difficulty: challenge2.difficulty, payload: challenge2.payload, expiresAt: challenge2.expiresAt, signature: challenge2.signature } }); } catch (error) { res.status(500).json({ success: false, error: "Failed to generate challenge" }); } }; const protect = (req, res, next) => { const clientIdentifier = getClientIdentifier(req); req.CaptchaLM = { verified: false, clientIdentifier }; const challengeId = req.headers[fullConfig.challengeIdHeader]; const solution = req.headers[fullConfig.solutionHeader]; if (!challengeId || !solution) { const { challenge: challenge2, expectedAnswer } = generator.generate(); verifier.storeChallenge(challenge2.id, expectedAnswer, challenge2.expiresAt); res.status(401).json({ success: false, error: "AI verification required", captchalm: { challenge: challenge2, instructions: buildSolvingInstructions(challenge2), howToSubmit: { headers: { [fullConfig.challengeIdHeader]: challenge2.id, [fullConfig.solutionHeader]: "" }, body: { _CaptchaLMChallenge: challenge2 }, note: "Retry your original request with these headers and body field added" } } }); return; } let challengeData; if (req.body && req.body._CaptchaLMChallenge) { challengeData = req.body._CaptchaLMChallenge; } else { res.status(401).json({ success: false, error: "Challenge data required. Include _CaptchaLMChallenge in body or use stateless mode." }); return; } const challengeSolution = { challengeId, solution }; const result = verifier.verify(challengeData, challengeSolution, clientIdentifier); if (!result.valid) { const statusCode = result.errorCode === "RATE_LIMITED" ? 429 : 401; res.status(statusCode).json({ success: false, error: result.error, errorCode: result.errorCode }); return; } req.CaptchaLM.verified = true; req.CaptchaLM.challenge = challengeData; next(); }; return { protect, challenge, generator, verifier }; } function createVerificationEndpoint(config) { const fullConfig = { ...DEFAULT_MIDDLEWARE_CONFIG, ...config }; const verifier = new ChallengeVerifier(fullConfig); return (req, res) => { const clientIdentifier = req.ip || "unknown"; const { challenge, solution } = req.body; if (!challenge || !solution) { res.status(400).json({ success: false, error: "Missing challenge or solution in request body" }); return; } const challengeSolution = { challengeId: challenge.id, solution }; const result = verifier.verifyStateless(challenge, challengeSolution, clientIdentifier); res.json({ success: result.valid, error: result.error, errorCode: result.errorCode }); }; } exports.CaptchaLM = CaptchaLM; exports.ChallengeGenerator = ChallengeGenerator; exports.ChallengeVerifier = ChallengeVerifier; exports.allFunctions = allFunctions; exports.createCaptchaLM = createCaptchaLM; exports.createExpressMiddleware = createExpressMiddleware; exports.createGenerator = createGenerator; exports.createVerificationEndpoint = createVerificationEndpoint; exports.createVerifier = createVerifier; exports.decode = decode; exports.decodeBase64 = decodeBase64; exports.decodeChain = decodeChain; exports.decodeHex = decodeHex; exports.decodeRot13 = decodeRot13; exports.encode = encode; exports.encodeBase64 = encodeBase64; exports.encodeChain = encodeChain; exports.encodeHex = encodeHex; exports.encodeRot13 = encodeRot13; exports.functionCategories = functionCategories; exports.getFunctionByName = getFunctionByName; exports.getFunctionsByCategory = getFunctionsByCategory; exports.getFunctionsByDifficulty = getFunctionsByDifficulty; exports.getRandomFunction = getRandomFunction; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map