From dd3aae50c9a7ed127ed474caf086e46d6e9d7b34 Mon Sep 17 00:00:00 2001 From: Dany LE Date: Sun, 28 Nov 2021 18:26:04 +0100 Subject: [PATCH] update lib antunnel plugings --- AntunnelPlugins/README.md | 1 + AntunnelPlugins/broadcast.ts | 31 +++++++++++++++++- AntunnelPlugins/build/debug/README.md | 1 + AntunnelPlugins/build/debug/main.d.ts | 20 ++++++++++- AntunnelPlugins/build/debug/main.js | 2 +- AntunnelPlugins/build/debug/package.json | 2 +- .../build/release/AntunnelPlugins.zip | Bin 4639 -> 4747 bytes AntunnelPlugins/package.json | 2 +- packages.json | 2 +- 9 files changed, 55 insertions(+), 6 deletions(-) diff --git a/AntunnelPlugins/README.md b/AntunnelPlugins/README.md index 28822c0..c155036 100644 --- a/AntunnelPlugins/README.md +++ b/AntunnelPlugins/README.md @@ -4,5 +4,6 @@ This package provides also the Typescript declaration file for application Development. ## Change logs +- v.0.1.2: minor changes on API - v.0.1.1: Added group query support - v.0.1.0: Antunnel API declaration and broadcast plugin \ No newline at end of file diff --git a/AntunnelPlugins/broadcast.ts b/AntunnelPlugins/broadcast.ts index dab9ee5..0401df3 100644 --- a/AntunnelPlugins/broadcast.ts +++ b/AntunnelPlugins/broadcast.ts @@ -265,7 +265,7 @@ namespace Antunnel { * @type {{[prop: number]: BroadcastGroup}} * @memberof BroadcastManager */ - private groups: { [prop: number]: BroadcastGroup }; + groups: { [prop: number]: BroadcastGroup }; /** * temporary list of group handles that wait for @@ -604,5 +604,34 @@ namespace Antunnel { arr.set(data, 4); this.sub.send(Antunnel.Msg.DATA, arr); } + + /** + * Get all the registered group + * + * @return {Uint8Array} + * @memberof BroadcastManager + */ + get_groups(): { [prop: number]: BroadcastGroup } + { + return this.groups; + } + + /** + * Get group by name + * + * @return {Uint8Array} + * @memberof BroadcastManager + */ + get_group(name: string): BroadcastGroup + { + for(let k in this.groups) + { + if(this.groups[k].groupname === name) + { + return this.groups[k]; + } + } + return undefined; + } } } \ No newline at end of file diff --git a/AntunnelPlugins/build/debug/README.md b/AntunnelPlugins/build/debug/README.md index 28822c0..c155036 100644 --- a/AntunnelPlugins/build/debug/README.md +++ b/AntunnelPlugins/build/debug/README.md @@ -4,5 +4,6 @@ This package provides also the Typescript declaration file for application Development. ## Change logs +- v.0.1.2: minor changes on API - v.0.1.1: Added group query support - v.0.1.0: Antunnel API declaration and broadcast plugin \ No newline at end of file diff --git a/AntunnelPlugins/build/debug/main.d.ts b/AntunnelPlugins/build/debug/main.d.ts index fe60094..6bf8ed6 100644 --- a/AntunnelPlugins/build/debug/main.d.ts +++ b/AntunnelPlugins/build/debug/main.d.ts @@ -446,7 +446,9 @@ declare namespace Antunnel { * @type {{[prop: number]: BroadcastGroup}} * @memberof BroadcastManager */ - private groups; + groups: { + [prop: number]: BroadcastGroup; + }; /** * temporary list of group handles that wait for * an connection confirmation from the backend @@ -546,5 +548,21 @@ declare namespace Antunnel { * @memberof BroadcastManager */ send(gid: number, data: Uint8Array): void; + /** + * Get all the registered group + * + * @return {Uint8Array} + * @memberof BroadcastManager + */ + get_groups(): { + [prop: number]: BroadcastGroup; + }; + /** + * Get group by name + * + * @return {Uint8Array} + * @memberof BroadcastManager + */ + get_group(name: string): BroadcastGroup; } } diff --git a/AntunnelPlugins/build/debug/main.js b/AntunnelPlugins/build/debug/main.js index 892524b..0e7e0c7 100644 --- a/AntunnelPlugins/build/debug/main.js +++ b/AntunnelPlugins/build/debug/main.js @@ -1 +1 @@ -var Antunnel;!function(s){let e;!function(s){s[s.OK=0]="OK",s[s.SUBSCRIBE=2]="SUBSCRIBE",s[s.UNSUBSCRIBE=3]="UNSUBSCRIBE",s[s.ERROR=1]="ERROR",s[s.DATA=6]="DATA",s[s.CTRL=7]="CTRL",s[s.CLOSE=5]="CLOSE",s[s.PING=8]="PING"}(e=s.AntunnelMSGType||(s.AntunnelMSGType={}))}(Antunnel||(Antunnel={})),function(s){let e,t;!function(s){s[s.SUBSCRIBE=10]="SUBSCRIBE",s[s.UNSUBSCRIBE=11]="UNSUBSCRIBE",s[s.QUERY_USER=12]="QUERY_USER",s[s.QUERY_GROUP=13]="QUERY_GROUP"}(e=s.BroadcastCTRLType||(s.BroadcastCTRLType={})),function(s){s[s.INIT=0]="INIT",s[s.SUBSCRIBED=1]="SUBSCRIBED",s[s.UNSUBSCRIBED=2]="UNSUBSCRIBED"}(t=s.BroadcastGroupState||(s.BroadcastGroupState={}));class n{constructor(s){this.groupname=s,this.users=new Set,this.onmessage=void 0,this.onready=void 0,this.onuseradd=void 0,this.onuserdel=void 0,this.onclose=void 0,this.user=OS.setting.user.name,this.mgr=void 0,this.state=t.INIT}close(){this.mgr&&this.id&&this.mgr.unsubscribe(this.id)}refresh(){this.mgr&&this.id&&this.mgr.query(this.id)}send(s){this.mgr.send(this.id,s)}}s.BroadcastGroup=n,s.BroadcastManager=class{constructor(s){this.sub=void 0,this.channel=s,this.tunnel=void 0,this.groups={},this.pendings={},this.ongroupadd=void 0,this.ongroupdel=void 0}connect(i){this.sub=new s.Subscriber(this.channel),this.sub.onopen=()=>{OS.announcer.osinfo(__("Subscriber {0}: Connected to the {1} channel",this.sub.id,this.channel)),i(!0)},this.sub.onerror=s=>{let e=s;s.data&&(e=new TextDecoder("utf-8").decode(s.data)),OS.announcer.oserror(__("Subscriber {0}: Error from the {1} channel: {2}",this.sub.id,this.channel,e),void 0)},this.sub.onmessage=e=>{if(e.data){let t=s.Msg.int_from(e.data.slice(0,4),0,4),n=this.groups[t];if(!n)return;n.onmessage&&n.onmessage(e.data.slice(4))}},this.sub.onctrl=i=>{let r={user:void 0,group:void 0,type:i.data[0],id:void 0};switch(r.type){case e.SUBSCRIBE:case e.UNSUBSCRIBE:let o=i.data[1]+2;if(r.user=new TextDecoder("utf-8").decode(i.data.slice(2,o)),r.id=s.Msg.int_from(i.data,o,4),o+=4,r.group=new TextDecoder("utf-8").decode(i.data.slice(o)),r.type===e.SUBSCRIBE){let s=this.pendings[r.group];s&&s.user===r.user&&(s.id=r.id,this.pendings[r.group]=void 0,delete this.pendings[r.group],this.groups[r.id]=s,s.state=t.SUBSCRIBED,this.ongroupadd&&this.ongroupadd(s),s.onready()),s=this.groups[r.id],s||(s=new n(r.group),s.id=r.id,s.state=t.SUBSCRIBED,s.mgr=this,this.groups[r.id]=s,this.ongroupadd&&this.ongroupadd(s),s.onready&&s.onready()),s.users.has(r.user)||(s.users.add(r.user),s.onuseradd&&s.onuseradd(r.user))}else{let s=this.groups[r.id];if(!s)return;s.user===r.user?(OS.announcer.osinfo(__("Subcriber {0}: leave group {1}",this.sub.id,r.group)),this.groups[r.id]=void 0,delete this.groups[r.id],s.state=t.UNSUBSCRIBED,s.onclose&&s.onclose(),this.ongroupdel&&this.ongroupdel(s)):(s.users.delete(r.user),s.onuserdel&&s.onuserdel(r.user))}break;case e.QUERY_USER:r.id=s.Msg.int_from(i.data,1,4),r.user=new TextDecoder("utf-8").decode(i.data.slice(5));let u=this.groups[r.id];if(!u)return;u.users.has(r.user)||(u.users.add(r.user),u.onuseradd&&u.onuseradd(r.user));break;case e.QUERY_GROUP:if(r.id=s.Msg.int_from(i.data,1,4),r.group=new TextDecoder("utf-8").decode(i.data.slice(5)),this.groups[r.id])return;this.groups[r.id]=new n(r.group),this.groups[r.id].id=r.id,this.groups[r.id].state=t.SUBSCRIBED,this.groups[r.id].mgr=this,this.ongroupadd&&this.ongroupadd(this.groups[r.id]),this.groups[r.id].onready&&this.groups[r.id].onready()}},this.sub.onclose=()=>{OS.announcer.osinfo(__("Subscriber {0}: Connection to {1} closed",this.sub.id,this.channel)),this.sub=void 0},this.tunnel.subscribe(this.sub)}setup(){return new Promise(async(e,t)=>{try{if(!s)throw new Error(__("Library not fould: %s","Antunnel").__());if(s.tunnel)this.tunnel=s.tunnel;else{await OS.GUI.pushService("Antunnel/AntunnelService");let e=OS.setting.system.tunnel_uri;if(!e)throw new Error(__("Unable to connect to: %s","Antunnel").__());await s.init(e),this.tunnel=s.tunnel}this.sub?e(!0):this.connect(e)}catch(e){s.tunnel&&s.tunnel.close(),t(__e(e))}})}unsubscribe(t){let n=new Uint8Array(5);n[0]=e.UNSUBSCRIBE,n.set(s.Msg.bytes_of(t,4),1),this.sub.send(s.Msg.CTRL,n)}query(t){let n=new Uint8Array(5);n[0]=e.QUERY_USER,n.set(s.Msg.bytes_of(t,4),1),this.sub.send(s.Msg.CTRL,n)}refresh(){let t=new Uint8Array(1);t[0]=e.QUERY_GROUP,this.sub.send(s.Msg.CTRL,t)}subscribe(t){let i=new Uint8Array(t.length+1);i[0]=e.SUBSCRIBE,i.set((new TextEncoder).encode(t),1),this.sub.send(s.Msg.CTRL,i);let r=new n(t);r.mgr=this,this.pendings[t]=r}teardown(){this.sub&&this.sub.close(),this.groups={},this.pendings={}}id(){return this.sub?this.sub.id:0}send(e,t){let n=new Uint8Array(t.length+4);n.set(s.Msg.bytes_of(e,4),0),n.set(t,4),this.sub.send(s.Msg.DATA,n)}}}(Antunnel||(Antunnel={})); \ No newline at end of file +var Antunnel;!function(s){let e;!function(s){s[s.OK=0]="OK",s[s.SUBSCRIBE=2]="SUBSCRIBE",s[s.UNSUBSCRIBE=3]="UNSUBSCRIBE",s[s.ERROR=1]="ERROR",s[s.DATA=6]="DATA",s[s.CTRL=7]="CTRL",s[s.CLOSE=5]="CLOSE",s[s.PING=8]="PING"}(e=s.AntunnelMSGType||(s.AntunnelMSGType={}))}(Antunnel||(Antunnel={})),function(s){let e,t;!function(s){s[s.SUBSCRIBE=10]="SUBSCRIBE",s[s.UNSUBSCRIBE=11]="UNSUBSCRIBE",s[s.QUERY_USER=12]="QUERY_USER",s[s.QUERY_GROUP=13]="QUERY_GROUP"}(e=s.BroadcastCTRLType||(s.BroadcastCTRLType={})),function(s){s[s.INIT=0]="INIT",s[s.SUBSCRIBED=1]="SUBSCRIBED",s[s.UNSUBSCRIBED=2]="UNSUBSCRIBED"}(t=s.BroadcastGroupState||(s.BroadcastGroupState={}));class n{constructor(s){this.groupname=s,this.users=new Set,this.onmessage=void 0,this.onready=void 0,this.onuseradd=void 0,this.onuserdel=void 0,this.onclose=void 0,this.user=OS.setting.user.name,this.mgr=void 0,this.state=t.INIT}close(){this.mgr&&this.id&&this.mgr.unsubscribe(this.id)}refresh(){this.mgr&&this.id&&this.mgr.query(this.id)}send(s){this.mgr.send(this.id,s)}}s.BroadcastGroup=n,s.BroadcastManager=class{constructor(s){this.sub=void 0,this.channel=s,this.tunnel=void 0,this.groups={},this.pendings={},this.ongroupadd=void 0,this.ongroupdel=void 0}connect(i){this.sub=new s.Subscriber(this.channel),this.sub.onopen=()=>{OS.announcer.osinfo(__("Subscriber {0}: Connected to the {1} channel",this.sub.id,this.channel)),i(!0)},this.sub.onerror=s=>{let e=s;s.data&&(e=new TextDecoder("utf-8").decode(s.data)),OS.announcer.oserror(__("Subscriber {0}: Error from the {1} channel: {2}",this.sub.id,this.channel,e),void 0)},this.sub.onmessage=e=>{if(e.data){let t=s.Msg.int_from(e.data.slice(0,4),0,4),n=this.groups[t];if(!n)return;n.onmessage&&n.onmessage(e.data.slice(4))}},this.sub.onctrl=i=>{let r={user:void 0,group:void 0,type:i.data[0],id:void 0};switch(r.type){case e.SUBSCRIBE:case e.UNSUBSCRIBE:let o=i.data[1]+2;if(r.user=new TextDecoder("utf-8").decode(i.data.slice(2,o)),r.id=s.Msg.int_from(i.data,o,4),o+=4,r.group=new TextDecoder("utf-8").decode(i.data.slice(o)),r.type===e.SUBSCRIBE){let s=this.pendings[r.group];s&&s.user===r.user&&(s.id=r.id,this.pendings[r.group]=void 0,delete this.pendings[r.group],this.groups[r.id]=s,s.state=t.SUBSCRIBED,this.ongroupadd&&this.ongroupadd(s),s.onready()),s=this.groups[r.id],s||(s=new n(r.group),s.id=r.id,s.state=t.SUBSCRIBED,s.mgr=this,this.groups[r.id]=s,this.ongroupadd&&this.ongroupadd(s),s.onready&&s.onready()),s.users.has(r.user)||(s.users.add(r.user),s.onuseradd&&s.onuseradd(r.user))}else{let s=this.groups[r.id];if(!s)return;s.user===r.user?(OS.announcer.osinfo(__("Subcriber {0}: leave group {1}",this.sub.id,r.group)),this.groups[r.id]=void 0,delete this.groups[r.id],s.state=t.UNSUBSCRIBED,s.onclose&&s.onclose(),this.ongroupdel&&this.ongroupdel(s)):(s.users.delete(r.user),s.onuserdel&&s.onuserdel(r.user))}break;case e.QUERY_USER:r.id=s.Msg.int_from(i.data,1,4),r.user=new TextDecoder("utf-8").decode(i.data.slice(5));let u=this.groups[r.id];if(!u)return;u.users.has(r.user)||(u.users.add(r.user),u.onuseradd&&u.onuseradd(r.user));break;case e.QUERY_GROUP:if(r.id=s.Msg.int_from(i.data,1,4),r.group=new TextDecoder("utf-8").decode(i.data.slice(5)),this.groups[r.id])return;this.groups[r.id]=new n(r.group),this.groups[r.id].id=r.id,this.groups[r.id].state=t.SUBSCRIBED,this.groups[r.id].mgr=this,this.ongroupadd&&this.ongroupadd(this.groups[r.id]),this.groups[r.id].onready&&this.groups[r.id].onready()}},this.sub.onclose=()=>{OS.announcer.osinfo(__("Subscriber {0}: Connection to {1} closed",this.sub.id,this.channel)),this.sub=void 0},this.tunnel.subscribe(this.sub)}setup(){return new Promise(async(e,t)=>{try{if(!s)throw new Error(__("Library not fould: %s","Antunnel").__());if(s.tunnel)this.tunnel=s.tunnel;else{await OS.GUI.pushService("Antunnel/AntunnelService");let e=OS.setting.system.tunnel_uri;if(!e)throw new Error(__("Unable to connect to: %s","Antunnel").__());await s.init(e),this.tunnel=s.tunnel}this.sub?e(!0):this.connect(e)}catch(e){s.tunnel&&s.tunnel.close(),t(__e(e))}})}unsubscribe(t){let n=new Uint8Array(5);n[0]=e.UNSUBSCRIBE,n.set(s.Msg.bytes_of(t,4),1),this.sub.send(s.Msg.CTRL,n)}query(t){let n=new Uint8Array(5);n[0]=e.QUERY_USER,n.set(s.Msg.bytes_of(t,4),1),this.sub.send(s.Msg.CTRL,n)}refresh(){let t=new Uint8Array(1);t[0]=e.QUERY_GROUP,this.sub.send(s.Msg.CTRL,t)}subscribe(t){let i=new Uint8Array(t.length+1);i[0]=e.SUBSCRIBE,i.set((new TextEncoder).encode(t),1),this.sub.send(s.Msg.CTRL,i);let r=new n(t);r.mgr=this,this.pendings[t]=r}teardown(){this.sub&&this.sub.close(),this.groups={},this.pendings={}}id(){return this.sub?this.sub.id:0}send(e,t){let n=new Uint8Array(t.length+4);n.set(s.Msg.bytes_of(e,4),0),n.set(t,4),this.sub.send(s.Msg.DATA,n)}get_groups(){return this.groups}get_group(s){for(let e in this.groups)if(this.groups[e].groupname===s)return this.groups[e]}}}(Antunnel||(Antunnel={})); \ No newline at end of file diff --git a/AntunnelPlugins/build/debug/package.json b/AntunnelPlugins/build/debug/package.json index b755fe7..8fddb64 100644 --- a/AntunnelPlugins/build/debug/package.json +++ b/AntunnelPlugins/build/debug/package.json @@ -6,7 +6,7 @@ "author": "Dany LE", "email": "mrsang@iohub.dev" }, - "version":"0.1.1-a", + "version":"0.1.2-a", "category":"Library", "iconclass":"fa fa-cog", "mimes":["none"], diff --git a/AntunnelPlugins/build/release/AntunnelPlugins.zip b/AntunnelPlugins/build/release/AntunnelPlugins.zip index a638bad4e2b37193e95bac62c65100db8dd7c12e..7b7bd3523ace0f78c86931df619dde5c6f0f36c2 100644 GIT binary patch literal 4747 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 literal 4639 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 lru&;k|CIe%`+sBzs{iPOt`_JzKL9{