Speed up Google Apps Script with slow Gmail message interactions

Gmail’s app script API call for getting a thread’s messages takes particularly long (timing captured below), so I’m looking for a way to asynchronously kick off each call in the page_count batch, and then wait for the results of all the .getMessages() calls at the end. Unfortunately, .getMessages() is not natively asynchronous, so how can I make this work?

for (var page_i = 0; page_i < unread_count; page_i += page_count) {
  Logger.log("Page count = " + page_i);
  var inbox_threads = GmailApp.getInboxThreads(page_i, page_count);
  // var inbox_threads = GmailApp.search('in:inbox', page_i, page_count);

  Logger.log("Got threads, getting messages...");
  for (var i = 0; i < inbox_threads.length; i++) {
    var message = inbox_threads[i].getMessages();
    var sender = message[0].getFrom(); 

    if (sender in cObj) {
      cObj[sender]++;
    } else {
      cObj[sender] = 1;
    }
  }
}

Execution log:

3:51:44 AM  Info    Page count = 0
3:51:45 AM  Info    Got threads, getting messages...
3:52:17 AM  Info    Page count = 250
3:52:17 AM  Info    Got threads, getting messages...
3:52:50 AM  Info    Page count = 500
3:52:51 AM  Info    Got threads, getting messages...
3:53:23 AM  Info    Page count = 750

How to Enable Safari Autofill for SMS OTP in Svelte Using BitsUI’s PinInput Component

I’m working on implementing an SMS OTP login in Svelte, similar to Auth0’s login flow. I’m using the PinInput component from BitsUI to handle the OTP input fields. My goal is to enable autofill for the OTP, particularly on iOS where autofill prompts appear after receiving an SMS with the code.

Here is my code:

<script lang="ts">
import { tick } from "svelte";
import { PinInput } from "bits-ui";

let pinInputRef: InstanceType<typeof PinInput.Root>;
let isPinVisible = $state(false);


async function handleSubmit(e: SubmitEvent) {
    e.preventDefault();
    if (!isPinVisible) {
        isPinVisible = true;
        await tick();
        // Find and focus the first PIN input after it becomes visible
        const firstInput = document.querySelector('[data-pin-input-input]') as HTMLInputElement;
        firstInput?.focus();
    }
}
</script>

<div class="flex flex-col justify-start items-center w-full p-6 gap-y-6">
    <form onsubmit={handleSubmit} class="w-full max-w-72 md:max-w-96 flex flex-col relative">
            <input
                type="tel"
                id="phone"
                name="phone"
                placeholder="(123) 456-7890"
                class="bg-white autofill-white text-xl outline-none text-center mb-6"
                autocomplete="tel-national"
                enterkeyhint="next"
                autofocus
            />

            <!-- PIN Input with fade-in animation -->
            <div class="transform transition-all duration-300 ease-out overflow-hidden {isPinVisible ? 'h-24 opacity-100 mb-6' : 'h-0 opacity-0'}">
                <PinInput.Root
                    bind:this={pinInputRef}
                    enterkeyhint="go"
                    autocomplete="one-time-code"
                    class="grid grid-cols-6 gap-2 w-full max-w-lg mx-auto text-lg font-semibold text-neutral-500"
                >
                    {#each Array(6) as _}
                        <PinInput.Input
                            class="aspect-square text-center bg-neutral-50 border-0 rounded-lg focus:ring-0 placeholder:text-neutral-300"
                            inputmode="numeric"
                        />
                    {/each}
                    <PinInput.HiddenInput autocomplete="one-time-code" />
                </PinInput.Root>
            </div>

            <!-- Button with slide-down animation -->
            <button
                type="submit"
                class="bg-neutral-500 text-white px-4 py-2 rounded-full transition-all duration-300 ease-out transform"
            >
                {isPinVisible ? 'Verify' : 'Continue'}
            </button>
    </form>
</div>

I can successfully focus the PIN input element to bring up iOS’s keyboard, but the OTP autofill prompt doesn’t recognize the input fields. I suspect this might be because the PinInput.HiddenInput element is actually handling the autofill, and since it’s hidden, iOS treats this as a security precaution.

How can I modify my code so that iOS recognizes the PinInput fields for OTP autofill? If the hidden input is causing the issue, is there a workaround to make autofill work while using BitsUI’s PinInput component?

How to check if html form submit was successful

I have an html form, and already did the validation, after which the form is submitted.
I want to check if the form was submit successfully, in terms of the internet connection. Because right now, it will say success, even if there is no connection to the internet, in which case i won’t receive the form. I saw a lot of other questions regarding this topic, however they are all about validation, which i already handled.

Here’s the code, in case it matters:

                <form name="kctform" action="http://nsa-us.com:5000/kfu" target="dummy" method="post" class="kct-form-javascript-link">
                    <div class="kform-ff">* Pflichtfelder</div>
                    <label for="name" class="name form-header-element kce">Ihr Name *</label>
                    <label for="name" class="name2 form-header-element bce">Vorname, Nachname *</label>
                    <input class="kform-input empty" name="name" id="name" type="text">
                    <label for="mail" class="mail form-header-element">E-Mail Addresse *</label>
                    <input class="kform-input" name="mail" id="mail" type="email">
                    <label for="phone" class="phone form-header-element bce">Telefon</label>
                    <input class="kform-input bce" name="phone" id="phone" type="text">
                    <label for="address" class="address form-header-element bce">Straße / Nummer *</label>
                    <input class="kform-input bce" name="address" id="address" type="text">
                    <label for="cityCode" class="cityCode form-header-element bce">Postleitzahl *</label>
                    <input class="kform-input bce" name="cityCode" id="cityCode" type="text">
                    <label for="about" class="about form-header-element kce">Betreff *</label>
                    <input class="kform-input kce" name="about" id="about" type="text">
                    <label for="main" class="main form-header-element kce">Ihre Nachricht *</label>
                    <label for="main" class="main2 form-header-element bce">Bewerbung *</label>
                    <textarea class="kform-input" name="main" id="main" type="text"></textarea>
                    <div for="datc" class="dataprot-checkbox-t">Ich stimme zu, dass meine Angaben aus dem Kontaktformular zur Beantwortung meiner Anfrage erhoben und verarbeitet werden. Sie können Ihre Einwilligung jederzeit für die Zukunft per E-Mail an [email protected] widerrufen. Detaillierte Informationen zum Umgang mit Nutzerdaten finden Sie in unserer <a href="../datenschutzerklärung">Datenschutzerklärung</a>.</div>
                    <div class="kform-c-div"><input class="kform-input kform-checkbox" name="datc" id="datc" type="checkbox">
                    <div class="checkDiv"><label for="datc" class="datc kform-checkbox-text">Ja, ich stimme zu. *</label><div class='echeckbox'>Checkbox ist ein Pflichtfeld!</div></div></div>
                    <button class="submitButton" type="submit">Senden</button>
                </form>
                <iframe name="dummy" id="dummy" style="display: none;"></iframe>
            </div>
    const kctForm = document.querySelector('.kct-form-javascript-link');
    kctForm.addEventListener('submit', e => {
        e.preventDefault();
        const checkbox = document.querySelector('.kform-input#datc');
        let sent = false;
        let send = true;
        if (f === 'ktf') {
            const name = kctForm.elements[0].value;
            const mail = kctForm.elements[1].value;
            const about = kctForm.elements[5].value;
            const main = kctForm.elements[6].value;
            const nameC = document.querySelector('.name');
            const mailC = document.querySelector('.mail');
            const aboutC = document.querySelector('.about');
            const mainC = document.querySelector('.main');
            let formElement = [name, mail, about, main];
            let formElementC = [nameC,mailC,aboutC,mainC];
            const varIsEmpty = str => !str.replace(/s/g, '').length
            for (let i = 0; i < formElement.length; i++) {
                let formE = formElement[i];
                let formC = formElementC[i];
                if (varIsEmpty(formE)) {
                    formC.classList.add('empty');
                    send = false;
                }
                else {
                    formC.classList.remove('empty');
                };
            };
        }
        else if (f === 'bef') {
            const name = kctForm.elements[0].value;
            const mail = kctForm.elements[1].value;
            //const phone = kctForm.elements[2].value;
            const address = kctForm.elements[3].value;
            const cityCode = kctForm.elements[4].value;
            const main = kctForm.elements[6].value;
            const nameC = document.querySelector('.name2');
            const mailC = document.querySelector('.mail');
            //const phoneC = document.querySelector('.phone');
            const addressC = document.querySelector('.address');
            const cityCodeC = document.querySelector('.cityCode');
            const mainC = document.querySelector('.main2');
            let formElement = [name, mail, /*phone, */address, cityCode, main];
            //let formElementT = ['name', 'mail', /*'phone',*/'address', 'cityCode', 'main'];
            let formElementC = [nameC,mailC,/*phoneC,*/addressC,cityCodeC,mainC];
            const varIsEmpty = str => !str.replace(/s/g, '').length;
            for (let i = 0; i < formElement.length; i++) {
                let formE = formElement[i];
                let formC = formElementC[i];
                if (varIsEmpty(formE)) {
                    formC.classList.add('empty');
                    send = false;
                }
                else {
                    formC.classList.remove('empty');
                };
            };
        };
        function checkboxF() {
            if (!checkbox.checked || window.innerWidth <= 511 && send === false) {
                document.querySelector('.echeckbox').classList.add('active');
                document.querySelector('.kform-c-div').classList.add('active');
            }
            else if (checkbox.checked && !window.innerWidth <= 511) {
                document.querySelector('.echeckbox').classList.remove('active');
                document.querySelector('.kform-c-div').classList.remove('active');
            };
            if (!checkbox.checked) {
                send = false;
            };
        };
        function changeEmptyWarningsBasedOnWidth() {
            if (window.innerWidth <= 511) {
                document.querySelector('.echeckbox').innerHTML = 'Pflichtfelder wurden nicht ausgefüllt!';
            }
            else {
                document.querySelector('.echeckbox').innerHTML = 'Checkbox ist ein Pflichtfeld!';
            };
        };
        checkboxF();
        changeEmptyWarningsBasedOnWidth();
        window.addEventListener('resize', changeEmptyWarningsBasedOnWidth, false);
        window.addEventListener('resize', checkboxF, false);
        if (send === true && sent === false) {
            e.submit();
            const submitButton = document.querySelector('.kform-div form .submitButton');
            submitButton.classList.add('success');
            submitButton.innerHTML = 'Gesendet!';
            sent = true;
            setTimeout(() => {
                document.kctform.reset();
            },2000);
            setTimeout(() => {
                submitButton.classList.remove('success');
                submitButton.innerHTML = 'Senden';
                sent = false;
            },10000);
        };

ERROR TypeError: Cannot read properties of null (reading ‘getAttribute’)

When data is empty i get this error-> ERROR TypeError: Cannot read properties of null (reading ‘getAttribute’)

Error:

chunk-UOKKXAU3.js:7 ERROR TypeError: Cannot read properties of null (reading 'getAttribute')
    at wT (chunk-FLTRA2LT.js:2:30682)
    at VM (chunk-FLTRA2LT.js:15:65614)
    at nnt (chunk-FLTRA2LT.js:15:65536)
    **at n.drawGraph (chunk-EXIXHUOH.js:33:1018)**
    at Object.next (chunk-EXIXHUOH.js:18:15207)
    at io.next (chunk-UOKKXAU3.js:3:3101)
    at we._next (chunk-UOKKXAU3.js:3:2784)
    at we.next (chunk-UOKKXAU3.js:3:2511)
    at Object.next (chunk-JDCFQ7WB.js:26:3066)
    at io.next (chunk-UOKKXAU3.js:3:3101)

The highlighted line no takes me to this code:

 drawGraph() {
    type EChartsOption = echarts.EChartsOption;
    **var chartDom = document.getElementById('networkGraph')!;** // error
    echarts.dispose(chartDom);
    this.mychart = echarts.init(chartDom);
......
.....
}

And when there is some data, I get this error-> ERROR TypeError: Cannot set properties of undefined (setting’dataIndex’)

I get this error when there is some data.

I am not getting why these error are coming. I would highly appreciate if someone can help.

Data received from server:

{
    "headers": {
        "normalizedNames": {},
        "lazyUpdate": null,
        "lazyInit": null,
        "headers": {}
    },
    "status": 200,
    "statusText": "OK",
    "url": "http://ip:port/handler/?action=trafficVisualizationServiceData",
    "ok": true,
    "type": 4,
    "body": {
        "httpstatuscode": 200,
        "AppData": {
            "data": [],
            "link": [],
            "categories": [
                {
                    "name": "CNF"
                },
                {
                    "name": "SCPE"
                },
                {
                    "name": "SCPI"
                },
                {
                    "name": "IPENDPOINT"
                }
            ]
        },
        "opStatusCode": 2000,
        "type": "SUCCESS",
        "message": "DATA RECEIVED SUCCESSFULLY"
    }
}

app.ts:

  drawGraph() {
    type EChartsOption = echarts.EChartsOption;
    var chartDom = document.getElementById('networkGraph')!;
    echarts.dispose(chartDom);
    this.mychart = echarts.init(chartDom);

    interface GraphNode {
      symbolSize: number;
      label?: {
        show?: boolean;
      };
    }

    this.mychart.showLoading();

    (this.graph = {
      nodes: this.nodes,
      links: this.links,
      categories: this.categories,
    }),
      this.mychart.hideLoading();
    console.log('nodes : ' + JSON.stringify(this.graph.nodes));

    let scpEgressThresholdTps = this.scpEgressThresholdTps;

    // this.impactedProxyList.has("")
    let impactedProxyListTemp: Set<string> = this.impactedProxyList;

    this.graph.nodes.forEach(function (node: any) {
      if (node.category == 'IPENDPOINT') {
        node.symbol = 'roundRect';
        node.symbolSize = 17;

        console.log('endpoint_kpi : ' + node.kpi);

        if (node.kpi === undefined) {
          node.itemStyle = {
            color: 'white', // Background color
            borderColor: '#818181', // Border color
            borderWidth: 1, // Border width
          };
        } else {
          node.itemStyle = {
            color: 'white', // Background color
            borderColor: '#FD5E5E',
            borderWidth: 5, // Border width
          };
        }
      } else if (node.category == 'SCPI') {
        node.symbol = 'circle';
        node.symbolSize = 20;

        let isProxyImpacted: boolean = false;
        Object.entries(impactedProxyListTemp).forEach(([key, value]) => {
          console.log('key_value : ' + key, value);
          if (value == node.proxyId) {
            isProxyImpacted = true;
          }
        });

        console.log('isProxyImpacted : ' + isProxyImpacted);
        if (isProxyImpacted) {
          console.error('this proxy is impacted.');
          node.itemStyle = {
            color: '#7EA6FF', // Background color
            borderColor: '#FD5E5E', // Border color
            borderWidth: 5, // Border width
          };
        } else if (node.value > scpEgressThresholdTps) {
          node.itemStyle = {
            color: '#7EA6FF', // Background color
            borderColor: '#fee902', // Border color
            borderWidth: 5, // Border width
          };
        } else {
          node.itemStyle = {
            color: '#7EA6FF', // Background color
            borderWidth: 1, // Border width
          };
        }
      } else if (node.category == 'SCPE') {
        node.symbol = 'circle';
        node.symbolSize = 20;

        let isProxyImpacted: boolean = false;
        Object.entries(impactedProxyListTemp).forEach(([key, value]) => {
          console.log('key_value : ' + key, value);
          if (value == node.proxyId) {
            isProxyImpacted = true;
          }
        });

        if (isProxyImpacted) {
          console.error('this proxy is impacted.');
          node.itemStyle = {
            color: '#3188FF', // Background color
            borderColor: '#FD5E5E', // Border color
            borderWidth: 5, // Border width
          };
        } else if (node.value > scpEgressThresholdTps) {
          node.itemStyle = {
            color: '#3188FF', // Background color
            borderColor: '#fee902', // Border color
            borderWidth: 5, // Border width
          };
        } else {
          node.itemStyle = {
            color: '#3188FF', // Background color
            borderWidth: 1, // Border width
          };
        }
      } else if (node.category == 'CNF') {
        node.symbol = 'roundRect';
        node.symbolSize = 26;
        node.itemStyle = {
          color: '#00A1BE', // Background color
          borderWidth: 1, // Border width
        };
      } else {
        node.symbol = 'circle';
        node.symbolSize = 13;
        node.itemStyle = {
          borderColor: '#5E42FA', // Border color
          borderWidth: 1, // Border width
        };
      }
    });

    this.graph.links.forEach(function (link: any) {
      console.log('isLinkImpacted : ' + link.isLinkImpacted);

      if (link.isLinkImpacted === undefined) {
        link.lineStyle = {
          width: 2, // Set width of the line
          type: 'solid', // Set type of the line to solid
          color: 'green', // Set color of the line
        };
      } else {
        link.lineStyle = {
          width: 2, // Set width of the line
          type: 'solid', // Set type of the line to solid
          color: 'red', // Set color of the line
        };
      }
    });


    this.mychart.setOption(this.option);
    this.option && this.mychart.setOption(this.option);
 
  }

HTML:

<div *ngIf="showConnectedGraph && isConnectedGraphDataAvailable " id="networkGraph" style="width: 100%; border-radius: 5px; height: 550px; background-color: white;"></div>
<div class="nodata" *ngIf="(showConnectedGraph) && (!isConnectedGraphDataAvailable)" style="width: 100%; border-radius: 5px; height: 550px; background-color: white;">Data Not Available</div>

How to Execute JavaScript Line by Line While Preserving Scope?

II am building a custom JavaScript debugger that allows users to execute code line by line, maintain scope across lines, and implement debugger functionalities like stepping into/over code, breakpoints, etc.

I attempted to execute code line by line using eval, but I encountered scope issues where variables and functions declared on one line were not accessible on subsequent lines. Here’s a minimal reproducible example of the problem:

let codeString = `
  function test(param){
    return param;
  }
  console.log(test("param"));
`;

let codeLines = codeString.split("n"); // Split code into lines

function execute() {
  for (let line of codeLines) {
    if (line.trim()) {
      try {
        eval(line); // Execute each line independently
      } catch (error) {
        console.error("Error executing line:", line, error);
      }
    }
  }
}

execute();

When running this code:

  1. Scope Issue: The test function declared on the first line is not accessible to the console.log statement on the second line because eval executes each line in isolation.

  2. Expected Behavior: I want each line to be executed in a way that preserves the shared execution context (like in a real JavaScript runtime).

How can I execute JavaScript code line by line while maintaining the scope and context between lines?

I am specifically looking for:

  • A way to preserve variable/function declarations across lines.

  • A method to enable low-level control for implementing debugging features like stepping and breakpoints.

I’ve tried exploring alternatives like the Function constructor but ran into similar scope issues. Any suggestions or insights would be greatly appreciated!

Nest Error Handling not Catching Errors when AuthGuard is Imported from my Custom Package

Problem:

I have a custom NPM package comprising utilities shared across different microservices built with Nest JS. The problem is that when AuthGuard throws an error with some data from my package, the Nest application catches it but only throws the default error. I am throwing the HttpException error with some data and I want the Nest application to throw the same data as response instead of the default.

Code:

The auth guard in the package is a simple guard that verifies the token and returns a response or an error

import { CanActivate, ExecutionContext, Injectable, HttpException } from '@nestjs/common';
import { verifyToken } from '../helpers';
@Injectable()
export class AuthGuard implements CanActivate {
  constructor() {}
  async canActivate(context: ExecutionContext): Promise<any> {
    const request = context.switchToHttp().getRequest();
    try {
      const token = request.headers.authorization?.split(' ')[1];
      return await verifyToken(
        token,
      );
    } catch (error) {
      console.log('⛈️⛈️⛈️⛈️⛈️⛈️', error);
       throw new HttpException(
        {
          errorDetails: "Token Expired",  
        },
        401,
      );    
  }
}

By this logic, I should be getting the error in the format of

{
errorDetails:"Token Expired"
}

but the NEST return a default error

{
    "statusCode": 500,
    "message": "Internal server error"
}

I have tried to create a Nest Module for the AuthGuard and import it into my app.module but it didn’t work. I have created global exception filter for it but it didn’t work also.

The same code works if I place it in a file inside my application.

How to alter Javascript code from SFTP to HTTPS to transfer file

I have SFTP code using Javascript for transfering file from our local on-prem server to cloud server (in this case is from ServiceNow MID Server to AWS S3). But I am required to transfer by using HTTPS protocol (port : 443). Question, by using below information,

  1. Can I reused same script but just changing port from 22 to 443?
  2. If cant, then how I can alter the existing code from SFTP into HTTPS?

Endpoint : sn-bucket-stg.vpce-xxx-xxx.s3.us-east-1.vpce.amazonaws.com
Port : 443
Username : sn-aws-user
Password : xxxxxxxxx
Objective : Transfer file A.pdf from Folder C:/AWS to S3 Bucket sn-bucket-stg/SNFolder

SFTP code :

var SFTP_Test = Class.create();
SFTP_Test.prototype = {
    initialize: function() {
        this.Properties = Packages.java.util.Properties;
        this.StringUtil = Packages.com.glide.util.StringUtil;
        this.BufferedWriter = Packages.java.io.BufferedWriter;
        this.File = Packages.java.io.File;
        this.FileWriter = Packages.java.io.FileWriter;
        this.FileReader = Packages.java.io.FileReader;
        this.Encrypter = new Packages.com.glide.util.Encrypter();
        this.FileOutputStream = Packages.java.io.FileOutputStream;
        this.FileInputStream = Packages.java.io.FileInputStream;
        this.BufferedReader = Packages.java.io.BufferedReader;
        this.InputStreamReader = Packages.java.io.InputStreamReader;
        this.OutputStraem = Packages.java.io.OutputStream;
        this.OutputStreamWriter = Packages.java.io.OutputStreamWriter;
        this.BufferedOutputStream = Packages.java.io.BufferedOutputStream;
        this.Thread = Packages.java.lang.Thread;

        this.targetServer = "sn-bucket-stg.vpce-xxx-xxx.s3.us-east-1.vpce.amazonaws.com";
        this.targetPort = "22";
        this.targetUsername = "sn-aws-user";
        this.targetPassword = "xxxxxxxxxxx";
        this.targetPath = "SNFolder";
        this.FILE_PATH = "C:/AWS";
        this.FILE_NAME = "A.pdf";
        this.TARGETFILENAME = "SN_A.pdf";
        this.PROCESSED_PATH = "C:/SNFolder/Backup";
        this.FILE_EXTENSION = "pdf";

    },


    FileTransfer: function() {
        try {
            var localFileName = this.FILE_PATH + "/" + this.FILE_NAME;
            var processedFileName = this.PROCESSED_PATH + "/" + this.TARGETFILENAME;
            this.log("Copying from local file : " + localFileName);

            this.sftpFile(this.targetServer, this.targetUsername, this.targetPassword, localFileName, this.targetPath + this.TARGETFILENAME, processedFileName);

        } catch (e) {
            this.log("Error : " + e);
        }
    },


    sftpFile: function(hostName, userName, password, localFileName, remoteFileName, processedFileName) {
        if (!this.targetPort) {
            this.targetPort = 22;
        }
        
        var authPassword = new Packages.com.glide.util.Encrypter().decrypt(password);
        
    var jsch = new Packages.com.jcraft.jsch.JSch();
    var jschSession = jsch.getSession(userName, hostName);
    jschSession.setPassword(authPassword);
    var config = this.Properties();
    config.put("StrictHostKeyChecking", "no");
    jschSession.setConfig(config);
    jschSession.connect();
        
    var channelSftp = jschSession.openChannel("sftp");
    channelSftp.connect();
         
            try {

                var inputFile = new this.File(localFileName);

                if (this.FILE_EXTENSION == 'xlsx') {
                    
                    var inputStream = this.FileInputStream(inputFile);
                    var outputStream = this.FileOutputStream(processedFileName);
                    var byteRead;
                    while ((byteRead = inputStream.read()) != -1) {
                        outputStream.write(byteRead);
                    }
                } else {
                    var fileReader = this.FileReader(inputFile); // A stream that connects to the text file
                    var bufferedReader = this.BufferedReader(fileReader); // Connect the FileReader to the BufferedReader
                    //create new file
                    // Creates a FileOutputStream
                    var outputFile = this.FileOutputStream(processedFileName);
                    // Creates an OutputStreamWriter
                    var output = this.OutputStreamWriter(outputFile);
                    var line;
                    while ((line = bufferedReader.readLine()) != null) {
                        
                        // Writes string to the file
                        output.write(line);
                        output.write("rn");
                    }

                    // Closes the reader
                    bufferedReader.close();
                    // Closes the writer
                    output.close();
                    this.log('copied file to ' + processedFileName);
                }
                //}

                //END
                channelSftp.put(processedFileName, remoteFileName);

                this.log("File has successfully copied to target path");

            } catch (e) {
                this.log('Error: ' + e);
            }
        channelSftp.exit();
            try {
                // kill connection
                jschSession.disconnect();
            } catch (e) {
                this.log('Error: ' + e);
            }
        //}

    },

    type: 'SFTP_Test'
};

Any ways to export and import annotation in PDF as JSON using Mozilla PdfJs Viewer

I recently tried annotating in PDF Viewer provided Mozilla where I can annotate and save the annotations within the pdf.

But my use case is to annotate and export the annotation as JSON, then I want to import it back while rendering the PDF.

I have tried,

// Gets all the annotation data as a map that can be exported as JSON
annotations = pdfDocument.annotationStorage.serializable(); 

// Tried loading the Annotations by setting it after the new render
pdfjsLib.getDocument({
  data: pdfData,
  cMapUrl: CMAP_URL,
  cMapPacked: CMAP_PACKED,
  enableXfa: true,
}).promise.then((_pdfDocument) => {
  for (let [key, value] of annotations) {
    pdfDocument.annotationStorage.setValue(key,value);
   }
  })

This stores the annotation to the storage, but it is not rendered in UI. Does anyone have the same use case and tried importing and exporting PDF annotation in Mozilla PDF Js?

Is there any method in the PDFViewer class to import the serialized annotation?

Your small input will be greatly appreciated. Thanks for reading my question.

Only for vue files with tag error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403

Getting the below given error when a vue file that contains code in tag is being pushed. The whole folder structure is only below 2mb. all js and json files are able to successfully push into the repo.
Authentication is done using personal access token.
Authenticate with GitHub using a token

  $ git push origin tmp/frontend
    Enumerating objects: 10, done.
    Counting objects: 100% (10/10), done.
    Delta compression using up to 8 threads
    Compressing objects: 100% (7/7), done.
    Writing objects: 100% (7/7), 1.39 KiB | 476.00 KiB/s, done.
    Total 7 (delta 2), reused 0 (delta 0), pack-reused 0
    error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403
    send-pack: unexpected disconnect while reading sideband packet
    fatal: the remote end hung up unexpectedly
    Everything up-to-date

How to implement Multiple tooltip or label for bargraphs using react-echarts

I have implemented a react-echarts bargraph with a tooltip on hover of the the graph, and it should show another label on hover of the tooltip, but not working as expected.

I tried with onMouseover events but it is not working as expected, I wanted a bargraph with a tooltip on hover of the graph and on hover of the tooltip it should show additional details of the graph as another tooltip beside the first one.

import React, { useState } from "react";
import ReactDOM from "react-dom";
import ReactECharts from "echarts-for-react";

const BarChartUpdated = ({ data, label, categories }: any) => {
  const [tooltipData, setTooltipData] = useState<{
    x: number;
    y: number;
    details: string;
    isVisible: boolean;
  } | null>(null);

  const generateColors = (categories: string[]) => {
    const colorMap: Record<string, string> = {};
    const colorPalette = [
      "#5470C6",
      "#91CC75",
      "#EE6666",
      "#FAC858",
      "#73C0DE",
      "#9A60B4",
      "#EA7CCC",
    ];
    categories.forEach((category, index) => {
      if (!colorMap[category]) {
        colorMap[category] = colorPalette[index % colorPalette.length];
      }
    });

    return colorMap;
  };

  const categoryColors = generateColors(categories);
  const sanitizedData = data.map((value: number) => (isNaN(value) ? 0 : value));
  const total = sanitizedData.reduce(
    (sum: number, value: number) => sum + value,
    0,
  );

  const option = {
    title: {
      text: label,
    },
    tooltip: {
      trigger: "axis",
      formatter: (params: any) => {
        return params
          .map(
            (item: any) =>
              `<b>${item.name}</b></br> Count: ${item.value > 0 ? item.value : 0}`,
          )
          .join("<br/>");
      },
    },
    toolbox: {
      feature: {
        saveAsImage: { show: true, title: "Save as Image" },
        dataZoom: { show: true, title: "Zoom" },
      },
    },
    xAxis: {
      type: "category",
      data: categories,
    },
    yAxis: {
      type: "value",
    },
    series: [
      {
        type: "bar",
        data: data.map((value: number, index: number) => ({
          value,
          itemStyle: {
            color: categoryColors[categories[index]],
          },
          label: {
            show: true,
            position: "top",
            formatter:
              total <= 0 ? "0%" : `${Math.ceil((value / total) * 100)}%`,
          },
        })),
      },
    ],
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
    },
  };

  const handleMouseOver = (event: any) => {
    if (event && event.data) {
      const { name, value } = event.data;
      const { offsetX, offsetY } = event.event; 
      setTooltipData({
        x: offsetX,
        y: offsetY,
        details: `More details for ${name}: Count ${value}`,
        isVisible: true,
      });
    }
  };


  const handleMouseOut = () => {
    setTooltipData((prev) => (prev ? { ...prev, isVisible: false } : null));
  };

  const handleTooltipMouseEnter = () => {
    setTooltipData((prev) => (prev ? { ...prev, isVisible: true } : null));
  };

  const handleTooltipMouseLeave = () => {
    setTooltipData(null);
  };

  return (
    <>
      <ReactECharts
        option={option}
        style={{ height: 400, width: "100%" }}
        onEvents={{
          mouseover: handleMouseOver,
          mouseout: handleMouseOut,
        }}
      />
      {tooltipData &&
        ReactDOM.createPortal(
          tooltipData.isVisible && (
            <div
              onMouseEnter={handleTooltipMouseEnter}
              onMouseLeave={handleTooltipMouseLeave}
              style={{
                position: "fixed",
                top: tooltipData.y,
                left: tooltipData.x,
                transform: "translate(-50%, -100%)",
                background: "white",
                border: "1px solid #ccc",
                padding: "10px",
                borderRadius: "4px",
                boxShadow: "0 2px 4px rgba(0,0,0,0.2)",
                zIndex: 1000,
              }}
            >
              {tooltipData.details}
            </div>
          ),
          document.body,
        )}
    </>
  );
};

export default BarChartUpdated;

How to sort a nested array of objects in karate framework

First need to sort array accountPeriodDetails by accountSystemID, and for each accountSystemID need to sort the array monthlySummary by paymentReportDate

``
`*def response =
'''
{
  "organization": {
    "accountPeriodDetails": [
      {
        "summaryPeriod": "P25M",
        "accountSystemID": "2",
        "tokenizedAccountNumber": "1X68MG14Z7Y5POID",
        "monthlySummary": [
          {
            "paymentReportDate": "2023-09-17",
            "accountTypeCode": "3",
            "currentCreditLineAmount": null,
            "mostRecentPaymentAmount": 7000,
            "currentBalanceAmount": 63198,
            "scheduledPaymentAmount": 0,
            "newChargesAmount": null,
            "totalPastDueAmount": 0
          },
          {
            "paymentReportDate": "2023-08-17",
            "accountTypeCode": "3",
            "currentCreditLineAmount": null,
            "mostRecentPaymentAmount": 10000,
            "currentBalanceAmount": 56522,
            "scheduledPaymentAmount": 0,
            "newChargesAmount": null,
            "totalPastDueAmount": 0
          }
        ]
      },
      {
        "summaryPeriod": "P25M",
        "accountSystemID": "3",
        "tokenizedAccountNumber": "MP3GOU0VQC4V5QQ0Z9GZVQUZNKH",
        "monthlySummary": [
          {
            "paymentReportDate": "2024-10-31",
            "accountTypeCode": "2",
            "currentCreditLineAmount": 100000,
            "mostRecentPaymentAmount": 100,
            "currentBalanceAmount": 76035,
            "scheduledPaymentAmount": null,
            "newChargesAmount": null,
            "totalPastDueAmount": 0
          },
          {
            "paymentReportDate": "2024-04-30",
            "accountTypeCode": "2",
            "currentCreditLineAmount": 100000,
            "mostRecentPaymentAmount": 432,
            "currentBalanceAmount": 40135,
            "scheduledPaymentAmount": null,
            "newChargesAmount": null,
            "totalPastDueAmount": 0
          }
        ]
      }
    ]
  }
}
  • def accountPeriodDetails = response1.organization.sbfeInformation.accountPeriodDetails
  • print accountPeriodDetails
  • def sorted1 = karate.sort(accountPeriodDetails, x => x.accountSystemID)
  • print sorted1`

I have tried to sort using karate.sort(accountPeriodDetails, x => x.accountSystemID), getting error
<eval>:1:36 Expected ,** but found =>**
karate.sort(accountPeriodDetails, x => x.accountSystemID)

Any javascript function solution will also be helpful

How to Implement Tab Overflow with Dropdown Menu in React (Similar to SAPUI5 IconTabBar)?

I’m working on a React application, and I need to implement tab navigation similar to SAPUI5’s IconTabBar component with an overflow behavior.

In the SAPUI5 example (link below), when there are too many tabs to fit in the available space, the excess tabs are moved to a dropdown/select list.

Example Link: SAPUI5 IconTabBar Overflow Example

How can I implement tabs that will show in a row, and if there are too many to fit, an overflow menu (select list) will appear to allow users to select the remaining tabs.

Are there any existing libraries or components I can use that implement this behavior easily?

How to make a complex JS library

I want to make and understand complex JS libraries like swiper or @apollo-client. It is quite obscure to search online and it goes without saying that my college classes did not include it.

I am willing to take the time and effort to learn and understand it.

I suppose I could peer into a node_modules folder and analyze, but surely there must have been a class or set of documents online that teach about this.

Where may I find the place where I can learn about building and understanding a complex JS library?