Error 404 sending access token to Classroom API

In the authentication process for accessing user resources through the Classroom API, I get a 404 error return right on the Ajax sending of the access token.

I have successfully completed the previous steps. That is, the token request with the granted permissions and remaining parameters, its processing to finally send it to the Classroom API and be able to access the resources.

The cause of the error is the string that represents the destination URL of the Ajax sending.

I have tried multiple strings but it always returns the 404 error. I have also been unable to find the solution by comparing it with the strings used for the Drive or Calendar API (which appear in the examples in the documentation on the Google Identity website (https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow?hl=es-419).

The Ajax submission is done in the following function:

async function trySampleRequest() {
    var params = JSON.parse(localStorage.getItem('oauth2-test-params'));
    
    if (params && params['access_token']) {
        // User authorized the request. Now, check which scopes were granted.
    
        if            
        (params['scope'].includes('https://www.googleapis.com/auth/classroom.courses')) {
            // User authorized read-only Drive activity permission.
            // Calling the APIs, etc.
            console.log("Include scope");
            var xhr = new XMLHttpRequest();
            xhr.open('GET',
                'crossDomain : true',
                'https://www.googleapis.com/classroom/v1/about?fields=user&' +
                'access_token=' + params['access_token']);
            xhr.onreadystatechange = function (e) {
                if (xhr.readyState === 4 && xhr.status === 200) {
                  
                } else if (xhr.readyState === 4 && xhr.status === 401) {
                    // Token invalid, so prompt for user permission.
                    oauth2SignIn();
                }
            };
            xhr.send(null);
        }

    }
}

The third argument of the open method of the XMLHttpRequest() object has the value:

‘https://www.googleapis.com/classroom/v1/about?fields=user&’ +
‘access_token=’ + params[‘access_token’]

The string fragment: ‘https://www.googleapis.com/classroom/v1/about?fields=user’
I have formed it by observing the string that the documentation uses in the case of the Drive API and adapting it to the values ​​that I have assumed should be those of the Classroom API.
The string fragment:

‘&’ + ‘access_token=’ + params[‘access_token’]’

It does not generate any doubt since it is the access_token parameter and is well formed.

Screenshot of the error displayed in the browser console

I have tried many options. Among others the following:

‘https://www.googleapis.com/classroom/v1/about?’

‘https://classroom.googleapis.com/classroom/v1/about?’

‘https://www.googleapis.com/classroom/v1?’

‘https://classroom.googleapis.com/classroom/v1?’

None of them work.

Could someone please tell me in which help document I could find information about this?

Thanks for your help!

incorrect base64 write/read with expo-file-system?

Here is a reproducible code: https://gist.github.com/somidad/1c347b650039a1a140baf6f232683bbe

Based on the result, what I understand is that:

  • If the length is multiple of 4, writing succeeds and reading correctly recovers the original string
  • Else if the length modulo 4 is equal to 1, writing raises an error
  • Else if the length modulo 4 is equal to 2 or 3, writing and reading did work, but the last character gets corrupted (and padding follows)

Is it expected behavior? Or am I missing something to get it to work as expected?

For those who may not want to visit my gist, I copied and pasted it below:

Code

import {
  documentDirectory,
  readAsStringAsync,
  writeAsStringAsync,
} from "expo-file-system";
import { Button, View } from "react-native";

export default function Temp() {
  async function temp(s: string) {
    try {
      await writeAsStringAsync(documentDirectory + "temp.txt", s, {});
      await writeAsStringAsync(documentDirectory + "temp.bin", s, {
        encoding: "base64",
      });
      const t2t = await readAsStringAsync(documentDirectory + "temp.txt", {});
      const b2b = await readAsStringAsync(documentDirectory + "temp.bin", {
        encoding: "base64",
      });
      console.log("Original".padEnd(48, " "), s);
      console.log("  Write as text, read as text:".padEnd(48, " "), t2t);
      console.log("  Write as base64, read as base64:".padEnd(48, " "), b2b);
    } catch (error) {
      console.error(error);
    }
  }

  return (
    <View style={{ padding: 8 }}>
      <Button
        title="Temp"
        onPress={async () => {
          console.log("Test");
          const s = "0123456789abcdef";
          for (let i = 1; i <= s.length; i++) {
            await temp(s.substring(0, i));
          }
          console.log("========");
        }}
      />
    </View>
  );
}

Result (log)

 (NOBRIDGE) LOG  Test
 (NOBRIDGE) ERROR  [Error: Call to function 'ExponentFileSystem.writeAsStringAsync' has been rejected.
→ Caused by: java.lang.IllegalArgumentException: bad base-64]
 (NOBRIDGE) LOG  Original                                         01
 (NOBRIDGE) LOG    Write as text, read as text:                   01
 (NOBRIDGE) LOG    Write as base64, read as base64:               0w==
 (NOBRIDGE) LOG  Original                                         012
 (NOBRIDGE) LOG    Write as text, read as text:                   012
 (NOBRIDGE) LOG    Write as base64, read as base64:               010=
 (NOBRIDGE) LOG  Original                                         0123
 (NOBRIDGE) LOG    Write as text, read as text:                   0123
 (NOBRIDGE) LOG    Write as base64, read as base64:               0123
(Omitted logs showing the same pattern)

Why do I get an absolute URL error in xero oauth for a valid URL?

I am using xero-node for javascript development but I am getting an error when I try and create the xero client. I call this code.

const consentUrl = await xero.buildConsentUrl()

I create the xero client with this code:

xero = new XeroClient({
  clientId: clientId,
  clientSecret: clientSecret,
  redirectUris: URL_REDIRECTS,
  scopes: ['openid', 'profile', 'projects']
  //state: 'not_currently_used', // custom params (optional)
  //httpTimeout: 3000 // ms (optional)
})

This throws the error:
index-C0C0EiKl.js:117 Uncaught (in promise) TypeError: only valid absolute URLs can be requested.

Looking at the debug I see it gets thrown in this code.

try {
    t = new DX1(e.url),  //thrown here
    delete e.url,
    RX1(/^(https?:)$/.test(t.protocol))
} catch {
    throw new TypeError("only valid absolute URLs can be requested")
}

The e.url that it is using when it calls this code is https://identity.xero.com/.well-known/openid-configuration. When I put this URL in my browser it returns valid configuration data. So I have no idea why this URL is unacceptable.

I’d appreciate any help from a xero expert here please.

Deletion from DB and storage issue

I’m building a website where users can create, edit, and share quizzes using unique links. Creator of quiz have the option to edit quiz questions or delete them as per their requirements. However, I’m encountering two issues related to handling these features with my Supabase database:

Issue 1: Duplicates When Updating Questions
When a quiz question is updated, the changes are saved in the database, but the updated question gets duplicated instead of replacing the original. I suspect this might be due to how I’m handling updates in the database.

Issue 2: Questions Not Deleting Properly
When I try to delete a quiz question, it doesn’t actually get removed from the database. There are no errors in the console, so I’m having difficulty identifying the root cause of the problem.

I’ve tried debugging and searching for solutions but haven’t been able to fix either issue. Below is the relevant code I’m using to handle updates and deletions:

const QuizCreator = ({ quizId, onCancel }: QuizCreatorProps) => {
  const [quizTitle, setQuizTitle] = useState("");
  const [questions, setQuestions] = useState<Question[]>([]);
  const { toast } = useToast();
  const { theme, isDark } = useTheme();
  const [deletedQuestions, setDeletedQuestions] = useState<Set<string>>(new Set());

  useEffect(() => {
    const fetchQuizData = async () => {
      if (!quizId) return;

      try {
        const { data: quiz, error: quizError } = await supabase
          .from("quizzes")
          .select("*")
          .eq("id", quizId)
          .single();

        if (quizError) throw quizError;

        setQuizTitle(quiz.title);

        const { data: questionData, error: questionsError } = await supabase
          .from("questions")
          .select("*")
          .eq("quiz_id", quizId);

        if (questionsError) throw questionsError;

        if (questionData) {
          const formattedQuestions: Question[] = questionData.map((q) => ({
            id: q.id,
            question: q.question,
            imageUrl: q.image_url,
            options: jsonToQuizOptions(q.options),
            correctAnswer: q.correct_answer,
            answerExplanation: q.answer_description,
          }));
          setQuestions(formattedQuestions);
        }
      } catch (error) {
        console.error("Error fetching quiz:", error);
        toast({
          title: "Error",
          description: "Failed to load quiz data",
          variant: "destructive",
        });
      }
    };

    fetchQuizData();
  }, [quizId, toast]);

  const handleSubmit = async (e: React.FormEvent, isDraft: boolean = false) => {
    e.preventDefault();
    try {
      const { data: { user } } = await supabase.auth.getUser();

      if (!user) {
        toast({
          title: "Error",
          description: "You must be logged in to create a quiz",
          variant: "destructive",
        });
        return;
      }

      const processedQuestions = questions.map((q) => ({
        id: q.id,
        ...q,
        options: quizOptionsToJson(q.options),
        correctAnswer: q.correctAnswer.toUpperCase(),
      }));

      let quizData;
      if (quizId) {
        const { data: quiz, error: quizError } = await supabase
          .from("quizzes")
          .update({ title: quizTitle, is_draft: isDraft })
          .eq("id", quizId)
          .select()
          .single();

        if (quizError) throw quizError;
        quizData = quiz;
      } else {
        const { data: quiz, error: quizError } = await supabase
          .from("quizzes")
          .insert({ title: quizTitle, is_draft: isDraft, creator_id: user.id })
          .select()
          .single();

        if (quizError) throw quizError;
        quizData = quiz;
      }

      // Delete removed questions
      if (deletedQuestions.size > 0) {
        const { error: deleteError } = await supabase
          .from("questions")
          .delete()
          .in("id", Array.from(deletedQuestions));

        if (deleteError) throw deleteError;
      }

      // Update existing questions
      const updatedQuestions = processedQuestions.filter(q => q.id);
      if (updatedQuestions.length > 0) {
        const { error: updateError } = await supabase
          .from("questions")
          .upsert(updatedQuestions.map(q => ({
            id: q.id,
            quiz_id: quizData.id,
            question: q.question,
            options: q.options,
            correct_answer: q.correctAnswer,
            answer_description: q.answerExplanation,
            image_url: q.imageUrl || null,
          })));

        if (updateError) throw updateError;
      }

      // Insert new questions
      const newQuestions = processedQuestions.filter(q => !q.id);
      if (newQuestions.length > 0) {
        const { error: insertError } = await supabase
          .from("questions")
          .insert(newQuestions.map(q => ({
            quiz_id: quizData.id,
            question: q.question,
            options: q.options,
            correct_answer: q.correctAnswer,
            answer_description: q.answerExplanation,
            image_url: q.imageUrl || null,
          })));

        if (insertError) throw insertError;
      }

      toast({
        title: isDraft ? "Draft Saved" : quizId ? "Quiz Updated!" : "Quiz Created!",
        description: isDraft
          ? "Your quiz has been saved as a draft."
          : "Quiz link copied to clipboard.",
      });

      if (!isDraft) {
        navigator.clipboard.writeText(`${window.location.origin}/quiz/${quizData.id}`);
      }

      onCancel();
    } catch (error) {
      console.error("Error saving quiz:", error);
      toast({
        title: "Error",
        description: "Failed to save the quiz.",
        variant: "destructive",
      });
    }
  };

  const removeQuestion = (index: number) => {
    const question = questions[index];
    if (question.id) {
      setDeletedQuestions(prev => new Set([...prev, question.id]));
    }
    setQuestions(questions.filter((_, i) => i !== index));
  };

  return (
    <div className={`min-h-screen ${isDark ? 'bg-dark' : 'bg-light'} py-8`}>
      <div className="max-w-3xl mx-auto px-4">
        <Button onClick={onCancel} className="mb-6 shadow-lg">
          <ArrowLeft className="h-4 w-4 mr-2" />
          Back to Dashboard
        </Button>

        <form onSubmit={(e) => handleSubmit(e, false)} className="space-y-8">
          <Card className="p-6 rounded-2xl shadow-lg">
            <QuizTitleInput value={quizTitle} onChange={setQuizTitle} />

            {questions.map((question, index) => (
              !deletedQuestions.has(question.id) && (
                <QuestionForm
                  key={question.id || index}
                  question={question}
                  index={index}
                  onQuestionChange={handleQuestionChange}
                  onRemoveQuestion={() => removeQuestion(index)}
                  totalQuestions={questions.length}
                  onImageUpload={(index: number, event: React.ChangeEvent<HTMLInputElement>) => {
                    const file = event.target.files?.[0];
                    if (file) {
                      const reader = new FileReader();
                      reader.onloadend = () => {
                        const updatedQuestions = [...questions];
                        updatedQuestions[index].imageUrl = reader.result as string;
                        setQuestions(updatedQuestions);
                      };
                      reader.readAsDataURL(file);
                    }
                  }}
                  onRemoveImage={() => {
                    const updatedQuestions = [...questions];
                    updatedQuestions[index].imageUrl = null;
                    setQuestions(updatedQuestions);
                  }}
                />
              )
            ))}

            <Button type="button" onClick={addQuestion} className="mt-4 w-full shadow-md">
              Add Question
            </Button>
            <QuizActionButtons
              onSaveDraft={(e) => handleSubmit(e, true)}
              isEditing={!!quizId}
            />
          </Card>
        </form>
      </div>
    </div>
  );
};

export default QuizCreator;

React useEffect Dependency Bug with Async Data Fetching

I’m building a React app where I fetch data from an API and update the state. However, I’m facing a bug related to useEffect dependencies, which is causing an infinite re-render loop. Here’s the relevant code:

import React, { useEffect, useState } from 'react';

const DataComponent = () => {
  const [data, setData] = useState([]);
  const [search, setSearch] = useState('');

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(`http://localhost:5000/data?q=${search}`);
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };

    fetchData();
  }, [search, data]);

  return (
    <div>
      <input
        type="text"
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Search..."
      />
      <ul>
        {data.map((item, index) => (
          <li key={index}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default DataComponent;

Problem:

  1. The useEffect dependency array includes data. However, if I remove data from the dependency array, React warns about “missing dependencies.”
  2. Keeping data in the dependency array causes an infinite re-render loop, as setData updates data, triggering the useEffect again.

What I’ve Tried:

  • Moving the fetchData function outside useEffect, but it didn’t resolve the issue.
  • Using a useRef to store the previous state, but the issue persisted.
  • Removing data from the dependency array avoids the loop but creates a React warning.

Questions:

  1. Why is this infinite loop occurring when data is part of the dependency array?
  2. What’s the correct way to handle this scenario without triggering React warnings or infinite loops?

I’ve looked at the React docs and other answers, but none specifically address this combination of async functions and dependency handling in useEffect. Any insights or solutions would be greatly appreciated!

Cannot create route’s draggabe line in leaflet routing

I’m creating a method for map class which responsible for plotting route on map and later be edited by dragging it. L.Routing.Control I do not consider as an option since it treats path’s coordinates as waypoints (both waypoints and plan option) and if disable waypoints we won’t be able to create new later when dragging the route. Now I’m trying to use L.Routing.Line since in docs it says Displays a route on the map, and allows adding new waypoints by dragging the line. which looks like what I need, but when I try to create on using following code

public addRoute(pathCoordinates: [number, number][]): void {
    const latLngPath = pathCoordinates.map(coord => new L.LatLng(coord[0], coord[1]));

    const routeLine = L.Routing.line({
        coordinates: latLngPath,
        waypoints: [latLngPath[0], latLngPath[latLngPath.length - 1]],
    }).addTo(this.map); 
}

I always got this error

Uncaught TypeError: wps is undefined
_findWaypointIndices leaflet-routing-machine.js:16984
_getWaypointIndices leaflet-routing-machine.js:17061
_extendToWaypoints leaflet-routing-machine.js:17011
initialize leaflet-routing-machine.js:16967
NewClass Class.js:24
line leaflet-routing-machine.js:16595
debugger eval code:1
addRoute mapService.ts:215

My guess that wps stands for waypoints, but even if I send waypoints: [] I’m still getting same error.
So the question is how to fix this creation problem or what another alternative for setting the route on the map and then changing it by dragging it.

Execute JSP containing JavaScript in background

I have a logic to generate pdf written in js in my JSP.

AutoDownloadPDF.jsp:

setInterval(function() {
        $.ajax({
        type: "POST",
        url: "GetInvoicesNo.jsp?EntityID=40&MappingID=<%=getMappingID%>",
        async: false,
        success: function(data){    
        var splitData =data.trim().split(',');
                $.each(splitData, function(index, value){
                if(value.trim()!=''){
                var url="GeneratePDF.jsp?EntityCode=<%=getEntityCode%>&InvoiceNo="+value;
        var printWindow = window.open(url,"_blank");
                 }
                        
            });     
                    
    }
    }); 
}, 90000);          

Current Setup:

I’m triggering AutoDownloadPDF.jsp through a Windows batch script like this:

msedge --app=http://localhost:8088/APP/AutoDownloadPDF.jsp 

The process works as intended; however, this method is unsuitable for my server environment because the server may log off, which causes Microsoft Edge to close and interrupts the process.

I tried tools like HTMLUnit and Playwright but didn’t work for me. It seems the problem is that the JSP depends heavily on browser-based JavaScript. Are there any tools or approaches that can handle this effectively?

Running jest in nest js project is taking a lot of heap memory

I am trying to run unit test on my project which is written with nest js framework. Irrespective of unit tests it is taking almost 4 gb of heap memory which is unexpected.

This is my jest.config.ts:

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  testMatch: ['**/*-test.ts', '**/**spec.ts', '.*\.spec\.ts$'],
  globalSetup: './spec/jest.setup.ts',
  globalTeardown: './spec/teardown.js',
  reporters: ['default', 'jest-junit'],
  moduleNameMapper: {
    '^apps/(.*)$': '<rootDir>/apps/$1',
    '^common/(.*)$': '<rootDir>/common/$1'
  },
  setupFilesAfterEnv: ['<rootDir>/spec/jest.setup.redis-mock.ts', '<rootDir>/apps/events/jest.setup.ts'],
  moduleFileExtensions: ['js', 'json', 'ts'],
  modulePaths: ['<rootDir>'],
  rootDir: '.',
  transform: {
    '^.+\.(t|j)s$': [
      'ts-jest',
      {
        diagnostics: false
      }
    ]
  },
  collectCoverageFrom: ['**/*.(t|j)s'],
  coverageDirectory: './coverage',
  roots: ['<rootDir>', '<rootDir>/apps/'],
  testPathIgnorePatterns: [
    '/node_modules/',
    '/dist/'
  ]
}

While running with node --no-compilation-cache --inspect-brk -r node_modules/.bin/jest --runInBand apps/events/src/events.controller.spec.ts command, I saw it has already occupied 4gb memory even before my globalSetup is initialized. Not sure why!

This is my tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2020",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "paths": {
      "apps/*": [
        "./apps/*"
      ],
      "common/*": [
        "./common/*"
      ]
    },
    "resolveJsonModule": true
  }
}

This is my apps/events/events.controller.spec.ts:

// import { Test, TestingModule } from '@nestjs/testing'
// import { EventsController } from './events.controller'

describe('EventsController', () => {

  describe('root', () => {
    it('should return "Hello World!"', () => {
      expect(true).toBe(true)
    })
  })
})

which is a very basic test case and should not require to load this much memory.

I am not sure if during initialization of test cases the libraries (my node_modules size is 2.2 gb) are loaded or not, if so what can I do. This is my package.json dependencies:

"dependencies": {
        "@analytics/google-analytics": "^0.5.3",
        "@azure/msal-node": "^2.16.2",
        "@babel/parser": "^7.24.6",
        "@clickhouse/client": "^1.6.0",
        "@dqbd/tiktoken": "^1.0.7",
        "@google-cloud/bigquery": "^5.12.0",
        "@google-cloud/dialogflow": "^5.8.0",
        "@google-cloud/logging-winston": "^4.0.4",
        "@google-cloud/monitoring": "^3.0.3",
        "@google-cloud/pubsub": "^2.15.1",
        "@google-cloud/pubsub_v3": "npm:@google-cloud/[email protected]",
        "@google-cloud/pubsub_v4": "npm:@google-cloud/[email protected]",
        "@google-cloud/secret-manager": "^5.0.1",
        "@google-cloud/storage": "^5.13.1",
        "@google-cloud/storage_v2": "npm:@google-cloud/[email protected]",
        "@google-cloud/tasks": "0.4.0",
        "@google-cloud/tasks_v3": "npm:@google-cloud/[email protected]",
        "@mhoc/axios-digest-auth": "^0.8.0",
        "@microsoft/microsoft-graph-client": "^3.0.5",
        "@mozilla/readability": "^0.4.4",
        "@nestjs/axios": "^3.0.2",
        "@nestjs/common": "^10.3.7",
        "@nestjs/config": "^3.2.2",
        "@nestjs/core": "^10.3.7",
        "@nestjs/mongoose": "^7.2.4",
        "@nestjs/platform-express": "^10.3.7",
        "@nestjs/swagger": "^7.3.1",
        "@types/bcrypt": "^3.0.0",
        "@types/bunyan": "^1.8.6",
        "@types/facebook-js-sdk": "^3.3.5",
        "@types/fluent-ffmpeg": "^2.1.20",
        "@types/google-libphonenumber": "^7.4.20",
        "@types/handlebars": "^4.0.37",
        "@types/json2csv": "^5.0.3",
        "@types/jsonwebtoken": "^8.5.1",
        "@types/lodash": "^4.14.168",
        "@types/mailgun-js": "^0.22.12",
        "@types/moment-timezone": "^0.5.30",
        "@types/multer": "^1.4.7",
        "@types/passport-local-mongoose": "^6.1.0",
        "@types/shortid": "^0.0.29",
        "@types/simple-oauth2": "^4.1.1",
        "@types/valid-url": "^1.0.3",
        "analytics": "^0.7.5",
        "atob": "^2.1.2",
        "authorizenet": "^1.0.8",
        "await-to-js": "^3.0.0",
        "aws-sdk": "^2.906.0",
        "axios": "^0.21.1",
        "basic-auth": "2.0.1",
        "bcrypt": "^5.0.1",
        "class-transformer": "0.4.0",
        "class-validator": "^0.13.1",
        "compression": "^1.7.4",
        "convert-array-to-csv": "^2.0.0",
        "cookie-parser": "^1.4.5",
        "crypto-js": "^3.1.9-1",
        "csvtojson": "^2.0.10",
        "currency.js": "^2.0.4",
        "date-fns": "2",
        "date-fns-tz": "2",
        "dialogflow": "^0.8.0",
        "dotenv-safe": "^8.2.0",
        "elasticsearch": "^16.7.2",
        "emoji-regex": "^10.3.0",
        "eslint-plugin-import": "^2.29.1",
        "fast-xml-parser": "^4.1.2",
        "fb": "^2.0.0",
        "firebase-admin": "12.1.0",
        "fluent-ffmpeg": "^2.1.2",
        "form-data": "^4.0.0",
        "fs": "^0.0.1-security",
        "geoip-country": "^4.1.9",
        "google-auth-library": "^7.10.1",
        "google-libphonenumber": "3.2.34",
        "google-spreadsheet": "^4.1.2",
        "googleapis": "^92.0.0",
        "handlebars": "^4.7.8",
        "handlebars-intl": "^1.1.2",
        "heapdump": "^0.3.15",
        "html-to-text": "^8.0.0",
        "ics": "^2.35.0",
        "id-shorter": "^0.1.2",
        "intuit-oauth": "^4.0.0",
        "ioredis": "^4.16.0",
        "isbot": "^5.1.6",
        "isomorphic-fetch": "^3.0.0",
        "javascript-obfuscator": "^4.1.0",
        "jest-junit": "^13.0.0",
        "jimp": "^0.22.10",
        "joi": "^17.4.0",
        "jsdom": "^19.0.0",
        "json-bigint": "^1.0.0",
        "json2csv": "^6.0.0-alpha.2",
        "jsonwebtoken": "^8.5.1",
        "jstoxml": "^3.2.6",
        "langfuse": "^3.24.0",
        "lodash": "4.17.19",
        "luxon": "^3.4.4",
        "mailgun-js": "^0.22.0",
        "mailgun.js": "^7.0.1",
        "marked": "^13.0.2",
        "md5": "^2.3.0",
        "metadata-scraper": "^0.2.61",
        "migrate-mongo": "^8.2.2",
        "mime-types": "^2.1.35",
        "mjml": "^4.10.4",
        "moment": "^2.29.4",
        "moment-timezone": "^0.5.43",
        "mongo-escape": "^2.0.6",
        "mongoose": "5.12.13",
        "mongoose-extend-schema": "^1.0.0",
        "mongoose-long": "^0.5.1",
        "mongoose6": "npm:mongoose@^6.8.2",
        "mysql2": "^2.3.3",
        "node-forge": "^1.3.1",
        "node-ical": "^0.16.1",
        "parse-reply": "^0.0.4",
        "passport-local-mongoose": "^6.1.0",
        "path": "^0.12.7",
        "path-to-regexp": "^7.1.0",
        "pdf-lib": "^1.17.1",
        "prom-client": "^14.0.1",
        "promisify-any": "2.0.1",
        "psl": "^1.8.0",
        "puppeteer": "^22.7.1",
        "qs": "^6.10.1",
        "sanitize-html": "^2.13.0",
        "sequelize": "^6.37.3",
        "sharp": "^0.33.5",
        "simple-oauth2": "1.5.0",
        "ts-loader": "^9.2.3",
        "ts-node": "^9.1.1",
        "tsconfig-paths": "^3.9.0",
        "type-is": "1.6.18",
        "typescript": "^5.4.4",
        "ulid": "^2.3.0",
        "url": "^0.11.0",
        "uuid": "^8.3.2",
        "uuid-mongodb": "^2.5.1",
        "uuidv4": "^6.2.7",
        "vm2": "^3.9.17",
        "webpack": "^5.93.0",
        "winston": "^3.3.3",
    },
    "devDependencies": {
        "@nestjs/cli": "^10.3.2",
        "@nestjs/schematics": "^10.1.1",
        "@nestjs/testing": "^10.3.7",
        "@types/authorizenet": "^1.0.0",
        "@types/bcrypt": "^3.0.0",
        "@types/bunyan": "^1.8.6",
        "@types/cookie-parser": "^1.4.2",
        "@types/cron": "^2.0.0",
        "@types/express": "^4.17.11",
        "@types/facebook-nodejs-business-sdk": "^15.0.5",
        "@types/google-libphonenumber": "^7.4.20",
        "@types/ioredis": "^4.28.10",
        "@types/ioredis-mock": "^8.2.2",
        "@types/isomorphic-fetch": "^0.0.36",
        "@types/jest": "^26.0.20",
        "@types/jsonwebtoken": "^8.5.1",
        "@types/lodash": "^4.14.168",
        "@types/mailgun-js": "^0.22.12",
        "@types/mjml-core": "^4.7.1",
        "@types/moment-timezone": "^0.5.30",
        "@types/multer": "^1.4.7",
        "@types/node": "^18.11.18",
        "@types/psl": "^1.1.0",
        "@types/request-ip": "^0.0.37",
        "@types/ssh2-sftp-client": "^9.0.1",
        "@types/supertest": "^2.0.10",
        "@typescript-eslint/eslint-plugin": "^7.6.0",
        "@typescript-eslint/parser": "^7.5.0",
        "eslint": "^8.0.0",
        "eslint-config-prettier": "^8.1.0",
        "eslint-import-resolver-typescript": "^3.6.1",
        "eslint-plugin-prettier": "^3.3.1",
        "firestore-jest-mock": "^0.17.0",
        "husky": "^7.0.4",
        "ioredis-mock": "^6.13.0",
        "jest": "^29.7.0",
        "lint-staged": "^11.0.0",
        "mongodb-memory-server": "^8.8.0",
        "prettier": "^2.2.1",
        "pretty-quick": "^3.1.1",
        "rimraf": "^3.0.2",
        "supertest": "^6.1.3",
        "ts-jest": "^29.1.3",
        "ts-loader": "^8.0.17",
        "tsconfig-paths": "^3.9.0",
        "tslint": "^6.1.3"
    }
    ```

Will appreciate if someone can help!

How to to find CPU hot spot functions in bun.js?

My JS program is slow, I want to find functions that consume most CPU time.

How can I do that? No precise timing or advanced charting needed, just a rough estimate to find names of couple most time consuming functions.

Example – the script contains a problematic function c the profiler should help to find it:

function a() {
  const r: string[] = []
  for (let i = 0; i < 500; i++) r.push(b(i))
  return r
}

function b(n: number): string {
  return c(n)
}

const letters = 'abcdefghijklmnopqrstuvwxyz'
function c(n: number): string {
  const b: string[] = []
  for (let i = 0; i < n; i++) {
    b.push(letters[i % letters.length])
    b.sort() // <= Problem
  }
  return b.join('')
}

function run () { a() }

;(globalThis as any).run = run

P.S. I tried using built-in bun.js debugger, but it has no CPU hotspot information. I tried bun --inspect main.ts and then switching to “Timelines” and clicking on ‘Record’ button and then executed run() in the console, and when it finished clicked ‘Stop recording’. It created the dump with statistics, but I have not found any CPU timing information.

Why are numbers not enclosed in quotation marks when NormalizeText ؟

I am trying to make NormalizeText But there is a part in the output, which is the numbers, which are not enclosed in quotation marks, and they are the last lines in the code. this is my code

 public static string SerializeAndNormalizeReceipt(object receipt)
 {
     if (receipt == null)
         return string.Empty;

     string serializedData = JsonConvert.SerializeObject(receipt, Formatting.None);

     string normalizedData = NormalizeText(serializedData);

     return normalizedData;
 }

 public static string NormalizeText(string text)
 {
     if (string.IsNullOrEmpty(text))
         return string.Empty;

     string cleanedText = Regex.Replace(text, @"[:,]", "");

     string normalizedText = Regex.Replace(cleanedText, @"[a-z]", m => m.Value.ToUpper());

     normalizedText = Regex.Replace(normalizedText, @"[u0600-u06FF]+", m => m.Value);

     normalizedText = Regex.Replace(normalizedText, @"[{}[]]", "");

     normalizedText = Regex.Replace(normalizedText, @"s+(?=[a-zA-Z0-9])", "");

     return normalizedText;
 }

and this is my output

"HEADER""DATETIMEISSUED""2024-12-30T173749""RECEIPTNUMBER""HAZ002""UUID""""PREVIOUSUUID""""REFERENCEOLDUUID""""CURRENCY""EGP""EXCHANGERATE"0"DOCUMENTTYPE""RECEIPTTYPE""S""TYPEVERSION""1.2""SELLER""RIN""332460843""COMPANYTRADENAME""سعاد محمد صالح حسن""BRANCHCODE""ABC""DEVICESERIALNUMBER""CWX02618""ACTIVITYCODE""8620""BRANCHADDRESS""COUNTRY""EG""GOVERNATE""CAIRO""REGIONCITY""CITYCENTER""STREET""16STREET""BUILDINGNUMBER""14BN""BUYER""TYPE""F""ID""313717919""NAME""TAXPAYER1""MOBILENUMBER""+201020567462""PAYMENTNUMBER""987654""ITEMDATA""INTERNALCODE""NA""DESCRIPTION""MEDICALCAREFORTHEELDERLY""ITEMTYPE""EGS""ITEMCODE""EG-332460843-M3""UNITTYPE""EA""QUANTITY"1"UNITPRICE"200"NETSALE"200"TOTALSALE"200"TOTAL"200"COMMERCIALDISCOUNTDATA""AMOUNT"0"DESCRIPTION""NODISCOUNT""RATE"0"ITEMDISCOUNTDATA""AMOUNT"0"DESCRIPTION""NODISCOUNT""RATE"0"TAXABLEITEMS""TAXTYPE""T1""AMOUNT"28"SUBTYPE""V009""RATE"14"TOTALSALES"200"TOTALCOMMERCIALDISCOUNT"0"TOTALITEMSDISCOUNT"0"EXTRARECEIPTDISCOUNTDATA""AMOUNT"0"DESCRIPTION""NODISCOUNT""RATE"0"NETAMOUNT"200"FEESAMOUNT"0"TOTALAMOUNT"228"TAXTOTALS""TAXTYPE""T1""AMOUNT"28"PAYMENTMETHOD""C""ADJUSTMENT"0"CONTRACTOR""NAME""CONTRACTOR1""AMOUNT"0"RATE"0

CSS Keyframe Continuous Horizontal Scroll buffering on loop start

I have programmed a continuous horizontal text scroll using CSS @keyframes and two identical div tags.

The issue I am facing is when the loop starts again, there is a small, but noticeable, pause before the text scrolls again. I would like the scroll to be continuous with no pause, even whether that means to not use CSS @keyframes and instead javascript/jQuery.

My code is below.

HTML

<div id="scrolling-header-parent-container">
   <div id="scrolling-header-container">
      <div class="scrolling-header-container-item">
         AUTHENTIC VIETNAMESE FOOD&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;INDIAN CUISINE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;CHURROS & COFFEE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;BUBBLE TEA&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;ESCAPE ROOM EXPERIENCE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;SOUFFLE PANCAKE & DESSERT CAFE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;COCKTAIL BAR&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;TAIWANESE FRIED CHICKEN&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;CHINESE HOTPOT&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;POLISH STREET FOOD&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;KOREAN BBQ&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;
      </div>
      <div class="scrolling-header-container-item">
         AUTHENTIC VIETNAMESE FOOD&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;INDIAN CUISINE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;CHURROS & COFFEE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;BUBBLE TEA&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;ESCAPE ROOM EXPERIENCE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;SOUFFLE PANCAKE & DESSERT CAFE&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;COCKTAIL BAR&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;TAIWANESE FRIED CHICKEN&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;CHINESE HOTPOT&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;POLISH STREET FOOD&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;KOREAN BBQ&nbsp;&nbsp;&nbsp;•&nbsp;&nbsp;&nbsp;
      </div>
   </div>
</div>

CSS (using SASS)

@keyframes infiniteScroll {
   from {  transform: translateX(0) }
   to {    transform: translateX(calc(0px - 50%)); }
}

#scrolling-header-parent-container {
   width: 100%;
   background-color: black;
   overflow-x: hidden;
    
   #scrolling-header-container {
      display: flex;
      width: fit-content;
      height: 8vh;
      font-size: 30px;
      align-items: center;
      overflow-x: hidden;
    
      animation-name: infiniteScroll;
      animation-duration: 20s;
      animation-iteration-count: infinite;
      animation-timing-function: linear;
    
      .scrolling-header-container-item {
         white-space: nowrap;
         color: white;
      }
   }
}

Why my button isnt in scrollview in code ( but it show it in phone ?)

<ScreenWapper bg="white">
            <Vie<ScreenWapper bg="white">
    <View styles={styles.container}>
        <Header title="Create Post" />
        <ScrollView contentContainerStyle={{gap: 20}} >
            
            {/* avatar */}
            <View style={styles.header}>
                <Avatar
                    uri={user?.image}
                    size={hp(6.5)}
                    rounded={theme.radius.xl}
                />
                <View styles={{gap: 2}}>
                    <Text style={styles.username}>
                        {
                            user && user.name
                        }
                    </Text>
                    <Text style={styles.publicText}>
                        Public
                    </Text>
                </View>
            </View>

            <View style={styles.textEditor}>
                <RichTextEditor editorRef={editorRef} onChange={body=> bodyRef.current = body}/>
            </View>
            {
                file && (
                    <View style={styles.file}>
                        {
                            getFileType(file) == 'video'? (
                                <Video
                                style={{flex: 1}}
                                source={{
                                    uri: getFileUri(file)
                                }}
                                useNativeControls
                                resizeMode='cover'
                                isLooping
                                />
                            ):(
                                <Image srouce={{uri: getFileUri(file)}} resizeMode='cover' style={{flex: 1}} />
                            )
                        }
                        <Pressable style={styles.closeIcon} onPress={()=>setFile(null)}>
                            <Icon name="delete" size={20} color="white" />
                        </Pressable>
                    </View>
                )
            }
            <View style={styles.media}>
                <Text style={styles.addImageText}>Add to your post</Text>
                <View style={styles.mediaIcons}>
                    <TouchableOpacity onPress={()=>onPick(true)}>
                        <Icon name="image" size={30} color={theme.colors.dark} />
                    </TouchableOpacity>
                    <TouchableOpacity onPress={()=>onPick(false)}>
                        <Icon name="video" size={33} color={theme.colors.dark} />
                    </TouchableOpacity>
                </View>
            </View> 
        </ScrollView>
        <Button
            buttonStyle={{height: hp(6.2)}}
            title="Post"
            loading={loading}
            hasShadow={false}
            onPress={onSubmit}
        />   
    </View>
</ScreenWapper>

I follow this tutorial: https://www.youtube.com/watch?v=YvODxoOTCGw in 25:33-26:03 his button is on the bottom of the screen. But for me, it show on the bottom of the add to your post. And the screen cant scrolling. enter image description here

This is the style of View.container

    container: {
        flex: 1,
        backgroundColor: 'red',
        marginBottom: 30,
        paddingHorizontal: wp(4), //FIX_HERE nhap lai
        gap: 15,
    },
  1. I have tried put this code in scroll View to make the content inside is bigger

     {Array.from({ length: 20 }).map((_, index) => (
        <Text key={index}>Dòng {index + 1}</Text>
      ))}
    

    And the post button got push down enter image description here

  2. I have tried keyboardShouldPersistTaps=”handled”but the result is the same

Issue with SHA mismatch despite matching Base64 content in JavaScript program

I’m working on a JavaScript app in NodeJs v22.11.0 with @octokit/rest 21.0.2 and crypto-js 4.2.0 and I’m encountering an issue where the text content of a file with accents (e.g., in Spanish) gets corrupted when pushed and pulled via the GitHub API. Specifically, if I push a file with the string "Quedar con la tía María", (“meet up with aunt María” in Spanish if you are curious), and then pull it back, I end up with incorrect characters in the decoded output. Depending on the decoding method used, I get one of these results:

"Quedar con la t�a Mar�a"
"Quedar con la tía MarÃa"
This issue occurs whether I use Buffer.from(repoFile.base64content, ‘base64’).toString(‘utf-8’) or atob(repoFile.base64content).

Additionally, the SHA hash calculated for the file after decoding is different from the original GitHub SHA. The SHA calculation works fine when there are no accented characters.

Here’s a minimal example to reproduce the issue:

import { Octokit } from "@octokit/rest";
import CryptoJS from 'crypto-js';

const octokit = new Octokit({ auth: 'personal-access-token' });

// Local file content with accented characters
const localFileContentString = 'Quedar con la tía María';
const localFile = { 
  path: 'Recordar.md', 
  sha: getSha(localFileContentString), 
  content: localFileContentString, 
  base64Content: btoa(localFileContentString) 
};

// Function to calculate SHA1 of file content
function getSha(fileContents) {
    const size = fileContents.length;
    const blobString = `blob ${size}${fileContents}`;
    return CryptoJS.SHA1(blobString).toString(CryptoJS.enc.Hex);
}

// Fetch the file content from GitHub repo
async function getRepoFile() {
    const existingFileResponse = await octokit.repos.getContent({
        owner: 'github-username',
        repo: 'vault-name',
        path: localFile.path
    });
    return { sha: existingFileResponse.data.sha, base64content: existingFileResponse.data.content.replace(/n/g, '') };
}

const repoFile = await getRepoFile();

console.log('EncodedRepoFile', repoFile, "n");
console.log('EncodedLocalFile', localFile, "n");

// Decode the base64 content from both the repo and local file
console.log('DecodedRepoFile', Buffer.from(repoFile.base64content, 'base64').toString());
console.log('DecodedLocalFile', Buffer.from(localFile.base64Content, 'base64').toString());

And the code output:

EncodedRepoFile {
  sha: '9fe35536cd6188e428ee04dcb559d69ecfb4d5d9',
  base64content: 'UXVlZGFyIGNvbiBsYSB0w61hIE1hcsOtYQoK'
} 

EncodedLocalFile {
  path: 'Recordar.md',
  sha: '9860966172762a56f5b3dec12d51d4b1fb1034e8',
  content: 'Quedar con la tía María',
  base64Content: 'UXVlZGFyIGNvbiBsYSB07WEgTWFy7WE='
} 

DecodedRepoFile Quedar con la tía María


DecodedLocalFile Quedar con la t�a Mar�a

I think the problem relies on how GitHub itself handles this special characters and I don’t know how to work my way around it. I’m using UFT-8 and I’ve tried changing the encoding to ISO-8859-1, getting the SHA of the corrupted string to at least check if I got the same SHA and checked through all the code that the encoding is consistent with libs like iconv-lite and chardet but none of that works.