Jules was unable to complete the task in time. Please review the work done so far and provide feedback for Jules to continue.

This commit is contained in:
google-labs-jules[bot] 2025-06-09 14:01:00 +00:00
parent eea059ce5a
commit 3140458246
42 changed files with 13026 additions and 390 deletions

302
diplomacy/utils/common.ts Normal file
View file

@ -0,0 +1,302 @@
// diplomacy/utils/common.ts
// Common utils symbols used in diplomacy network code.
import * as crypto from 'crypto';
import { Buffer } from 'buffer'; // Needed for Base64 operations
// Placeholder for exceptions - will be imported from a dedicated exceptions file later
class CommonKeyException extends Error {
constructor(key: string) {
super(`Common key found: ${key}`);
this.name = 'CommonKeyException';
}
}
// Datetime since timestamp 0.
export const EPOCH = new Date(Date.UTC(1970, 0, 1, 0, 0, 0));
// Regex used for conversion from camel case to snake case.
const REGEX_CONSECUTIVE_UPPER_CASES = /[A-Z]{2,}/g; // Adjusted for JS: removed compile, added g
const REGEX_LOWER_THEN_UPPER_CASES = /([a-z0-9])([A-Z])/g; // Adjusted for JS: removed compile, added g
const REGEX_UNDERSCORE_THEN_LETTER = /_([a-z])/g; // Adjusted for JS: removed compile, added g
const REGEX_START_BY_LOWERCASE = /^[a-z]/; // Adjusted for JS: removed compile
/**
* Hash long password to allow bcrypt to handle password longer than 72 characters.
* Module private method.
* @param password - password to hash.
* @returns The hashed password, base64 encoded.
*/
function _sub_hash_password(password: string): string {
// Bcrypt only handles passwords up to 72 characters. We use this hashing method as a work around.
// Suggested in bcrypt PyPI page (2018/02/08 12:36 EST): https://pypi.python.org/pypi/bcrypt/3.1.0
const hash = crypto.createHash('sha256');
hash.update(password, 'utf-8');
return hash.digest('base64');
}
/**
* Check if password matches hashed.
* NOTE: This is a STUB and NOT a secure bcrypt replacement.
* @param password - password to check.
* @param hashed - a password hashed with hash_password().
* @returns Indicates if the password matches the hash.
*/
export function is_valid_password(password: string, hashed: string): boolean {
console.warn("`is_valid_password` is using a STUB comparison and is NOT SECURE.");
// In a real scenario, use a library like bcrypt.compareSync()
// This stub assumes `hashed` is the output of our stubbed `hash_password`.
const sub_hashed_password = _sub_hash_password(password);
// Example: if hash_password just appended a salt, check that.
// This is highly insecure and just for structure.
return hashed.startsWith(sub_hashed_password);
}
/**
* Hash password. Accepts password longer than 72 characters. Public method.
* NOTE: This is a STUB and NOT a secure bcrypt replacement.
* @param password - The password to hash
* @returns The hashed password.
*/
export function hash_password(password: string): string {
console.warn("`hash_password` is a STUB and is NOT SECURE. It does not use bcrypt salts.");
// In a real scenario, use a library like bcrypt.hashSync()
const sub_hashed = _sub_hash_password(password);
// This is just a placeholder, not a real salt process.
return `${sub_hashed}$INSECURE_STUB_SALT`;
}
/**
* Generate a token with 2 * n_bytes characters (n_bytes bytes encoded in hexadecimal).
*/
export function generate_token(n_bytes: number = 128): string {
return crypto.randomBytes(n_bytes).toString('hex');
}
/**
* Check if given variable is a dictionary-like object (plain object in JS/TS).
* @param dict_to_check - Object to check.
* @returns Indicates if the object is a plain object.
*/
export function is_dictionary(dict_to_check: any): boolean {
return typeof dict_to_check === 'object' && dict_to_check !== null && !Array.isArray(dict_to_check) && !(dict_to_check instanceof Date);
}
/**
* Check if given variable is a sequence-like object (array in JS/TS).
* Note that strings will not be considered as sequences.
* @param seq_to_check - Sequence-like object to check.
* @returns Indicates if the object is array-like.
*/
export function is_sequence(seq_to_check: any): boolean {
// Strings and dicts are not valid sequences.
if (typeof seq_to_check === 'string' || is_dictionary(seq_to_check)) {
return false;
}
return Array.isArray(seq_to_check) || (typeof seq_to_check === 'object' && seq_to_check !== null && typeof (seq_to_check as any)[Symbol.iterator] === 'function');
}
/**
* Convert a string (expected to be in camel case) to snake case.
* @param name - string to convert.
* @returns snake case version of given name.
*/
export function camel_case_to_snake_case(name: string): string {
if (name === '') {
return name;
}
// Python: separated_consecutive_uppers = REGEX_CONSECUTIVE_UPPER_CASES.sub(lambda m: '_'.join(c for c in m.group(0)), name)
// JS:
let separated_consecutive_uppers = name.replace(REGEX_CONSECUTIVE_UPPER_CASES, (match) => {
return Array.from(match).join('_');
});
// Python: return REGEX_LOWER_THEN_UPPER_CASES.sub(r'\1_\2', separated_consecutive_uppers).lower()
// JS:
return separated_consecutive_uppers.replace(REGEX_LOWER_THEN_UPPER_CASES, '$1_$2').toLowerCase();
}
/**
* Convert a string (expected to be in snake case) to camel case and convert first letter
* to upper case if it's in lowercase.
* @param name - string to convert.
* @returns camel case version of given name.
*/
export function snake_case_to_upper_camel_case(name: string): string {
if (name === '') {
return name;
}
// Python: first_lower_case_to_upper = REGEX_START_BY_LOWERCASE.sub(lambda m: m.group(0).upper(), name)
// JS:
let first_lower_case_to_upper = name.replace(REGEX_START_BY_LOWERCASE, (match) => match.toUpperCase());
// Python: return REGEX_UNDERSCORE_THEN_LETTER.sub(lambda m: m.group(1).upper(), first_lower_case_to_upper)
// JS:
return first_lower_case_to_upper.replace(REGEX_UNDERSCORE_THEN_LETTER, (match, charAfterUnderscore) => charAfterUnderscore.toUpperCase());
}
/**
* Check that dictionaries does not share keys.
*/
export function assert_no_common_keys(dict1: object, dict2: object): void {
const keys1 = Object.keys(dict1);
const keys2 = Object.keys(dict2);
let smallest_dict_keys: string[], biggest_dict: object;
if (keys1.length < keys2.length) {
smallest_dict_keys = keys1;
biggest_dict = dict2;
} else {
smallest_dict_keys = keys2;
biggest_dict = dict1;
}
for (const key of smallest_dict_keys) {
if (key in biggest_dict) {
throw new CommonKeyException(key);
}
}
}
/**
* Return current timestamp with microsecond resolution.
* Note: JavaScript's Date.now() is milliseconds. For microseconds, multiply by 1000.
* For more precise microsecond timing, process.hrtime() could be used in Node.js,
* but this simple multiplication matches the Python version's intent if not its exact precision source.
*/
export function timestamp_microseconds(): number {
// Python: delta = datetime.now() - EPOCH
// return (delta.days * 24 * 60 * 60 + delta.seconds) * 1000000 + delta.microseconds
return Date.now() * 1000;
}
/**
* Return a new class to be used as string comparator for sorting.
*/
export function str_cmp_class<T extends string>(compare_function: (a: T, b: T) => number): { new(value: T): { value: T, toString(): string } } {
class StringComparator {
public value: T;
private cmp_fn: (a: T, b: T) => number;
constructor(value: T) {
this.value = value; // Already a string in TS context generally
this.cmp_fn = compare_function;
}
toString(): string {
return this.value;
}
// These methods are what Array.prototype.sort() would look for if comparing instances directly.
// However, usually, you pass a compare function to sort.
// For compatibility with Python's use (e.g. storing these wrapped objects in a list and calling sort()),
// one might need a custom sort that extracts .value or uses these.
// For direct use in JS sort: list.sort((a, b) => a.cmp_fn(a.value, b.value))
// Not directly used by Array.sort in JS, but good for completeness if objects are compared.
equals(other: StringComparator | string): boolean {
const otherValue = (typeof other === 'string') ? other : other.value;
return this.cmp_fn(this.value, otherValue as T) === 0;
}
lessThan(other: StringComparator | string): boolean {
const otherValue = (typeof other === 'string') ? other : other.value;
return this.cmp_fn(this.value, otherValue as T) < 0;
}
}
// To make it somewhat unique like Python's id()-based naming, though not strictly necessary in TS.
// Object.defineProperty(StringComparator, 'name', { value: `StringComparator${Date.now()}${Math.random()}` });
return StringComparator;
}
/**
* Convert element to a string and make sure string is wrapped in either simple quotes
* (if contains double quotes) or double quotes (if contains simple quotes).
* If no quotes, or both types of quotes, defaults to double quotes.
*/
export function to_string_with_quoting(element: any): string {
const s_element = String(element);
const hasDouble = s_element.includes('"');
const hasSingle = s_element.includes("'");
if (hasDouble && !hasSingle) {
return `'${s_element}'`;
}
// Default to double quotes if no quotes, or if both types are present (escaping would be needed for correctness in that case)
return `"${s_element}"`;
}
export class StringableCode {
public readonly code: number | null;
public readonly message: string;
constructor(code: number | string, message?: string) {
if (typeof code === 'string' && message === undefined) {
const message_parts = code.split(':');
if (message_parts.length > 1 && /^\d+$/.test(message_parts[0]!)) {
this.code = parseInt(message_parts[0]!, 10);
this.message = message_parts.slice(1).join(':');
} else {
this.code = null;
this.message = code;
}
} else if (typeof code === 'number' && message !== undefined) {
this.code = code;
this.message = message;
} else if (typeof code === 'number' && message === undefined) { // Only code provided
this.code = code;
this.message = String(code);
}
else { // Fallback or error
this.code = null;
this.message = String(code); // Treat code as message if types are unexpected
}
}
equals(other: StringableCode | string | number): boolean {
if (other instanceof StringableCode) {
return this.code === other.code;
}
// In Python, it compared self._message == str(other).
// For more robustness, we might compare code if other is number, message if string.
if (typeof other === 'number' && this.code !== null) {
return this.code === other;
}
return this.message === String(other);
}
// __hash__ is not directly applicable in JS/TS objects for Map keys in the same way.
// If used as Map keys, the object reference is used, or toString() for string-keyed maps.
// Python's __mod__ was for string formatting like 'Error: %s' % value
format(...values: any[]): StringableCode {
let formatted_message = this.message;
for (const value of values) {
formatted_message = formatted_message.replace(/%s/, String(value)); // Simple %s replacement
}
// More sophisticated formatting would require a proper sprintf-js or similar.
return new StringableCode(this.code, formatted_message);
}
toString(): string {
return this.message;
}
get repr(): string { // Getter for representation
return `${this.code}:${this.message}`;
}
}
// Stub for Tornado utilities, as Tornado is Python-specific.
export class Tornado {
/**
* Modify exception handler method of given IO loop so that IO loop stops and raises
* as soon as an exception is thrown from a callback.
* @param io_loop - IO loop (Tornado specific, type any for TS)
*/
static stop_loop_on_callback_error(io_loop: any): void {
console.warn("Tornado.stop_loop_on_callback_error is a STUB and not applicable in typical Node.js environments.");
// In a Node.js/TS environment, unhandled exceptions in async operations
// would typically be caught by process.on('uncaughtException') or promise .catch() handlers.
}
}