aotrautils 0.0.1827 → 0.0.1829

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
 
2
2
 
3
- /*utils COMMONS library associated with aotra version : «1_29072022-2359 (17/03/2026-14:14:07)»*/
3
+ /*utils COMMONS library associated with aotra version : «1_29072022-2359 (17/03/2026-16:46:30)»*/
4
4
  /*-----------------------------------------------------------------------------*/
5
5
 
6
6
 
@@ -5076,7 +5076,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
5076
5076
 
5077
5077
 
5078
5078
 
5079
- /*utils CLIENT library associated with aotra version : «1_29072022-2359 (17/03/2026-14:14:07)»*/
5079
+ /*utils CLIENT library associated with aotra version : «1_29072022-2359 (17/03/2026-16:46:30)»*/
5080
5080
  /*-----------------------------------------------------------------------------*/
5081
5081
  /* ## Utility global methods in a browser (htmljs) client environment.
5082
5082
  *
@@ -13161,7 +13161,7 @@ createOritaMicroClient=function(url, port, isNode=false, staticMicroClientIdPara
13161
13161
 
13162
13162
 
13163
13163
 
13164
- /*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (17/03/2026-14:14:07)»*/
13164
+ /*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (17/03/2026-16:46:30)»*/
13165
13165
  /*-----------------------------------------------------------------------------*/
13166
13166
 
13167
13167
 
@@ -14490,10 +14490,10 @@ function rayVsUnitSphereClosestPoint(p, r) {
14490
14490
  // MUST REMAIN AT THE END OF THIS LIBRARY FILE !
14491
14491
 
14492
14492
  AOTRAUTILS_GEOMETRY_LIB_IS_LOADED=true;
14493
- /*utils 3D library associated with aotra version : «1_29072022-2359 (17/03/2026-14:14:07)»*/
14493
+ /*utils 3D library associated with aotra version : «1_29072022-2359 (17/03/2026-16:46:30)»*/
14494
14494
  /*-----------------------------------------------------------------------------*/
14495
14495
 
14496
- /*utils AI library associated with aotra version : «1_29072022-2359 (17/03/2026-14:14:07)»*/
14496
+ /*utils AI library associated with aotra version : «1_29072022-2359 (17/03/2026-16:46:30)»*/
14497
14497
  /*-----------------------------------------------------------------------------*/
14498
14498
 
14499
14499
 
@@ -14639,7 +14639,7 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
14639
14639
 
14640
14640
 
14641
14641
 
14642
- /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (17/03/2026-14:14:07)»*/
14642
+ /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (17/03/2026-16:46:30)»*/
14643
14643
  /*-----------------------------------------------------------------------------*/
14644
14644
 
14645
14645
 
@@ -15645,6 +15645,1356 @@ function capitalize(str){
15645
15645
  }
15646
15646
 
15647
15647
 
15648
+ /* ## Utility global methods in a javascript, console (nodejs) or vanilla javascript with no browser environment.
15649
+ *
15650
+ * This set of methods gathers utility generic-purpose methods usable in any JS project.
15651
+ * Several authors of snippets published freely on the Internet contributed to this library.
15652
+ * Feel free to use/modify-enhance/publish them under the terms of its license.
15653
+ *
15654
+ * # Library name : «aotrautils»
15655
+ * # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
15656
+ * # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
15657
+ * # Author email : info@alqemia.com
15658
+ * # Organization name : Alqemia
15659
+ * # Organization email : admin@alqemia.com
15660
+ * # Organization website : https://alqemia.com
15661
+ *
15662
+ *
15663
+ */
15664
+
15665
+
15666
+ // COMPATIBILITY browser javascript / nodejs environment :
15667
+ if(typeof(window)==="undefined") window=global;
15668
+
15669
+
15670
+
15671
+ // OLD : socket.io :
15672
+ // https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
15673
+ // https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
15674
+ // https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
15675
+ // https://socket.io/docs/v4/client-socket-instance/
15676
+
15677
+ // NEW : ws :
15678
+ // https://github.com/websockets/ws#installing
15679
+ // https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
15680
+ // ON BROWSER SIDE : Native Websockets :
15681
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
15682
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
15683
+
15684
+
15685
+
15686
+ // =================================================================================
15687
+ // NODEJS UTILS
15688
+
15689
+ const FILE_ENCODING="utf8";
15690
+
15691
+ const ADD_CORS_HEADER=true;
15692
+
15693
+
15694
+ // DEBUG
15695
+ console.log("DEBUG : Importing «fs» dependency.");
15696
+
15697
+
15698
+ if(typeof(require)!=="undefined" && typeof(fs)==="undefined" ){
15699
+ window.fs=require("fs");
15700
+ }
15701
+
15702
+ // For debug (WORKAROUND):
15703
+ fs=window.fs;
15704
+
15705
+
15706
+ // Nodejs filesystem utils :
15707
+ if(typeof(fs)==="undefined"){
15708
+ // TRACE
15709
+ console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
15710
+ window.getPersister=()=>{ return null; };
15711
+
15712
+ }else{
15713
+
15714
+
15715
+ window.getPersister=function(dataDirPath,prefix=""){
15716
+
15717
+ let self={
15718
+
15719
+ dataDirPath:dataDirPath,
15720
+ prefix:prefix,
15721
+
15722
+ //FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
15723
+ /*private*/getPath:function(clientId="noclient", repositoryName="norepository"){
15724
+ // let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
15725
+ let path=`${self.dataDirPath}`
15726
+ + (blank(self.prefix)?"":(self.prefix+"."))
15727
+ +`${clientId}.${repositoryName}.json`;
15728
+ return path;
15729
+ },
15730
+
15731
+ readTreeObjectFromFile:function(clientId="noclient", repositoryName="norepository", keepClassName=false){
15732
+
15733
+ let path=self.getPath(clientId,repositoryName);
15734
+ let resultFlat=null;
15735
+
15736
+ try{
15737
+
15738
+ resultFlat=fs.readFileSync(path, FILE_ENCODING);
15739
+
15740
+ }catch(error){
15741
+ // TRACE
15742
+ console.log("ERROR : Could not read file «"+path+"».");
15743
+
15744
+ return null;
15745
+ }
15746
+
15747
+
15748
+ let resultData={};
15749
+
15750
+ // 1)
15751
+ if(!empty(resultFlat)) resultData=parseJSON(resultFlat);
15752
+
15753
+ // 2)
15754
+ if(!empty(resultData) && isFlatMap(resultData)){
15755
+ // CAUTION : We have to keep the type information, here too ! (in the sub-objects)
15756
+ resultData=getAsTreeStructure(resultData, keepClassName);
15757
+ }
15758
+
15759
+ return resultData;
15760
+ },
15761
+
15762
+
15763
+ saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
15764
+
15765
+ if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
15766
+ dataFlatForClient=getAsFlatStructure(dataFlatForClient);
15767
+ }
15768
+
15769
+ // reserved characters : -/\^$*+?.()|[]{}
15770
+ // CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
15771
+ let dataFlatStr=stringifyObject(dataFlatForClient)
15772
+ // We «aerate» the produced JSON :
15773
+ .replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
15774
+ // ...except for inline, escaped JSON string representations :
15775
+ .replace(/\\\":[\w]*\{\n/gim,"\\\":{");
15776
+ // NO : .replace(/}/gim,"}\n");
15777
+
15778
+
15779
+ let path=self.getPath(clientId,repositoryName);
15780
+ fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
15781
+ if(error){
15782
+ // TRACE
15783
+ console.log("ERROR : Could not write file «"+path+"»:",error);
15784
+ throw error;
15785
+ }
15786
+ if(doOnSuccess) doOnSuccess(dataFlatForClient);
15787
+ });
15788
+ }
15789
+
15790
+ };
15791
+
15792
+
15793
+ return self;
15794
+ };
15795
+
15796
+ }
15797
+
15798
+
15799
+ window.fileExists=(filePath)=>{
15800
+ if(typeof(fs)=="undefined"){
15801
+ // TRACE
15802
+ lognow("ERROR : «fs» node dependency is not available ! Cannot test if file exists.");
15803
+ return null;
15804
+ }
15805
+ return fs.existsSync(filePath);
15806
+ };
15807
+
15808
+
15809
+
15810
+ // Nodejs server launching helper functions :
15811
+ //Networking management :
15812
+ //- WEBSOCKETS AND NODEJS :
15813
+
15814
+ function isConnected(clientSocket){
15815
+ if(!WebsocketImplementation.useSocketIOImplementation)
15816
+ return (clientSocket.readyState===WebSocket.OPEN)
15817
+ return (clientSocket.connected);
15818
+ }
15819
+
15820
+ // Client-side or Server-side :
15821
+ window.getURLOrConsoleParameter=(parameterName)=>{
15822
+ if(typeof(getURLParameter)!="undefined") return getURLParameter(parameterName);
15823
+ return getConsoleParam(parameterName);
15824
+ }
15825
+
15826
+
15827
+ // Server-side :
15828
+
15829
+ getConsoleServerParams=function(portParam=null, certPathParam=null, keyPathParam=null, ignoreConsoleArgs=false, argsOffset=0){
15830
+
15831
+ // Node dependencies :
15832
+ // https=require("https");
15833
+ // fs=require("fs");
15834
+
15835
+ if(typeof(https)==="undefined"){
15836
+ // TRACE
15837
+ console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
15838
+ return null;
15839
+ }
15840
+ if(typeof(fs)==="undefined"){
15841
+ // TRACE
15842
+ console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
15843
+ return null;
15844
+ }
15845
+
15846
+ const result={};
15847
+
15848
+ // We read the command-line arguments if needed :
15849
+ const argCLPort=getConsoleParam(0, argsOffset);
15850
+ const argCLCertPath=getConsoleParam(1, argsOffset);
15851
+ const argCLKeyPath=getConsoleParam(2, argsOffset);
15852
+
15853
+ // Console, command-line arguments OVERRIDE parameters values :
15854
+
15855
+ result.port=(ignoreConsoleArgs?portParam:nonull(argCLPort,portParam));
15856
+ result.certPath=null;
15857
+ result.keyPath=null;
15858
+ result.isSecure=!!(certPathParam || keyPathParam);
15859
+
15860
+ if(result.isSecure){
15861
+ result.certPath=(ignoreConsoleArgs?certPathParam:nonull(argCLCertPath,certPathParam));
15862
+ result.keyPath=(ignoreConsoleArgs?keyPathParam:nonull(argCLKeyPath,keyPathParam));
15863
+ }
15864
+
15865
+ // Eventual encryption options :
15866
+ result.sslOptions=null;
15867
+ if(result.isSecure){
15868
+ result.sslOptions={
15869
+ cert: fs.readFileSync(result.certPath),
15870
+ key: fs.readFileSync(result.keyPath),
15871
+ };
15872
+ }
15873
+
15874
+ return result;
15875
+ }
15876
+
15877
+ getConsoleParam=function(indexOrName=0, argsOffset=2/*arg 0 is «node» and arg 1 is the js file*/){
15878
+ if(!process){
15879
+ throw new Error("ERROR : Cannot extract console parameter in this context !");
15880
+ }
15881
+
15882
+ const allArgs=process.argv;
15883
+ if(empty(allArgs)) return null;
15884
+
15885
+ // Case indexOrName is a number for the parameter index :
15886
+ if(!isString(indexOrName) && isNumber(indexOrName)){
15887
+
15888
+ // DBG
15889
+ lognow(">>>>>>>>>>>>>>>allArgs:",allArgs);
15890
+
15891
+ return allArgs[indexOrName+argsOffset];
15892
+ }
15893
+
15894
+ // Case indexOrName is a string for the parameter name :
15895
+ for(let i=argsOffset;i<allArgs.length;i++){
15896
+ const val=allArgs[i];
15897
+ if(!contains(val,"="))
15898
+ continue;
15899
+ const splits=val.split("=");
15900
+ if(splits.length<2)
15901
+ continue;
15902
+ const name=splits[0].trim();
15903
+ const value=splits[1].trim();
15904
+ if(name==indexOrName)
15905
+ return value;
15906
+ }
15907
+ return null;
15908
+ }
15909
+
15910
+
15911
+
15912
+ window.getConsoleCLI=(doOnCommands={"makeSandiwch":()=>{}}, promptText="Enter command> ")=>{
15913
+
15914
+ const readline = require("node:readline");
15915
+
15916
+ const cliInterface = readline.createInterface({
15917
+ input: process.stdin,
15918
+ output: process.stdout,
15919
+ prompt: nonoull(promptText,"Enter command> ")
15920
+ });
15921
+
15922
+ cliInterface.prompt();
15923
+
15924
+ cliInterface.on("line", (line) => {
15925
+ const input = line.trim();
15926
+ switch (input) {
15927
+ case "quit":
15928
+ console.log("Bye!");
15929
+ cliInterface.close();
15930
+ break;
15931
+ case "help":
15932
+ console.log("Available commands: quit, help and :", Object.keys(doOnCommands));
15933
+ break;
15934
+ default:
15935
+ if(doOnCommands) doOnCommands[input]();
15936
+ break;
15937
+ }
15938
+
15939
+ // Show the prompt again :
15940
+ cliInterface.prompt();
15941
+
15942
+ }).on("close", () => {
15943
+ process.exit(0);
15944
+ });
15945
+
15946
+ return cliInterface
15947
+ };
15948
+
15949
+
15950
+
15951
+
15952
+
15953
+ // NODE ONLY SERVER / CLIENTS :
15954
+ WebsocketImplementation={
15955
+
15956
+
15957
+ isNodeContext:true,
15958
+ useSocketIOImplementation:false,
15959
+ useFlatStrings:false,
15960
+
15961
+ // COMMON METHODS
15962
+ /*private static*/isInRoom(clientSocket, clientsRoomsTag){
15963
+ return (!clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag));
15964
+ },
15965
+
15966
+
15967
+ //
15968
+ // CONSOLE NODE SERVER/CLIENT
15969
+ //
15970
+ getStatic:(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false)=>{
15971
+
15972
+ WebsocketImplementation.isNodeContext=isNodeContext;
15973
+ WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
15974
+
15975
+ if(!WebsocketImplementation.useSocketIOImplementation){
15976
+ // TRACE
15977
+ lognow("INFO : (SERVER/CLIENT) Using native WebSocket implementation.");
15978
+
15979
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
15980
+ if(isNodeContext){
15981
+ if(typeof(WebSocket)==="undefined"){
15982
+ // TRACE
15983
+ console.log("«ws» SERVER library not called yet, calling it now.");
15984
+
15985
+ window.WebSocket=require("ws");
15986
+
15987
+ if(typeof(WebSocket)==="undefined"){
15988
+ // TRACE
15989
+ console.log("ERROR : «ws» CONSOLE/BROWSER CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
15990
+ }
15991
+ }
15992
+ }
15993
+
15994
+ }else{
15995
+ // TRACE
15996
+ lognow("INFO : (SERVER/CLIENT) Using socket.io websocket implementation.");
15997
+
15998
+ if(isNodeContext){
15999
+
16000
+ // NODE SERVER :
16001
+ // DBG
16002
+ lognow("INFO : Loading NODE SERVER socket.io libraries.")
16003
+ if(typeof(Socket)==="undefined"){
16004
+ // TRACE
16005
+ console.log("«socket.io» NODE SERVER library not called yet, calling it now.");
16006
+ Socket=require("socket.io");
16007
+ }
16008
+ if(typeof(Socket)==="undefined"){
16009
+ // TRACE
16010
+ console.log("ERROR : «socket.io» NODE CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
16011
+ }
16012
+
16013
+ // NODE CLIENT :
16014
+ // DBG
16015
+ lognow("INFO : Loading NODE CLIENT socket.io libraries.")
16016
+ if(typeof(io)==="undefined"){
16017
+ // TRACE
16018
+ console.log("«socket.io-client» NODE CLIENT library not called yet, calling it now.");
16019
+ io=require("socket.io-client");
16020
+ }
16021
+ if(typeof(io)==="undefined"){
16022
+ // TRACE
16023
+ console.log("ERROR : «socket-client.io» NODE CLIENT library not found. Cannot launch nodejs server. Aborting.");
16024
+ }
16025
+ }
16026
+
16027
+
16028
+ }
16029
+
16030
+ // *********************************************************************************
16031
+
16032
+ return WebsocketImplementation;
16033
+ },
16034
+
16035
+
16036
+ /*private*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
16037
+
16038
+ const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
16039
+
16040
+ let dataResult=eventOrMessage;
16041
+
16042
+ try{
16043
+ dataResult=(WebsocketImplementation.useFlatStrings || isString(eventOrMessage)?parseJSON(eventOrMessage):eventOrMessage);
16044
+ }catch(error1){
16045
+ // TRACE
16046
+ lognow(`ERROR : Failed to parse JSON for string «${dataResult}»`,error1);
16047
+ dataResult=(isString(eventOrMessage)?eventOrMessage:stringifyObject(eventOrMessage));
16048
+ }
16049
+
16050
+ return dataResult;
16051
+ },
16052
+
16053
+
16054
+ getServer:(listenableServer)=>{
16055
+
16056
+ if(!WebsocketImplementation.isNodeContext){
16057
+ // TRACE
16058
+ throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
16059
+ }
16060
+
16061
+
16062
+ // TODO : FIXME : Use one single interface !
16063
+ // NODE SERVER MODE ONLY :
16064
+ let serverSocket;
16065
+ if(!WebsocketImplementation.useSocketIOImplementation){
16066
+ serverSocket=new WebSocket.Server({ "server":listenableServer });
16067
+ }else{
16068
+
16069
+ // NOW : socket.io :
16070
+ // Loading socket.io
16071
+ // OLD SYNTAX : serverSocket=Socket.listen(listenableServer);
16072
+ serverSocket=new Socket.Server(listenableServer);
16073
+
16074
+ if(listenableServer.cert || listenableServer.key){
16075
+ // TRACE :
16076
+ lognow("WARN : CAUTION ! ON TODAY (01/08/2022) socket.io SERVER LIBRARY IS BOGUS AND WON'T WORK (node clients can't connect !!) IF listenableServer IS A https SERVER !!!");
16077
+ }
16078
+
16079
+ // Setting up the disconnect event for a client :
16080
+ serverSocket.on("endConnection",()=>{
16081
+ serverSocket.disconnect();
16082
+ });
16083
+ }
16084
+
16085
+ serverSocket.on("error",(error)=>{
16086
+ // TRACE
16087
+ lognow("ERROR : An error occurred when trying to start the server : ",error);
16088
+
16089
+ });
16090
+
16091
+ // NODE SERVER INSTANCE :
16092
+ const nodeServerInstance=new NodeServerInstance(serverSocket, listenableServer);
16093
+
16094
+ // Join room server part protocol :
16095
+ nodeServerInstance.receive("protocol",(message, clientSocket)=>{
16096
+ nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
16097
+ },{listenerMessageType:"joinRoom"});
16098
+
16099
+ // To make the server aware of the clients connections states :
16100
+ nodeServerInstance.serverSocket.on("close", function close() {
16101
+
16102
+ // TODO : FIXME : Use one single interface !
16103
+ if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
16104
+ // OLD : else serverClients=nodeServerInstance.serverSocket.sockets.clients();
16105
+ else serverClients=nodeServerInstance.serverSocket.sockets.sockets;
16106
+
16107
+ serverClients.forEach((clientSocket)=>{
16108
+ clearInterval(clientSocket.stateCheckInterval);
16109
+ });
16110
+ });
16111
+
16112
+ return nodeServerInstance;
16113
+ },
16114
+
16115
+ // DO NOT USE DIRECTLY, USE INSTEAD initClient(...) (this function uses connectToServer(...)) !
16116
+ // NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
16117
+ connectToServer:(serverURL, port, isSecure=false, timeout)=>{
16118
+
16119
+ // TRACE
16120
+ lognow("INFO : Using socket library flavor : "+(WebsocketImplementation.isNodeContext?"node (client/server-side)":"browser (client-side only)"));
16121
+
16122
+ if(WebsocketImplementation.isNodeContext)
16123
+ return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
16124
+ else
16125
+ return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
16126
+ },
16127
+
16128
+
16129
+ //
16130
+ // NODE CLIENT
16131
+ //
16132
+ /*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
16133
+
16134
+ // NODE CLIENT MODE ONLY :
16135
+ let clientSocket;
16136
+ if(!WebsocketImplementation.useSocketIOImplementation){
16137
+
16138
+ // NEW : ws :
16139
+ if(typeof(WebSocket)==="undefined"){
16140
+ // TRACE
16141
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
16142
+ return null;
16143
+ }
16144
+
16145
+ clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{
16146
+ // CAUTION : SECURITY BREACH :
16147
+ // BUT ALSO NECESSARY TO ALLOW SELF-SIGNED CERTIFICATES USAGE WITH THE YESBOT SYSTEM !
16148
+ rejectUnauthorized:false, // (THIS IS A KNOWN SECURITY BREACH)
16149
+ secure: isSecure
16150
+ });
16151
+
16152
+ clientSocket.addEventListener("error", error=>{
16153
+ // TRACE
16154
+ lognow("ERROR : (NODEJS) A WebSocket client error occurred while trying to connect to server:", error.message);
16155
+ });
16156
+
16157
+ }else{
16158
+ // NOW : socket.io :
16159
+ //client on server-side:
16160
+
16161
+ if(WebsocketImplementation.isNodeContext && typeof(io)!=="undefined"){
16162
+
16163
+ // OLD SYNTAX: clientSocket=Socket.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16164
+ // NO : clientSocket=new Socket.Client(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16165
+ clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure, autoConnect:true});
16166
+ // UNUSEFUL (since we have the autoconnect:true option) : clientSocket.connect();
16167
+
16168
+ clientSocket.on("connect_error", error=>{
16169
+ // TRACE
16170
+ lognow("ERROR : (NODEJS) A SocketIO client error occurred while trying to connect to server:", error.message);
16171
+ });
16172
+
16173
+ }
16174
+ }
16175
+
16176
+ // DBG
16177
+ lognow("DEBUG : CLIENT : clientSocket created:");
16178
+
16179
+ const nodeClientInstance=new ClientInstance(clientSocket);
16180
+ return nodeClientInstance;
16181
+ },
16182
+
16183
+ //
16184
+ // BROWSER CLIENT
16185
+ //
16186
+
16187
+ /*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
16188
+
16189
+ // NEW : ws :
16190
+ if(typeof(WebSocket)==="undefined"){
16191
+ // TRACE
16192
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
16193
+ return null;
16194
+ }
16195
+
16196
+ // TODO : FIXME : Use one single interface !
16197
+ // BROWSER CLIENT MODE ONLY :
16198
+ let clientSocket;
16199
+ if(!WebsocketImplementation.useSocketIOImplementation){
16200
+ // CAUTION : PARAMETER rejectUnauthorized:false WILL DO NOTHING,
16201
+ // BECAUSE THIS IS COMPLETLY HANDLED BY THE BROWSER SECURITY POLICY !
16202
+ // SO TO CLEAR THE SSL ERROR :
16203
+ // - FIRST GO TO THE HTTPS:// SERVER ADDRESS WITH BROWSER
16204
+ // - THEN ADD THE SECURITY EXCEPTION IN THE BROWSER !
16205
+ clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
16206
+
16207
+ clientSocket.addEventListener("error", error=>{
16208
+ // TRACE
16209
+ lognow("ERROR : (BROWSER) A WebSocket client error occurred while trying to connect to server:", error.message);
16210
+ });
16211
+
16212
+ }else if(typeof(io)!=="undefined"){
16213
+ // OLD SYNTAX :clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16214
+ // ALTERNATIVE :
16215
+ clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16216
+
16217
+ clientSocket.on("connect_error", error=>{
16218
+ // TRACE
16219
+ lognow("ERROR : (BROWSER) A SocketIO client error occurred while trying to connect to server:", error.message);
16220
+ });
16221
+
16222
+ }
16223
+
16224
+ // BROWSER CLIENT INSTANCE :
16225
+ const browserClientInstance=new ClientInstance(clientSocket);
16226
+ return browserClientInstance;
16227
+ },
16228
+
16229
+ };
16230
+
16231
+
16232
+
16233
+ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
16234
+
16235
+ const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
16236
+
16237
+
16238
+
16239
+ if(typeof(https)==="undefined"){
16240
+ // TRACE
16241
+ console.log("«https» SERVER library not called yet, calling it now.");
16242
+ https=require("https");
16243
+ }
16244
+ if(typeof(http)==="undefined"){
16245
+ // TRACE
16246
+ console.log("«http» SERVER library not called yet, calling it now.");
16247
+ http=require("http");
16248
+ }
16249
+
16250
+
16251
+ const DEFAULT_HANDLER=function(request, response){
16252
+
16253
+ const url=request.url;
16254
+
16255
+ let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
16256
+ if(contains(url,excludedStr)){
16257
+ return true;
16258
+ }
16259
+ });
16260
+
16261
+ if(isURLInExclusionZone){
16262
+ // TRACE
16263
+ console.log("ERROR 403 forbidden access error :");
16264
+ console.log(error);
16265
+
16266
+ response.writeHead(403);
16267
+ response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
16268
+ response.end();
16269
+ return;
16270
+ }
16271
+
16272
+
16273
+
16274
+ const urlFile="." + url;
16275
+
16276
+ let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
16277
+ if(filePath=="./") filePath="./index.html";
16278
+
16279
+ let contentType="text/html";
16280
+
16281
+ const headers={ "Content-Type": contentType };
16282
+
16283
+ // To remove the CORS error message (cf. https://medium.com/@dtkatz/3-ways-to-fix-the-cors-error-and-how-access-control-allow-origin-works-d97d55946d9)
16284
+ if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
16285
+
16286
+
16287
+ fs.readFile(filePath, function(error, fileContent){
16288
+ if(error){
16289
+ if(error.code=="ENOENT"){
16290
+ // TRACE
16291
+ console.log("ERROR 404 file not found :"+filePath);
16292
+
16293
+ fs.readFile("./404.html", function(error, fileContent){
16294
+ response.writeHead(200, headers);
16295
+ response.end(fileContent, "utf-8");
16296
+ });
16297
+
16298
+ }else {
16299
+
16300
+ // TRACE
16301
+ console.log("ERROR 500 server error :");
16302
+ console.log(error);
16303
+
16304
+ response.writeHead(500);
16305
+ response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
16306
+ response.end();
16307
+
16308
+ }
16309
+ }else {
16310
+
16311
+ // TRACE
16312
+ console.log("INFO 200 OK :"+filePath);
16313
+
16314
+
16315
+ response.writeHead(200, headers);
16316
+ response.end(fileContent, "utf-8");
16317
+
16318
+ // res.writeHead(200);
16319
+ // res.end("hello world\n");
16320
+ // res.sendFile(__dirname + "/public/index.html");
16321
+
16322
+ }
16323
+ });
16324
+
16325
+ };
16326
+
16327
+
16328
+ const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
16329
+
16330
+
16331
+
16332
+ let listenableServer;
16333
+ if(sslOptions){
16334
+ let httpsServer=https.createServer(sslOptions, handler).listen(port);
16335
+ // TRACE
16336
+ console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
16337
+ listenableServer=httpsServer;
16338
+ }else{
16339
+ let httpServer=http.createServer(handler).listen(port);
16340
+ // TRACE
16341
+ console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
16342
+ listenableServer=httpServer;
16343
+ }
16344
+
16345
+
16346
+ const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
16347
+
16348
+ // When a client connects, we execute the callback :
16349
+ // CAUTION : MUST BE CALLED ONLY ONCE !
16350
+ server.onConnectionToClient((serverParam, clientSocketParam)=>{
16351
+ if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
16352
+ });
16353
+
16354
+
16355
+ server.onFinalize((serverParam)=>{
16356
+
16357
+ // DBG
16358
+ lognow("onFinalize() server");
16359
+
16360
+ if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
16361
+ });
16362
+
16363
+
16364
+ // TRACE
16365
+ console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
16366
+
16367
+
16368
+
16369
+
16370
+
16371
+ return server;
16372
+ }
16373
+
16374
+
16375
+ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null,
16376
+ /*OPTIONAL*/portParam,
16377
+ /*OPTIONAL*/certPathParam,
16378
+ /*OPTIONAL*/keyPathParam){
16379
+
16380
+ // TRACE
16381
+ console.log("Server launched.");
16382
+ console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
16383
+ console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
16384
+ // EXAMPLE : node orita-srv.js hash orita.global@keyHash 1234567890
16385
+ console.log("Server launched.");
16386
+
16387
+
16388
+ // We read the command-line arguments if needed :
16389
+
16390
+ let argCLPort;
16391
+ let argCLCertPath;
16392
+ let argCLKeyPath;
16393
+
16394
+ let serverConfig={};
16395
+ let isForceUnsecure;
16396
+
16397
+ let isHashAsked=false;
16398
+ let clearTextParam=null;
16399
+ let persisterId=null;
16400
+
16401
+
16402
+ process.argv.forEach(function (val, i){
16403
+ if(!val) return;
16404
+ // 0 corresponds to «node / nodejs»
16405
+ if(i<=1) return; // 1 corresponds to « <server.js> »
16406
+ else if(i==2){
16407
+ if(val==="hash") isHashAsked=true;
16408
+ }else if(i==3){
16409
+ if(!isHashAsked){
16410
+ try{
16411
+ const jsonConf=parseJSON(val);
16412
+ argCLPort=jsonConf.port;
16413
+ argCLCertPath=jsonConf.sslCertPath;
16414
+ argCLKeyPath=jsonConf.sslKeyPath;
16415
+ serverConfig=nonull(jsonConf.serverConfig,{});
16416
+ }catch(err1){
16417
+ lognow("WARN : Cannot parse argument JSON string «"+val+"».");
16418
+ }
16419
+ } else persisterId=val;
16420
+ }else if(i==4){
16421
+ if(isHashAsked) clearTextParam=val;
16422
+ }
16423
+ });
16424
+ isForceUnsecure=(argCLCertPath==="unsecure");
16425
+
16426
+
16427
+
16428
+
16429
+
16430
+ const aotraNodeServer={config:serverConfig};
16431
+ aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
16432
+
16433
+ if(isHashAsked){
16434
+ // We instanciate a temporary persister just to read the key hash file:
16435
+ const persister=getPersister("./");
16436
+ let persisterIdSplits=persisterId.split("@");
16437
+ if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
16438
+ // TRACE
16439
+ console.log("ERROR : No persister repository IDs provided correctly. Cannot read key hash. Aborting hash generation.");
16440
+ return aotraNodeServer;
16441
+ }
16442
+ const persisterClientId=persisterIdSplits[0];
16443
+ const persisterRepositoryName=persisterIdSplits[1];
16444
+ let globalKeyHashObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
16445
+ if(!globalKeyHashObject || !globalKeyHashObject.keyHash){
16446
+ // TRACE
16447
+ console.log("WARN : No key hash found. Generating one now.");
16448
+ globalKeyHashObject={keyHash:getUUID(), hashes:[]};
16449
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
16450
+ // TRACE
16451
+ console.log("INFO : Key hash generated and saved successfully.");
16452
+ });
16453
+ }
16454
+ const globalKeyHash=globalKeyHashObject.keyHash;
16455
+
16456
+ let firstHash=getHashedString(clearTextParam);
16457
+
16458
+ let generatedHash=getHashedString( firstHash + globalKeyHash, "SHA-256", true);// (we use the heavy treatment thing.)
16459
+ globalKeyHashObject.hashes.push(generatedHash);
16460
+
16461
+ // We update the repository :
16462
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
16463
+ // TRACE
16464
+ console.log("INFO : Hash added to repository and saved successfully.");
16465
+ });
16466
+
16467
+ // OUTPUT
16468
+ console.log("Here is your key : share it with your main clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
16469
+
16470
+ return aotraNodeServer;
16471
+ }
16472
+
16473
+
16474
+ const DEFAULT_PORT=nonull(argCLPort,25000);
16475
+ const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
16476
+ const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
16477
+
16478
+
16479
+ let port=portParam ? portParam : DEFAULT_PORT;
16480
+ let certPath=null;
16481
+ let keyPath=null;
16482
+
16483
+ if(!isForceUnsecure){
16484
+ certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
16485
+ keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
16486
+ }
16487
+
16488
+
16489
+
16490
+
16491
+ // UNUSEFUL :
16492
+ //aotraNodeServer.serverManager.microClientsSockets=[];
16493
+ // UNUSEFUL :
16494
+ //aotraNodeServer.serverManager.mainClientsSockets=[];
16495
+
16496
+ aotraNodeServer.serverManager.start=function(){
16497
+
16498
+ // Eventual encryption options :
16499
+ let sslOptions=null;
16500
+ if(!isForceUnsecure){
16501
+ if(typeof(fs)==="undefined"){
16502
+ // TRACE
16503
+ lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
16504
+ }else{
16505
+ try{
16506
+ sslOptions={
16507
+ cert: fs.readFileSync(certPath, {encoding: "utf8"}),
16508
+ key: fs.readFileSync(keyPath, {encoding: "utf8"}),
16509
+ };
16510
+ }catch(exception){
16511
+ // TRACE
16512
+ lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
16513
+ }
16514
+ }
16515
+ }
16516
+
16517
+ aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
16518
+
16519
+
16520
+ return aotraNodeServer;
16521
+ };
16522
+
16523
+ return aotraNodeServer;
16524
+ }
16525
+
16526
+ // ========================= FUSRODA SERVER : =========================
16527
+
16528
+ //
16529
+ // DOES NOT WORK : USE Java FusrodaServer instead :
16530
+ //
16531
+ ///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
16532
+ //createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=4000){
16533
+ //
16534
+ // // https://www.npmjs.com/package/screenshot-desktop
16535
+ // // https://github.com/octalmage/robotjs
16536
+ // // npm install --save screenshot-desktop
16537
+ // //
16538
+ // // sudo apt-get install libxtst-dev libx11-dev
16539
+ // // npm install --save robotjs
16540
+ //
16541
+ // // http://getrobot.net/docs/usage.html
16542
+ // //
16543
+ // // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
16544
+ //
16545
+ // const screenshot=require("screenshot-desktop");
16546
+ //
16547
+ // const REFRESH_SCREENSHOTS_MILLIS=500;
16548
+ //
16549
+ //
16550
+ // const server=initNodeServerInfrastructureWrapper(
16551
+ // // On each client connection :
16552
+ // // (serverParam, clientSocketParam)=>{},
16553
+ // null,
16554
+ // // On server finalization :
16555
+ // (serverParam)=>{
16556
+ //
16557
+ // serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
16558
+ // serverParam.addToRoom(clientSocket,"clients");
16559
+ // });
16560
+ //
16561
+ //
16562
+ // serverParam.sendScreenshotsRoutine=setInterval(()=>{
16563
+ // if(serverParam.isScreenshotStarted) return;
16564
+ //
16565
+ // serverParam.isScreenshotStarted=true;
16566
+ //
16567
+ // screenshot().then((img) => {
16568
+ //
16569
+ // const data={image:img,listenerMessageType:"imageData"};
16570
+ // serverParam.send("message", data, "clients");
16571
+ //
16572
+ // serverParam.isScreenshotStarted=false;
16573
+ //
16574
+ // }).catch((error) => {
16575
+ // // TRACE
16576
+ // lognow("ERROR : Error during screenshot :",error);
16577
+ // });
16578
+ //
16579
+ // },REFRESH_SCREENSHOTS_MILLIS);
16580
+ //
16581
+ //
16582
+ // },portParam,certPathParam,keyPathParam);
16583
+ //
16584
+ //
16585
+ // // const doOnConnect=(serverParam, clientSocketParam)=>{
16586
+ // // };
16587
+ // // const doOnFinalizeServer=(serverParam)=>{
16588
+ // // /*DO NOTHING*/
16589
+ // // };
16590
+ // // const server={};
16591
+ // // server.start=(port=4000)=>{
16592
+ // // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
16593
+ // // };
16594
+ //
16595
+ // return server;
16596
+ //}
16597
+
16598
+
16599
+ // ========================= UTILITY SERVERSIDE METHODS : =========================
16600
+
16601
+ class ListManager{
16602
+
16603
+ constructor(config){
16604
+ this.config=config;
16605
+ this.maxItemsNumber=nonull(this.config.max,999);
16606
+ this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
16607
+ this.sessionDurationSeconds=nonull(this.config.duration,null);
16608
+ this.mode=nonull(this.config.mode,"startAtConnexion");
16609
+
16610
+ this.itemsInfos={};
16611
+ this.time=null;
16612
+ this.started=false;
16613
+ }
16614
+
16615
+ addItem(id,item){
16616
+ if(id==null){
16617
+ // TRACE
16618
+ lognow("ERROR : Cannot add item with no id.");
16619
+ return;
16620
+ }
16621
+ if(!item){
16622
+ // TRACE
16623
+ lognow("ERROR : Cannot add null item.");
16624
+ return;
16625
+ }
16626
+ const numberOfItemsCurrently=getArraySize(this.itemsInfos);
16627
+
16628
+ // DBG
16629
+ lognow(">>>>>>>>>numberOfItemsCurrently:",numberOfItemsCurrently);
16630
+ lognow(">>>>>>>>>Object.keys(arrayOfValues):",Object.keys(this.itemsInfos));
16631
+
16632
+
16633
+ if(this.maxItemsNumber<=numberOfItemsCurrently){
16634
+ // TRACE
16635
+ lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
16636
+ return;
16637
+ }
16638
+
16639
+
16640
+ if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
16641
+
16642
+ // DBG
16643
+ lognow(">>>>>>>>>START SESSION !!!!");
16644
+
16645
+ this.startSession();
16646
+ }
16647
+ this.itemsInfos[id]={
16648
+ item:item,
16649
+ time:getNow(),
16650
+ };
16651
+ }
16652
+
16653
+ startSession(){
16654
+ this.time=getNow();
16655
+ this.started=true;
16656
+ }
16657
+
16658
+ stopSession(){
16659
+ this.started=false;
16660
+ }
16661
+
16662
+ isSessionActive(){
16663
+
16664
+ // DBG
16665
+ lognow(" !!! this.sessionDurationSeconds : "+this.sessionDurationSeconds);
16666
+ lognow(" !!! this.started : "+this.started);
16667
+ lognow(" !!! this.time : "+this.time);
16668
+
16669
+
16670
+ if(!this.sessionDurationSeconds) return true;
16671
+ if(!this.started) return false;
16672
+ const result=!hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
16673
+
16674
+ // DBG
16675
+ lognow(" !!! HAS DELAY PASSED : "+result);
16676
+
16677
+ return result;
16678
+ }
16679
+
16680
+ isClientActive(clientId){
16681
+
16682
+ // DBG
16683
+ lognow(" this.isSessionActive()"+this.isSessionActive());
16684
+
16685
+ if(!this.isSessionActive()) return false;
16686
+ const clientPosition=this.getItemPosition(clientId);
16687
+ //CAUTION : Client position starts at 1 !
16688
+ const clientIndex=(clientPosition-1);
16689
+ const result=(clientIndex<=this.maxItemsNumber && clientIndex<=this.simultaneousItemsNumber);
16690
+
16691
+ return result;
16692
+ }
16693
+
16694
+ removeItemById(id){
16695
+ if(id==null){
16696
+ // TRACE
16697
+ lognow("ERROR : Cannot remove item, no id specified.");
16698
+ return;
16699
+ }
16700
+ if(!this.itemsInfos[id]){
16701
+ // TRACE
16702
+ lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
16703
+ return;
16704
+ }
16705
+ delete this.itemsInfos[id];
16706
+ }
16707
+
16708
+ removeItem(item){
16709
+ if(item==null){
16710
+ // TRACE
16711
+ lognow("ERROR : Cannot remove item, none specified.");
16712
+ return;
16713
+ }
16714
+
16715
+ let id=null;
16716
+ foreach(this.itemsInfos,(itemInfos,key)=>{
16717
+ if(itemInfos.item===item
16718
+ // DEBUG ONLY :
16719
+ || (itemInfos.item.id && item.id && itemInfos.item.id===item.id)
16720
+ ){
16721
+ id=key;
16722
+ return "break";
16723
+ }
16724
+
16725
+ });
16726
+
16727
+ if(!id){
16728
+ // TRACE
16729
+ lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
16730
+ return;
16731
+ }
16732
+
16733
+ this.removeItemById(id);
16734
+ }
16735
+
16736
+ // Goes from 1 to <length>:
16737
+ getItemPosition(id){
16738
+ if(id==null){
16739
+ // TRACE
16740
+ lognow("ERROR : Cannot calculate item position, no id specified.");
16741
+ return null;
16742
+ }
16743
+ if(!this.itemsInfos[id]){
16744
+ // TRACE
16745
+ lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
16746
+ return null;
16747
+ }
16748
+ let result=0;
16749
+ foreach(this.itemsInfos, (itemInfo, key)=>{
16750
+ result++;
16751
+ if(id===key) return "break";
16752
+ },null,(item1, item2)=>item1.time<item2.time);
16753
+ return result;
16754
+ }
16755
+
16756
+ getItemsNumber(){
16757
+ return getArraySize(this.itemsInfos);
16758
+ }
16759
+
16760
+ };
16761
+
16762
+ getListManager=function(config){
16763
+ return new ListManager(config);
16764
+ };
16765
+
16766
+
16767
+ // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
16768
+ // NO : IN A NODE CONTEXT WITH require("...") WILL RESULT IN AN UNDEFINED FUNCTION ERROR !!!
16769
+ // function performHTTPRequest(...){...
16770
+ // USE THIS INSTEAD :
16771
+ performHTTPRequest=function(completeURL, httpMethod="GET", headers={}, requestBodyOrNamedArgs=null, isNodeContext=false, addCORSHeader=ADD_CORS_HEADER){
16772
+
16773
+ // Body
16774
+ let requestBodyOrNamedArgsStr=null;
16775
+ if(requestBodyOrNamedArgs){
16776
+ if(isString(requestBodyOrNamedArgs) && !isObject(requestBodyOrNamedArgs)){
16777
+ // If it's already a string, we assume it's a valid JSOn string already :
16778
+ requestBodyOrNamedArgsStr=requestBodyOrNamedArgs.replace(/[\r\n]+/gim, ""); // We remove all the breaklines
16779
+ }else{
16780
+ try{
16781
+ requestBodyOrNamedArgsStr=stringifyObject(requestBodyOrNamedArgs).replace(/[\r\n]+/gim, ""); // We remove all the breaklines
16782
+ }catch(parseErr){
16783
+ requestBodyOrNamedArgsStr=""+requestBodyOrNamedArgs;
16784
+ }
16785
+ }
16786
+ }
16787
+
16788
+ let body=null;
16789
+ // Not the same way to send parameters, with POST/PUT http method :
16790
+ if(contains(["POST","PUT"],httpMethod) && requestBodyOrNamedArgs){
16791
+ body=requestBodyOrNamedArgsStr;
16792
+ }
16793
+
16794
+ // Headers
16795
+ if(contains(["POST","PUT"],httpMethod)){
16796
+ headers["Content-Type"]="application/json";
16797
+
16798
+ if(requestBodyOrNamedArgsStr && isString(body)){
16799
+ headers["Content-Length"]=Buffer.byteLength(requestBodyOrNamedArgsStr);
16800
+ }
16801
+
16802
+ // To remove the CORS error message (cf. https://medium.com/@dtkatz/3-ways-to-fix-the-cors-error-and-how-access-control-allow-origin-works-d97d55946d9)
16803
+ if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
16804
+
16805
+ } else if(httpMethod==="GET" && requestBodyOrNamedArgs){
16806
+ // Not the same way to send parameters in GET http method :
16807
+ // DBG
16808
+ lognow("unformatted API URL : "+completeURL);
16809
+
16810
+ completeURL=appendGetParameters(completeURL, requestBodyOrNamedArgs);
16811
+
16812
+ // DBG
16813
+ lognow("formatted API URL : "+completeURL);
16814
+ }
16815
+
16816
+ // CASE BROWSER CONTEXT :
16817
+ if(!isNodeContext || typeof(require)=="undefined"){
16818
+ // TRACE
16819
+ lognow("INFO : We are not running in a browser context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using browser library.");
16820
+
16821
+ return new Promise((resolve,reject)=>{
16822
+ fetch(completeURL, {
16823
+ method: httpMethod,
16824
+ headers: headers,
16825
+ body: body,
16826
+ })
16827
+ // STRANGE : DOES NOT WORK !!:
16828
+ //.then(response => response.json())
16829
+ // STRANGE : DOES WORK :
16830
+ .then(response => {
16831
+
16832
+ // DBG
16833
+ console.log("~~~~~~~~~~~response :",response);
16834
+
16835
+ return response.json();
16836
+ }).then(data => {
16837
+
16838
+ // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
16839
+ const status=data.status;
16840
+ // if(status!=200 && data.error){
16841
+ // // const error=nonull(data.error, data.detail);
16842
+ // const error=data.error;
16843
+ // // TRACE
16844
+ // console.error("Error:", error);
16845
+ // reject({error:""+error, httpStatus:status});
16846
+ // return;
16847
+ // }
16848
+
16849
+ // DBG
16850
+ console.log("~~~~~~~~~~~data :",data);
16851
+
16852
+ data.httpStatus=status;
16853
+ resolve(data);
16854
+ }).catch(error => {
16855
+ // TRACE
16856
+ console.error("Error:", error);
16857
+ reject({error:`${error}`, httpStatus:null});
16858
+ });
16859
+ });
16860
+ }// else :
16861
+
16862
+ // CASE NODEJS CONTEXT :
16863
+
16864
+ // TRACE
16865
+ lognow("INFO : We are running in a nodejs context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using nodejs library.");
16866
+
16867
+
16868
+ const isSecure=(!empty(completeURL) && contains(completeURL.toLowerCase(),"https://"));
16869
+ const httpHandler=isSecure?require("https"):require("http");
16870
+
16871
+ // Options for the HTTP request
16872
+ const options = {
16873
+ url: completeURL,
16874
+ method: httpMethod,
16875
+ };
16876
+
16877
+ if(contains(["POST","PUT"],httpMethod)){
16878
+ options.json=true;
16879
+ }
16880
+
16881
+ options.headers=headers;
16882
+
16883
+ return new Promise((resolve,reject)=>{
16884
+
16885
+ // Create the HTTP request
16886
+ // DOES NOT WORK : const request = httpHandler.request(options, (response) => {
16887
+ // UNLESS YOU SPECIFY in options : hostname, port, path
16888
+ const request = httpHandler.request(completeURL, options, (response) => {
16889
+
16890
+ let responseDataStr = "";
16891
+
16892
+ // A chunk of data has been received.
16893
+ response.on("data", (chunk) => {
16894
+ responseDataStr += chunk;
16895
+ // // DEBUG ONLY
16896
+ // const str = Buffer.from(chunk).toString("utf8");
16897
+ // lognow(">>>str:"+str);
16898
+ });
16899
+
16900
+ // The whole response has been received.
16901
+ response.on("end", () => {
16902
+
16903
+ try{
16904
+ let responseData;
16905
+ try{
16906
+ responseData=parseJSON(responseDataStr);
16907
+ }catch(parseError1){
16908
+ // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
16909
+ // DEBUG
16910
+ responseDataStr=responseDataStr.replace(/'/gim,"`");
16911
+ try{
16912
+ responseData=parseJSON(responseDataStr);
16913
+ }catch(parseError2){
16914
+ // DBG
16915
+ lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError2);
16916
+ resolve( {response:response, responseDataStr:responseDataStr} );
16917
+ return;
16918
+ }
16919
+ }
16920
+
16921
+ // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
16922
+ const status=response.statusCode;
16923
+ // if(status!=200){
16924
+ // const error=nonull(response.statusMessage, "No error message for error code "+status);
16925
+ // // TRACE
16926
+ // console.error("Error:", error);
16927
+ // reject({error:""+error, httpStatus:status});
16928
+ // return;
16929
+ // }
16930
+
16931
+ resolve( {responseData:responseData, response:response, responseDataStr:responseDataStr, httpStatus: status} );
16932
+ }catch(parseError3){
16933
+ // DBG
16934
+ lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError3);
16935
+ resolve( {response:response, responseDataStr:responseDataStr} );
16936
+ return;
16937
+ }
16938
+
16939
+ });
16940
+ });
16941
+
16942
+ // Handle errors
16943
+ request.on("error", (error) => {
16944
+ reject({error:error, httpStatus:null});
16945
+ });
16946
+
16947
+ // Not the same way to send parameters, with POST/PUT http method :
16948
+ if(contains(["POST","PUT"],httpMethod) && body){
16949
+ request.write(body);
16950
+ }
16951
+
16952
+ // End the request
16953
+ request.end();
16954
+
16955
+ });
16956
+
16957
+ };
16958
+
16959
+
16960
+
16961
+
16962
+ replacePathVariablesNamesWithValuesIfPossible=function(apiURL, namedArgs){
16963
+ let result=apiURL;
16964
+ foreach(namedArgs,(namedArgValue, namedArgKey)=>{
16965
+ result=result.replace("{"+namedArgKey+"}",namedArgValue);
16966
+ });
16967
+ return result;
16968
+ };
16969
+
16970
+ appendGetParameters=function(apiURL, namedArgsParam){
16971
+ if(nothing(namedArgsParam)) return "";
16972
+ try{
16973
+
16974
+ const namedArgs=isString(namedArgsParam)?parseJSON(namedArgsParam):namedArgsParam;
16975
+
16976
+ let result=apiURL;
16977
+
16978
+ const paramCouples=[];
16979
+ foreach(namedArgs,(value,key)=>{paramCouples.push(key+"="+value);});
16980
+
16981
+ if(!empty(paramCouples)) result+=("?"+paramCouples.join("&"));
16982
+
16983
+ return result;
16984
+ }catch(parseError){
16985
+ // TRACE
16986
+ lognow("ERROR : Could not parse string parameters object «"+namedArgsParam+"», aborting GET parameter request string calculation:", parseError);
16987
+ return "";
16988
+ }
16989
+ };
16990
+
16991
+
16992
+
16993
+
16994
+
16995
+
16996
+
16997
+
15648
16998
  /* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.
15649
16999
  *
15650
17000
  * This set of methods gathers utility generic-purpose methods usable in any JS project.
aotrautils/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aotrautils",
3
- "version": "0.0.1827",
3
+ "version": "0.0.1829",
4
4
  "main": "aotrautils.build.js",
5
5
  "description": "A library for vanilla javascript utils (client-side) used in aotra javascript CMS",
6
6
  "author": "Jeremie Ratomposon <info@alqemia.com> (https://alqemia.com)",