From 1b0c6b188484f83e4faa35fe4e0c9cb3f8caad5d Mon Sep 17 00:00:00 2001 From: Rolands Date: Wed, 24 Dec 2025 09:51:57 +0200 Subject: [PATCH 1/4] fix client ready() promise implementation --- core/core-client.ts | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/core/core-client.ts b/core/core-client.ts index 110cf34..4f4dea1 100644 --- a/core/core-client.ts +++ b/core/core-client.ts @@ -52,7 +52,7 @@ export class SocioClient extends LogHandler { #ws: WebSocket | null = null; #client_id:ClientID = ''; #latency:number = 0; - #is_ready: Function | boolean = false; + #ready_state = new ReadyState(); #authenticated=false; #queries: Map = new Map(); //keeps a dict of all subscribed queries @@ -145,7 +145,7 @@ export class SocioClient extends LogHandler { this.#client_id = ''; this.#ws = null; this.#latency = Infinity; - this.#is_ready = false; + this.#ready_state = new ReadyState(); this.#authenticated = false; this.#queries.clear(); this.#props.clear(); @@ -175,13 +175,9 @@ export class SocioClient extends LogHandler { await this.#GetReconToken(); //get new recon token and push to local storage } - if (this.#is_ready !== false && typeof this.#is_ready === "function") - this.#is_ready(true); //resolve promise to true - else - this.#is_ready = true; - if (this.verbose) this.done(`Socio WebSocket [${this.config.name}] connected.`); - - this.#is_ready = true; + //set ready state to true and resolve ready() promise, if exists + this.#ready_state.resolve(true); + if (this.verbose) this.done(`Socio WebSocket [${this.config?.name || this.#client_id || 'NAME'}] connected.`); // once ready, attempt to use the given name as a global identifier. No problem, if this fails. //persistance would restore the old name, so no need to announce again. @@ -732,8 +728,9 @@ export class SocioClient extends LogHandler { get web_socket() { return this.#ws; } //the WebSocket instance has some useful properties https://developer.mozilla.org/en-US/docs/Web/API/WebSocket#instance_properties get client_address_info() { return { url: this.#ws?.url, protocol: this.#ws?.protocol, extensions: this.#ws?.extensions }; } //for convenience get latency() { return this.#latency; } //shows the latency in ms of the initial connection handshake to determine network speed for this session. Might be useful to inform the user, if its slow. - ready(): Promise { return this.#is_ready === true ? (new Promise(res => res(true))) : (new Promise(res => this.#is_ready = res)) } + ready() { return this.#ready_state.promise;} Close() { this.#ws?.close(); } + get is_ready() {return this.#ready_state.is_ready === true} async #GetReconToken(name: string = this.config.name as string){ const { id, prom } = this.CreateQueryPromise(); @@ -863,4 +860,21 @@ export function Uint8ArrayToSocioFileBase64(file_bin: ArrayBuffer, chunkSize = 0 // https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa return window.btoa(binaryString); //The btoa() method creates a Base64-encoded ASCII string from a binary string (i.e., a string in which each character in the string is treated as a byte of binary data). -} \ No newline at end of file +} + +class ReadyState { + promise: Promise; + #resolve!: (value: T) => void; + is_ready = false; + + constructor() { + this.promise = new Promise(res => { + this.#resolve = res; + }); + } + + resolve(value: T) { + this.is_ready = true; + this.#resolve(value); + } +} From 0d79fce195ec1e76fa926075891efd2466c21be5 Mon Sep 17 00:00:00 2001 From: Rolands Date: Wed, 24 Dec 2025 10:06:58 +0200 Subject: [PATCH 2/4] pub --- core/package-lock.json | 4 ++-- core/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/package-lock.json b/core/package-lock.json index 3c8bcc4..0a5d5e3 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "socio", - "version": "1.14.1", + "version": "1.14.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "socio", - "version": "1.14.1", + "version": "1.14.2", "license": "MIT", "dependencies": { "js-yaml": "^4.1.0", diff --git a/core/package.json b/core/package.json index 6386da9..9069812 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "socio", - "version": "1.14.1", + "version": "1.14.2", "description": "A WebSocket Real-Time Communication (RTC) API framework.", "main": "./dist/core.js", "type": "module", From cc78c27090f4cb22df93c109d5774d7da22f2454 Mon Sep 17 00:00:00 2001 From: Rolands Date: Thu, 25 Dec 2025 11:04:13 +0200 Subject: [PATCH 3/4] UpdatePropVal returns 0 if nothing to send --- core/core-server.ts | 4 ++-- core/package-lock.json | 4 ++-- core/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/core-server.ts b/core/core-server.ts index d1f79a1..bc182b0 100644 --- a/core/core-server.ts +++ b/core/core-server.ts @@ -860,7 +860,7 @@ export class SocioServer extends LogHandler { GetPropVal(key: PropKey): PropValue | undefined{ return this.#props.get(key)?.val; } - //UpdatePropVal does not set the new val, rather it calls the assigner, which is responsible for setting the new value. + //UpdatePropVal does not set the new val, rather it calls the assigner, which is responsible for setting the new value. Props by default use SetPropVal as assigner, which just sets the new value. UpdatePropVal(key: PropKey, new_val: PropValue, sender_client_id: ClientID | null, send_as_diff = this.#prop_upd_diff):Bit{//this will propogate the change, if it is assigned, to all subscriptions const prop = this.#props.get(key); if (!prop) throw new E(`Prop key [${key}] not registered! [#prop-update-not-found]`); @@ -872,7 +872,7 @@ export class SocioServer extends LogHandler { if (prop.assigner(key, new_val, sender_client_id ? this.#sessions.get(sender_client_id) : undefined)) {//if the prop was passed and the value was set successfully, then update all the subscriptions const new_assigned_prop_val = this.GetPropVal(key); //should be GetPropVal, bcs i cant know how the assigner changed the val. But since it runs once per update, then i can cache this call here right after the assigner. const prop_val_diff = diff_lib.getDiff(old_prop_val, new_assigned_prop_val); - if (prop_val_diff.length === 0) return 1; //dont do anything further, if the prop val didnt actually change. This is efficient and removes long feedback loops for global props across many users + if (prop_val_diff.length === 0) return 0; //dont do anything further, if the prop val didnt actually change. This is efficient and removes long feedback loops for global props across many users. Return 0 to indicate no update was sent. for (const [client_id, args] of prop.updates.entries()) { if (args?.rate_limiter && args.rate_limiter?.CheckLimit()) continue; //ratelimit check for this client diff --git a/core/package-lock.json b/core/package-lock.json index 0a5d5e3..5e095da 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "socio", - "version": "1.14.2", + "version": "1.14.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "socio", - "version": "1.14.2", + "version": "1.14.3", "license": "MIT", "dependencies": { "js-yaml": "^4.1.0", diff --git a/core/package.json b/core/package.json index 9069812..0cb88c8 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "socio", - "version": "1.14.2", + "version": "1.14.3", "description": "A WebSocket Real-Time Communication (RTC) API framework.", "main": "./dist/core.js", "type": "module", From 38e889d6a3591061ae605e6bb19dd7d4554b7278 Mon Sep 17 00:00:00 2001 From: Rolands Date: Thu, 25 Dec 2025 11:13:28 +0200 Subject: [PATCH 4/4] force update --- core/core-server.ts | 4 ++-- core/package-lock.json | 4 ++-- core/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/core-server.ts b/core/core-server.ts index bc182b0..1e963de 100644 --- a/core/core-server.ts +++ b/core/core-server.ts @@ -861,7 +861,7 @@ export class SocioServer extends LogHandler { return this.#props.get(key)?.val; } //UpdatePropVal does not set the new val, rather it calls the assigner, which is responsible for setting the new value. Props by default use SetPropVal as assigner, which just sets the new value. - UpdatePropVal(key: PropKey, new_val: PropValue, sender_client_id: ClientID | null, send_as_diff = this.#prop_upd_diff):Bit{//this will propogate the change, if it is assigned, to all subscriptions + UpdatePropVal(key: PropKey, new_val: PropValue, sender_client_id: ClientID | null, send_as_diff = this.#prop_upd_diff, force:boolean=false):Bit{//this will propogate the change, if it is assigned, to all subscriptions const prop = this.#props.get(key); if (!prop) throw new E(`Prop key [${key}] not registered! [#prop-update-not-found]`); @@ -872,7 +872,7 @@ export class SocioServer extends LogHandler { if (prop.assigner(key, new_val, sender_client_id ? this.#sessions.get(sender_client_id) : undefined)) {//if the prop was passed and the value was set successfully, then update all the subscriptions const new_assigned_prop_val = this.GetPropVal(key); //should be GetPropVal, bcs i cant know how the assigner changed the val. But since it runs once per update, then i can cache this call here right after the assigner. const prop_val_diff = diff_lib.getDiff(old_prop_val, new_assigned_prop_val); - if (prop_val_diff.length === 0) return 0; //dont do anything further, if the prop val didnt actually change. This is efficient and removes long feedback loops for global props across many users. Return 0 to indicate no update was sent. + if (prop_val_diff.length === 0 && force === false) return 0; //dont do anything further, if the prop val didnt actually change. This is efficient and removes long feedback loops for global props across many users. Return 0 to indicate no update was sent. for (const [client_id, args] of prop.updates.entries()) { if (args?.rate_limiter && args.rate_limiter?.CheckLimit()) continue; //ratelimit check for this client diff --git a/core/package-lock.json b/core/package-lock.json index 5e095da..9bc3302 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "socio", - "version": "1.14.3", + "version": "1.14.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "socio", - "version": "1.14.3", + "version": "1.14.4", "license": "MIT", "dependencies": { "js-yaml": "^4.1.0", diff --git a/core/package.json b/core/package.json index 0cb88c8..b63b522 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "socio", - "version": "1.14.3", + "version": "1.14.4", "description": "A WebSocket Real-Time Communication (RTC) API framework.", "main": "./dist/core.js", "type": "module",