fix: Race condition on init & add method for overriding config options (#172)

* fix: Race condition on init & add method for overriding config options before configured() is set

Signed-off-by: Pecacheu <3608878+Pecacheu@users.noreply.github.com>

* fix: Cleanup lock when finished

Signed-off-by: Pecacheu <3608878+Pecacheu@users.noreply.github.com>

* fix: Improve comments

Signed-off-by: Pecacheu <3608878+Pecacheu@users.noreply.github.com>

* fix: Add config param to preConfig

Signed-off-by: Pecacheu <3608878+Pecacheu@users.noreply.github.com>

---------

Signed-off-by: Pecacheu <3608878+Pecacheu@users.noreply.github.com>
This commit is contained in:
Ray
2026-07-27 11:38:20 -04:00
committed by GitHub
parent 30b8505bc9
commit 7ee09129fd
+27 -15
View File
@@ -189,7 +189,8 @@ export class Client extends AsyncEventEmitter<Events> {
readonly options: ClientOptions;
readonly events: EventClient<1>;
configuration: RevoltConfig | undefined;
readonly configuration: RevoltConfig | undefined;
#configLock?: Promise<void>;
#session: Session | undefined;
user: User | undefined;
@@ -203,8 +204,10 @@ export class Client extends AsyncEventEmitter<Events> {
#setConnectionFailureCount: Setter<number>;
#reconnectTimeout: number | undefined;
#slowmodeTimers = new Map<string, ReturnType<typeof setTimeout>>();
/**
* Create Stoat.js Client
* @param configuration Deprecated - Please use `Client.initConfig` if you need to override config.
*/
constructor(options?: Partial<ClientOptions>, configuration?: RevoltConfig) {
super();
@@ -256,8 +259,6 @@ export class Client extends AsyncEventEmitter<Events> {
this.configured = configured;
this.#setConfigured = setConfigured;
this.#fetchConfiguration();
const [ready, setReady] = createSignal(false);
this.ready = ready;
this.#setReady = setReady;
@@ -343,13 +344,28 @@ export class Client extends AsyncEventEmitter<Events> {
}
/**
* Fetches the configuration of the server if it has not been already fetched.
* Fetches the server config. This is called automatically by `login()` or `loginBot()`,
* but you can call it first manually if you need to override any config options.
*
* Override example:
* ```ts
* await client.initConfig((config) => {
* config.ws = "wss://example.com";
* });
* ```
*/
async #fetchConfiguration(): Promise<void> {
if (!this.configuration) {
this.configuration = await this.api.get("/");
this.#setConfigured(true);
async initConfig(preConfig?: (config: RevoltConfig) => void): Promise<void> {
if (!this.#configLock && !this.configuration) {
//Create promise lock to avoid race condition
this.#configLock = (async () => {
//@ts-expect-error readonly override
this.configuration = await this.api.get("/");
preConfig?.(this.configuration);
this.#setConfigured(true);
this.#configLock = undefined;
})();
}
return this.#configLock;
}
/**
@@ -370,7 +386,7 @@ export class Client extends AsyncEventEmitter<Events> {
* @returns An on-boarding function if on-boarding is required, undefined otherwise
*/
async login(details: DataLogin): Promise<void> {
await this.#fetchConfiguration();
await this.initConfig();
const data = await this.api.post("/auth/session/login", details);
if (data.result === "Success") {
this.#session = data;
@@ -393,7 +409,7 @@ export class Client extends AsyncEventEmitter<Events> {
* @param token Bot token
*/
async loginBot(token: string): Promise<void> {
await this.#fetchConfiguration();
await this.initConfig();
this.#session = token;
this.#updateHeaders();
this.connect();
@@ -614,10 +630,6 @@ export class Client extends AsyncEventEmitter<Events> {
* Backend enforced limits for the logged in user
*/
get limits(): UserLimits | undefined {
if (!this.configured() || !this.user) {
return;
}
return this.user.limits;
if (this.configured() && this.user) return this.user.limits;
}
}