From 7d6815813672fb79263afb656be87ded5dabbd71 Mon Sep 17 00:00:00 2001 From: Rolands Date: Tue, 10 Dec 2024 10:44:25 +0100 Subject: [PATCH] prop proxy fixes --- core/core-client.ts | 41 +++++++++++++++++++++++++---------------- core/core-server.ts | 2 ++ core/package-lock.json | 4 ++-- core/package.json | 2 +- core/types.d.ts | 2 +- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/core/core-client.ts b/core/core-client.ts index 2806b3c..281a66d 100644 --- a/core/core-client.ts +++ b/core/core-client.ts @@ -42,6 +42,7 @@ type DiscoveryReturn = { NAME: { [nameOrId: string]: { id: string; name?: string; ip: string;[key: string]: any } }; AS_ARRAY: { id: string; name?: string; ip: string;[key: string]: any }[]; }; +type PropSubOpts = { rate_limit?: RateLimit | null, receive_initial_update?: boolean }; //"Because he not only wants to perform well, he wants to be well received  —  and the latter lies outside his control." /Epictetus/ export class SocioClient extends LogHandler { @@ -230,12 +231,12 @@ export class SocioClient extends LogHandler { if (typeof prop.subs[(data as C_PROP_UPD_data).id] === 'function'){ // get the new factual value value of the prop either from the server or from applying the diff from server if ((data as C_PROP_UPD_data).hasOwnProperty('prop_val')){ //take factual - //@ts-expect-error - prop_val = (data as C_PROP_UPD_data).prop_val - }else{ //apply diff - //@ts-expect-error - prop_val = diff_lib.applyDiff(prop.val, (data as C_PROP_UPD_data).prop_val_diff); + prop_val = (data as C_PROP_UPD_data).prop_val; + } + else if ((data as C_PROP_UPD_data).hasOwnProperty('prop_val_diff')) { //apply diff + prop_val = diff_lib.applyDiff(prop.val, (data as C_PROP_UPD_data).prop_val_diff!); } + else throw new E('Prop upd data didnt have either factual val or diff val to use. [#prop-upd-no-val]', {kind, data}); prop.val = prop_val; //set the new val // afterwards call the client subscription with the new val and the received diff @@ -546,7 +547,7 @@ export class SocioClient extends LogHandler { return prom as Promise; } } - SubscribeProp(prop_name: PropKey, onUpdate: PropUpdateCallback, { rate_limit = null, receive_initial_update = true }: { rate_limit?: RateLimit | null, receive_initial_update?: boolean } = {}): Promise<{ id: id, result: { success: Bit } } | any> { + SubscribeProp(prop_name: PropKey, onUpdate: PropUpdateCallback, { rate_limit = null, receive_initial_update = true }: PropSubOpts = {}): Promise<{ id: id, result: { success: Bit } } | any> { //the prop name on the backend that is a key in the object if (typeof onUpdate !== "function") throw new E('Subscription onUpdate is not function, but has to be.'); @@ -593,8 +594,7 @@ export class SocioClient extends LogHandler { } catch (e: err) { this.#HandleClientError(e); return null; } } // get a PropProxy object. The prop has to be a js object datatype on the server side. This automagically handles the PropGet, PropSet and SubscribeProp base functions for you. - async Prop(prop_name: PropKey){ - const client_this = this; //use for inside the Proxy scope + async Prop(prop_name: PropKey, { prop_sub_opts = {}, prop_upd_as_diff = false}:{ prop_sub_opts?: PropSubOpts, prop_upd_as_diff?: boolean} = {}){ const prop = await this.GetProp(prop_name, false); if(prop === undefined){ this.#HandleClientError(new E(`Couldnt retrieve server prop [${prop_name}]`, { prop_name, prop })); @@ -604,6 +604,9 @@ export class SocioClient extends LogHandler { this.#HandleClientError(new E(`Can only proxy js objects, but [${prop_name}] is not an object.`, { prop_name, prop })); return undefined; } + + let from_sub: boolean = false; + const LocalSetProp = this.SetProp.bind(this); //use for inside the Proxy scope const prop_proxy = new Proxy(prop, { get(p: PropValue, property) { // log(`${prop_name} GET`, {p, property}); @@ -612,21 +615,27 @@ export class SocioClient extends LogHandler { //ive run tests in other projects and the async set does work fine. TS doesnt want to allow it for some reason set(p: PropValue, property, new_val) { // log(`${prop_name} SET`, { p, property, new_val }); - //the sub will run this too, which means the value would've already been set - if (p[property] !== new_val){ - p[property] = new_val; - client_this.SetProp.bind(client_this)(prop_name, p); - } + + // always set the val + p[property] = new_val; + + // but since the sub runs this too, then check if this is from there, bcs we dont want to send back what we receive - thats redundant + if (from_sub !== true) + LocalSetProp(prop_name, p, prop_upd_as_diff); return true; // return (await client_this.SetProp.bind(client_this)(prop_name, p))?.result?.success === 1; } }); - await client_this.SubscribeProp(prop_name, (new_val) => { + await this.SubscribeProp(prop_name, (new_val) => { // log(`${prop_name} SUB UPD`, { new_val }); - for (const [key, val] of Object.entries(new_val)) prop_proxy[key] = val; //set each key, bcs cant just assign the new obj, bcs then it wouldnt be a proxy anymore - }); + + from_sub = true; + //set each key, bcs cant just assign the new obj, bcs then it wouldnt be a proxy anymore + for (const [key, val] of Object.entries(new_val)) prop_proxy[key] = val; + from_sub = false; + }, prop_sub_opts); return prop_proxy; } diff --git a/core/core-server.ts b/core/core-server.ts index 61095af..2560448 100644 --- a/core/core-server.ts +++ b/core/core-server.ts @@ -857,6 +857,7 @@ export class SocioServer extends LogHandler { //Dont think JS allows such ref pointers to work. But this then keeps the correct val. //This idea works bcs the mutator of the data should be the first to run this and all other session will get informed here with that sessions diff. + log('---here', {key, new_val, old_prop_val, sender_client_id, send_as_diff}); 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); @@ -868,6 +869,7 @@ export class SocioServer extends LogHandler { //do the thing if (this.#sessions.has(client_id)){ + log('---here2', { sender_client_id, client_id, args }); //prepare object of both cases const upd_data = { id: args.id, prop:key }; diff --git a/core/package-lock.json b/core/package-lock.json index 6aa124b..d603cdf 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "socio", - "version": "1.13.6", + "version": "1.13.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "socio", - "version": "1.13.6", + "version": "1.13.7", "license": "MIT", "dependencies": { "js-yaml": "^4.1.0", diff --git a/core/package.json b/core/package.json index fb45918..71ffc00 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "socio", - "version": "1.13.6", + "version": "1.13.7", "description": "A WebSocket Real-Time Communication (RTC) API framework.", "main": "./dist/core.js", "type": "module", diff --git a/core/types.d.ts b/core/types.d.ts index c3e8d1b..b397aee 100644 --- a/core/types.d.ts +++ b/core/types.d.ts @@ -100,7 +100,7 @@ type C_RES_data = data_base & data_result_block; type C_UPD_data = data_base & data_result_block; type C_AUTH_data = data_base & data_result_block; type C_GET_PERM_data = data_base & data_result_block & { verb: string, table: string }; -type C_PROP_UPD_data = data_base & { prop: string } & ({ prop_val: PropValue } | { prop_val_diff: diff_lib.rdiffResult[] }); +type C_PROP_UPD_data = data_base & { prop: string } & ({ prop_val?: PropValue , prop_val_diff?: diff_lib.rdiffResult[] }); type C_RECON_Data = data_base & data_result_block & { old_client_id: ClientID, auth: boolean, name?:string }; type C_RECV_FILES_Data = data_base & data_result_block & { files: SocioFiles }; // type C_PROP_REG_data = data_base & data_result_block & { prop?: string, initial_value: any, opts: Omit };