aotrautils 0.0.1828 → 0.0.1830

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:38:52)»*/
3
+ /*utils COMMONS library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:48)»*/
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:38:52)»*/
5079
+ /*utils CLIENT library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:48)»*/
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:38:52)»*/
13164
+ /*utils GEOMETRY library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:48)»*/
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:38:52)»*/
14493
+ /*utils 3D library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:48)»*/
14494
14494
  /*-----------------------------------------------------------------------------*/
14495
14495
 
14496
- /*utils AI library associated with aotra version : «1_29072022-2359 (17/03/2026-14:38:52)»*/
14496
+ /*utils AI library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:48)»*/
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:38:52)»*/
14642
+ /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:48)»*/
14643
14643
  /*-----------------------------------------------------------------------------*/
14644
14644
 
14645
14645
 
@@ -15645,6 +15645,1352 @@ 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
+ return allArgs[indexOrName+argsOffset];
15888
+ }
15889
+
15890
+ // Case indexOrName is a string for the parameter name :
15891
+ for(let i=argsOffset;i<allArgs.length;i++){
15892
+ const val=allArgs[i];
15893
+ if(!contains(val,"="))
15894
+ continue;
15895
+ const splits=val.split("=");
15896
+ if(splits.length<2)
15897
+ continue;
15898
+ const name=splits[0].trim();
15899
+ const value=splits[1].trim();
15900
+ if(name==indexOrName)
15901
+ return value;
15902
+ }
15903
+ return null;
15904
+ }
15905
+
15906
+
15907
+
15908
+ window.getConsoleCLI=(doOnCommands={"makeSandiwch":()=>{}}, promptText="Enter command> ")=>{
15909
+
15910
+ const readline = require("node:readline");
15911
+
15912
+ const cliInterface = readline.createInterface({
15913
+ input: process.stdin,
15914
+ output: process.stdout,
15915
+ prompt: nonoull(promptText,"Enter command> ")
15916
+ });
15917
+
15918
+ cliInterface.prompt();
15919
+
15920
+ cliInterface.on("line", (line) => {
15921
+ const input = line.trim();
15922
+ switch (input) {
15923
+ case "quit":
15924
+ console.log("Bye!");
15925
+ cliInterface.close();
15926
+ break;
15927
+ case "help":
15928
+ console.log("Available commands: quit, help and :", Object.keys(doOnCommands));
15929
+ break;
15930
+ default:
15931
+ if(doOnCommands) doOnCommands[input]();
15932
+ break;
15933
+ }
15934
+
15935
+ // Show the prompt again :
15936
+ cliInterface.prompt();
15937
+
15938
+ }).on("close", () => {
15939
+ process.exit(0);
15940
+ });
15941
+
15942
+ return cliInterface
15943
+ };
15944
+
15945
+
15946
+
15947
+
15948
+
15949
+ // NODE ONLY SERVER / CLIENTS :
15950
+ WebsocketImplementation={
15951
+
15952
+
15953
+ isNodeContext:true,
15954
+ useSocketIOImplementation:false,
15955
+ useFlatStrings:false,
15956
+
15957
+ // COMMON METHODS
15958
+ /*private static*/isInRoom(clientSocket, clientsRoomsTag){
15959
+ return (!clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag));
15960
+ },
15961
+
15962
+
15963
+ //
15964
+ // CONSOLE NODE SERVER/CLIENT
15965
+ //
15966
+ getStatic:(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false)=>{
15967
+
15968
+ WebsocketImplementation.isNodeContext=isNodeContext;
15969
+ WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
15970
+
15971
+ if(!WebsocketImplementation.useSocketIOImplementation){
15972
+ // TRACE
15973
+ lognow("INFO : (SERVER/CLIENT) Using native WebSocket implementation.");
15974
+
15975
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
15976
+ if(isNodeContext){
15977
+ if(typeof(WebSocket)==="undefined"){
15978
+ // TRACE
15979
+ console.log("«ws» SERVER library not called yet, calling it now.");
15980
+
15981
+ window.WebSocket=require("ws");
15982
+
15983
+ if(typeof(WebSocket)==="undefined"){
15984
+ // TRACE
15985
+ console.log("ERROR : «ws» CONSOLE/BROWSER CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
15986
+ }
15987
+ }
15988
+ }
15989
+
15990
+ }else{
15991
+ // TRACE
15992
+ lognow("INFO : (SERVER/CLIENT) Using socket.io websocket implementation.");
15993
+
15994
+ if(isNodeContext){
15995
+
15996
+ // NODE SERVER :
15997
+ // DBG
15998
+ lognow("INFO : Loading NODE SERVER socket.io libraries.")
15999
+ if(typeof(Socket)==="undefined"){
16000
+ // TRACE
16001
+ console.log("«socket.io» NODE SERVER library not called yet, calling it now.");
16002
+ Socket=require("socket.io");
16003
+ }
16004
+ if(typeof(Socket)==="undefined"){
16005
+ // TRACE
16006
+ console.log("ERROR : «socket.io» NODE CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
16007
+ }
16008
+
16009
+ // NODE CLIENT :
16010
+ // DBG
16011
+ lognow("INFO : Loading NODE CLIENT socket.io libraries.")
16012
+ if(typeof(io)==="undefined"){
16013
+ // TRACE
16014
+ console.log("«socket.io-client» NODE CLIENT library not called yet, calling it now.");
16015
+ io=require("socket.io-client");
16016
+ }
16017
+ if(typeof(io)==="undefined"){
16018
+ // TRACE
16019
+ console.log("ERROR : «socket-client.io» NODE CLIENT library not found. Cannot launch nodejs server. Aborting.");
16020
+ }
16021
+ }
16022
+
16023
+
16024
+ }
16025
+
16026
+ // *********************************************************************************
16027
+
16028
+ return WebsocketImplementation;
16029
+ },
16030
+
16031
+
16032
+ /*private*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
16033
+
16034
+ const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
16035
+
16036
+ let dataResult=eventOrMessage;
16037
+
16038
+ try{
16039
+ dataResult=(WebsocketImplementation.useFlatStrings || isString(eventOrMessage)?parseJSON(eventOrMessage):eventOrMessage);
16040
+ }catch(error1){
16041
+ // TRACE
16042
+ lognow(`ERROR : Failed to parse JSON for string «${dataResult}»`,error1);
16043
+ dataResult=(isString(eventOrMessage)?eventOrMessage:stringifyObject(eventOrMessage));
16044
+ }
16045
+
16046
+ return dataResult;
16047
+ },
16048
+
16049
+
16050
+ getServer:(listenableServer)=>{
16051
+
16052
+ if(!WebsocketImplementation.isNodeContext){
16053
+ // TRACE
16054
+ throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
16055
+ }
16056
+
16057
+
16058
+ // TODO : FIXME : Use one single interface !
16059
+ // NODE SERVER MODE ONLY :
16060
+ let serverSocket;
16061
+ if(!WebsocketImplementation.useSocketIOImplementation){
16062
+ serverSocket=new WebSocket.Server({ "server":listenableServer });
16063
+ }else{
16064
+
16065
+ // NOW : socket.io :
16066
+ // Loading socket.io
16067
+ // OLD SYNTAX : serverSocket=Socket.listen(listenableServer);
16068
+ serverSocket=new Socket.Server(listenableServer);
16069
+
16070
+ if(listenableServer.cert || listenableServer.key){
16071
+ // TRACE :
16072
+ 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 !!!");
16073
+ }
16074
+
16075
+ // Setting up the disconnect event for a client :
16076
+ serverSocket.on("endConnection",()=>{
16077
+ serverSocket.disconnect();
16078
+ });
16079
+ }
16080
+
16081
+ serverSocket.on("error",(error)=>{
16082
+ // TRACE
16083
+ lognow("ERROR : An error occurred when trying to start the server : ",error);
16084
+
16085
+ });
16086
+
16087
+ // NODE SERVER INSTANCE :
16088
+ const nodeServerInstance=new NodeServerInstance(serverSocket, listenableServer);
16089
+
16090
+ // Join room server part protocol :
16091
+ nodeServerInstance.receive("protocol",(message, clientSocket)=>{
16092
+ nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
16093
+ },{listenerMessageType:"joinRoom"});
16094
+
16095
+ // To make the server aware of the clients connections states :
16096
+ nodeServerInstance.serverSocket.on("close", function close() {
16097
+
16098
+ // TODO : FIXME : Use one single interface !
16099
+ if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
16100
+ // OLD : else serverClients=nodeServerInstance.serverSocket.sockets.clients();
16101
+ else serverClients=nodeServerInstance.serverSocket.sockets.sockets;
16102
+
16103
+ serverClients.forEach((clientSocket)=>{
16104
+ clearInterval(clientSocket.stateCheckInterval);
16105
+ });
16106
+ });
16107
+
16108
+ return nodeServerInstance;
16109
+ },
16110
+
16111
+ // DO NOT USE DIRECTLY, USE INSTEAD initClient(...) (this function uses connectToServer(...)) !
16112
+ // NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
16113
+ connectToServer:(serverURL, port, isSecure=false, timeout)=>{
16114
+
16115
+ // TRACE
16116
+ lognow("INFO : Using socket library flavor : "+(WebsocketImplementation.isNodeContext?"node (client/server-side)":"browser (client-side only)"));
16117
+
16118
+ if(WebsocketImplementation.isNodeContext)
16119
+ return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
16120
+ else
16121
+ return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
16122
+ },
16123
+
16124
+
16125
+ //
16126
+ // NODE CLIENT
16127
+ //
16128
+ /*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
16129
+
16130
+ // NODE CLIENT MODE ONLY :
16131
+ let clientSocket;
16132
+ if(!WebsocketImplementation.useSocketIOImplementation){
16133
+
16134
+ // NEW : ws :
16135
+ if(typeof(WebSocket)==="undefined"){
16136
+ // TRACE
16137
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
16138
+ return null;
16139
+ }
16140
+
16141
+ clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{
16142
+ // CAUTION : SECURITY BREACH :
16143
+ // BUT ALSO NECESSARY TO ALLOW SELF-SIGNED CERTIFICATES USAGE WITH THE YESBOT SYSTEM !
16144
+ rejectUnauthorized:false, // (THIS IS A KNOWN SECURITY BREACH)
16145
+ secure: isSecure
16146
+ });
16147
+
16148
+ clientSocket.addEventListener("error", error=>{
16149
+ // TRACE
16150
+ lognow("ERROR : (NODEJS) A WebSocket client error occurred while trying to connect to server:", error.message);
16151
+ });
16152
+
16153
+ }else{
16154
+ // NOW : socket.io :
16155
+ //client on server-side:
16156
+
16157
+ if(WebsocketImplementation.isNodeContext && typeof(io)!=="undefined"){
16158
+
16159
+ // OLD SYNTAX: clientSocket=Socket.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16160
+ // NO : clientSocket=new Socket.Client(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16161
+ clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure, autoConnect:true});
16162
+ // UNUSEFUL (since we have the autoconnect:true option) : clientSocket.connect();
16163
+
16164
+ clientSocket.on("connect_error", error=>{
16165
+ // TRACE
16166
+ lognow("ERROR : (NODEJS) A SocketIO client error occurred while trying to connect to server:", error.message);
16167
+ });
16168
+
16169
+ }
16170
+ }
16171
+
16172
+ // DBG
16173
+ lognow("DEBUG : CLIENT : clientSocket created:");
16174
+
16175
+ const nodeClientInstance=new ClientInstance(clientSocket);
16176
+ return nodeClientInstance;
16177
+ },
16178
+
16179
+ //
16180
+ // BROWSER CLIENT
16181
+ //
16182
+
16183
+ /*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
16184
+
16185
+ // NEW : ws :
16186
+ if(typeof(WebSocket)==="undefined"){
16187
+ // TRACE
16188
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
16189
+ return null;
16190
+ }
16191
+
16192
+ // TODO : FIXME : Use one single interface !
16193
+ // BROWSER CLIENT MODE ONLY :
16194
+ let clientSocket;
16195
+ if(!WebsocketImplementation.useSocketIOImplementation){
16196
+ // CAUTION : PARAMETER rejectUnauthorized:false WILL DO NOTHING,
16197
+ // BECAUSE THIS IS COMPLETLY HANDLED BY THE BROWSER SECURITY POLICY !
16198
+ // SO TO CLEAR THE SSL ERROR :
16199
+ // - FIRST GO TO THE HTTPS:// SERVER ADDRESS WITH BROWSER
16200
+ // - THEN ADD THE SECURITY EXCEPTION IN THE BROWSER !
16201
+ clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
16202
+
16203
+ clientSocket.addEventListener("error", error=>{
16204
+ // TRACE
16205
+ lognow("ERROR : (BROWSER) A WebSocket client error occurred while trying to connect to server:", error.message);
16206
+ });
16207
+
16208
+ }else if(typeof(io)!=="undefined"){
16209
+ // OLD SYNTAX :clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16210
+ // ALTERNATIVE :
16211
+ clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
16212
+
16213
+ clientSocket.on("connect_error", error=>{
16214
+ // TRACE
16215
+ lognow("ERROR : (BROWSER) A SocketIO client error occurred while trying to connect to server:", error.message);
16216
+ });
16217
+
16218
+ }
16219
+
16220
+ // BROWSER CLIENT INSTANCE :
16221
+ const browserClientInstance=new ClientInstance(clientSocket);
16222
+ return browserClientInstance;
16223
+ },
16224
+
16225
+ };
16226
+
16227
+
16228
+
16229
+ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
16230
+
16231
+ const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
16232
+
16233
+
16234
+
16235
+ if(typeof(https)==="undefined"){
16236
+ // TRACE
16237
+ console.log("«https» SERVER library not called yet, calling it now.");
16238
+ https=require("https");
16239
+ }
16240
+ if(typeof(http)==="undefined"){
16241
+ // TRACE
16242
+ console.log("«http» SERVER library not called yet, calling it now.");
16243
+ http=require("http");
16244
+ }
16245
+
16246
+
16247
+ const DEFAULT_HANDLER=function(request, response){
16248
+
16249
+ const url=request.url;
16250
+
16251
+ let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
16252
+ if(contains(url,excludedStr)){
16253
+ return true;
16254
+ }
16255
+ });
16256
+
16257
+ if(isURLInExclusionZone){
16258
+ // TRACE
16259
+ console.log("ERROR 403 forbidden access error :");
16260
+ console.log(error);
16261
+
16262
+ response.writeHead(403);
16263
+ response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
16264
+ response.end();
16265
+ return;
16266
+ }
16267
+
16268
+
16269
+
16270
+ const urlFile="." + url;
16271
+
16272
+ let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
16273
+ if(filePath=="./") filePath="./index.html";
16274
+
16275
+ let contentType="text/html";
16276
+
16277
+ const headers={ "Content-Type": contentType };
16278
+
16279
+ // 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)
16280
+ if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
16281
+
16282
+
16283
+ fs.readFile(filePath, function(error, fileContent){
16284
+ if(error){
16285
+ if(error.code=="ENOENT"){
16286
+ // TRACE
16287
+ console.log("ERROR 404 file not found :"+filePath);
16288
+
16289
+ fs.readFile("./404.html", function(error, fileContent){
16290
+ response.writeHead(200, headers);
16291
+ response.end(fileContent, "utf-8");
16292
+ });
16293
+
16294
+ }else {
16295
+
16296
+ // TRACE
16297
+ console.log("ERROR 500 server error :");
16298
+ console.log(error);
16299
+
16300
+ response.writeHead(500);
16301
+ response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
16302
+ response.end();
16303
+
16304
+ }
16305
+ }else {
16306
+
16307
+ // TRACE
16308
+ console.log("INFO 200 OK :"+filePath);
16309
+
16310
+
16311
+ response.writeHead(200, headers);
16312
+ response.end(fileContent, "utf-8");
16313
+
16314
+ // res.writeHead(200);
16315
+ // res.end("hello world\n");
16316
+ // res.sendFile(__dirname + "/public/index.html");
16317
+
16318
+ }
16319
+ });
16320
+
16321
+ };
16322
+
16323
+
16324
+ const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
16325
+
16326
+
16327
+
16328
+ let listenableServer;
16329
+ if(sslOptions){
16330
+ let httpsServer=https.createServer(sslOptions, handler).listen(port);
16331
+ // TRACE
16332
+ console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
16333
+ listenableServer=httpsServer;
16334
+ }else{
16335
+ let httpServer=http.createServer(handler).listen(port);
16336
+ // TRACE
16337
+ console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
16338
+ listenableServer=httpServer;
16339
+ }
16340
+
16341
+
16342
+ const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
16343
+
16344
+ // When a client connects, we execute the callback :
16345
+ // CAUTION : MUST BE CALLED ONLY ONCE !
16346
+ server.onConnectionToClient((serverParam, clientSocketParam)=>{
16347
+ if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
16348
+ });
16349
+
16350
+
16351
+ server.onFinalize((serverParam)=>{
16352
+
16353
+ // DBG
16354
+ lognow("onFinalize() server");
16355
+
16356
+ if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
16357
+ });
16358
+
16359
+
16360
+ // TRACE
16361
+ console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
16362
+
16363
+
16364
+
16365
+
16366
+
16367
+ return server;
16368
+ }
16369
+
16370
+
16371
+ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null,
16372
+ /*OPTIONAL*/portParam,
16373
+ /*OPTIONAL*/certPathParam,
16374
+ /*OPTIONAL*/keyPathParam){
16375
+
16376
+ // TRACE
16377
+ console.log("Server launched.");
16378
+ console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
16379
+ console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
16380
+ // EXAMPLE : node orita-srv.js hash orita.global@keyHash 1234567890
16381
+ console.log("Server launched.");
16382
+
16383
+
16384
+ // We read the command-line arguments if needed :
16385
+
16386
+ let argCLPort;
16387
+ let argCLCertPath;
16388
+ let argCLKeyPath;
16389
+
16390
+ let serverConfig={};
16391
+ let isForceUnsecure;
16392
+
16393
+ let isHashAsked=false;
16394
+ let clearTextParam=null;
16395
+ let persisterId=null;
16396
+
16397
+
16398
+ process.argv.forEach(function (val, i){
16399
+ if(!val) return;
16400
+ // 0 corresponds to «node / nodejs»
16401
+ if(i<=1) return; // 1 corresponds to « <server.js> »
16402
+ else if(i==2){
16403
+ if(val==="hash") isHashAsked=true;
16404
+ }else if(i==3){
16405
+ if(!isHashAsked){
16406
+ try{
16407
+ const jsonConf=parseJSON(val);
16408
+ argCLPort=jsonConf.port;
16409
+ argCLCertPath=jsonConf.sslCertPath;
16410
+ argCLKeyPath=jsonConf.sslKeyPath;
16411
+ serverConfig=nonull(jsonConf.serverConfig,{});
16412
+ }catch(err1){
16413
+ lognow("WARN : Cannot parse argument JSON string «"+val+"».");
16414
+ }
16415
+ } else persisterId=val;
16416
+ }else if(i==4){
16417
+ if(isHashAsked) clearTextParam=val;
16418
+ }
16419
+ });
16420
+ isForceUnsecure=(argCLCertPath==="unsecure");
16421
+
16422
+
16423
+
16424
+
16425
+
16426
+ const aotraNodeServer={config:serverConfig};
16427
+ aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
16428
+
16429
+ if(isHashAsked){
16430
+ // We instanciate a temporary persister just to read the key hash file:
16431
+ const persister=getPersister("./");
16432
+ let persisterIdSplits=persisterId.split("@");
16433
+ if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
16434
+ // TRACE
16435
+ console.log("ERROR : No persister repository IDs provided correctly. Cannot read key hash. Aborting hash generation.");
16436
+ return aotraNodeServer;
16437
+ }
16438
+ const persisterClientId=persisterIdSplits[0];
16439
+ const persisterRepositoryName=persisterIdSplits[1];
16440
+ let globalKeyHashObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
16441
+ if(!globalKeyHashObject || !globalKeyHashObject.keyHash){
16442
+ // TRACE
16443
+ console.log("WARN : No key hash found. Generating one now.");
16444
+ globalKeyHashObject={keyHash:getUUID(), hashes:[]};
16445
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
16446
+ // TRACE
16447
+ console.log("INFO : Key hash generated and saved successfully.");
16448
+ });
16449
+ }
16450
+ const globalKeyHash=globalKeyHashObject.keyHash;
16451
+
16452
+ let firstHash=getHashedString(clearTextParam);
16453
+
16454
+ let generatedHash=getHashedString( firstHash + globalKeyHash, "SHA-256", true);// (we use the heavy treatment thing.)
16455
+ globalKeyHashObject.hashes.push(generatedHash);
16456
+
16457
+ // We update the repository :
16458
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
16459
+ // TRACE
16460
+ console.log("INFO : Hash added to repository and saved successfully.");
16461
+ });
16462
+
16463
+ // OUTPUT
16464
+ console.log("Here is your key : share it with your main clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
16465
+
16466
+ return aotraNodeServer;
16467
+ }
16468
+
16469
+
16470
+ const DEFAULT_PORT=nonull(argCLPort,25000);
16471
+ const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
16472
+ const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
16473
+
16474
+
16475
+ let port=portParam ? portParam : DEFAULT_PORT;
16476
+ let certPath=null;
16477
+ let keyPath=null;
16478
+
16479
+ if(!isForceUnsecure){
16480
+ certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
16481
+ keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
16482
+ }
16483
+
16484
+
16485
+
16486
+
16487
+ // UNUSEFUL :
16488
+ //aotraNodeServer.serverManager.microClientsSockets=[];
16489
+ // UNUSEFUL :
16490
+ //aotraNodeServer.serverManager.mainClientsSockets=[];
16491
+
16492
+ aotraNodeServer.serverManager.start=function(){
16493
+
16494
+ // Eventual encryption options :
16495
+ let sslOptions=null;
16496
+ if(!isForceUnsecure){
16497
+ if(typeof(fs)==="undefined"){
16498
+ // TRACE
16499
+ lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
16500
+ }else{
16501
+ try{
16502
+ sslOptions={
16503
+ cert: fs.readFileSync(certPath, {encoding: "utf8"}),
16504
+ key: fs.readFileSync(keyPath, {encoding: "utf8"}),
16505
+ };
16506
+ }catch(exception){
16507
+ // TRACE
16508
+ lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
16509
+ }
16510
+ }
16511
+ }
16512
+
16513
+ aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
16514
+
16515
+
16516
+ return aotraNodeServer;
16517
+ };
16518
+
16519
+ return aotraNodeServer;
16520
+ }
16521
+
16522
+ // ========================= FUSRODA SERVER : =========================
16523
+
16524
+ //
16525
+ // DOES NOT WORK : USE Java FusrodaServer instead :
16526
+ //
16527
+ ///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
16528
+ //createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=4000){
16529
+ //
16530
+ // // https://www.npmjs.com/package/screenshot-desktop
16531
+ // // https://github.com/octalmage/robotjs
16532
+ // // npm install --save screenshot-desktop
16533
+ // //
16534
+ // // sudo apt-get install libxtst-dev libx11-dev
16535
+ // // npm install --save robotjs
16536
+ //
16537
+ // // http://getrobot.net/docs/usage.html
16538
+ // //
16539
+ // // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
16540
+ //
16541
+ // const screenshot=require("screenshot-desktop");
16542
+ //
16543
+ // const REFRESH_SCREENSHOTS_MILLIS=500;
16544
+ //
16545
+ //
16546
+ // const server=initNodeServerInfrastructureWrapper(
16547
+ // // On each client connection :
16548
+ // // (serverParam, clientSocketParam)=>{},
16549
+ // null,
16550
+ // // On server finalization :
16551
+ // (serverParam)=>{
16552
+ //
16553
+ // serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
16554
+ // serverParam.addToRoom(clientSocket,"clients");
16555
+ // });
16556
+ //
16557
+ //
16558
+ // serverParam.sendScreenshotsRoutine=setInterval(()=>{
16559
+ // if(serverParam.isScreenshotStarted) return;
16560
+ //
16561
+ // serverParam.isScreenshotStarted=true;
16562
+ //
16563
+ // screenshot().then((img) => {
16564
+ //
16565
+ // const data={image:img,listenerMessageType:"imageData"};
16566
+ // serverParam.send("message", data, "clients");
16567
+ //
16568
+ // serverParam.isScreenshotStarted=false;
16569
+ //
16570
+ // }).catch((error) => {
16571
+ // // TRACE
16572
+ // lognow("ERROR : Error during screenshot :",error);
16573
+ // });
16574
+ //
16575
+ // },REFRESH_SCREENSHOTS_MILLIS);
16576
+ //
16577
+ //
16578
+ // },portParam,certPathParam,keyPathParam);
16579
+ //
16580
+ //
16581
+ // // const doOnConnect=(serverParam, clientSocketParam)=>{
16582
+ // // };
16583
+ // // const doOnFinalizeServer=(serverParam)=>{
16584
+ // // /*DO NOTHING*/
16585
+ // // };
16586
+ // // const server={};
16587
+ // // server.start=(port=4000)=>{
16588
+ // // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
16589
+ // // };
16590
+ //
16591
+ // return server;
16592
+ //}
16593
+
16594
+
16595
+ // ========================= UTILITY SERVERSIDE METHODS : =========================
16596
+
16597
+ class ListManager{
16598
+
16599
+ constructor(config){
16600
+ this.config=config;
16601
+ this.maxItemsNumber=nonull(this.config.max,999);
16602
+ this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
16603
+ this.sessionDurationSeconds=nonull(this.config.duration,null);
16604
+ this.mode=nonull(this.config.mode,"startAtConnexion");
16605
+
16606
+ this.itemsInfos={};
16607
+ this.time=null;
16608
+ this.started=false;
16609
+ }
16610
+
16611
+ addItem(id,item){
16612
+ if(id==null){
16613
+ // TRACE
16614
+ lognow("ERROR : Cannot add item with no id.");
16615
+ return;
16616
+ }
16617
+ if(!item){
16618
+ // TRACE
16619
+ lognow("ERROR : Cannot add null item.");
16620
+ return;
16621
+ }
16622
+ const numberOfItemsCurrently=getArraySize(this.itemsInfos);
16623
+
16624
+ // DBG
16625
+ lognow(">>>>>>>>>numberOfItemsCurrently:",numberOfItemsCurrently);
16626
+ lognow(">>>>>>>>>Object.keys(arrayOfValues):",Object.keys(this.itemsInfos));
16627
+
16628
+
16629
+ if(this.maxItemsNumber<=numberOfItemsCurrently){
16630
+ // TRACE
16631
+ lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
16632
+ return;
16633
+ }
16634
+
16635
+
16636
+ if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
16637
+
16638
+ // DBG
16639
+ lognow(">>>>>>>>>START SESSION !!!!");
16640
+
16641
+ this.startSession();
16642
+ }
16643
+ this.itemsInfos[id]={
16644
+ item:item,
16645
+ time:getNow(),
16646
+ };
16647
+ }
16648
+
16649
+ startSession(){
16650
+ this.time=getNow();
16651
+ this.started=true;
16652
+ }
16653
+
16654
+ stopSession(){
16655
+ this.started=false;
16656
+ }
16657
+
16658
+ isSessionActive(){
16659
+
16660
+ // DBG
16661
+ lognow(" !!! this.sessionDurationSeconds : "+this.sessionDurationSeconds);
16662
+ lognow(" !!! this.started : "+this.started);
16663
+ lognow(" !!! this.time : "+this.time);
16664
+
16665
+
16666
+ if(!this.sessionDurationSeconds) return true;
16667
+ if(!this.started) return false;
16668
+ const result=!hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
16669
+
16670
+ // DBG
16671
+ lognow(" !!! HAS DELAY PASSED : "+result);
16672
+
16673
+ return result;
16674
+ }
16675
+
16676
+ isClientActive(clientId){
16677
+
16678
+ // DBG
16679
+ lognow(" this.isSessionActive()"+this.isSessionActive());
16680
+
16681
+ if(!this.isSessionActive()) return false;
16682
+ const clientPosition=this.getItemPosition(clientId);
16683
+ //CAUTION : Client position starts at 1 !
16684
+ const clientIndex=(clientPosition-1);
16685
+ const result=(clientIndex<=this.maxItemsNumber && clientIndex<=this.simultaneousItemsNumber);
16686
+
16687
+ return result;
16688
+ }
16689
+
16690
+ removeItemById(id){
16691
+ if(id==null){
16692
+ // TRACE
16693
+ lognow("ERROR : Cannot remove item, no id specified.");
16694
+ return;
16695
+ }
16696
+ if(!this.itemsInfos[id]){
16697
+ // TRACE
16698
+ lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
16699
+ return;
16700
+ }
16701
+ delete this.itemsInfos[id];
16702
+ }
16703
+
16704
+ removeItem(item){
16705
+ if(item==null){
16706
+ // TRACE
16707
+ lognow("ERROR : Cannot remove item, none specified.");
16708
+ return;
16709
+ }
16710
+
16711
+ let id=null;
16712
+ foreach(this.itemsInfos,(itemInfos,key)=>{
16713
+ if(itemInfos.item===item
16714
+ // DEBUG ONLY :
16715
+ || (itemInfos.item.id && item.id && itemInfos.item.id===item.id)
16716
+ ){
16717
+ id=key;
16718
+ return "break";
16719
+ }
16720
+
16721
+ });
16722
+
16723
+ if(!id){
16724
+ // TRACE
16725
+ lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
16726
+ return;
16727
+ }
16728
+
16729
+ this.removeItemById(id);
16730
+ }
16731
+
16732
+ // Goes from 1 to <length>:
16733
+ getItemPosition(id){
16734
+ if(id==null){
16735
+ // TRACE
16736
+ lognow("ERROR : Cannot calculate item position, no id specified.");
16737
+ return null;
16738
+ }
16739
+ if(!this.itemsInfos[id]){
16740
+ // TRACE
16741
+ lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
16742
+ return null;
16743
+ }
16744
+ let result=0;
16745
+ foreach(this.itemsInfos, (itemInfo, key)=>{
16746
+ result++;
16747
+ if(id===key) return "break";
16748
+ },null,(item1, item2)=>item1.time<item2.time);
16749
+ return result;
16750
+ }
16751
+
16752
+ getItemsNumber(){
16753
+ return getArraySize(this.itemsInfos);
16754
+ }
16755
+
16756
+ };
16757
+
16758
+ getListManager=function(config){
16759
+ return new ListManager(config);
16760
+ };
16761
+
16762
+
16763
+ // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
16764
+ // NO : IN A NODE CONTEXT WITH require("...") WILL RESULT IN AN UNDEFINED FUNCTION ERROR !!!
16765
+ // function performHTTPRequest(...){...
16766
+ // USE THIS INSTEAD :
16767
+ performHTTPRequest=function(completeURL, httpMethod="GET", headers={}, requestBodyOrNamedArgs=null, isNodeContext=false, addCORSHeader=ADD_CORS_HEADER){
16768
+
16769
+ // Body
16770
+ let requestBodyOrNamedArgsStr=null;
16771
+ if(requestBodyOrNamedArgs){
16772
+ if(isString(requestBodyOrNamedArgs) && !isObject(requestBodyOrNamedArgs)){
16773
+ // If it's already a string, we assume it's a valid JSOn string already :
16774
+ requestBodyOrNamedArgsStr=requestBodyOrNamedArgs.replace(/[\r\n]+/gim, ""); // We remove all the breaklines
16775
+ }else{
16776
+ try{
16777
+ requestBodyOrNamedArgsStr=stringifyObject(requestBodyOrNamedArgs).replace(/[\r\n]+/gim, ""); // We remove all the breaklines
16778
+ }catch(parseErr){
16779
+ requestBodyOrNamedArgsStr=""+requestBodyOrNamedArgs;
16780
+ }
16781
+ }
16782
+ }
16783
+
16784
+ let body=null;
16785
+ // Not the same way to send parameters, with POST/PUT http method :
16786
+ if(contains(["POST","PUT"],httpMethod) && requestBodyOrNamedArgs){
16787
+ body=requestBodyOrNamedArgsStr;
16788
+ }
16789
+
16790
+ // Headers
16791
+ if(contains(["POST","PUT"],httpMethod)){
16792
+ headers["Content-Type"]="application/json";
16793
+
16794
+ if(requestBodyOrNamedArgsStr && isString(body)){
16795
+ headers["Content-Length"]=Buffer.byteLength(requestBodyOrNamedArgsStr);
16796
+ }
16797
+
16798
+ // 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)
16799
+ if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
16800
+
16801
+ } else if(httpMethod==="GET" && requestBodyOrNamedArgs){
16802
+ // Not the same way to send parameters in GET http method :
16803
+ // DBG
16804
+ lognow("unformatted API URL : "+completeURL);
16805
+
16806
+ completeURL=appendGetParameters(completeURL, requestBodyOrNamedArgs);
16807
+
16808
+ // DBG
16809
+ lognow("formatted API URL : "+completeURL);
16810
+ }
16811
+
16812
+ // CASE BROWSER CONTEXT :
16813
+ if(!isNodeContext || typeof(require)=="undefined"){
16814
+ // TRACE
16815
+ lognow("INFO : We are not running in a browser context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using browser library.");
16816
+
16817
+ return new Promise((resolve,reject)=>{
16818
+ fetch(completeURL, {
16819
+ method: httpMethod,
16820
+ headers: headers,
16821
+ body: body,
16822
+ })
16823
+ // STRANGE : DOES NOT WORK !!:
16824
+ //.then(response => response.json())
16825
+ // STRANGE : DOES WORK :
16826
+ .then(response => {
16827
+
16828
+ // DBG
16829
+ console.log("~~~~~~~~~~~response :",response);
16830
+
16831
+ return response.json();
16832
+ }).then(data => {
16833
+
16834
+ // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
16835
+ const status=data.status;
16836
+ // if(status!=200 && data.error){
16837
+ // // const error=nonull(data.error, data.detail);
16838
+ // const error=data.error;
16839
+ // // TRACE
16840
+ // console.error("Error:", error);
16841
+ // reject({error:""+error, httpStatus:status});
16842
+ // return;
16843
+ // }
16844
+
16845
+ // DBG
16846
+ console.log("~~~~~~~~~~~data :",data);
16847
+
16848
+ data.httpStatus=status;
16849
+ resolve(data);
16850
+ }).catch(error => {
16851
+ // TRACE
16852
+ console.error("Error:", error);
16853
+ reject({error:`${error}`, httpStatus:null});
16854
+ });
16855
+ });
16856
+ }// else :
16857
+
16858
+ // CASE NODEJS CONTEXT :
16859
+
16860
+ // TRACE
16861
+ lognow("INFO : We are running in a nodejs context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using nodejs library.");
16862
+
16863
+
16864
+ const isSecure=(!empty(completeURL) && contains(completeURL.toLowerCase(),"https://"));
16865
+ const httpHandler=isSecure?require("https"):require("http");
16866
+
16867
+ // Options for the HTTP request
16868
+ const options = {
16869
+ url: completeURL,
16870
+ method: httpMethod,
16871
+ };
16872
+
16873
+ if(contains(["POST","PUT"],httpMethod)){
16874
+ options.json=true;
16875
+ }
16876
+
16877
+ options.headers=headers;
16878
+
16879
+ return new Promise((resolve,reject)=>{
16880
+
16881
+ // Create the HTTP request
16882
+ // DOES NOT WORK : const request = httpHandler.request(options, (response) => {
16883
+ // UNLESS YOU SPECIFY in options : hostname, port, path
16884
+ const request = httpHandler.request(completeURL, options, (response) => {
16885
+
16886
+ let responseDataStr = "";
16887
+
16888
+ // A chunk of data has been received.
16889
+ response.on("data", (chunk) => {
16890
+ responseDataStr += chunk;
16891
+ // // DEBUG ONLY
16892
+ // const str = Buffer.from(chunk).toString("utf8");
16893
+ // lognow(">>>str:"+str);
16894
+ });
16895
+
16896
+ // The whole response has been received.
16897
+ response.on("end", () => {
16898
+
16899
+ try{
16900
+ let responseData;
16901
+ try{
16902
+ responseData=parseJSON(responseDataStr);
16903
+ }catch(parseError1){
16904
+ // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
16905
+ // DEBUG
16906
+ responseDataStr=responseDataStr.replace(/'/gim,"`");
16907
+ try{
16908
+ responseData=parseJSON(responseDataStr);
16909
+ }catch(parseError2){
16910
+ // DBG
16911
+ lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError2);
16912
+ resolve( {response:response, responseDataStr:responseDataStr} );
16913
+ return;
16914
+ }
16915
+ }
16916
+
16917
+ // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
16918
+ const status=response.statusCode;
16919
+ // if(status!=200){
16920
+ // const error=nonull(response.statusMessage, "No error message for error code "+status);
16921
+ // // TRACE
16922
+ // console.error("Error:", error);
16923
+ // reject({error:""+error, httpStatus:status});
16924
+ // return;
16925
+ // }
16926
+
16927
+ resolve( {responseData:responseData, response:response, responseDataStr:responseDataStr, httpStatus: status} );
16928
+ }catch(parseError3){
16929
+ // DBG
16930
+ lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError3);
16931
+ resolve( {response:response, responseDataStr:responseDataStr} );
16932
+ return;
16933
+ }
16934
+
16935
+ });
16936
+ });
16937
+
16938
+ // Handle errors
16939
+ request.on("error", (error) => {
16940
+ reject({error:error, httpStatus:null});
16941
+ });
16942
+
16943
+ // Not the same way to send parameters, with POST/PUT http method :
16944
+ if(contains(["POST","PUT"],httpMethod) && body){
16945
+ request.write(body);
16946
+ }
16947
+
16948
+ // End the request
16949
+ request.end();
16950
+
16951
+ });
16952
+
16953
+ };
16954
+
16955
+
16956
+
16957
+
16958
+ replacePathVariablesNamesWithValuesIfPossible=function(apiURL, namedArgs){
16959
+ let result=apiURL;
16960
+ foreach(namedArgs,(namedArgValue, namedArgKey)=>{
16961
+ result=result.replace("{"+namedArgKey+"}",namedArgValue);
16962
+ });
16963
+ return result;
16964
+ };
16965
+
16966
+ appendGetParameters=function(apiURL, namedArgsParam){
16967
+ if(nothing(namedArgsParam)) return "";
16968
+ try{
16969
+
16970
+ const namedArgs=isString(namedArgsParam)?parseJSON(namedArgsParam):namedArgsParam;
16971
+
16972
+ let result=apiURL;
16973
+
16974
+ const paramCouples=[];
16975
+ foreach(namedArgs,(value,key)=>{paramCouples.push(key+"="+value);});
16976
+
16977
+ if(!empty(paramCouples)) result+=("?"+paramCouples.join("&"));
16978
+
16979
+ return result;
16980
+ }catch(parseError){
16981
+ // TRACE
16982
+ lognow("ERROR : Could not parse string parameters object «"+namedArgsParam+"», aborting GET parameter request string calculation:", parseError);
16983
+ return "";
16984
+ }
16985
+ };
16986
+
16987
+
16988
+
16989
+
16990
+
16991
+
16992
+
16993
+
15648
16994
  /* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.
15649
16995
  *
15650
16996
  * 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.1828",
3
+ "version": "0.0.1830",
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)",