Merge branch 'main' of https://github.com/Rolands-Laucis/Socio into message-pack

This commit is contained in:
Rolands 2025-12-26 20:18:10 +02:00
commit 1b90bf624a
4 changed files with 29 additions and 13 deletions

View file

@ -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<boolean>();
#authenticated = false;
#queries: Map<id, QueryObject | QueryPromise> = new Map(); //keeps a dict of all subscribed queries
@ -146,7 +146,7 @@ export class SocioClient extends LogHandler {
this.#client_id = '';
this.#ws = null;
this.#latency = Infinity;
this.#is_ready = false;
this.#ready_state = new ReadyState<boolean>();
this.#authenticated = false;
this.#queries.clear();
this.#props.clear();
@ -177,9 +177,7 @@ export class SocioClient extends LogHandler {
}
//set ready state to true and resolve ready() promise, if exists
if (this.#is_ready !== true && typeof this.#is_ready === "function")
this.#is_ready(true); //resolve promise to true
this.#is_ready = true;
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.
@ -731,8 +729,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<boolean> { 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();
@ -864,4 +863,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).
}
}
class ReadyState<T> {
promise: Promise<T>;
#resolve!: (value: T) => void;
is_ready = false;
constructor() {
this.promise = new Promise<T>(res => {
this.#resolve = res;
});
}
resolve(value: T) {
this.is_ready = true;
this.#resolve(value);
}
}

View file

@ -866,8 +866,8 @@ 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(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 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, 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]`);
@ -878,7 +878,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 && 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

View file

@ -1,12 +1,12 @@
{
"name": "socio",
"version": "1.14.1",
"version": "1.14.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "socio",
"version": "1.14.1",
"version": "1.14.4",
"license": "MIT",
"dependencies": {
"@msgpack/msgpack": "^3.1.2",

View file

@ -1,6 +1,6 @@
{
"name": "socio",
"version": "1.14.1",
"version": "1.14.4",
"description": "A WebSocket Real-Time Communication (RTC) API framework.",
"main": "./dist/core.js",
"type": "module",