Add settings page for managing forecasting API key and URL via UI

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
jtricerolph 2026-07-20 14:37:36 +00:00
parent cae411eae7
commit 1c411e402e
19809 changed files with 1962608 additions and 97 deletions

View file

@ -0,0 +1,72 @@
import { Semaphore } from "./semaphore.mjs";
//#region src/promise/mutex.ts
/**
* A Mutex (mutual exclusion lock) for async functions.
* It allows only one async task to access a critical section at a time.
*
* @example
* const mutex = new Mutex();
*
* async function criticalSection() {
* await mutex.acquire();
* try {
* // This code section cannot be executed simultaneously
* } finally {
* mutex.release();
* }
* }
*
* criticalSection();
* criticalSection(); // This call will wait until the first call releases the mutex.
*/
var Mutex = class {
semaphore = new Semaphore(1);
/**
* Checks if the mutex is currently locked.
* @returns True if the mutex is locked, false otherwise.
*
* @example
* const mutex = new Mutex();
* console.log(mutex.isLocked); // false
* await mutex.acquire();
* console.log(mutex.isLocked); // true
* mutex.release();
* console.log(mutex.isLocked); // false
*/
get isLocked() {
return this.semaphore.available === 0;
}
/**
* Acquires the mutex, blocking if necessary until it is available.
* @returns A promise that resolves when the mutex is acquired.
*
* @example
* const mutex = new Mutex();
* await mutex.acquire();
* try {
* // This code section cannot be executed simultaneously
* } finally {
* mutex.release();
* }
*/
async acquire() {
return this.semaphore.acquire();
}
/**
* Releases the mutex, allowing another waiting task to proceed.
*
* @example
* const mutex = new Mutex();
* await mutex.acquire();
* try {
* // This code section cannot be executed simultaneously
* } finally {
* mutex.release(); // Allows another waiting task to proceed.
* }
*/
release() {
this.semaphore.release();
}
};
//#endregion
export { Mutex };