aotrautils-srv 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:39:15)»*/
3
+ /*utils COMMONS library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:52)»*/
4
4
  /*-----------------------------------------------------------------------------*/
5
5
 
6
6
 
@@ -5076,7 +5076,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
5076
5076
 
5077
5077
 
5078
5078
 
5079
- /*utils AI library associated with aotra version : «1_29072022-2359 (17/03/2026-14:39:15)»*/
5079
+ /*utils AI library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:52)»*/
5080
5080
  /*-----------------------------------------------------------------------------*/
5081
5081
 
5082
5082
 
@@ -5222,7 +5222,7 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
5222
5222
 
5223
5223
 
5224
5224
 
5225
- /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (17/03/2026-14:39:15)»*/
5225
+ /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (17/03/2026-16:49:52)»*/
5226
5226
  /*-----------------------------------------------------------------------------*/
5227
5227
 
5228
5228
 
@@ -6228,6 +6228,1352 @@ function capitalize(str){
6228
6228
  }
6229
6229
 
6230
6230
 
6231
+ /* ## Utility global methods in a javascript, console (nodejs) or vanilla javascript with no browser environment.
6232
+ *
6233
+ * This set of methods gathers utility generic-purpose methods usable in any JS project.
6234
+ * Several authors of snippets published freely on the Internet contributed to this library.
6235
+ * Feel free to use/modify-enhance/publish them under the terms of its license.
6236
+ *
6237
+ * # Library name : «aotrautils»
6238
+ * # Library license : HGPL(Help Burma) (see aotra README information for details : https://alqemia.com/aotra.js )
6239
+ * # Author name : Jérémie Ratomposon (massively helped by his native country free education system)
6240
+ * # Author email : info@alqemia.com
6241
+ * # Organization name : Alqemia
6242
+ * # Organization email : admin@alqemia.com
6243
+ * # Organization website : https://alqemia.com
6244
+ *
6245
+ *
6246
+ */
6247
+
6248
+
6249
+ // COMPATIBILITY browser javascript / nodejs environment :
6250
+ if(typeof(window)==="undefined") window=global;
6251
+
6252
+
6253
+
6254
+ // OLD : socket.io :
6255
+ // https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
6256
+ // https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
6257
+ // https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
6258
+ // https://socket.io/docs/v4/client-socket-instance/
6259
+
6260
+ // NEW : ws :
6261
+ // https://github.com/websockets/ws#installing
6262
+ // https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
6263
+ // ON BROWSER SIDE : Native Websockets :
6264
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
6265
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
6266
+
6267
+
6268
+
6269
+ // =================================================================================
6270
+ // NODEJS UTILS
6271
+
6272
+ const FILE_ENCODING="utf8";
6273
+
6274
+ const ADD_CORS_HEADER=true;
6275
+
6276
+
6277
+ // DEBUG
6278
+ console.log("DEBUG : Importing «fs» dependency.");
6279
+
6280
+
6281
+ if(typeof(require)!=="undefined" && typeof(fs)==="undefined" ){
6282
+ window.fs=require("fs");
6283
+ }
6284
+
6285
+ // For debug (WORKAROUND):
6286
+ fs=window.fs;
6287
+
6288
+
6289
+ // Nodejs filesystem utils :
6290
+ if(typeof(fs)==="undefined"){
6291
+ // TRACE
6292
+ console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
6293
+ window.getPersister=()=>{ return null; };
6294
+
6295
+ }else{
6296
+
6297
+
6298
+ window.getPersister=function(dataDirPath,prefix=""){
6299
+
6300
+ let self={
6301
+
6302
+ dataDirPath:dataDirPath,
6303
+ prefix:prefix,
6304
+
6305
+ //FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
6306
+ /*private*/getPath:function(clientId="noclient", repositoryName="norepository"){
6307
+ // let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
6308
+ let path=`${self.dataDirPath}`
6309
+ + (blank(self.prefix)?"":(self.prefix+"."))
6310
+ +`${clientId}.${repositoryName}.json`;
6311
+ return path;
6312
+ },
6313
+
6314
+ readTreeObjectFromFile:function(clientId="noclient", repositoryName="norepository", keepClassName=false){
6315
+
6316
+ let path=self.getPath(clientId,repositoryName);
6317
+ let resultFlat=null;
6318
+
6319
+ try{
6320
+
6321
+ resultFlat=fs.readFileSync(path, FILE_ENCODING);
6322
+
6323
+ }catch(error){
6324
+ // TRACE
6325
+ console.log("ERROR : Could not read file «"+path+"».");
6326
+
6327
+ return null;
6328
+ }
6329
+
6330
+
6331
+ let resultData={};
6332
+
6333
+ // 1)
6334
+ if(!empty(resultFlat)) resultData=parseJSON(resultFlat);
6335
+
6336
+ // 2)
6337
+ if(!empty(resultData) && isFlatMap(resultData)){
6338
+ // CAUTION : We have to keep the type information, here too ! (in the sub-objects)
6339
+ resultData=getAsTreeStructure(resultData, keepClassName);
6340
+ }
6341
+
6342
+ return resultData;
6343
+ },
6344
+
6345
+
6346
+ saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
6347
+
6348
+ if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
6349
+ dataFlatForClient=getAsFlatStructure(dataFlatForClient);
6350
+ }
6351
+
6352
+ // reserved characters : -/\^$*+?.()|[]{}
6353
+ // CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
6354
+ let dataFlatStr=stringifyObject(dataFlatForClient)
6355
+ // We «aerate» the produced JSON :
6356
+ .replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
6357
+ // ...except for inline, escaped JSON string representations :
6358
+ .replace(/\\\":[\w]*\{\n/gim,"\\\":{");
6359
+ // NO : .replace(/}/gim,"}\n");
6360
+
6361
+
6362
+ let path=self.getPath(clientId,repositoryName);
6363
+ fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
6364
+ if(error){
6365
+ // TRACE
6366
+ console.log("ERROR : Could not write file «"+path+"»:",error);
6367
+ throw error;
6368
+ }
6369
+ if(doOnSuccess) doOnSuccess(dataFlatForClient);
6370
+ });
6371
+ }
6372
+
6373
+ };
6374
+
6375
+
6376
+ return self;
6377
+ };
6378
+
6379
+ }
6380
+
6381
+
6382
+ window.fileExists=(filePath)=>{
6383
+ if(typeof(fs)=="undefined"){
6384
+ // TRACE
6385
+ lognow("ERROR : «fs» node dependency is not available ! Cannot test if file exists.");
6386
+ return null;
6387
+ }
6388
+ return fs.existsSync(filePath);
6389
+ };
6390
+
6391
+
6392
+
6393
+ // Nodejs server launching helper functions :
6394
+ //Networking management :
6395
+ //- WEBSOCKETS AND NODEJS :
6396
+
6397
+ function isConnected(clientSocket){
6398
+ if(!WebsocketImplementation.useSocketIOImplementation)
6399
+ return (clientSocket.readyState===WebSocket.OPEN)
6400
+ return (clientSocket.connected);
6401
+ }
6402
+
6403
+ // Client-side or Server-side :
6404
+ window.getURLOrConsoleParameter=(parameterName)=>{
6405
+ if(typeof(getURLParameter)!="undefined") return getURLParameter(parameterName);
6406
+ return getConsoleParam(parameterName);
6407
+ }
6408
+
6409
+
6410
+ // Server-side :
6411
+
6412
+ getConsoleServerParams=function(portParam=null, certPathParam=null, keyPathParam=null, ignoreConsoleArgs=false, argsOffset=0){
6413
+
6414
+ // Node dependencies :
6415
+ // https=require("https");
6416
+ // fs=require("fs");
6417
+
6418
+ if(typeof(https)==="undefined"){
6419
+ // TRACE
6420
+ console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
6421
+ return null;
6422
+ }
6423
+ if(typeof(fs)==="undefined"){
6424
+ // TRACE
6425
+ console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
6426
+ return null;
6427
+ }
6428
+
6429
+ const result={};
6430
+
6431
+ // We read the command-line arguments if needed :
6432
+ const argCLPort=getConsoleParam(0, argsOffset);
6433
+ const argCLCertPath=getConsoleParam(1, argsOffset);
6434
+ const argCLKeyPath=getConsoleParam(2, argsOffset);
6435
+
6436
+ // Console, command-line arguments OVERRIDE parameters values :
6437
+
6438
+ result.port=(ignoreConsoleArgs?portParam:nonull(argCLPort,portParam));
6439
+ result.certPath=null;
6440
+ result.keyPath=null;
6441
+ result.isSecure=!!(certPathParam || keyPathParam);
6442
+
6443
+ if(result.isSecure){
6444
+ result.certPath=(ignoreConsoleArgs?certPathParam:nonull(argCLCertPath,certPathParam));
6445
+ result.keyPath=(ignoreConsoleArgs?keyPathParam:nonull(argCLKeyPath,keyPathParam));
6446
+ }
6447
+
6448
+ // Eventual encryption options :
6449
+ result.sslOptions=null;
6450
+ if(result.isSecure){
6451
+ result.sslOptions={
6452
+ cert: fs.readFileSync(result.certPath),
6453
+ key: fs.readFileSync(result.keyPath),
6454
+ };
6455
+ }
6456
+
6457
+ return result;
6458
+ }
6459
+
6460
+ getConsoleParam=function(indexOrName=0, argsOffset=2/*arg 0 is «node» and arg 1 is the js file*/){
6461
+ if(!process){
6462
+ throw new Error("ERROR : Cannot extract console parameter in this context !");
6463
+ }
6464
+
6465
+ const allArgs=process.argv;
6466
+ if(empty(allArgs)) return null;
6467
+
6468
+ // Case indexOrName is a number for the parameter index :
6469
+ if(!isString(indexOrName) && isNumber(indexOrName)){
6470
+ return allArgs[indexOrName+argsOffset];
6471
+ }
6472
+
6473
+ // Case indexOrName is a string for the parameter name :
6474
+ for(let i=argsOffset;i<allArgs.length;i++){
6475
+ const val=allArgs[i];
6476
+ if(!contains(val,"="))
6477
+ continue;
6478
+ const splits=val.split("=");
6479
+ if(splits.length<2)
6480
+ continue;
6481
+ const name=splits[0].trim();
6482
+ const value=splits[1].trim();
6483
+ if(name==indexOrName)
6484
+ return value;
6485
+ }
6486
+ return null;
6487
+ }
6488
+
6489
+
6490
+
6491
+ window.getConsoleCLI=(doOnCommands={"makeSandiwch":()=>{}}, promptText="Enter command> ")=>{
6492
+
6493
+ const readline = require("node:readline");
6494
+
6495
+ const cliInterface = readline.createInterface({
6496
+ input: process.stdin,
6497
+ output: process.stdout,
6498
+ prompt: nonoull(promptText,"Enter command> ")
6499
+ });
6500
+
6501
+ cliInterface.prompt();
6502
+
6503
+ cliInterface.on("line", (line) => {
6504
+ const input = line.trim();
6505
+ switch (input) {
6506
+ case "quit":
6507
+ console.log("Bye!");
6508
+ cliInterface.close();
6509
+ break;
6510
+ case "help":
6511
+ console.log("Available commands: quit, help and :", Object.keys(doOnCommands));
6512
+ break;
6513
+ default:
6514
+ if(doOnCommands) doOnCommands[input]();
6515
+ break;
6516
+ }
6517
+
6518
+ // Show the prompt again :
6519
+ cliInterface.prompt();
6520
+
6521
+ }).on("close", () => {
6522
+ process.exit(0);
6523
+ });
6524
+
6525
+ return cliInterface
6526
+ };
6527
+
6528
+
6529
+
6530
+
6531
+
6532
+ // NODE ONLY SERVER / CLIENTS :
6533
+ WebsocketImplementation={
6534
+
6535
+
6536
+ isNodeContext:true,
6537
+ useSocketIOImplementation:false,
6538
+ useFlatStrings:false,
6539
+
6540
+ // COMMON METHODS
6541
+ /*private static*/isInRoom(clientSocket, clientsRoomsTag){
6542
+ return (!clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag));
6543
+ },
6544
+
6545
+
6546
+ //
6547
+ // CONSOLE NODE SERVER/CLIENT
6548
+ //
6549
+ getStatic:(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false)=>{
6550
+
6551
+ WebsocketImplementation.isNodeContext=isNodeContext;
6552
+ WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
6553
+
6554
+ if(!WebsocketImplementation.useSocketIOImplementation){
6555
+ // TRACE
6556
+ lognow("INFO : (SERVER/CLIENT) Using native WebSocket implementation.");
6557
+
6558
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
6559
+ if(isNodeContext){
6560
+ if(typeof(WebSocket)==="undefined"){
6561
+ // TRACE
6562
+ console.log("«ws» SERVER library not called yet, calling it now.");
6563
+
6564
+ window.WebSocket=require("ws");
6565
+
6566
+ if(typeof(WebSocket)==="undefined"){
6567
+ // TRACE
6568
+ console.log("ERROR : «ws» CONSOLE/BROWSER CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
6569
+ }
6570
+ }
6571
+ }
6572
+
6573
+ }else{
6574
+ // TRACE
6575
+ lognow("INFO : (SERVER/CLIENT) Using socket.io websocket implementation.");
6576
+
6577
+ if(isNodeContext){
6578
+
6579
+ // NODE SERVER :
6580
+ // DBG
6581
+ lognow("INFO : Loading NODE SERVER socket.io libraries.")
6582
+ if(typeof(Socket)==="undefined"){
6583
+ // TRACE
6584
+ console.log("«socket.io» NODE SERVER library not called yet, calling it now.");
6585
+ Socket=require("socket.io");
6586
+ }
6587
+ if(typeof(Socket)==="undefined"){
6588
+ // TRACE
6589
+ console.log("ERROR : «socket.io» NODE CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
6590
+ }
6591
+
6592
+ // NODE CLIENT :
6593
+ // DBG
6594
+ lognow("INFO : Loading NODE CLIENT socket.io libraries.")
6595
+ if(typeof(io)==="undefined"){
6596
+ // TRACE
6597
+ console.log("«socket.io-client» NODE CLIENT library not called yet, calling it now.");
6598
+ io=require("socket.io-client");
6599
+ }
6600
+ if(typeof(io)==="undefined"){
6601
+ // TRACE
6602
+ console.log("ERROR : «socket-client.io» NODE CLIENT library not found. Cannot launch nodejs server. Aborting.");
6603
+ }
6604
+ }
6605
+
6606
+
6607
+ }
6608
+
6609
+ // *********************************************************************************
6610
+
6611
+ return WebsocketImplementation;
6612
+ },
6613
+
6614
+
6615
+ /*private*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
6616
+
6617
+ const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
6618
+
6619
+ let dataResult=eventOrMessage;
6620
+
6621
+ try{
6622
+ dataResult=(WebsocketImplementation.useFlatStrings || isString(eventOrMessage)?parseJSON(eventOrMessage):eventOrMessage);
6623
+ }catch(error1){
6624
+ // TRACE
6625
+ lognow(`ERROR : Failed to parse JSON for string «${dataResult}»`,error1);
6626
+ dataResult=(isString(eventOrMessage)?eventOrMessage:stringifyObject(eventOrMessage));
6627
+ }
6628
+
6629
+ return dataResult;
6630
+ },
6631
+
6632
+
6633
+ getServer:(listenableServer)=>{
6634
+
6635
+ if(!WebsocketImplementation.isNodeContext){
6636
+ // TRACE
6637
+ throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
6638
+ }
6639
+
6640
+
6641
+ // TODO : FIXME : Use one single interface !
6642
+ // NODE SERVER MODE ONLY :
6643
+ let serverSocket;
6644
+ if(!WebsocketImplementation.useSocketIOImplementation){
6645
+ serverSocket=new WebSocket.Server({ "server":listenableServer });
6646
+ }else{
6647
+
6648
+ // NOW : socket.io :
6649
+ // Loading socket.io
6650
+ // OLD SYNTAX : serverSocket=Socket.listen(listenableServer);
6651
+ serverSocket=new Socket.Server(listenableServer);
6652
+
6653
+ if(listenableServer.cert || listenableServer.key){
6654
+ // TRACE :
6655
+ 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 !!!");
6656
+ }
6657
+
6658
+ // Setting up the disconnect event for a client :
6659
+ serverSocket.on("endConnection",()=>{
6660
+ serverSocket.disconnect();
6661
+ });
6662
+ }
6663
+
6664
+ serverSocket.on("error",(error)=>{
6665
+ // TRACE
6666
+ lognow("ERROR : An error occurred when trying to start the server : ",error);
6667
+
6668
+ });
6669
+
6670
+ // NODE SERVER INSTANCE :
6671
+ const nodeServerInstance=new NodeServerInstance(serverSocket, listenableServer);
6672
+
6673
+ // Join room server part protocol :
6674
+ nodeServerInstance.receive("protocol",(message, clientSocket)=>{
6675
+ nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
6676
+ },{listenerMessageType:"joinRoom"});
6677
+
6678
+ // To make the server aware of the clients connections states :
6679
+ nodeServerInstance.serverSocket.on("close", function close() {
6680
+
6681
+ // TODO : FIXME : Use one single interface !
6682
+ if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
6683
+ // OLD : else serverClients=nodeServerInstance.serverSocket.sockets.clients();
6684
+ else serverClients=nodeServerInstance.serverSocket.sockets.sockets;
6685
+
6686
+ serverClients.forEach((clientSocket)=>{
6687
+ clearInterval(clientSocket.stateCheckInterval);
6688
+ });
6689
+ });
6690
+
6691
+ return nodeServerInstance;
6692
+ },
6693
+
6694
+ // DO NOT USE DIRECTLY, USE INSTEAD initClient(...) (this function uses connectToServer(...)) !
6695
+ // NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
6696
+ connectToServer:(serverURL, port, isSecure=false, timeout)=>{
6697
+
6698
+ // TRACE
6699
+ lognow("INFO : Using socket library flavor : "+(WebsocketImplementation.isNodeContext?"node (client/server-side)":"browser (client-side only)"));
6700
+
6701
+ if(WebsocketImplementation.isNodeContext)
6702
+ return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
6703
+ else
6704
+ return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
6705
+ },
6706
+
6707
+
6708
+ //
6709
+ // NODE CLIENT
6710
+ //
6711
+ /*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
6712
+
6713
+ // NODE CLIENT MODE ONLY :
6714
+ let clientSocket;
6715
+ if(!WebsocketImplementation.useSocketIOImplementation){
6716
+
6717
+ // NEW : ws :
6718
+ if(typeof(WebSocket)==="undefined"){
6719
+ // TRACE
6720
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
6721
+ return null;
6722
+ }
6723
+
6724
+ clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{
6725
+ // CAUTION : SECURITY BREACH :
6726
+ // BUT ALSO NECESSARY TO ALLOW SELF-SIGNED CERTIFICATES USAGE WITH THE YESBOT SYSTEM !
6727
+ rejectUnauthorized:false, // (THIS IS A KNOWN SECURITY BREACH)
6728
+ secure: isSecure
6729
+ });
6730
+
6731
+ clientSocket.addEventListener("error", error=>{
6732
+ // TRACE
6733
+ lognow("ERROR : (NODEJS) A WebSocket client error occurred while trying to connect to server:", error.message);
6734
+ });
6735
+
6736
+ }else{
6737
+ // NOW : socket.io :
6738
+ //client on server-side:
6739
+
6740
+ if(WebsocketImplementation.isNodeContext && typeof(io)!=="undefined"){
6741
+
6742
+ // OLD SYNTAX: clientSocket=Socket.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6743
+ // NO : clientSocket=new Socket.Client(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6744
+ clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure, autoConnect:true});
6745
+ // UNUSEFUL (since we have the autoconnect:true option) : clientSocket.connect();
6746
+
6747
+ clientSocket.on("connect_error", error=>{
6748
+ // TRACE
6749
+ lognow("ERROR : (NODEJS) A SocketIO client error occurred while trying to connect to server:", error.message);
6750
+ });
6751
+
6752
+ }
6753
+ }
6754
+
6755
+ // DBG
6756
+ lognow("DEBUG : CLIENT : clientSocket created:");
6757
+
6758
+ const nodeClientInstance=new ClientInstance(clientSocket);
6759
+ return nodeClientInstance;
6760
+ },
6761
+
6762
+ //
6763
+ // BROWSER CLIENT
6764
+ //
6765
+
6766
+ /*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
6767
+
6768
+ // NEW : ws :
6769
+ if(typeof(WebSocket)==="undefined"){
6770
+ // TRACE
6771
+ lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
6772
+ return null;
6773
+ }
6774
+
6775
+ // TODO : FIXME : Use one single interface !
6776
+ // BROWSER CLIENT MODE ONLY :
6777
+ let clientSocket;
6778
+ if(!WebsocketImplementation.useSocketIOImplementation){
6779
+ // CAUTION : PARAMETER rejectUnauthorized:false WILL DO NOTHING,
6780
+ // BECAUSE THIS IS COMPLETLY HANDLED BY THE BROWSER SECURITY POLICY !
6781
+ // SO TO CLEAR THE SSL ERROR :
6782
+ // - FIRST GO TO THE HTTPS:// SERVER ADDRESS WITH BROWSER
6783
+ // - THEN ADD THE SECURITY EXCEPTION IN THE BROWSER !
6784
+ clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
6785
+
6786
+ clientSocket.addEventListener("error", error=>{
6787
+ // TRACE
6788
+ lognow("ERROR : (BROWSER) A WebSocket client error occurred while trying to connect to server:", error.message);
6789
+ });
6790
+
6791
+ }else if(typeof(io)!=="undefined"){
6792
+ // OLD SYNTAX :clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6793
+ // ALTERNATIVE :
6794
+ clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6795
+
6796
+ clientSocket.on("connect_error", error=>{
6797
+ // TRACE
6798
+ lognow("ERROR : (BROWSER) A SocketIO client error occurred while trying to connect to server:", error.message);
6799
+ });
6800
+
6801
+ }
6802
+
6803
+ // BROWSER CLIENT INSTANCE :
6804
+ const browserClientInstance=new ClientInstance(clientSocket);
6805
+ return browserClientInstance;
6806
+ },
6807
+
6808
+ };
6809
+
6810
+
6811
+
6812
+ launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
6813
+
6814
+ const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
6815
+
6816
+
6817
+
6818
+ if(typeof(https)==="undefined"){
6819
+ // TRACE
6820
+ console.log("«https» SERVER library not called yet, calling it now.");
6821
+ https=require("https");
6822
+ }
6823
+ if(typeof(http)==="undefined"){
6824
+ // TRACE
6825
+ console.log("«http» SERVER library not called yet, calling it now.");
6826
+ http=require("http");
6827
+ }
6828
+
6829
+
6830
+ const DEFAULT_HANDLER=function(request, response){
6831
+
6832
+ const url=request.url;
6833
+
6834
+ let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
6835
+ if(contains(url,excludedStr)){
6836
+ return true;
6837
+ }
6838
+ });
6839
+
6840
+ if(isURLInExclusionZone){
6841
+ // TRACE
6842
+ console.log("ERROR 403 forbidden access error :");
6843
+ console.log(error);
6844
+
6845
+ response.writeHead(403);
6846
+ response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
6847
+ response.end();
6848
+ return;
6849
+ }
6850
+
6851
+
6852
+
6853
+ const urlFile="." + url;
6854
+
6855
+ let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
6856
+ if(filePath=="./") filePath="./index.html";
6857
+
6858
+ let contentType="text/html";
6859
+
6860
+ const headers={ "Content-Type": contentType };
6861
+
6862
+ // 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)
6863
+ if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
6864
+
6865
+
6866
+ fs.readFile(filePath, function(error, fileContent){
6867
+ if(error){
6868
+ if(error.code=="ENOENT"){
6869
+ // TRACE
6870
+ console.log("ERROR 404 file not found :"+filePath);
6871
+
6872
+ fs.readFile("./404.html", function(error, fileContent){
6873
+ response.writeHead(200, headers);
6874
+ response.end(fileContent, "utf-8");
6875
+ });
6876
+
6877
+ }else {
6878
+
6879
+ // TRACE
6880
+ console.log("ERROR 500 server error :");
6881
+ console.log(error);
6882
+
6883
+ response.writeHead(500);
6884
+ response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
6885
+ response.end();
6886
+
6887
+ }
6888
+ }else {
6889
+
6890
+ // TRACE
6891
+ console.log("INFO 200 OK :"+filePath);
6892
+
6893
+
6894
+ response.writeHead(200, headers);
6895
+ response.end(fileContent, "utf-8");
6896
+
6897
+ // res.writeHead(200);
6898
+ // res.end("hello world\n");
6899
+ // res.sendFile(__dirname + "/public/index.html");
6900
+
6901
+ }
6902
+ });
6903
+
6904
+ };
6905
+
6906
+
6907
+ const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
6908
+
6909
+
6910
+
6911
+ let listenableServer;
6912
+ if(sslOptions){
6913
+ let httpsServer=https.createServer(sslOptions, handler).listen(port);
6914
+ // TRACE
6915
+ console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
6916
+ listenableServer=httpsServer;
6917
+ }else{
6918
+ let httpServer=http.createServer(handler).listen(port);
6919
+ // TRACE
6920
+ console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
6921
+ listenableServer=httpServer;
6922
+ }
6923
+
6924
+
6925
+ const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
6926
+
6927
+ // When a client connects, we execute the callback :
6928
+ // CAUTION : MUST BE CALLED ONLY ONCE !
6929
+ server.onConnectionToClient((serverParam, clientSocketParam)=>{
6930
+ if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
6931
+ });
6932
+
6933
+
6934
+ server.onFinalize((serverParam)=>{
6935
+
6936
+ // DBG
6937
+ lognow("onFinalize() server");
6938
+
6939
+ if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
6940
+ });
6941
+
6942
+
6943
+ // TRACE
6944
+ console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
6945
+
6946
+
6947
+
6948
+
6949
+
6950
+ return server;
6951
+ }
6952
+
6953
+
6954
+ initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null,
6955
+ /*OPTIONAL*/portParam,
6956
+ /*OPTIONAL*/certPathParam,
6957
+ /*OPTIONAL*/keyPathParam){
6958
+
6959
+ // TRACE
6960
+ console.log("Server launched.");
6961
+ console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
6962
+ console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
6963
+ // EXAMPLE : node orita-srv.js hash orita.global@keyHash 1234567890
6964
+ console.log("Server launched.");
6965
+
6966
+
6967
+ // We read the command-line arguments if needed :
6968
+
6969
+ let argCLPort;
6970
+ let argCLCertPath;
6971
+ let argCLKeyPath;
6972
+
6973
+ let serverConfig={};
6974
+ let isForceUnsecure;
6975
+
6976
+ let isHashAsked=false;
6977
+ let clearTextParam=null;
6978
+ let persisterId=null;
6979
+
6980
+
6981
+ process.argv.forEach(function (val, i){
6982
+ if(!val) return;
6983
+ // 0 corresponds to «node / nodejs»
6984
+ if(i<=1) return; // 1 corresponds to « <server.js> »
6985
+ else if(i==2){
6986
+ if(val==="hash") isHashAsked=true;
6987
+ }else if(i==3){
6988
+ if(!isHashAsked){
6989
+ try{
6990
+ const jsonConf=parseJSON(val);
6991
+ argCLPort=jsonConf.port;
6992
+ argCLCertPath=jsonConf.sslCertPath;
6993
+ argCLKeyPath=jsonConf.sslKeyPath;
6994
+ serverConfig=nonull(jsonConf.serverConfig,{});
6995
+ }catch(err1){
6996
+ lognow("WARN : Cannot parse argument JSON string «"+val+"».");
6997
+ }
6998
+ } else persisterId=val;
6999
+ }else if(i==4){
7000
+ if(isHashAsked) clearTextParam=val;
7001
+ }
7002
+ });
7003
+ isForceUnsecure=(argCLCertPath==="unsecure");
7004
+
7005
+
7006
+
7007
+
7008
+
7009
+ const aotraNodeServer={config:serverConfig};
7010
+ aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
7011
+
7012
+ if(isHashAsked){
7013
+ // We instanciate a temporary persister just to read the key hash file:
7014
+ const persister=getPersister("./");
7015
+ let persisterIdSplits=persisterId.split("@");
7016
+ if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
7017
+ // TRACE
7018
+ console.log("ERROR : No persister repository IDs provided correctly. Cannot read key hash. Aborting hash generation.");
7019
+ return aotraNodeServer;
7020
+ }
7021
+ const persisterClientId=persisterIdSplits[0];
7022
+ const persisterRepositoryName=persisterIdSplits[1];
7023
+ let globalKeyHashObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
7024
+ if(!globalKeyHashObject || !globalKeyHashObject.keyHash){
7025
+ // TRACE
7026
+ console.log("WARN : No key hash found. Generating one now.");
7027
+ globalKeyHashObject={keyHash:getUUID(), hashes:[]};
7028
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
7029
+ // TRACE
7030
+ console.log("INFO : Key hash generated and saved successfully.");
7031
+ });
7032
+ }
7033
+ const globalKeyHash=globalKeyHashObject.keyHash;
7034
+
7035
+ let firstHash=getHashedString(clearTextParam);
7036
+
7037
+ let generatedHash=getHashedString( firstHash + globalKeyHash, "SHA-256", true);// (we use the heavy treatment thing.)
7038
+ globalKeyHashObject.hashes.push(generatedHash);
7039
+
7040
+ // We update the repository :
7041
+ persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
7042
+ // TRACE
7043
+ console.log("INFO : Hash added to repository and saved successfully.");
7044
+ });
7045
+
7046
+ // OUTPUT
7047
+ console.log("Here is your key : share it with your main clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
7048
+
7049
+ return aotraNodeServer;
7050
+ }
7051
+
7052
+
7053
+ const DEFAULT_PORT=nonull(argCLPort,25000);
7054
+ const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
7055
+ const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
7056
+
7057
+
7058
+ let port=portParam ? portParam : DEFAULT_PORT;
7059
+ let certPath=null;
7060
+ let keyPath=null;
7061
+
7062
+ if(!isForceUnsecure){
7063
+ certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
7064
+ keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
7065
+ }
7066
+
7067
+
7068
+
7069
+
7070
+ // UNUSEFUL :
7071
+ //aotraNodeServer.serverManager.microClientsSockets=[];
7072
+ // UNUSEFUL :
7073
+ //aotraNodeServer.serverManager.mainClientsSockets=[];
7074
+
7075
+ aotraNodeServer.serverManager.start=function(){
7076
+
7077
+ // Eventual encryption options :
7078
+ let sslOptions=null;
7079
+ if(!isForceUnsecure){
7080
+ if(typeof(fs)==="undefined"){
7081
+ // TRACE
7082
+ lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
7083
+ }else{
7084
+ try{
7085
+ sslOptions={
7086
+ cert: fs.readFileSync(certPath, {encoding: "utf8"}),
7087
+ key: fs.readFileSync(keyPath, {encoding: "utf8"}),
7088
+ };
7089
+ }catch(exception){
7090
+ // TRACE
7091
+ lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
7092
+ }
7093
+ }
7094
+ }
7095
+
7096
+ aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
7097
+
7098
+
7099
+ return aotraNodeServer;
7100
+ };
7101
+
7102
+ return aotraNodeServer;
7103
+ }
7104
+
7105
+ // ========================= FUSRODA SERVER : =========================
7106
+
7107
+ //
7108
+ // DOES NOT WORK : USE Java FusrodaServer instead :
7109
+ //
7110
+ ///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
7111
+ //createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=4000){
7112
+ //
7113
+ // // https://www.npmjs.com/package/screenshot-desktop
7114
+ // // https://github.com/octalmage/robotjs
7115
+ // // npm install --save screenshot-desktop
7116
+ // //
7117
+ // // sudo apt-get install libxtst-dev libx11-dev
7118
+ // // npm install --save robotjs
7119
+ //
7120
+ // // http://getrobot.net/docs/usage.html
7121
+ // //
7122
+ // // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
7123
+ //
7124
+ // const screenshot=require("screenshot-desktop");
7125
+ //
7126
+ // const REFRESH_SCREENSHOTS_MILLIS=500;
7127
+ //
7128
+ //
7129
+ // const server=initNodeServerInfrastructureWrapper(
7130
+ // // On each client connection :
7131
+ // // (serverParam, clientSocketParam)=>{},
7132
+ // null,
7133
+ // // On server finalization :
7134
+ // (serverParam)=>{
7135
+ //
7136
+ // serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
7137
+ // serverParam.addToRoom(clientSocket,"clients");
7138
+ // });
7139
+ //
7140
+ //
7141
+ // serverParam.sendScreenshotsRoutine=setInterval(()=>{
7142
+ // if(serverParam.isScreenshotStarted) return;
7143
+ //
7144
+ // serverParam.isScreenshotStarted=true;
7145
+ //
7146
+ // screenshot().then((img) => {
7147
+ //
7148
+ // const data={image:img,listenerMessageType:"imageData"};
7149
+ // serverParam.send("message", data, "clients");
7150
+ //
7151
+ // serverParam.isScreenshotStarted=false;
7152
+ //
7153
+ // }).catch((error) => {
7154
+ // // TRACE
7155
+ // lognow("ERROR : Error during screenshot :",error);
7156
+ // });
7157
+ //
7158
+ // },REFRESH_SCREENSHOTS_MILLIS);
7159
+ //
7160
+ //
7161
+ // },portParam,certPathParam,keyPathParam);
7162
+ //
7163
+ //
7164
+ // // const doOnConnect=(serverParam, clientSocketParam)=>{
7165
+ // // };
7166
+ // // const doOnFinalizeServer=(serverParam)=>{
7167
+ // // /*DO NOTHING*/
7168
+ // // };
7169
+ // // const server={};
7170
+ // // server.start=(port=4000)=>{
7171
+ // // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
7172
+ // // };
7173
+ //
7174
+ // return server;
7175
+ //}
7176
+
7177
+
7178
+ // ========================= UTILITY SERVERSIDE METHODS : =========================
7179
+
7180
+ class ListManager{
7181
+
7182
+ constructor(config){
7183
+ this.config=config;
7184
+ this.maxItemsNumber=nonull(this.config.max,999);
7185
+ this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
7186
+ this.sessionDurationSeconds=nonull(this.config.duration,null);
7187
+ this.mode=nonull(this.config.mode,"startAtConnexion");
7188
+
7189
+ this.itemsInfos={};
7190
+ this.time=null;
7191
+ this.started=false;
7192
+ }
7193
+
7194
+ addItem(id,item){
7195
+ if(id==null){
7196
+ // TRACE
7197
+ lognow("ERROR : Cannot add item with no id.");
7198
+ return;
7199
+ }
7200
+ if(!item){
7201
+ // TRACE
7202
+ lognow("ERROR : Cannot add null item.");
7203
+ return;
7204
+ }
7205
+ const numberOfItemsCurrently=getArraySize(this.itemsInfos);
7206
+
7207
+ // DBG
7208
+ lognow(">>>>>>>>>numberOfItemsCurrently:",numberOfItemsCurrently);
7209
+ lognow(">>>>>>>>>Object.keys(arrayOfValues):",Object.keys(this.itemsInfos));
7210
+
7211
+
7212
+ if(this.maxItemsNumber<=numberOfItemsCurrently){
7213
+ // TRACE
7214
+ lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
7215
+ return;
7216
+ }
7217
+
7218
+
7219
+ if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
7220
+
7221
+ // DBG
7222
+ lognow(">>>>>>>>>START SESSION !!!!");
7223
+
7224
+ this.startSession();
7225
+ }
7226
+ this.itemsInfos[id]={
7227
+ item:item,
7228
+ time:getNow(),
7229
+ };
7230
+ }
7231
+
7232
+ startSession(){
7233
+ this.time=getNow();
7234
+ this.started=true;
7235
+ }
7236
+
7237
+ stopSession(){
7238
+ this.started=false;
7239
+ }
7240
+
7241
+ isSessionActive(){
7242
+
7243
+ // DBG
7244
+ lognow(" !!! this.sessionDurationSeconds : "+this.sessionDurationSeconds);
7245
+ lognow(" !!! this.started : "+this.started);
7246
+ lognow(" !!! this.time : "+this.time);
7247
+
7248
+
7249
+ if(!this.sessionDurationSeconds) return true;
7250
+ if(!this.started) return false;
7251
+ const result=!hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
7252
+
7253
+ // DBG
7254
+ lognow(" !!! HAS DELAY PASSED : "+result);
7255
+
7256
+ return result;
7257
+ }
7258
+
7259
+ isClientActive(clientId){
7260
+
7261
+ // DBG
7262
+ lognow(" this.isSessionActive()"+this.isSessionActive());
7263
+
7264
+ if(!this.isSessionActive()) return false;
7265
+ const clientPosition=this.getItemPosition(clientId);
7266
+ //CAUTION : Client position starts at 1 !
7267
+ const clientIndex=(clientPosition-1);
7268
+ const result=(clientIndex<=this.maxItemsNumber && clientIndex<=this.simultaneousItemsNumber);
7269
+
7270
+ return result;
7271
+ }
7272
+
7273
+ removeItemById(id){
7274
+ if(id==null){
7275
+ // TRACE
7276
+ lognow("ERROR : Cannot remove item, no id specified.");
7277
+ return;
7278
+ }
7279
+ if(!this.itemsInfos[id]){
7280
+ // TRACE
7281
+ lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
7282
+ return;
7283
+ }
7284
+ delete this.itemsInfos[id];
7285
+ }
7286
+
7287
+ removeItem(item){
7288
+ if(item==null){
7289
+ // TRACE
7290
+ lognow("ERROR : Cannot remove item, none specified.");
7291
+ return;
7292
+ }
7293
+
7294
+ let id=null;
7295
+ foreach(this.itemsInfos,(itemInfos,key)=>{
7296
+ if(itemInfos.item===item
7297
+ // DEBUG ONLY :
7298
+ || (itemInfos.item.id && item.id && itemInfos.item.id===item.id)
7299
+ ){
7300
+ id=key;
7301
+ return "break";
7302
+ }
7303
+
7304
+ });
7305
+
7306
+ if(!id){
7307
+ // TRACE
7308
+ lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
7309
+ return;
7310
+ }
7311
+
7312
+ this.removeItemById(id);
7313
+ }
7314
+
7315
+ // Goes from 1 to <length>:
7316
+ getItemPosition(id){
7317
+ if(id==null){
7318
+ // TRACE
7319
+ lognow("ERROR : Cannot calculate item position, no id specified.");
7320
+ return null;
7321
+ }
7322
+ if(!this.itemsInfos[id]){
7323
+ // TRACE
7324
+ lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
7325
+ return null;
7326
+ }
7327
+ let result=0;
7328
+ foreach(this.itemsInfos, (itemInfo, key)=>{
7329
+ result++;
7330
+ if(id===key) return "break";
7331
+ },null,(item1, item2)=>item1.time<item2.time);
7332
+ return result;
7333
+ }
7334
+
7335
+ getItemsNumber(){
7336
+ return getArraySize(this.itemsInfos);
7337
+ }
7338
+
7339
+ };
7340
+
7341
+ getListManager=function(config){
7342
+ return new ListManager(config);
7343
+ };
7344
+
7345
+
7346
+ // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
7347
+ // NO : IN A NODE CONTEXT WITH require("...") WILL RESULT IN AN UNDEFINED FUNCTION ERROR !!!
7348
+ // function performHTTPRequest(...){...
7349
+ // USE THIS INSTEAD :
7350
+ performHTTPRequest=function(completeURL, httpMethod="GET", headers={}, requestBodyOrNamedArgs=null, isNodeContext=false, addCORSHeader=ADD_CORS_HEADER){
7351
+
7352
+ // Body
7353
+ let requestBodyOrNamedArgsStr=null;
7354
+ if(requestBodyOrNamedArgs){
7355
+ if(isString(requestBodyOrNamedArgs) && !isObject(requestBodyOrNamedArgs)){
7356
+ // If it's already a string, we assume it's a valid JSOn string already :
7357
+ requestBodyOrNamedArgsStr=requestBodyOrNamedArgs.replace(/[\r\n]+/gim, ""); // We remove all the breaklines
7358
+ }else{
7359
+ try{
7360
+ requestBodyOrNamedArgsStr=stringifyObject(requestBodyOrNamedArgs).replace(/[\r\n]+/gim, ""); // We remove all the breaklines
7361
+ }catch(parseErr){
7362
+ requestBodyOrNamedArgsStr=""+requestBodyOrNamedArgs;
7363
+ }
7364
+ }
7365
+ }
7366
+
7367
+ let body=null;
7368
+ // Not the same way to send parameters, with POST/PUT http method :
7369
+ if(contains(["POST","PUT"],httpMethod) && requestBodyOrNamedArgs){
7370
+ body=requestBodyOrNamedArgsStr;
7371
+ }
7372
+
7373
+ // Headers
7374
+ if(contains(["POST","PUT"],httpMethod)){
7375
+ headers["Content-Type"]="application/json";
7376
+
7377
+ if(requestBodyOrNamedArgsStr && isString(body)){
7378
+ headers["Content-Length"]=Buffer.byteLength(requestBodyOrNamedArgsStr);
7379
+ }
7380
+
7381
+ // 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)
7382
+ if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
7383
+
7384
+ } else if(httpMethod==="GET" && requestBodyOrNamedArgs){
7385
+ // Not the same way to send parameters in GET http method :
7386
+ // DBG
7387
+ lognow("unformatted API URL : "+completeURL);
7388
+
7389
+ completeURL=appendGetParameters(completeURL, requestBodyOrNamedArgs);
7390
+
7391
+ // DBG
7392
+ lognow("formatted API URL : "+completeURL);
7393
+ }
7394
+
7395
+ // CASE BROWSER CONTEXT :
7396
+ if(!isNodeContext || typeof(require)=="undefined"){
7397
+ // TRACE
7398
+ lognow("INFO : We are not running in a browser context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using browser library.");
7399
+
7400
+ return new Promise((resolve,reject)=>{
7401
+ fetch(completeURL, {
7402
+ method: httpMethod,
7403
+ headers: headers,
7404
+ body: body,
7405
+ })
7406
+ // STRANGE : DOES NOT WORK !!:
7407
+ //.then(response => response.json())
7408
+ // STRANGE : DOES WORK :
7409
+ .then(response => {
7410
+
7411
+ // DBG
7412
+ console.log("~~~~~~~~~~~response :",response);
7413
+
7414
+ return response.json();
7415
+ }).then(data => {
7416
+
7417
+ // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
7418
+ const status=data.status;
7419
+ // if(status!=200 && data.error){
7420
+ // // const error=nonull(data.error, data.detail);
7421
+ // const error=data.error;
7422
+ // // TRACE
7423
+ // console.error("Error:", error);
7424
+ // reject({error:""+error, httpStatus:status});
7425
+ // return;
7426
+ // }
7427
+
7428
+ // DBG
7429
+ console.log("~~~~~~~~~~~data :",data);
7430
+
7431
+ data.httpStatus=status;
7432
+ resolve(data);
7433
+ }).catch(error => {
7434
+ // TRACE
7435
+ console.error("Error:", error);
7436
+ reject({error:`${error}`, httpStatus:null});
7437
+ });
7438
+ });
7439
+ }// else :
7440
+
7441
+ // CASE NODEJS CONTEXT :
7442
+
7443
+ // TRACE
7444
+ lognow("INFO : We are running in a nodejs context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using nodejs library.");
7445
+
7446
+
7447
+ const isSecure=(!empty(completeURL) && contains(completeURL.toLowerCase(),"https://"));
7448
+ const httpHandler=isSecure?require("https"):require("http");
7449
+
7450
+ // Options for the HTTP request
7451
+ const options = {
7452
+ url: completeURL,
7453
+ method: httpMethod,
7454
+ };
7455
+
7456
+ if(contains(["POST","PUT"],httpMethod)){
7457
+ options.json=true;
7458
+ }
7459
+
7460
+ options.headers=headers;
7461
+
7462
+ return new Promise((resolve,reject)=>{
7463
+
7464
+ // Create the HTTP request
7465
+ // DOES NOT WORK : const request = httpHandler.request(options, (response) => {
7466
+ // UNLESS YOU SPECIFY in options : hostname, port, path
7467
+ const request = httpHandler.request(completeURL, options, (response) => {
7468
+
7469
+ let responseDataStr = "";
7470
+
7471
+ // A chunk of data has been received.
7472
+ response.on("data", (chunk) => {
7473
+ responseDataStr += chunk;
7474
+ // // DEBUG ONLY
7475
+ // const str = Buffer.from(chunk).toString("utf8");
7476
+ // lognow(">>>str:"+str);
7477
+ });
7478
+
7479
+ // The whole response has been received.
7480
+ response.on("end", () => {
7481
+
7482
+ try{
7483
+ let responseData;
7484
+ try{
7485
+ responseData=parseJSON(responseDataStr);
7486
+ }catch(parseError1){
7487
+ // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
7488
+ // DEBUG
7489
+ responseDataStr=responseDataStr.replace(/'/gim,"`");
7490
+ try{
7491
+ responseData=parseJSON(responseDataStr);
7492
+ }catch(parseError2){
7493
+ // DBG
7494
+ lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError2);
7495
+ resolve( {response:response, responseDataStr:responseDataStr} );
7496
+ return;
7497
+ }
7498
+ }
7499
+
7500
+ // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
7501
+ const status=response.statusCode;
7502
+ // if(status!=200){
7503
+ // const error=nonull(response.statusMessage, "No error message for error code "+status);
7504
+ // // TRACE
7505
+ // console.error("Error:", error);
7506
+ // reject({error:""+error, httpStatus:status});
7507
+ // return;
7508
+ // }
7509
+
7510
+ resolve( {responseData:responseData, response:response, responseDataStr:responseDataStr, httpStatus: status} );
7511
+ }catch(parseError3){
7512
+ // DBG
7513
+ lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError3);
7514
+ resolve( {response:response, responseDataStr:responseDataStr} );
7515
+ return;
7516
+ }
7517
+
7518
+ });
7519
+ });
7520
+
7521
+ // Handle errors
7522
+ request.on("error", (error) => {
7523
+ reject({error:error, httpStatus:null});
7524
+ });
7525
+
7526
+ // Not the same way to send parameters, with POST/PUT http method :
7527
+ if(contains(["POST","PUT"],httpMethod) && body){
7528
+ request.write(body);
7529
+ }
7530
+
7531
+ // End the request
7532
+ request.end();
7533
+
7534
+ });
7535
+
7536
+ };
7537
+
7538
+
7539
+
7540
+
7541
+ replacePathVariablesNamesWithValuesIfPossible=function(apiURL, namedArgs){
7542
+ let result=apiURL;
7543
+ foreach(namedArgs,(namedArgValue, namedArgKey)=>{
7544
+ result=result.replace("{"+namedArgKey+"}",namedArgValue);
7545
+ });
7546
+ return result;
7547
+ };
7548
+
7549
+ appendGetParameters=function(apiURL, namedArgsParam){
7550
+ if(nothing(namedArgsParam)) return "";
7551
+ try{
7552
+
7553
+ const namedArgs=isString(namedArgsParam)?parseJSON(namedArgsParam):namedArgsParam;
7554
+
7555
+ let result=apiURL;
7556
+
7557
+ const paramCouples=[];
7558
+ foreach(namedArgs,(value,key)=>{paramCouples.push(key+"="+value);});
7559
+
7560
+ if(!empty(paramCouples)) result+=("?"+paramCouples.join("&"));
7561
+
7562
+ return result;
7563
+ }catch(parseError){
7564
+ // TRACE
7565
+ lognow("ERROR : Could not parse string parameters object «"+namedArgsParam+"», aborting GET parameter request string calculation:", parseError);
7566
+ return "";
7567
+ }
7568
+ };
7569
+
7570
+
7571
+
7572
+
7573
+
7574
+
7575
+
7576
+
6231
7577
  /* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.
6232
7578
  *
6233
7579
  * This set of methods gathers utility generic-purpose methods usable in any JS project.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aotrautils-srv",
3
- "version": "0.0.1827",
3
+ "version": "0.0.1829",
4
4
  "main": "aotrautils-srv.build.js",
5
5
  "description": "A library for vanilla javascript utils (server-side) used in aotra javascript CMS",
6
6
  "author": "Jeremie Ratomposon <info@alqemia.com> (https://alqemia.com)",