aotrautils-srv 0.0.1808 → 0.0.1810

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 (01/03/2026-21:47:30)»*/
3
+ /*utils COMMONS library associated with aotra version : «1_29072022-2359 (14/03/2026-03:54:12)»*/
4
4
  /*-----------------------------------------------------------------------------*/
5
5
 
6
6
 
@@ -1631,10 +1631,11 @@ window.PeriodicalExecuter=class PeriodicalExecuter{
1631
1631
  //================================================================
1632
1632
 
1633
1633
  // Node dependencies :
1634
- if(typeof(require)!="undefined" && typeof(sjcl)=="undefined")
1635
- sjcl=require("sjcl");
1636
-
1637
- ////
1634
+ if(typeof(require)!=="undefined" && typeof(sjcl)==="undefined"){
1635
+ window.sjcl=require("sjcl");
1636
+ }
1637
+ // For debug (WORKAROUND):
1638
+ sjcl=window.sjcl;
1638
1639
 
1639
1640
 
1640
1641
  // NOT AOTESTABLE !
@@ -5075,7 +5076,7 @@ AOTRAUTILS_LIB_IS_LOADED=true;
5075
5076
 
5076
5077
 
5077
5078
 
5078
- /*utils AI library associated with aotra version : «1_29072022-2359 (01/03/2026-21:47:30)»*/
5079
+ /*utils AI library associated with aotra version : «1_29072022-2359 (14/03/2026-03:54:12)»*/
5079
5080
  /*-----------------------------------------------------------------------------*/
5080
5081
 
5081
5082
 
@@ -5221,7 +5222,7 @@ getOpenAIAPIClient=(modelName, apiURL, agentRole, defaultPrompt)=>{
5221
5222
 
5222
5223
 
5223
5224
 
5224
- /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (01/03/2026-21:47:30)»*/
5225
+ /*utils CONSOLE library associated with aotra version : «1_29072022-2359 (14/03/2026-03:54:12)»*/
5225
5226
  /*-----------------------------------------------------------------------------*/
5226
5227
 
5227
5228
 
@@ -5317,6 +5318,9 @@ class AORTACServerCell{
5317
5318
 
5318
5319
  this.handleCommonListeners();
5319
5320
 
5321
+ // TRACE
5322
+ lognow("AORTAC Server cell started on URL : ",this.selfOrigin);
5323
+
5320
5324
  return this;
5321
5325
  }
5322
5326
 
@@ -5892,7 +5896,7 @@ window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model,
5892
5896
 
5893
5897
 
5894
5898
 
5895
- /* ## Utility global methods in a javascript, console (nodejs) or vanilla javascript with no browser environment.
5899
+ /* ## Utility methods in a javascript, for internationalzation (i18n) subsystem (server & client)
5896
5900
  *
5897
5901
  * This set of methods gathers utility generic-purpose methods usable in any JS project.
5898
5902
  * Several authors of snippets published freely on the Internet contributed to this library.
@@ -5915,1301 +5919,309 @@ window.getAORTACClient=function(serverNodeOrigin="ws://127.0.0.1:40000", model,
5915
5919
  if(typeof(window)==="undefined") window=global;
5916
5920
 
5917
5921
 
5922
+ // ==================================================================================================================
5918
5923
 
5919
5924
 
5920
- // OLD : socket.io :
5921
- // https://stackoverflow.com/questions/31156884/how-to-use-https-on-node-js-using-express-socket-io
5922
- // https://stackoverflow.com/questions/6599470/node-js-socket-io-with-ssl
5923
- // https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener
5924
- // https://socket.io/docs/v4/client-socket-instance/
5925
-
5926
- // NEW : ws :
5927
- // https://github.com/websockets/ws#installing
5928
- // https://github.com/websockets/ws/blob/master/doc/ws.md#event-message
5929
- // ON BROWSER SIDE : Native Websockets :
5930
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
5931
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
5932
-
5933
- // We have to import both implementations, regardless of which one is chosen :
5934
- //Socket=require("socket.io");
5935
- //WebSocket=require("ws");
5936
-
5937
-
5938
-
5939
-
5940
- // =================================================================================
5941
- // NODEJS UTILS
5942
-
5943
- const FILE_ENCODING="utf8";
5944
-
5945
- const ADD_CORS_HEADER=true;
5946
-
5947
-
5948
-
5949
- if(typeof(require)!=="undefined" && typeof(fs)==="undefined" ){
5950
- window.fs=require("fs");
5951
- }
5952
5925
 
5953
- fs=window.fs;
5954
5926
 
5955
- // Nodejs filesystem utils :
5956
- if(typeof(fs)==="undefined"){
5957
- // TRACE
5958
- console.log("WARN : Could not find the nodejs dependency «fs», aborting persister setup.");
5959
- getPersister=()=>{ return null; };
5927
+ // A little method to help i18ning aotra user interface :
5928
+ const I18N_REPOSITORY_VARIABLE_NAME="I18N_KEYS";
5929
+ window.i18n=(languagesAndStrings, langParam=null)=>{
5960
5930
 
5961
- }else{
5931
+ // ******************* CONFIG *******************
5932
+ const PREFERED_LANGUAGE="en";
5962
5933
 
5934
+ const NO_TRANSLATION_LABEL="NO TRANSLATION FOUND";
5963
5935
 
5964
- getPersister=function(dataDirPath,prefix=""){
5965
-
5966
- let self={
5967
-
5968
- dataDirPath:dataDirPath,
5969
- prefix:prefix,
5970
-
5971
- //FILE_NAME_PATTERN:"data.clientId.repositoryName.json",
5972
- /*private*/getPath:function(clientId="noclient", repositoryName="norepository"){
5973
- // let path=self.FILE_NAME_PATTERN.replace(new RegExp("@clientId@","g"),clientId);
5974
- let path=`${self.dataDirPath}`
5975
- + (blank(self.prefix)?"":(self.prefix+"."))
5976
- +`${clientId}.${repositoryName}.json`;
5977
- return path;
5978
- },
5979
-
5980
- readTreeObjectFromFile:function(clientId="noclient", repositoryName="norepository", keepClassName=false){
5981
-
5982
- let path=self.getPath(clientId,repositoryName);
5983
- let resultFlat=null;
5984
-
5985
- try{
5936
+ const ADD_SYSTEMATICALLY_TO_REPOSITORY=true;
5937
+ const MAX_CHARACTERS_FOR_TRANSLATION_KEY=64; // Really if you have more than 64 characters in commons, then it really should be the same string !
5986
5938
 
5987
- resultFlat=fs.readFileSync(path, FILE_ENCODING);
5988
-
5989
- }catch(error){
5990
- // TRACE
5991
- console.log("ERROR : Could not read file «"+path+"».");
5992
-
5993
- return null;
5994
- }
5995
-
5996
-
5997
- let resultData={};
5998
-
5999
- // 1)
6000
- if(!empty(resultFlat)) resultData=parseJSON(resultFlat);
6001
-
6002
- // 2)
6003
- if(!empty(resultData) && isFlatMap(resultData)){
6004
- // CAUTION : We have to keep the type information, here too ! (in the sub-objects)
6005
- resultData=getAsTreeStructure(resultData, keepClassName);
6006
- }
6007
-
6008
- return resultData;
6009
- },
5939
+ // ***************** END CONFIG *****************
6010
5940
 
6011
-
6012
- saveDataToFileForClient:function(clientId,repositoryName,dataFlatForClient,forceKeepUnflatten=false,doOnSuccess=null){
6013
-
6014
- if(!empty(dataFlatForClient) && !isFlatMap(dataFlatForClient) && !forceKeepUnflatten){
6015
- dataFlatForClient=getAsFlatStructure(dataFlatForClient);
6016
- }
6017
-
6018
- // reserved characters : -/\^$*+?.()|[]{}
6019
- // CANNOT USE stringifyObject(...) function because we are in a common, lower-level library !
6020
- let dataFlatStr=stringifyObject(dataFlatForClient)
6021
- // We «aerate» the produced JSON :
6022
- .replace(/":[\w]*\{/gim,"\":{\n").replace(/,"/gim,",\n\"")
6023
- // ...except for inline, escaped JSON string representations :
6024
- .replace(/\\\":[\w]*\{\n/gim,"\\\":{");
6025
- // NO : .replace(/}/gim,"}\n");
6026
-
6027
-
6028
- let path=self.getPath(clientId,repositoryName);
6029
- fs.writeFile(path, dataFlatStr, FILE_ENCODING, (error) => {
6030
- if(error){
6031
- // TRACE
6032
- console.log("ERROR : Could not write file «"+path+"»:",error);
6033
- throw error;
6034
- }
6035
- if(doOnSuccess) doOnSuccess(dataFlatForClient);
6036
- });
6037
- }
6038
-
6039
- };
6040
-
6041
-
6042
- return self;
6043
- };
6044
5941
 
6045
- }
6046
-
6047
-
6048
- window.fileExists=(filePath)=>{
6049
- if(typeof(fs)=="undefined"){
5942
+ if(!languagesAndStrings){
6050
5943
  // TRACE
6051
- lognow("ERROR : «fs» node dependency is not available ! Cannot test if file exists.");
6052
- return null;
5944
+ console.log("ERROR : No parameter for i18n provided!",languagesAndStrings);
5945
+ return NO_TRANSLATION_LABEL;
6053
5946
  }
6054
- return fs.existsSync(filePath);
6055
- };
6056
-
6057
-
6058
-
6059
- // Nodejs server launching helper functions :
6060
- //Networking management :
6061
- //- WEBSOCKETS AND NODEJS :
6062
-
6063
- function isConnected(clientSocket){
6064
- if(!WebsocketImplementation.useSocketIOImplementation)
6065
- return (clientSocket.readyState===WebSocket.OPEN)
6066
- return (clientSocket.connected);
6067
- }
6068
-
6069
-
6070
-
6071
- // -Server :
6072
-
6073
- getConsoleServerParams=function(portParam=null, certPathParam=null, keyPathParam=null, argsOffset=0, ignoreConsoleArgs=false){
6074
5947
 
6075
- // Node dependencies :
6076
- // https=require("https");
6077
- // fs=require("fs");
6078
-
6079
- if(typeof(https)==="undefined"){
6080
- // TRACE
6081
- console.log("WARN : Could not find the nodejs dependency «https», aborting SSL setup.");
6082
- return null;
5948
+ let lang=null;
5949
+ if(langParam){
5950
+ lang=langParam;
5951
+ }else{
5952
+ // Parameters priority :
5953
+ if(window){
5954
+ if(window.getURLParameter){
5955
+ lang=nonull(window.getURLParameter("language"), window.getURLParameter("lang"));
5956
+ }
5957
+ if(!lang && (window.language || window.lang)){
5958
+ lang=nonull(window.language, window.lang);
5959
+ }
5960
+ }else if(global){ // (NodeJS compatibility)
5961
+ if(global.getURLParameter){
5962
+ lang=nonull(global.getURLParameter("language"), global.getURLParameter("lang"));
5963
+ }
5964
+ if(!lang && (global.language || global.lang)){
5965
+ lang=nonull(global.language, global.lang);
5966
+ }
5967
+ }
5968
+ lang=nonull(lang, PREFERED_LANGUAGE);
6083
5969
  }
6084
- if(typeof(fs)==="undefined"){
5970
+
5971
+ if(contains(["capitalize","append"],lang)){
6085
5972
  // TRACE
6086
- console.log("WARN : Could not find the nodejs dependency «fs», aborting SSL setup.");
6087
- return null;
5973
+ console.log("WARN : Language parameter has for value a reserved attribute keyword, using default language «"+PREFERED_LANGUAGE+"»");
5974
+ lang=PREFERED_LANGUAGE;
6088
5975
  }
6089
-
6090
- const result={};
6091
5976
 
6092
- // We read the command-line arguments if needed :
6093
- const argCLPort=getConsoleParam(1,argsOffset);
6094
- const argCLCertPath=getConsoleParam(2,argsOffset);
6095
- const argCLKeyPath=getConsoleParam(3,argsOffset);
6096
5977
 
6097
- // Console, command-line arguments OVERRIDE parameters values :
5978
+ let result=null;
6098
5979
 
6099
- result.port=(ignoreConsoleArgs?portParam:nonull(argCLPort,portParam));
6100
- result.certPath=null;
6101
- result.keyPath=null;
6102
- result.isSecure=!!(certPathParam || keyPathParam);
6103
5980
 
6104
- if(result.isSecure){
6105
- result.certPath=(ignoreConsoleArgs?certPathParam:nonull(argCLCertPath,certPathParam));
6106
- result.keyPath=(ignoreConsoleArgs?keyPathParam:nonull(argCLKeyPath,keyPathParam));
6107
- }
5981
+ if(!isString(languagesAndStrings)){ // Case direct i18n translation, with no translation keys :
5982
+
5983
+ result=languagesAndStrings[lang];
6108
5984
 
6109
- // Eventual encryption options :
6110
- result.sslOptions=null;
6111
- if(result.isSecure){
6112
- result.sslOptions={
6113
- cert: fs.readFileSync(result.certPath),
6114
- key: fs.readFileSync(result.keyPath),
6115
- };
6116
- }
5985
+ if (!result){
5986
+ // TRACE
5987
+ console.log("ERROR : No label found for language «"+lang+"»!",languagesAndStrings);
5988
+ }
6117
5989
 
6118
- return result;
6119
- }
6120
-
6121
- getConsoleParam=function(index=0, argsOffset=0){
6122
- let result=null;
6123
- if(!process){
6124
- throw new Error("ERROR : Cannot extract console parameter in this context !");
6125
- }
6126
- process.argv.forEach((val, i)=>{
6127
- if(i<=argsOffset+1) return;
6128
- else if(i==argsOffset+index+1) result=val;
6129
- });
6130
- return result;
6131
- }
6132
-
6133
-
5990
+ if (!result){
5991
+ for(key in languagesAndStrings){
5992
+ if (!languagesAndStrings.hasOwnProperty(key))
5993
+ continue;
5994
+ // We take the first that we find, in the other languages:
5995
+ result=languagesAndStrings[key];
5996
+ break;
5997
+ }
5998
+ }
6134
5999
 
6135
- window.getConsoleCLI=(doOnCommands={"makeSandiwch":()=>{}}, promptText="Enter command> ")=>{
6000
+ if (!result) return NO_TRANSLATION_LABEL;
6001
+
6002
+
6003
+ // We add this «rogue» usage of i18n to the whole repository at all useful ends :
6004
+ if(ADD_SYSTEMATICALLY_TO_REPOSITORY){
6005
+ // let declarable={};
6006
+ let maxLength=Math.min(result.length, MAX_CHARACTERS_FOR_TRANSLATION_KEY);
6007
+ let key=toConvention("camel", result.substring(0,maxLength).toLowerCase() );
6008
+ // let calculatedKey=radicalKey+"_"+getUUID("short");
6009
+ // declarable[calculatedKey]=languagesAndStrings;
6010
+ i18n.declareSingleEntry(key, languagesAndStrings);
6011
+ }
6136
6012
 
6137
- const readline = require("node:readline");
6013
+ // Eventual post-treatments :
6014
+ if(languagesAndStrings.capitalize) result=capitalize(result);
6015
+ if(languagesAndStrings.append) result+=languagesAndStrings.append;
6138
6016
 
6139
- const cliInterface = readline.createInterface({
6140
- input: process.stdin,
6141
- output: process.stdout,
6142
- prompt: nonoull(promptText,"Enter command> ")
6143
- });
6017
+ return result;
6018
+ }
6019
+ // else // Case we use translation keys : (here, languagesAndStrings parameter holds just the wished translation key)
6144
6020
 
6145
- cliInterface.prompt();
6146
-
6147
- cliInterface.on("line", (line) => {
6148
- const input = line.trim();
6149
- switch (input) {
6150
- case "quit":
6151
- console.log("Bye!");
6152
- cliInterface.close();
6153
- break;
6154
- case "help":
6155
- console.log("Available commands: quit, help and :", Object.keys(doOnCommands));
6156
- break;
6157
- default:
6158
- if(doOnCommands) doOnCommands[input]();
6159
- break;
6160
- }
6161
-
6162
- // Show the prompt again :
6163
- cliInterface.prompt();
6164
-
6165
- }).on("close", () => {
6166
- process.exit(0);
6167
- });
6168
6021
 
6169
- return cliInterface
6022
+ let repository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
6023
+ if(!repository){
6024
+ // TRACE
6025
+ console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
6026
+ return NO_TRANSLATION_LABEL;
6027
+ }
6028
+
6029
+ // OLD (ie. splitted format :)
6030
+ // let repositoryForLang=repository[lang];
6031
+ // if (!repositoryForLang){
6032
+ // // TRACE
6033
+ // console.log("ERROR : No translation found for language «"+lang+"»!",languagesAndStrings);
6034
+ // return NO_TRANSLATION_LABEL;
6035
+ // }
6036
+ // result=repositoryForLang[languagesAndStrings]; // Here, languagesAndStrings is just a bundle key.
6037
+ // if (!result) return NO_TRANSLATION_LABEL+" (for key «"+languagesAndStrings+"»)";
6038
+
6039
+ let languagesAndStringsForTranslationKey=repository[languagesAndStrings];
6040
+ if (!languagesAndStringsForTranslationKey){
6041
+ // SILENT ERROR :
6042
+ // // TRACE
6043
+ // console.log("ERROR : No translation found for key «"+languagesAndStrings+"» for language «"+lang+"»!");
6044
+ // NO, OLD : return NO_TRANSLATION_LABEL;
6045
+ return languagesAndStrings;
6046
+ }
6047
+ result=i18n(languagesAndStringsForTranslationKey, lang); // Since we use the same format !
6048
+
6049
+ return result;
6170
6050
  };
6171
6051
 
6172
6052
 
6053
+ i18n.declareSingleEntry=function(key, languagesAndStringsEntry){
6054
+ let newRepository;
6055
+ let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
6056
+ if(!foundRepository){
6057
+ // TRACE
6058
+ console.log("ERROR : No repository variable found for repository «"+I18N_REPOSITORY_VARIABLE_NAME+"»!");
6059
+ return;
6060
+ }
6061
+
6062
+ let foundEntryForKey=foundRepository[key];
6063
+ if(!foundEntryForKey){
6064
+ foundRepository[key]=languagesAndStringsEntry;
6065
+ // }else{
6066
+ // // TRACE
6067
+ // console.log("WARN : Duplicate entry for translation key «"+key+"», keeping only the first declared :",foundEntryForKey);
6068
+ }
6069
+
6070
+ };
6173
6071
 
6174
6072
 
6073
+ //USAGE :
6074
+ //Example :
6075
+ //i18n.declare({
6076
+ // "commencer":{"fr":"Commencer","en":"Start"}
6077
+ //});
6078
+ i18n.declare=function(repositoryInfos){
6079
+ let newRepository;
6080
+ let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
6081
+ if(foundRepository){
6082
+ // // TRACE
6083
+ // console.log("WARN : A repository has already been declared, merging.",foundRepository);
6084
+ newRepository=merge(foundRepository, repositoryInfos);
6085
+ }else{
6086
+ newRepository=repositoryInfos;
6087
+ }
6088
+ (window?window:global)[I18N_REPOSITORY_VARIABLE_NAME]=newRepository;
6089
+ };
6175
6090
 
6176
- // NODE ONLY SERVER / CLIENTS :
6177
- WebsocketImplementation={
6178
-
6179
-
6180
- isNodeContext:true,
6181
- useSocketIOImplementation:false,
6182
- useFlatStrings:false,
6183
-
6184
- // COMMON METHODS
6185
- /*private static*/isInRoom(clientSocket, clientsRoomsTag){
6186
- return (!clientsRoomsTag || empty(clientsRoomsTag) || contains(clientsRoomsTag, clientSocket.clientRoomTag));
6187
- },
6188
-
6189
-
6190
- //
6191
- // CONSOLE NODE SERVER/CLIENT
6192
- //
6193
- getStatic:(isNodeContext=true, useSocketIOImplementation=/*DEBUG*/false)=>{
6091
+ // USAGE :
6092
+ // Example :
6093
+ //i18n.declareSplitted({
6094
+ // "fr":{
6095
+ // "commencer":"Commencer",
6096
+ // },
6097
+ // "en":{
6098
+ // "commencer":"Start",
6099
+ // },
6100
+ //});
6101
+ i18n.declareSplitted=function(repositoryInfos){
6102
+ let nonSplittedFormat={};
6103
+
6104
+ foreach(repositoryInfos,(allKeys, lang)=>{
6105
+ foreach(allKeys,(text, key)=>{
6194
6106
 
6195
- WebsocketImplementation.isNodeContext=isNodeContext;
6196
- WebsocketImplementation.useSocketIOImplementation=useSocketIOImplementation;
6197
-
6198
- if(!WebsocketImplementation.useSocketIOImplementation){
6199
- // TRACE
6200
- lognow("INFO : (SERVER/CLIENT) Using native WebSocket implementation.");
6201
-
6202
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications
6203
- if(isNodeContext){
6204
- if(typeof(WebSocket)==="undefined"){
6205
- // TRACE
6206
- console.log("«ws» SERVER library not called yet, calling it now.");
6207
-
6208
- WebSocket=require("ws");
6209
- if(typeof(WebSocket)==="undefined"){
6210
- // TRACE
6211
- console.log("ERROR : «ws» CONSOLE/BROWSER CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
6212
- }
6213
- }
6214
- }
6215
-
6216
- }else{
6217
- // TRACE
6218
- lognow("INFO : (SERVER/CLIENT) Using socket.io websocket implementation.");
6219
-
6220
- if(isNodeContext){
6221
-
6222
- // NODE SERVER :
6223
- // DBG
6224
- lognow("INFO : Loading NODE SERVER socket.io libraries.")
6225
- if(typeof(Socket)==="undefined"){
6226
- // TRACE
6227
- console.log("«socket.io» NODE SERVER library not called yet, calling it now.");
6228
- Socket=require("socket.io");
6229
- }
6230
- if(typeof(Socket)==="undefined"){
6231
- // TRACE
6232
- console.log("ERROR : «socket.io» NODE CLIENT/SERVER library not found. Cannot launch nodejs server. Aborting.");
6233
- }
6234
-
6235
- // NODE CLIENT :
6236
- // DBG
6237
- lognow("INFO : Loading NODE CLIENT socket.io libraries.")
6238
- if(typeof(io)==="undefined"){
6239
- // TRACE
6240
- console.log("«socket.io-client» NODE CLIENT library not called yet, calling it now.");
6241
- io=require("socket.io-client");
6242
- }
6243
- if(typeof(io)==="undefined"){
6244
- // TRACE
6245
- console.log("ERROR : «socket-client.io» NODE CLIENT library not found. Cannot launch nodejs server. Aborting.");
6246
- }
6247
- }
6248
-
6249
-
6107
+ let foundKeyInfos=nonSplittedFormat[key];
6108
+ if(!foundKeyInfos){
6109
+ nonSplittedFormat[key]={};
6110
+ foundKeyInfos=nonSplittedFormat[key];
6250
6111
  }
6251
-
6252
- // *********************************************************************************
6253
-
6254
- return WebsocketImplementation;
6255
- },
6256
-
6257
-
6258
- /*private*/getMessageDataBothImplementations:(eventOrMessageParam)=>{
6259
-
6260
- const eventOrMessage=(!WebsocketImplementation.useSocketIOImplementation?eventOrMessageParam.data:eventOrMessageParam);
6261
-
6262
- let dataResult=eventOrMessage;
6263
-
6264
- try{
6265
- dataResult=(WebsocketImplementation.useFlatStrings || isString(eventOrMessage)?parseJSON(eventOrMessage):eventOrMessage);
6266
- }catch(error1){
6267
- // TRACE
6268
- lognow(`ERROR : Failed to parse JSON for string «${dataResult}»`,error1);
6269
- dataResult=(isString(eventOrMessage)?eventOrMessage:stringifyObject(eventOrMessage));
6270
- }
6271
-
6272
- return dataResult;
6273
- },
6274
-
6275
-
6276
- getServer:(listenableServer)=>{
6277
6112
 
6278
- if(!WebsocketImplementation.isNodeContext){
6113
+ let foundTextForLanguage=foundKeyInfos[lang];
6114
+ if(foundTextForLanguage){
6279
6115
  // TRACE
6280
- throw new Error("ERROR : SERVER : Server launch is not supported in a non-nodejs context for any implementation.");
6281
- }
6282
-
6283
-
6284
- // TODO : FIXME : Use one single interface !
6285
- // NODE SERVER MODE ONLY :
6286
- let serverSocket;
6287
- if(!WebsocketImplementation.useSocketIOImplementation){
6288
- serverSocket=new WebSocket.Server({ "server":listenableServer });
6116
+ console.log("WARN : Duplicate entry for translation key «"+key+"» for language «"+lang+"», keeping only the first declared :",foundTextForLanguage);
6289
6117
  }else{
6290
-
6291
- // NOW : socket.io :
6292
- // Loading socket.io
6293
- // OLD SYNTAX : serverSocket=Socket.listen(listenableServer);
6294
- serverSocket=new Socket.Server(listenableServer);
6295
-
6296
- if(listenableServer.cert || listenableServer.key){
6297
- // TRACE :
6298
- 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 !!!");
6299
- }
6300
-
6301
- // Setting up the disconnect event for a client :
6302
- serverSocket.on("endConnection",()=>{
6303
- serverSocket.disconnect();
6304
- });
6118
+ foundKeyInfos[lang]=text;
6305
6119
  }
6306
6120
 
6307
- serverSocket.on("error",(error)=>{
6308
- // TRACE
6309
- lognow("ERROR : An error occurred when trying to start the server : ",error);
6310
-
6311
- });
6121
+ });
6122
+ });
6312
6123
 
6313
- // NODE SERVER INSTANCE :
6314
- const nodeServerInstance=new NodeServerInstance(serverSocket, listenableServer);
6315
-
6316
- // Join room server part protocol :
6317
- nodeServerInstance.receive("protocol",(message, clientSocket)=>{
6318
- nodeServerInstance.addToRoom(clientSocket, message.clientRoomTag);
6319
- },{listenerMessageType:"joinRoom"});
6320
-
6321
- // To make the server aware of the clients connections states :
6322
- nodeServerInstance.serverSocket.on("close", function close() {
6323
-
6324
- // TODO : FIXME : Use one single interface !
6325
- if(!WebsocketImplementation.useSocketIOImplementation) serverClients=nodeServerInstance.serverSocket.clients;
6326
- // OLD : else serverClients=nodeServerInstance.serverSocket.sockets.clients();
6327
- else serverClients=nodeServerInstance.serverSocket.sockets.sockets;
6328
-
6329
- serverClients.forEach((clientSocket)=>{
6330
- clearInterval(clientSocket.stateCheckInterval);
6331
- });
6332
- });
6124
+ // Then we use the standard declaration method :
6125
+ i18n.declare(nonSplittedFormat);
6126
+ };
6333
6127
 
6334
- return nodeServerInstance;
6335
- },
6128
+ // For generating languages files helping purposes :
6129
+ i18n.displayAll=function(displaySingleEntriesCalls=false){
6130
+ let foundRepository=(window?window:global)[I18N_REPOSITORY_VARIABLE_NAME];
6131
+ if(!foundRepository){
6132
+ // TRACE
6133
+ console.log("ERROR : No repository found under the variable name «"+I18N_REPOSITORY_VARIABLE_NAME+"»");
6134
+ }else{
6135
+ console.log("TRACE : Variable under the name of «"+I18N_REPOSITORY_VARIABLE_NAME+"»:", foundRepository);
6136
+ console.log("\n\n");
6137
+
6138
+ let message="";
6139
+ foreach(foundRepository, (entry, entryKey)=>{
6140
+ let normalizedEntry={};
6141
+ if(entry.capitalize) normalizedEntry.capitalize=entry.capitalize;
6142
+ if(entry.append) normalizedEntry.append=entry.append;
6143
+ foreach(entry,(e,k)=>{
6144
+ normalizedEntry[k]=e;
6145
+ }
6146
+ ,(i,key)=>{ return (!contains(["append","capitalize"],key)); }
6147
+ ,(i1,i2)=>{ return (i1.key==i2.key?0:(i1.key=="fr"?-1:1));/*(Sorry, French always comes first !)*/}
6148
+ );
6149
+ let entryStr=stringifyObject(normalizedEntry);
6150
+ message+="\""+entryKey+"\":"+entryStr+",\n";
6151
+ if(displaySingleEntriesCalls) message+="// i18n("+entryStr+");\n";
6152
+ });
6153
+
6154
+ console.log("\n"+message);
6336
6155
 
6337
- // DO NOT USE DIRECTLY, USE INSTEAD initClient(...) (this function uses connectToServer(...)) !
6338
- // NODE / BROWSER CLIENT CONNECTS TO SERVER MAIN ENTRYPOINT:
6339
- connectToServer:(serverURL, port, isSecure=false, timeout)=>{
6156
+ }
6157
+ }
6340
6158
 
6341
- // TRACE
6342
- lognow("INFO : Using socket library flavor : "+(WebsocketImplementation.isNodeContext?"node (client/server-side)":"browser (client-side only)"));
6343
6159
 
6344
- if(WebsocketImplementation.isNodeContext)
6345
- return WebsocketImplementation.connectToServerFromNode(serverURL, port, isSecure, timeout);
6346
- else
6347
- return WebsocketImplementation.connectToServerFromBrowser(serverURL, port, isSecure, timeout);
6348
- },
6349
-
6350
-
6351
- //
6352
- // NODE CLIENT
6353
- //
6354
- /*private*/connectToServerFromNode:(serverURL, port, isSecure, timeout)=>{
6355
-
6356
- // NODE CLIENT MODE ONLY :
6357
- let clientSocket;
6358
- if(!WebsocketImplementation.useSocketIOImplementation){
6359
-
6360
- // NEW : ws :
6361
- if(typeof(WebSocket)==="undefined"){
6362
- // TRACE
6363
- lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
6364
- return null;
6365
- }
6366
6160
 
6367
- clientSocket=new WebSocket(serverURL+":"+port,/*WORKAROUND:*/{
6368
- // CAUTION : SECURITY BREACH :
6369
- // BUT ALSO NECESSARY TO ALLOW SELF-SIGNED CERTIFICATES USAGE WITH THE YESBOT SYSTEM !
6370
- rejectUnauthorized:false, // (THIS IS A KNOWN SECURITY BREACH)
6371
- secure: isSecure
6372
- });
6373
-
6374
- clientSocket.addEventListener("error", error=>{
6375
- // TRACE
6376
- lognow("ERROR : (NODEJS) A WebSocket client error occurred while trying to connect to server:", error.message);
6377
- });
6378
-
6379
- }else{
6380
- // NOW : socket.io :
6381
- //client on server-side:
6382
-
6383
- if(WebsocketImplementation.isNodeContext && typeof(io)!=="undefined"){
6384
-
6385
- // OLD SYNTAX: clientSocket=Socket.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6386
- // NO : clientSocket=new Socket.Client(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6387
- clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure, autoConnect:true});
6388
- // UNUSEFUL (since we have the autoconnect:true option) : clientSocket.connect();
6389
-
6390
- clientSocket.on("connect_error", error=>{
6391
- // TRACE
6392
- lognow("ERROR : (NODEJS) A SocketIO client error occurred while trying to connect to server:", error.message);
6393
- });
6394
6161
 
6395
- }
6396
- }
6397
-
6398
- // DBG
6399
- lognow("DEBUG : CLIENT : clientSocket created:");
6400
6162
 
6401
- const nodeClientInstance=new ClientInstance(clientSocket);
6402
- return nodeClientInstance;
6403
- },
6404
-
6405
- //
6406
- // BROWSER CLIENT
6407
- //
6408
-
6409
- /*private*/connectToServerFromBrowser:(serverURL, port, isSecure, timeout)=>{
6410
-
6411
- // NEW : ws :
6412
- if(typeof(WebSocket)==="undefined"){
6413
- // TRACE
6414
- lognow("ERROR : CLIENT : Could not find websocket client lib, aborting client connection.");
6415
- return null;
6416
- }
6163
+ function isCharacterToSkip(c){
6164
+ return RegExp("\\s").test(c) || contains([",","\'","´","\"",":","-","(",")","[","]","<",">","!","?","%",".","/","=",],c);
6165
+ }
6166
+
6167
+ function snakize(strParam, separator="_"){
6168
+ let result="";
6169
+ for(let i=0;i<strParam.length;i++){
6170
+ let c=strParam[i];
6171
+ if(isCharacterToSkip(c)){
6172
+ result+=separator;
6173
+ }else{
6174
+ result+=c.toLowerCase();
6175
+ }
6176
+ if(separator===".") result=result.replace(/\\.+/,separator);
6177
+ else result=result.replace(RegExp(separator+"+"),separator);
6178
+ }
6179
+ return result;
6180
+ }
6181
+
6182
+ function toConvention(type="snake",str){
6183
+ if(empty(str)) return str;
6184
+ str=str.trim();
6185
+ let result="";
6186
+ if(type==="camel"){
6187
+ for(let i=0;i<str.length;i++){
6188
+ let c=str[i];
6417
6189
 
6418
- // TODO : FIXME : Use one single interface !
6419
- // BROWSER CLIENT MODE ONLY :
6420
- let clientSocket;
6421
- if(!WebsocketImplementation.useSocketIOImplementation){
6422
- // CAUTION : PARAMETER rejectUnauthorized:false WILL DO NOTHING,
6423
- // BECAUSE THIS IS COMPLETLY HANDLED BY THE BROWSER SECURITY POLICY !
6424
- // SO TO CLEAR THE SSL ERROR :
6425
- // - FIRST GO TO THE HTTPS:// SERVER ADDRESS WITH BROWSER
6426
- // - THEN ADD THE SECURITY EXCEPTION IN THE BROWSER !
6427
- clientSocket=new WebSocket(serverURL+":"+port,["ws","wss"]);
6428
-
6429
- clientSocket.addEventListener("error", error=>{
6430
- // TRACE
6431
- lognow("ERROR : (BROWSER) A WebSocket client error occurred while trying to connect to server:", error.message);
6432
- });
6190
+ if(isCharacterToSkip(c)){
6191
+ let nextChar="";
6433
6192
 
6434
- }else if(typeof(io)!=="undefined"){
6435
- // OLD SYNTAX :clientSocket=io.connect(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6436
- // ALTERNATIVE :
6437
- clientSocket=io(serverURL + ":" + port,{timeout: timeout, secure: isSecure});
6193
+ if(i<str.length-1){// If we are not at the very last character :
6194
+ do{
6195
+ nextChar=str[i+1];
6196
+ i++;
6197
+ }while(isCharacterToSkip(nextChar) && i<str.length-1);
6198
+ if(isCharacterToSkip(nextChar)) nextChar="";
6199
+ }
6200
+ result+=nextChar.toUpperCase();
6201
+ }else{
6438
6202
 
6439
- clientSocket.on("connect_error", error=>{
6440
- // TRACE
6441
- lognow("ERROR : (BROWSER) A SocketIO client error occurred while trying to connect to server:", error.message);
6442
- });
6443
-
6203
+ result+=c;
6444
6204
  }
6445
6205
 
6446
- // BROWSER CLIENT INSTANCE :
6447
- const browserClientInstance=new ClientInstance(clientSocket);
6448
- return browserClientInstance;
6449
- },
6450
-
6451
- };
6452
-
6453
-
6454
-
6455
- launchNodeHTTPServer=function(port, doOnConnect=null, doOnFinalizeServer=null, /*OPTIONAL*/sslOptions=null, httpHandlerParam=null, addCORSHeader=ADD_CORS_HEADER){
6456
-
6457
- const EXCLUDED_FILENAMES_PARTS=[".keyHash.",".pem"];
6458
-
6459
-
6460
-
6461
- if(typeof(https)==="undefined"){
6462
- // TRACE
6463
- console.log("«https» SERVER library not called yet, calling it now.");
6464
- https=require("https");
6465
- }
6466
- if(typeof(http)==="undefined"){
6467
- // TRACE
6468
- console.log("«http» SERVER library not called yet, calling it now.");
6469
- http=require("http");
6470
- }
6471
-
6472
-
6473
- const DEFAULT_HANDLER=function(request, response){
6474
-
6475
- const url=request.url;
6476
-
6477
- let isURLInExclusionZone=!!foreach(EXCLUDED_FILENAMES_PARTS,(excludedStr)=>{
6478
- if(contains(url,excludedStr)){
6479
- return true;
6480
- }
6481
- });
6482
-
6483
- if(isURLInExclusionZone){
6484
- // TRACE
6485
- console.log("ERROR 403 forbidden access error :");
6486
- console.log(error);
6487
-
6488
- response.writeHead(403);
6489
- response.end("Sorry, cannot access resource : error: "+error.code+" ..\n");
6490
- response.end();
6491
- return;
6492
- }
6493
-
6494
-
6495
-
6496
- const urlFile="." + url;
6497
-
6498
- let filePath=urlFile.indexOf("?")!==-1?urlFile.split("?")[0]:urlFile;
6499
- if(filePath=="./") filePath="./index.html";
6500
-
6501
- let contentType="text/html";
6502
-
6503
- const headers={ "Content-Type": contentType };
6504
-
6505
- // 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)
6506
- if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
6507
-
6508
-
6509
- fs.readFile(filePath, function(error, fileContent){
6510
- if(error){
6511
- if(error.code=="ENOENT"){
6512
- // TRACE
6513
- console.log("ERROR 404 file not found :"+filePath);
6514
-
6515
- fs.readFile("./404.html", function(error, fileContent){
6516
- response.writeHead(200, headers);
6517
- response.end(fileContent, "utf-8");
6518
- });
6519
-
6520
- }else {
6521
-
6522
- // TRACE
6523
- console.log("ERROR 500 server error :");
6524
- console.log(error);
6525
-
6526
- response.writeHead(500);
6527
- response.end("Sorry, check with the site admin for error: "+error.code+" ..\n");
6528
- response.end();
6529
-
6530
- }
6531
- }else {
6532
-
6533
- // TRACE
6534
- console.log("INFO 200 OK :"+filePath);
6535
-
6536
-
6537
- response.writeHead(200, headers);
6538
- response.end(fileContent, "utf-8");
6539
-
6540
- // res.writeHead(200);
6541
- // res.end("hello world\n");
6542
- // res.sendFile(__dirname + "/public/index.html");
6543
-
6544
- }
6545
- });
6546
-
6547
- };
6548
-
6549
-
6550
- const handler=nonull(httpHandlerParam, DEFAULT_HANDLER);
6551
-
6552
-
6553
-
6554
- let listenableServer;
6555
- if(sslOptions){
6556
- let httpsServer=https.createServer(sslOptions, handler).listen(port);
6557
- // TRACE
6558
- console.log("INFO : SERVER : HTTPS Server launched and listening on port " + port + "!");
6559
- listenableServer=httpsServer;
6560
- }else{
6561
- let httpServer=http.createServer(handler).listen(port);
6562
- // TRACE
6563
- console.log("INFO : SERVER : HTTP Server launched and listening on port " + port + "!");
6564
- listenableServer=httpServer;
6565
- }
6566
-
6567
-
6568
- const server=WebsocketImplementation.getStatic(true).getServer(listenableServer);
6569
-
6570
- // When a client connects, we execute the callback :
6571
- // CAUTION : MUST BE CALLED ONLY ONCE !
6572
- server.onConnectionToClient((serverParam, clientSocketParam)=>{
6573
- if(doOnConnect) doOnConnect(serverParam, clientSocketParam);
6574
- });
6575
-
6576
-
6577
- server.onFinalize((serverParam)=>{
6578
-
6579
- // DBG
6580
- lognow("onFinalize() server");
6581
-
6582
- if(doOnFinalizeServer) doOnFinalizeServer(serverParam);
6583
- });
6584
-
6585
-
6586
- // TRACE
6587
- console.log("INFO : SERVER : Generic Nodejs server launched and listening on port:" + port + "!");
6588
-
6589
-
6590
-
6591
-
6592
-
6593
- return server;
6594
- }
6595
-
6596
-
6597
- initNodeServerInfrastructureWrapper=function(doOnClientConnection=null, doOnFinalizeServer=null, /*OPTIONAL*/portParam, /*OPTIONAL*/certPathParam, /*OPTIONAL*/keyPathParam){
6598
-
6599
- // TRACE
6600
- console.log("Server launched.");
6601
- console.log("Usage : node <server.js> conf {port:[port], sslCertPath:[ssl certificate path | unsecure ], sslKeyPath:[ssl key path], serverConfig:[JSON server configuration]}");
6602
- console.log("Or (to generate password hash) : node <server.js> hash <clientId@repositoryName> <clearTextSecretString>");
6603
- // EXAMPLE : node orita-srv.js hash orita.global@keyHash 1234567890
6604
- console.log("Server launched.");
6605
-
6606
-
6607
- // We read the command-line arguments if needed :
6608
-
6609
- let argCLPort;
6610
- let argCLCertPath;
6611
- let argCLKeyPath;
6612
-
6613
- let serverConfig={};
6614
- let isForceUnsecure;
6615
-
6616
- let isHashAsked=false;
6617
- let clearTextParam=null;
6618
- let persisterId=null;
6619
-
6620
-
6621
- process.argv.forEach(function (val, i){
6622
- if(!val) return;
6623
- // 0 corresponds to «node / nodejs»
6624
- if(i<=1) return; // 1 corresponds to « <server.js> »
6625
- else if(i==2){
6626
- if(val==="hash") isHashAsked=true;
6627
- }else if(i==3){
6628
- if(!isHashAsked){
6629
- try{
6630
- const jsonConf=parseJSON(val);
6631
- argCLPort=jsonConf.port;
6632
- argCLCertPath=jsonConf.sslCertPath;
6633
- argCLKeyPath=jsonConf.sslKeyPath;
6634
- serverConfig=nonull(jsonConf.serverConfig,{});
6635
- }catch(err1){
6636
- lognow("ERROR : Cannot parse argument JSON string «"+val+"».",err1);
6637
- }
6638
- } else persisterId=val;
6639
- }else if(i==4){
6640
- if(isHashAsked) clearTextParam=val;
6641
- }
6642
- });
6643
- isForceUnsecure=(argCLCertPath==="unsecure");
6644
-
6645
-
6646
-
6647
-
6648
-
6649
- const aotraNodeServer={config:serverConfig};
6650
- aotraNodeServer.serverManager={ start:()=>{/*DEFAULT START FUNCTION, WILL BE OVERRIDEN LATER*/}};
6651
-
6652
- if(isHashAsked){
6653
- // We instanciate a temporary persister just to read the key hash file:
6654
- const persister=getPersister("./");
6655
- let persisterIdSplits=persisterId.split("@");
6656
- if(empty(persisterIdSplits) || persisterIdSplits.length!=2){
6657
- // TRACE
6658
- console.log("ERROR : No persister repository IDs provided correctly. Cannot read key hash. Aborting hash generation.");
6659
- return aotraNodeServer;
6660
- }
6661
- const persisterClientId=persisterIdSplits[0];
6662
- const persisterRepositoryName=persisterIdSplits[1];
6663
- let globalKeyHashObject=persister.readTreeObjectFromFile(persisterClientId, persisterRepositoryName);
6664
- if(!globalKeyHashObject || !globalKeyHashObject.keyHash){
6665
- // TRACE
6666
- console.log("WARN : No key hash found. Generating one now.");
6667
- globalKeyHashObject={keyHash:getUUID(), hashes:[]};
6668
- persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
6669
- // TRACE
6670
- console.log("INFO : Key hash generated and saved successfully.");
6671
- });
6672
- }
6673
- const globalKeyHash=globalKeyHashObject.keyHash;
6674
-
6675
- let firstHash=getHashedString(clearTextParam);
6676
-
6677
- let generatedHash=getHashedString( firstHash + globalKeyHash, "SHA-256", true);// (we use the heavy treatment thing.)
6678
- globalKeyHashObject.hashes.push(generatedHash);
6679
-
6680
- // We update the repository :
6681
- persister.saveDataToFileForClient(persisterClientId,persisterRepositoryName,globalKeyHashObject,false,()=>{
6682
- // TRACE
6683
- console.log("INFO : Hash added to repository and saved successfully.");
6684
- });
6685
-
6686
- // OUTPUT
6687
- console.log("Here is your key : share it with your main clients but DO NOT LEAK IT !\n********************\n"+clearTextParam+"\n********************\n");
6688
-
6689
- return aotraNodeServer;
6690
- }
6691
-
6692
-
6693
- const DEFAULT_PORT=nonull(argCLPort,25000);
6694
- const DEFAULT_CERT_PATH=nonull(argCLCertPath,"cert.pem");
6695
- const DEFAULT_KEY_PATH=nonull(argCLKeyPath,"key.key");
6696
-
6697
-
6698
- let port=portParam ? portParam : DEFAULT_PORT;
6699
- let certPath=null;
6700
- let keyPath=null;
6701
-
6702
- if(!isForceUnsecure){
6703
- certPath=certPathParam?certPathParam:DEFAULT_CERT_PATH;
6704
- keyPath=keyPathParam?keyPathParam:DEFAULT_KEY_PATH;
6705
- }
6706
-
6707
-
6708
- // UNUSEFUL :
6709
- //aotraNodeServer.serverManager.microClientsSockets=[];
6710
- // UNUSEFUL :
6711
- //aotraNodeServer.serverManager.mainClientsSockets=[];
6712
-
6713
- aotraNodeServer.serverManager.start=function(){
6714
-
6715
- // Eventual encryption options :
6716
- let sslOptions=null;
6717
- if(!isForceUnsecure){
6718
- if(typeof(fs)==="undefined"){
6719
- // TRACE
6720
- lognow("ERROR : «fs» node subsystem not present, cannot access files. Aborting SSL configuration of server.");
6721
- }else{
6722
- try{
6723
- sslOptions={
6724
- cert: fs.readFileSync(certPath, {encoding: "utf8"}),
6725
- key: fs.readFileSync(keyPath, {encoding: "utf8"}),
6726
- };
6727
- }catch(exception){
6728
- // TRACE
6729
- lognow("ERROR : Could not open SSL files certPath:«"+certPath+"» or keyPath:«"+keyPath+"». Aborting SSL configuration of server.");
6730
- }
6731
- }
6732
- }
6733
-
6734
- aotraNodeServer.server=launchNodeHTTPServer(port, doOnClientConnection, doOnFinalizeServer, sslOptions);
6735
-
6736
-
6737
- return aotraNodeServer;
6738
- };
6739
-
6740
- return aotraNodeServer;
6741
- }
6742
-
6743
- // ========================= FUSRODA SERVER : =========================
6744
-
6745
- //
6746
- // DOES NOT WORK : USE Java FusrodaServer instead :
6747
- //
6748
- ///*FUSRODA server stands from FSRD SERVER, for Fucking Simple Remote Desktop SERVER*/
6749
- //createFusrodaServer=function(certPathParam=null,keyPathParam=null,portParam=4000){
6750
- //
6751
- // // https://www.npmjs.com/package/screenshot-desktop
6752
- // // https://github.com/octalmage/robotjs
6753
- // // npm install --save screenshot-desktop
6754
- // //
6755
- // // sudo apt-get install libxtst-dev libx11-dev
6756
- // // npm install --save robotjs
6757
- //
6758
- // // http://getrobot.net/docs/usage.html
6759
- // //
6760
- // // apt-get install build-essential python libxt-dev libxtst-dev libxinerama-dev -y
6761
- //
6762
- // const screenshot=require("screenshot-desktop");
6763
- //
6764
- // const REFRESH_SCREENSHOTS_MILLIS=500;
6765
- //
6766
- //
6767
- // const server=initNodeServerInfrastructureWrapper(
6768
- // // On each client connection :
6769
- // // (serverParam, clientSocketParam)=>{},
6770
- // null,
6771
- // // On server finalization :
6772
- // (serverParam)=>{
6773
- //
6774
- // serverParam.receive("protocol_fusroda", (message, clientSocket)=> {
6775
- // serverParam.addToRoom(clientSocket,"clients");
6776
- // });
6777
- //
6778
- //
6779
- // serverParam.sendScreenshotsRoutine=setInterval(()=>{
6780
- // if(serverParam.isScreenshotStarted) return;
6781
- //
6782
- // serverParam.isScreenshotStarted=true;
6783
- //
6784
- // screenshot().then((img) => {
6785
- //
6786
- // const data={image:img,listenerMessageType:"imageData"};
6787
- // serverParam.send("message", data, "clients");
6788
- //
6789
- // serverParam.isScreenshotStarted=false;
6790
- //
6791
- // }).catch((error) => {
6792
- // // TRACE
6793
- // lognow("ERROR : Error during screenshot :",error);
6794
- // });
6795
- //
6796
- // },REFRESH_SCREENSHOTS_MILLIS);
6797
- //
6798
- //
6799
- // },portParam,certPathParam,keyPathParam);
6800
- //
6801
- //
6802
- // // const doOnConnect=(serverParam, clientSocketParam)=>{
6803
- // // };
6804
- // // const doOnFinalizeServer=(serverParam)=>{
6805
- // // /*DO NOTHING*/
6806
- // // };
6807
- // // const server={};
6808
- // // server.start=(port=4000)=>{
6809
- // // server.httpServer=launchNodeHTTPServer(port, doOnConnect, doOnFinalizeServer, sslOptions);
6810
- // // };
6811
- //
6812
- // return server;
6813
- //}
6814
-
6815
-
6816
- // ========================= UTILITY SERVERSIDE METHODS : =========================
6817
-
6818
- class ListManager{
6819
-
6820
- constructor(config){
6821
- this.config=config;
6822
- this.maxItemsNumber=nonull(this.config.max,999);
6823
- this.simultaneousItemsNumber=nonull(this.config.simultaneous,1);
6824
- this.sessionDurationSeconds=nonull(this.config.duration,null);
6825
- this.mode=nonull(this.config.mode,"startAtConnexion");
6826
-
6827
- this.itemsInfos={};
6828
- this.time=null;
6829
- this.started=false;
6830
- }
6831
-
6832
- addItem(id,item){
6833
- if(id==null){
6834
- // TRACE
6835
- lognow("ERROR : Cannot add item with no id.");
6836
- return;
6837
- }
6838
- if(!item){
6839
- // TRACE
6840
- lognow("ERROR : Cannot add null item.");
6841
- return;
6842
- }
6843
- const numberOfItemsCurrently=getArraySize(this.itemsInfos);
6844
-
6845
- // DBG
6846
- lognow(">>>>>>>>>numberOfItemsCurrently:",numberOfItemsCurrently);
6847
- lognow(">>>>>>>>>Object.keys(arrayOfValues):",Object.keys(this.itemsInfos));
6848
-
6849
-
6850
- if(this.maxItemsNumber<=numberOfItemsCurrently){
6851
- // TRACE
6852
- lognow("ERROR : Cannot add item with id «"+id+"», list already full.");
6853
- return;
6854
- }
6855
-
6856
-
6857
- if(numberOfItemsCurrently==0 && this.mode==="startAtConnexion"){
6858
-
6859
- // DBG
6860
- lognow(">>>>>>>>>START SESSION !!!!");
6861
-
6862
- this.startSession();
6863
- }
6864
- this.itemsInfos[id]={
6865
- item:item,
6866
- time:getNow(),
6867
- };
6868
- }
6869
-
6870
- startSession(){
6871
- this.time=getNow();
6872
- this.started=true;
6873
- }
6874
-
6875
- stopSession(){
6876
- this.started=false;
6877
- }
6878
-
6879
- isSessionActive(){
6880
-
6881
- // DBG
6882
- lognow(" !!! this.sessionDurationSeconds : "+this.sessionDurationSeconds);
6883
- lognow(" !!! this.started : "+this.started);
6884
- lognow(" !!! this.time : "+this.time);
6885
-
6886
-
6887
- if(!this.sessionDurationSeconds) return true;
6888
- if(!this.started) return false;
6889
- const result=!hasDelayPassed(this.time, this.sessionDurationSeconds*1000);
6890
-
6891
- // DBG
6892
- lognow(" !!! HAS DELAY PASSED : "+result);
6893
-
6894
- return result;
6895
- }
6896
-
6897
- isClientActive(clientId){
6898
-
6899
- // DBG
6900
- lognow(" this.isSessionActive()"+this.isSessionActive());
6901
-
6902
- if(!this.isSessionActive()) return false;
6903
- const clientPosition=this.getItemPosition(clientId);
6904
- //CAUTION : Client position starts at 1 !
6905
- const clientIndex=(clientPosition-1);
6906
- const result=(clientIndex<=this.maxItemsNumber && clientIndex<=this.simultaneousItemsNumber);
6907
-
6908
- return result;
6909
- }
6910
-
6911
- removeItemById(id){
6912
- if(id==null){
6913
- // TRACE
6914
- lognow("ERROR : Cannot remove item, no id specified.");
6915
- return;
6916
- }
6917
- if(!this.itemsInfos[id]){
6918
- // TRACE
6919
- lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
6920
- return;
6921
- }
6922
- delete this.itemsInfos[id];
6923
- }
6924
-
6925
- removeItem(item){
6926
- if(item==null){
6927
- // TRACE
6928
- lognow("ERROR : Cannot remove item, none specified.");
6929
- return;
6930
- }
6931
-
6932
- let id=null;
6933
- foreach(this.itemsInfos,(itemInfos,key)=>{
6934
- if(itemInfos.item===item
6935
- // DEBUG ONLY :
6936
- || (itemInfos.item.id && item.id && itemInfos.item.id===item.id)
6937
- ){
6938
- id=key;
6939
- return "break";
6940
- }
6941
6206
 
6942
- });
6943
-
6944
- if(!id){
6945
- // TRACE
6946
- lognow("ERROR : Cannot remove item, item not found for id «"+id+"».");
6947
- return;
6948
6207
  }
6949
-
6950
- this.removeItemById(id);
6951
- }
6952
-
6953
- // Goes from 1 to <length>:
6954
- getItemPosition(id){
6955
- if(id==null){
6956
- // TRACE
6957
- lognow("ERROR : Cannot calculate item position, no id specified.");
6958
- return null;
6959
- }
6960
- if(!this.itemsInfos[id]){
6961
- // TRACE
6962
- lognow("ERROR : Cannot calculate item position, item not found for id «"+id+"».");
6963
- return null;
6964
- }
6965
- let result=0;
6966
- foreach(this.itemsInfos, (itemInfo, key)=>{
6967
- result++;
6968
- if(id===key) return "break";
6969
- },null,(item1, item2)=>item1.time<item2.time);
6970
- return result;
6971
- }
6972
-
6973
- getItemsNumber(){
6974
- return getArraySize(this.itemsInfos);
6975
- }
6976
-
6977
- };
6978
-
6979
- getListManager=function(config){
6980
- return new ListManager(config);
6981
- };
6982
-
6983
-
6984
- // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
6985
- // NO : IN A NODE CONTEXT WITH require("...") WILL RESULT IN AN UNDEFINED FUNCTION ERROR !!!
6986
- // function performHTTPRequest(...){...
6987
- // USE THIS INSTEAD :
6988
- performHTTPRequest=function(completeURL, httpMethod="GET", headers={}, requestBodyOrNamedArgs=null, isNodeContext=false, addCORSHeader=ADD_CORS_HEADER){
6989
-
6990
- // Body
6991
- let requestBodyOrNamedArgsStr=null;
6992
- if(requestBodyOrNamedArgs){
6993
- if(isString(requestBodyOrNamedArgs) && !isObject(requestBodyOrNamedArgs)){
6994
- // If it's already a string, we assume it's a valid JSOn string already :
6995
- requestBodyOrNamedArgsStr=requestBodyOrNamedArgs.replace(/[\r\n]+/gim, ""); // We remove all the breaklines
6208
+ }else{
6209
+ if(type==="bundle"){
6210
+ result=snakize(str,".");
6996
6211
  }else{
6997
- try{
6998
- requestBodyOrNamedArgsStr=stringifyObject(requestBodyOrNamedArgs).replace(/[\r\n]+/gim, ""); // We remove all the breaklines
6999
- }catch(parseErr){
7000
- requestBodyOrNamedArgsStr=""+requestBodyOrNamedArgs;
7001
- }
6212
+ result=snakize(str);
7002
6213
  }
7003
6214
  }
7004
6215
 
7005
- let body=null;
7006
- // Not the same way to send parameters, with POST/PUT http method :
7007
- if(contains(["POST","PUT"],httpMethod) && requestBodyOrNamedArgs){
7008
- body=requestBodyOrNamedArgsStr;
7009
- }
7010
-
7011
- // Headers
7012
- if(contains(["POST","PUT"],httpMethod)){
7013
- headers["Content-Type"]="application/json";
7014
-
7015
- if(requestBodyOrNamedArgsStr && isString(body)){
7016
- headers["Content-Length"]=Buffer.byteLength(requestBodyOrNamedArgsStr);
7017
- }
7018
-
7019
- // 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)
7020
- if(addCORSHeader) headers["Access-Control-Allow-Origin"]="*";
7021
-
7022
- } else if(httpMethod==="GET" && requestBodyOrNamedArgs){
7023
- // Not the same way to send parameters in GET http method :
7024
- // DBG
7025
- lognow("unformatted API URL : "+completeURL);
7026
-
7027
- completeURL=appendGetParameters(completeURL, requestBodyOrNamedArgs);
7028
-
7029
- // DBG
7030
- lognow("formatted API URL : "+completeURL);
7031
- }
7032
-
7033
- // CASE BROWSER CONTEXT :
7034
- if(!isNodeContext || typeof(require)=="undefined"){
7035
- // TRACE
7036
- lognow("INFO : We are not running in a browser context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using browser library.");
7037
-
7038
- return new Promise((resolve,reject)=>{
7039
- fetch(completeURL, {
7040
- method: httpMethod,
7041
- headers: headers,
7042
- body: body,
7043
- })
7044
- // STRANGE : DOES NOT WORK !!:
7045
- //.then(response => response.json())
7046
- // STRANGE : DOES WORK :
7047
- .then(response => {
7048
-
7049
- // DBG
7050
- console.log("~~~~~~~~~~~response :",response);
7051
-
7052
- return response.json();
7053
- }).then(data => {
7054
-
7055
- // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
7056
- const status=data.status;
7057
- // if(status!=200 && data.error){
7058
- // // const error=nonull(data.error, data.detail);
7059
- // const error=data.error;
7060
- // // TRACE
7061
- // console.error("Error:", error);
7062
- // reject({error:""+error, httpStatus:status});
7063
- // return;
7064
- // }
7065
-
7066
- // DBG
7067
- console.log("~~~~~~~~~~~data :",data);
7068
-
7069
- data.httpStatus=status;
7070
- resolve(data);
7071
- }).catch(error => {
7072
- // TRACE
7073
- console.error("Error:", error);
7074
- reject({error:`${error}`, httpStatus:null});
7075
- });
7076
- });
7077
- }// else :
7078
-
7079
- // CASE NODEJS CONTEXT :
7080
-
7081
- // TRACE
7082
- lognow("INFO : We are running in a nodejs context (isNodeContext:"+isNodeContext+";typeof(require):"+(typeof(require))+"). Using nodejs library.");
7083
-
7084
-
7085
- const isSecure=(!empty(completeURL) && contains(completeURL.toLowerCase(),"https://"));
7086
- const httpHandler=isSecure?require("https"):require("http");
7087
-
7088
- // Options for the HTTP request
7089
- const options = {
7090
- url: completeURL,
7091
- method: httpMethod,
7092
- };
7093
-
7094
- if(contains(["POST","PUT"],httpMethod)){
7095
- options.json=true;
7096
- }
7097
-
7098
- options.headers=headers;
7099
-
7100
- return new Promise((resolve,reject)=>{
7101
-
7102
- // Create the HTTP request
7103
- // DOES NOT WORK : const request = httpHandler.request(options, (response) => {
7104
- // UNLESS YOU SPECIFY in options : hostname, port, path
7105
- const request = httpHandler.request(completeURL, options, (response) => {
7106
-
7107
- let responseDataStr = "";
7108
-
7109
- // A chunk of data has been received.
7110
- response.on("data", (chunk) => {
7111
- responseDataStr += chunk;
7112
- // // DEBUG ONLY
7113
- // const str = Buffer.from(chunk).toString("utf8");
7114
- // lognow(">>>str:"+str);
7115
- });
7116
-
7117
- // The whole response has been received.
7118
- response.on("end", () => {
7119
-
7120
- try{
7121
- let responseData;
7122
- try{
7123
- responseData=parseJSON(responseDataStr);
7124
- }catch(parseError1){
7125
- // CAUTION ! TODO : FIXME : We replace in response all single quotes with "`" (egrave) in order to avoid errors !
7126
- // DEBUG
7127
- responseDataStr=responseDataStr.replace(/'/gim,"`");
7128
- try{
7129
- responseData=parseJSON(responseDataStr);
7130
- }catch(parseError2){
7131
- // DBG
7132
- lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError2);
7133
- resolve( {response:response, responseDataStr:responseDataStr} );
7134
- return;
7135
- }
7136
- }
7137
-
7138
- // CAUTION : .request(...) => statusCode, .fetch(...) => status (performHTTPRequest(...) uses fetch(...))
7139
- const status=response.statusCode;
7140
- // if(status!=200){
7141
- // const error=nonull(response.statusMessage, "No error message for error code "+status);
7142
- // // TRACE
7143
- // console.error("Error:", error);
7144
- // reject({error:""+error, httpStatus:status});
7145
- // return;
7146
- // }
7147
-
7148
- resolve( {responseData:responseData, response:response, responseDataStr:responseDataStr, httpStatus: status} );
7149
- }catch(parseError3){
7150
- // DBG
7151
- lognow("WARN : Could not JSON parse the response data ! Must deal with the raw string:«"+responseDataStr+"»", parseError3);
7152
- resolve( {response:response, responseDataStr:responseDataStr} );
7153
- return;
7154
- }
7155
-
7156
- });
7157
- });
7158
-
7159
- // Handle errors
7160
- request.on("error", (error) => {
7161
- reject({error:error, httpStatus:null});
7162
- });
7163
-
7164
- // Not the same way to send parameters, with POST/PUT http method :
7165
- if(contains(["POST","PUT"],httpMethod) && body){
7166
- request.write(body);
7167
- }
7168
-
7169
- // End the request
7170
- request.end();
7171
-
7172
- });
7173
-
7174
- };
7175
-
7176
-
7177
-
7178
-
7179
- replacePathVariablesNamesWithValuesIfPossible=function(apiURL, namedArgs){
7180
- let result=apiURL;
7181
- foreach(namedArgs,(namedArgValue, namedArgKey)=>{
7182
- result=result.replace("{"+namedArgKey+"}",namedArgValue);
7183
- });
7184
6216
  return result;
7185
- };
7186
-
7187
- appendGetParameters=function(apiURL, namedArgsParam){
7188
- if(nothing(namedArgsParam)) return "";
7189
- try{
7190
-
7191
- const namedArgs=isString(namedArgsParam)?parseJSON(namedArgsParam):namedArgsParam;
7192
-
7193
- let result=apiURL;
7194
-
7195
- const paramCouples=[];
7196
- foreach(namedArgs,(value,key)=>{paramCouples.push(key+"="+value);});
7197
-
7198
- if(!empty(paramCouples)) result+=("?"+paramCouples.join("&"));
7199
-
7200
- return result;
7201
- }catch(parseError){
7202
- // TRACE
7203
- lognow("ERROR : Could not parse string parameters object «"+namedArgsParam+"», aborting GET parameter request string calculation:", parseError);
7204
- return "";
7205
- }
7206
- };
7207
-
7208
-
7209
-
6217
+ }
7210
6218
 
7211
6219
 
7212
6220
 
6221
+ function capitalize(str){
6222
+ if(nothing(str)) return "";
6223
+ return str.replace(/(?:^|\s)\S/m, firstChar => firstChar.toUpperCase()); // We replace only the first character.
6224
+ }
7213
6225
 
7214
6226
 
7215
6227
  /* ## Utility network global methods in a javascript, console (nodejs), or vanilla javascript with no browser environment.