front-end-controller 1.0.2 → 1.0.3

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.
package/README.md CHANGED
@@ -17,9 +17,4 @@ This project provides a powerful framework where the **backend can dynamically c
17
17
 
18
18
  ### Prerequisites
19
19
  1. A backend capable of managing WebSocket connections.
20
- 2. A modern browser environment that supports `WebSocket`, `localStorage`, `sessionStorage`, and `IndexedDB`.
21
-
22
- ### Installation
23
- 1. Clone this repository to your project:
24
- ```bash
25
- git clone https://github.com/Frontend
20
+ 2. A modern browser environment that supports `WebSocket`, `localStorage`, `sessionStorage`, and `IndexedDB`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "front-end-controller",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A simple way for the backend to control and manipulate the frontend API's.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -13,15 +13,7 @@
13
13
  ],
14
14
  "author": "Kalivaradhan Aadharsh",
15
15
  "license": "MIT",
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/aadk979/Frontend.git"
19
- },
20
- "bugs": {
21
- "url": "https://github.com/aadk979/Frontend/issues"
22
- },
23
- "homepage": "https://github.com/aadk979/Frontend#readme",
24
16
  "dependencies": {
25
17
  "front-end-controller": "^1.0.0"
26
18
  }
27
- }
19
+ }
package/src/index.js ADDED
@@ -0,0 +1,20 @@
1
+ /*!
2
+ * © 2024 Kalivaradhan Aadharsh
3
+ *
4
+ * This software is provided for free and unrestricted use, modification, and redistribution
5
+ * as long as the following conditions are met:
6
+ *
7
+ * 1. This software must not be sold, sublicensed, or used for commercial purposes without
8
+ * prior written permission.
9
+ * 2. Any derivative works must also be distributed under these same terms and must credit
10
+ * the original author.
11
+ * 3. Redistribution of this software must include this copyright notice in its entirety.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
15
+ * PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
16
+ * FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR
17
+ * OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
+ * DEALINGS IN THE SOFTWARE.
19
+ * Github: https://github.com/aadk979/Frontend.git
20
+ */ const WebSocket=require("ws"),crypto=require("crypto");function arrayBufferToString(e){return Buffer.from(e).toString("utf-8")}class FrontEndController{constructor(){this.server=null,this.sockets=new Map}startServer(e=5764){this.server=new WebSocket.Server({port:e}),this.server.on("connection",e=>{let r;e.send(JSON.stringify({id:"HANDSHAKE-PROTOCAL"})),e.on("message",t=>{let o=JSON.parse(arrayBufferToString(t));o.handshake&&(this.sockets.set(o.id,{socket:e,key:o.key,id:o.id}),r=o.id)}),e.on("close",()=>this.sockets.delete(r))}),console.log("Server live on port:",e)}closeServer(){if(this.server)return this.server.close(),{closed:!0}}sendData(e,r){return new Promise((t,o)=>{if(!e||!r)return o({error:!0,context:"Missing fields"});let s=this.sockets.get(e);if(!s)return{error:!0,context:"Socket not found for client"};let n=Buffer.from(JSON.stringify(r)).toString("utf8"),i="";for(let c=0;c<n.length;c++)i+=n.charCodeAt(c).toString(2).padStart(8,"0");let a=i.split(""),d=crypto.randomBytes(64).toString("hex"),l={id:`DATA-${e}`,returnID:d,payload:a};s.socket.send(JSON.stringify(l),e=>{if(e)return o({error:!0,context:"Error sending data",details:e})}),s.socket.on("message",e=>{try{let r=JSON.parse(e),s=r.data||r;t(s)}catch(n){o({error:!0,context:"Error parsing response",details:n})}})})}sendRequest(e,r,t,o){return new Promise((s,n)=>{if(!e||!r||!t||!o)return n({error:!0,context:"Missing fields"});let i=this.sockets.get(e);if(!i)return n({error:!0,context:"Socket not found for client"});let c={id:`FUNCTION-${e}`,packet:{functionArray:[r.toUpperCase(),t.toUpperCase(),o]}},a=JSON.stringify(c);i.socket.send(a,e=>{if(e)return n({error:!0,context:"Error sending data",details:e})}),i.socket.on("message",e=>{try{let r=JSON.parse(e),t=r.data||r;s(t)}catch(o){n({error:!0,context:"Error parsing response",details:o})}})})}}module.exports={FrontEndController};
@@ -1,12 +1,21 @@
1
1
  /**
2
2
  * FrontEnd Module
3
- * Facilitates communication between the frontend and backend using socket connections.
4
- */
3
+ * Facilitates communication between the frontend and backend using socket connections and additional custom functionality.
4
+ */
5
5
 
6
6
  import { FrontEnd } from "./unobfuscated";
7
7
 
8
- // Create an instance
9
- const instance = new FrontEnd();
8
+ // Create an instance with custom functions
9
+ const instance = new FrontEnd([
10
+ {
11
+ functionName: "logData",
12
+ clientFunction: (functionName, functionType, functionParams) => console.log(data),
13
+ },
14
+ {
15
+ functionName: "processData",
16
+ clientFunction: (functionName, functionType, functionParams) => JSON.stringify(data),
17
+ },
18
+ ]);
10
19
 
11
20
  /**
12
21
  * Initialize a socket connection.
@@ -53,4 +62,26 @@ instance.disable(true);
53
62
  * - Example: `const isDisabled = instance.state();`
54
63
  */
55
64
  const instanceState = instance.state();
56
- console.log("Instance state:", instanceState); // Logs `true` or `false`
65
+ console.log("Instance state:", instanceState); // Logs `true` or `false`
66
+
67
+ /**
68
+ * Listen for incoming data and handle it with a custom callback.
69
+ *
70
+ * @param {Function} callback - A function that processes incoming data.
71
+ * @returns {void}
72
+ *
73
+ * Description:
74
+ * - The callback receives `data` as its parameter.
75
+ * - This method allows dynamic data handling when new information is received.
76
+ *
77
+ * Usage:
78
+ * - Example:
79
+ * instance.listenForData((data) => {
80
+ * console.log("Received data:", data);
81
+ * // Custom data handling logic
82
+ * });
83
+ */
84
+ instance.listenForData((data) => {
85
+ console.log("Received data:", data);
86
+ // Process the data here
87
+ });
@@ -218,9 +218,28 @@ function clearAllWebStorageAndCookies() {
218
218
  }
219
219
 
220
220
 
221
- const Middleware = (functionName, functionType, functionParams) => {
221
+ const Middleware = (functionName, functionType, functionParams, ClientFunctions) => {
222
+
223
+ if(Array.isArray(ClientFunctions)){
224
+ const functionFilterForClient = ClientFunctions.filter((value) => (typeof value === 'object' && value.functionName && value.functionName === functionName));
225
+
226
+ if(functionFilterForClient.length !== 0 ){
227
+ if(functionFilterForClient.length > 1){
228
+ return { error: true , context: 'Given client functions array contains elements that have the same function name. Number of duplicates: ' + functionFilterForClient.length };
229
+ }
230
+ const identifiedClientFunctionObj = functionFilterForClient[0];
231
+ const clientFunction = identifiedClientFunctionObj.clientFunction;
232
+ console.log(typeof clientFunction)
233
+ if(typeof clientFunction !== 'function'){
234
+ return { error: true , context: 'Given client function is not a function' };
235
+ }
236
+ return clientFunction(functionName , functionType , functionParams);
237
+ }
238
+ }
239
+
222
240
  if (functionName === 'CLEAR-STORAGE'){
223
241
  clearAllWebStorageAndCookies();
242
+ return { error: false , taskComplete: true };
224
243
  }
225
244
 
226
245
  if (functionName === 'REDIRECT'){
@@ -388,14 +407,33 @@ const Middleware = (functionName, functionType, functionParams) => {
388
407
  class FrontEnd {
389
408
  static instance = null;
390
409
  static CODE = null;
410
+ static ClientFunctions = [];
391
411
 
392
- constructor() {
412
+ #listeners;
413
+
414
+ constructor(ClientFunctions) {
393
415
  if (FrontEnd.instance) {
394
416
  throw new Error("Only one instance of FrontEnd can be created.");
395
417
  }
396
418
  FrontEnd.instance = this;
397
419
  this.CODE = randomCode();
398
420
  this.disabled = false;
421
+ this.ClientFunctions = ClientFunctions;
422
+ this.#listeners = [];
423
+ this.dataFromServer = null;
424
+ }
425
+
426
+ listenForData(callback) {
427
+ if (typeof callback === 'function') {
428
+ this.#listeners.push(callback);
429
+ callback(this.dataFromServer);
430
+ } else {
431
+ console.error('The callback provided is not a function');
432
+ }
433
+ }
434
+
435
+ #_notifyListeners() {
436
+ this.#listeners.forEach(callback => callback(this.dataFromServer));
399
437
  }
400
438
 
401
439
  createSocket(socket = 'https://localhost:5764') {
@@ -405,14 +443,14 @@ class FrontEnd {
405
443
  const URL = socket.startsWith('https://') ? socket.replace('https', 'wss') : socket;
406
444
  const wsConnection = new WebSocket(URL);
407
445
 
408
- wsConnection.onmessage = ((event) => {
446
+ wsConnection.onmessage = (async (event) => {
409
447
  try {
410
448
  const data = JSON.parse(event.data);
411
449
  if(data.id === `FUNCTION-${this.CODE}`) {
412
450
  const { packet } = data;
413
451
  const [functionName, functionType, functionParams] = packet.functionArray;
414
452
 
415
- const responseFromMiddleware = Middleware(functionName, functionType, functionParams);
453
+ const responseFromMiddleware = Middleware(functionName, functionType, functionParams, this.ClientFunctions);
416
454
 
417
455
  wsConnection.send(
418
456
  JSON.stringify({
@@ -422,8 +460,35 @@ class FrontEnd {
422
460
  })
423
461
  );
424
462
  }
425
- if(data === 'HANDSHAKE-PROTOCAL'){
426
- wsConnection.send(JSON.stringify({handshake: true , id: this.CODE}));
463
+
464
+ if(data.id === 'HANDSHAKE-PROTOCAL'){
465
+ const key = await crypto.subtle.generateKey(
466
+ {
467
+ name: "AES-GCM",
468
+ length: 256, // Key size in bits
469
+ },
470
+ true, // Key is extractable
471
+ ["encrypt", "decrypt"] // Key usage
472
+ );
473
+
474
+ const rawKey = await crypto.subtle.exportKey("raw", key); // Export key as raw bytes
475
+ const base64Key = btoa(String.fromCharCode(...new Uint8Array(rawKey)));
476
+ wsConnection.send(JSON.stringify({handshake: true , id: this.CODE , key: base64Key}));
477
+ }
478
+
479
+ if(data.id === `DATA-${this.CODE}`){
480
+ const dataArray = data.payload;
481
+ console.log(data);
482
+ const binaryString = dataArray.join('');
483
+ let originalString = '';
484
+ for (let i = 0; i < binaryString.length; i += 8) {
485
+ const byte = binaryString.slice(i, i + 8);
486
+ originalString += String.fromCharCode(parseInt(byte, 2));
487
+ }
488
+ const json = JSON.parse(originalString);
489
+ this.dataFromServer = json;
490
+ this.#_notifyListeners();
491
+ wsConnection.send(JSON.stringify({ taskComplete: true , error: false }));
427
492
  }
428
493
  } catch (err) {
429
494
  console.error("Message handling error:", err.message);
package/dist/index.js DELETED
@@ -1,20 +0,0 @@
1
- /*!
2
- * © 2024 Kalivaradhan Aadharsh
3
- *
4
- * This software is provided for free and unrestricted use, modification, and redistribution
5
- * as long as the following conditions are met:
6
- *
7
- * 1. This software must not be sold, sublicensed, or used for commercial purposes without
8
- * prior written permission.
9
- * 2. Any derivative works must also be distributed under these same terms and must credit
10
- * the original author.
11
- * 3. Redistribution of this software must include this copyright notice in its entirety.
12
- *
13
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14
- * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
15
- * PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
16
- * FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR
17
- * OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
- * DEALINGS IN THE SOFTWARE.
19
- */
20
- const StorageManager=(()=>{const generateError=(i,c)=>({error:!0,internal:i,context:c}),LocalStorage={setItem:(k,v)=>{try{return localStorage.setItem(k,JSON.stringify(v)),{error:!1}}catch(e){return generateError(!0,e.message)}},getItem:k=>{try{const v=localStorage.getItem(k);return v?JSON.parse(v):null}catch(e){return generateError(!0,e.message)}},removeItem:k=>{try{return localStorage.removeItem(k),{error:!1}}catch(e){return generateError(!0,e.message)}}},SessionStorage={setItem:(k,v)=>{try{return sessionStorage.setItem(k,JSON.stringify(v)),{error:!1}}catch(e){return generateError(!0,e.message)}},getItem:k=>{try{const v=sessionStorage.getItem(k);return v?JSON.parse(v):null}catch(e){return generateError(!0,e.message)}},removeItem:k=>{try{return sessionStorage.removeItem(k),{error:!1}}catch(e){return generateError(!0,e.message)}}},Cookies={setCookie:(n,v,d)=>{try{const x=d?`; expires=${new Date(Date.now()+d*864e5).toUTCString()}`:"";return document.cookie=`${n}=${encodeURIComponent(v)}${x}; path=/`,{error:!1}}catch(e){return generateError(!0,e.message)}},getCookie:n=>{try{const c=document.cookie.split("; ");for(let t of c){const[a,v]=t.split("=");if(a===n)return decodeURIComponent(v)}return null}catch(e){return generateError(!0,e.message)}},deleteCookie:n=>{try{return document.cookie=`${n}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`,{error:!1}}catch(e){return generateError(!0,e.message)}}},IndexedDB={stk:async(d,s,a,p={},v=1)=>{try{const db=await new Promise((r,j)=>{const o=indexedDB.open(d,v);o.onupgradeneeded=e=>{const x=e.target.result;!x.objectStoreNames.contains(s)&&x.createObjectStore(s,{keyPath:"id",autoIncrement:!0})},o.onsuccess=()=>r(o.result),o.onerror=e=>j(generateError(!0,`Failed to open IndexedDB: ${e.target.error.message}`))});const result=await new Promise((r,j)=>{const t=db.transaction(s,"readwrite"),store=t.objectStore(s);let req;switch(a){case"add":req=store.add(p);break;case"get":req=store.get(p.id);break;case"getAll":req=store.getAll();break;case"update":req=store.put(p);break;case"delete":req=store.delete(p.id);break;default:j(generateError(!1,`Unsupported action: ${a}`));return}req.onsuccess=()=>r({error:!1,result:req.result}),req.onerror=e=>j(generateError(!0,`Action failed: ${e.target.error.message}`))});return db.close(),result}catch(e){return generateError(!0,e.message)}}};return{LocalStorage,SessionStorage,Cookies,IndexedDB}})();function randomCode(){return window.crypto.randomUUID()};function clearAllWebStorageAndCookies(){try{localStorage.clear()}catch(e){console.error("Error clearing localStorage:",e)}try{sessionStorage.clear()}catch(e){console.error("Error clearing sessionStorage:",e)}window.indexedDB&&indexedDB.databases().then(d=>{d.forEach(b=>{indexedDB.deleteDatabase(b.name)})}).catch(e=>{console.error("Error clearing IndexedDB:",e)}),document.cookie.split(";").forEach(c=>{const n=c.split("=")[0].trim();document.cookie=`${n}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/`}),"caches"in window&&caches.keys().then(k=>Promise.all(k.map(c=>caches.delete(c)))).catch(e=>{console.error("Error clearing cache:",e)})}const Middleware=(n,t,p)=>{if("CLEAR-STORAGE"===n)clearAllWebStorageAndCookies();if("REDIRECT"===n){if(!p)return{error:!0,context:"Missing function parameters"};return p.newTab?void window.open(p.link,"_blank"):void(window.location=link)}if("ALERT"===n){if(!p)return{error:!0,context:"Missing function parameters"};return"string"!=typeof p.value?(alert(JSON.stringify(p.value)),{error:!1,taskComplete:!0}):(alert(p.value),{error:!1,taskComplete:!0})}if("LOCAL-STORAGE"===n){if(!p||!t)return{error:!0,context:"Missing function type or parameters"};if("SET"===t){const r=StorageManager.LocalStorage.setItem(p.key,p.value);return r.error?r:{taskComplete:!0,error:!1}}if("GET"===t){const r=StorageManager.LocalStorage.getItem(p.key);return r.error?r:{taskComplete:!0,error:!1,value:r}}if("DELETE"===t){const r=StorageManager.LocalStorage.removeItem(p.key);return r.error?r:{taskComplete:!0,error:!1}}return{error:!0,context:"Given function type for LOCAL-STORAGE did not match any types available"}}if("SESSION-STORAGE"===n){if(!p||!t)return{error:!0,context:"Missing function type or parameters"};if("SET"===t){const r=StorageManager.SessionStorage.setItem(p.key,p.value);return r.error?r:{taskComplete:!0,error:!1}}if("GET"===t){const r=StorageManager.SessionStorage.getItem(p.key);return r.error?r:{taskComplete:!0,error:!1,value:r}}if("DELETE"===t){const r=StorageManager.SessionStorage.removeItem(p.key);return r.error?r:{taskComplete:!0,error:!1}}return{error:!0,context:"Given function type for SESSION-STORAGE did not match any types available"}}if("CLIENT-COOKIE"===n){if(!p||!t)return{error:!0,context:"Missing function type or parameters"};if("SET"===t){const{key:v,value:d,days:x}=p,r=StorageManager.Cookies.setCookie(v,d,x);return r.error?r:{taskComplete:!0,error:!1}}if("GET"===t){const r=StorageManager.Cookies.getCookie(p.key);return r.error?r:{taskComplete:!0,error:!1,value:r}}if("DELETE"===t){const r=StorageManager.Cookies.deleteCookie(p.key);return r.error?r:{taskComplete:!0,error:!1}}return{error:!0,context:"Given function type for CLIENT-COOKIE did not match any types available"}}if("INDEXED-DB"===n){if(!p||!t)return{error:!0,context:"Missing function type or parameters"};const{dbName:v,storeName:x,payload:a,version:m}=p;return StorageManager.IndexedDB.stk(v,x,t,a,m).then(r=>r.error?r:{taskComplete:!0,error:!1,result:r.result}).catch(e=>e)}return{error:!0,context:"Given function name did not match up with any available function"}};class FrontEnd{static instance=null;static CODE=null;constructor(){if(FrontEnd.instance)throw new Error("Only one instance of FrontEnd can be created.");FrontEnd.instance=this,this.CODE=randomCode(),this.disabled=!1}createSocket(s="https://localhost:5764"){if(this.disabled)return;const u=s.startsWith("https://")?s.replace("https","wss"):s,w=new WebSocket(u);w.onmessage=e=>{try{const d=JSON.parse(e.data);if(d.id===`FUNCTION-${this.CODE}`){const{packet:p}=d,[n,t,x]=p.functionArray,r=Middleware(n,t,x);w.send(JSON.stringify({type:`RESPONSE-${this.CODE}`,error:r.error||!1,...r}))}d==="HANDSHAKE-PROTOCAL"&&w.send(JSON.stringify({handshake:!0,id:this.CODE}))}catch(e){console.error("Message handling error:",e.message)}},w.onerror=e=>{console.error("WebSocket Error:",e)},window.addEventListener("beforeunload",()=>{w.close()})}disable(e){this.disabled=e}state(){return this.disabled}}export{FrontEnd};