Alternative to puppeteer-intercept-and-modify-requests for Efficient Request Interception in Puppeteer

I am working on a project where I need to intercept and modify network requests using Puppeteer. Currently, I am using the puppeteer-intercept-and-modify-requests library, but I’ve encountered issues where it reduces the Lighthouse performance score, even when minimal modifications are applied to the requests.

What I’ve Tried:

  1. Used puppeteer-intercept-and-modify-requests with a simple pass-through function:
modifyResponse: async (response) => {
    try {
        return undefined; // Pass-through without changes
    } catch (error) {
        console.error(`Error: ${error.message}`);
        return { body: response.body }; // Fallback
    }
};
  1. Limited request interception to specific resource types and URL patterns. Despite this, I noticed additional latency that affects Lighthouse’s performance score.

Requirements:

• The library should allow easy interception and modification of requests.
• It should be lightweight and optimized to minimize overhead.
• Compatibility with Puppeteer or similar headless browser libraries is essential.

Questions:

  1. Are there any alternative libraries or methods for intercepting and modifying requests in Puppeteer without significant performance impact?
  2. Is it possible to achieve this level of interception efficiently with a proxy-based tool (e.g., mitmproxy)? If so, how does it compare to Puppeteer-based solutions?

Any recommendations or insights would be greatly appreciated. Thank you!

Klaviyo integration oauth revoke api

Issue with Revoking Integration via OAuth API
We are attempting to revoke the Klaviyo integration for a user using the /oauth/revoke API. While the API call succeeds with a 200 OK response, the app is not being removed from the “Integrations” page in the user’s Klaviyo account.

Here are the specifics:

API Request Details:
Endpoint: https://a.klaviyo.com/oauth/revoke
Token provided: refresh_token (using token_type_hint: refresh_token)
Authorization: Basic header with client credentials
Response:
Status Code: 200
Response Body: {}
Below is the code we are using for this operation
`

let response:any;

const formData = await request.formData();

const requestData: Record<string, string> = {};

formData.forEach((value, key) => {

requestData[key] = String(value);

});

let user = await prisma.user.findFirst({

where:{

    name:requestData.store_name,

    app_name:requestData.app_name,

}

});

if(user){

const accessToken = await refresh_token(user?.id);

const klaviyoClientId = process.env.KLAVIYO_CLIENT_ID;

const klaviyoClientSecret = process.env.KLAVIYO_CLIENT_SECRET;

if(accessToken){

    let data = new URLSearchParams({

        'token_type_hint': 'refresh_token',

        "token": `Bearer ${accessToken}`

    })

    const url = 'https://a.klaviyo.com/oauth/revoke';

    const options = {

        method: 'GET',

        headers: {

            'Authorization': 'Basic ' + btoa(`${klaviyoClientId}:${klaviyoClientSecret}`),

            'Content-Type': 'application/x-www-form-urlencoded'

          },

          body:data,

    };

    const response = await fetch(url, options);

    if(!response.ok){

        let response = json({status:'error', msg:'Something went wrong'});

        await cors(request, response); // this mutates the Response object

        return response;

    }

    else{

        let update = prisma.user.update({

            where:{

                id:user.id

            },

            data:{

                api_token:null,

            }

        })

        let response = json({status:'success', msg:'App disconnected successfully'});

        await cors(request, response); // this mutates the Response object

        return response;

    }

}

else{

    return "abc";

}

}`
Despite the success response, the app remains visible in the user’s Klaviyo integrations list. Could you confirm whether additional steps are required to completely remove the app from the Klaviyo integrations page? If not, please advise on how to resolve this issue.

I tried sending latest refresh token as well as I tried by getting fresh access token and revoking it

What’s the best approach to maintain multiple versions of the project? [closed]

I’ve been maintaining a piece of software written in React, which has 3 versions with different number of features. Let’s call them Lite, Basic and Pro. Currently all 3 versions are on separate repos each and there’s a lot of duplicate code. Also, if I want to implement a feature or fix a bug on all (or 2) versions, I have to do it multiple times.

I wanted to somehow extract duplicate code to some Common repo and reuse them in all 3 versions, but I’m running into issues when trying to use components of which children are mixed. For example I have something like this (each component in a separate file, but for clarity I show here in one place):

const ComponentCommon = () => {
  return (
    <>
      <ComponentCommonA>
      <ComponentCommonB>
    </>
  )
}

const ComponentCommonA = () => {
  return (
    <>
      <ComponentCommonC>
      <ComponentCommonD>
      <ComponentLiteA> // Should be only in Lite
      <ComponentBasicA> // Should be only in Basic
    </>
  )
}

const ComponentLiteA = () => {
  return (
    <>
      <ComponentCommonE>
      <ComponentCommonF>
    </>
  )
}

const ComponentBasicA = () => {
  return (
    <>
      <ComponentCommonE>
      <ComponentCommonG>
    </>
  )
}

const ComponentCommonE = () => {
  return (
    <>
      <ComponentCommonJ>
      <ComponentCommonK>
      <ComponentLiteB> // Should be only in Lite
      <ComponentBasicB> // Should be only in Basic
    </>
  )
}

Similar issue with types, models and side scripts/utils too.

I’m struggling to figure out the best way to put components to their respective repos. Also with exports and imports. The final packages for each version are bundled with Webpack and distributed manually. Each repo is cloned locally. I tried having linked packages in the Basic package.json like this:

  "devDependencies": {
    "v-common": "file:../version-common",
  },

Also on the other (Common) side:

  "devDependencies": {
    "v-basic": "file:../version-basic",
  },

But I struggle to make it work seamlessly. What would be the best approach to set the workflow to reduce duplication and make exports/imports less confusing?

I am trying to use D3 libraries but not in a framework

I have this “borrowed” code , I am trying to get working I know that the original dev installed these D3 libs using npm “npm install d3-array”

<!DOCTYPE html>
<html style='height: 100%;'>
<head>
    <meta charset='UTF-8' />

    <title>Bookmarks Treemap</title>
    <!-- BUG: The following stylesheet cannot be @import-ed in anothere CSS file for some reason -->
    <link rel='stylesheet' type='text/css' href='chrome://global/skin/in-content/common.css' />
    <link rel='stylesheet' type='text/css' href='/_styles/style.css' />
    <!--
    D3 licensed under the ISC License
    Copyright © 2010–2024 Mike Bostock
    License text: https://spdx.org/licenses/ISC.html
    -->




    
    <script src='/node_modules/d3-array/dist/d3-array.min.js'></script>
    <script src='/node_modules/d3-color/dist/d3-color.min.js'></script>
    <script src='/node_modules/d3-format/dist/d3-format.min.js'></script>
    <script src='/node_modules/d3-hierarchy/dist/d3-hierarchy.min.js'></script>
    <script src='/node_modules/d3-interpolate/dist/d3-interpolate.min.js'></script>
    <script src='/node_modules/d3-scale/dist/d3-scale.min.js'></script>
    <script src='/node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.min.js'></script>
    <script src='/node_modules/d3-selection/dist/d3-selection.min.js'></script>
    <script src='/node_modules/d3-time/dist/d3-time.min.js'></script>
    <script src='/node_modules/d3-time-format/dist/d3-time-format.min.js'></script>
    <script src='/node_modules/internmap/dist/internmap.min.js'></script>
    <script src='/options_default.js'></script> 
    <!-- Extension's default options -->



    <script src='js/script.js'></script>
</head>
<body style='margin: 0; padding: 0; overflow: hidden;'>
    <svg id='treemap'></svg>
</body>
</html>

but I would like to just have links to online CDNs like this

    <script type="module">
import {min} from "https://cdn.jsdelivr.net/npm/d3-array@3/+esm";
const m = min(array);
</script>

<script type="module">
import {rgb} from "https://cdn.skypack.dev/d3-color@3";
const steelblue = d3.rgb("steelblue");
</script>

<script type="module">
import {format} from "https://cdn.skypack.dev/d3-format@3";
const f = format(".2s");
</script>

<script type="module">
import {treemap} from "https://cdn.skypack.dev/d3-hierarchy@3";
const tree = treemap();
</script>

<script type="module">
import {interpolateRgb} from "https://cdn.skypack.dev/d3-interpolate@3";
const interpolate = interpolateRgb("steelblue", "brown");
</script>

<script type="module">
import {scaleLinear} from "https://cdn.skypack.dev/d3-scale@4";
const x = scaleLinear();
</script>

<script type="module">
import {selectAll} from "https://cdn.skypack.dev/d3-selection@3";
const div = selectAll("div");
</script>

<script type="module">
import {timeDay} from "https://cdn.skypack.dev/d3-time@3";
const day = timeDay();
</script>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/internmap.min.js"></script>

but I am missing entries for

any ideas on how I can do this correctly, do I need it to be in a framework?

WordPress: How to add additional field in a form for submitting posts by users and assign it to the correct taxonomy?

I have the free version of the User Submitted Posts plugin that lets user create WordPress posts. It has one dropdown for the category of the post, however, I need a second one where users choose a location. I’ve set “location” as a taxonomy. I used JS in my Code snippets plugin to transform one of the free text fields to a dropdown that displays the “location” options, however, the location choice that the user makes when filling the form isn’t assigned to the published post. The post has nothing in the location category. Please help me achieve this with code as I can’t afford Premium plugins at the moment. This is the JS code:

document.addEventListener('DOMContentLoaded', function () {
    // Target the custom field by its ID
    const customField = document.getElementById('user-submitted-custom');

    if (customField) {
        // Create a new select element
        const locationDropdown = document.createElement('select');
        locationDropdown.setAttribute('id', 'user-submitted-location');
        locationDropdown.setAttribute('name', 'usp_custom_field'); // Same name as the original input
        locationDropdown.setAttribute('data-required', 'true');
        locationDropdown.required = true;
        locationDropdown.classList.add('usp-input');

        // Fetch terms from the 'location' taxonomy
        fetch('/wp-json/wp/v2/location?per_page=100')
            .then(response => {
                if (!response.ok) {
                    throw new Error('Network response was not ok');
                }
                return response.json();
            })
            .then(locations => {
                // Add a default placeholder option
                const defaultOption = new Option('Select a Location', '', true, false);
                locationDropdown.add(defaultOption);

                // Add location terms as options
                locations.forEach(location => {
                    const option = new Option(location.name, location.id);
                    locationDropdown.add(option);
                });

                // Replace the original input field with the dropdown
                customField.replaceWith(locationDropdown);
            })
            .catch(error => {
                console.error('Error fetching locations:', error);
            });
    } else {
        console.error('Custom field not found!');
    }
});

I don’t know how to achieve it, I asked ChatGPT which provided this PHP snippet but it didn’t work:

add_action('usp_post_submit', 'assign_custom_taxonomies', 10, 2);
function assign_custom_taxonomies($post_id, $data) {
    // Check if location field exists and assign terms
    if (!empty($data['usp_custom_field'])) {
        $locations = array_map('intval', (array) $data['usp_custom_field']); // Ensure it's an array of integers
        wp_set_post_terms($post_id, $locations, 'location');
    }
}

Sending Request To A Server [closed]

Good Day.
I have been participating in an online game where a free share will be distributed to like 5 players through a button that pops up the fastest ones to click gets to claim it.
Someone Advised me to use browser on a VPS which is located closer to the website hosting the game to reduce latency. I did and I noticed an Improvement but now with over 500 players I still seems to be left behind. before I use setinterval to click() the button and later started using Mutation observer but still cant get it right. Please Is there a way to send and receive request from the game server faster?

Someone Advised me to use browser on a VPS which is located closer to the website hosting the game to reduce latency. I did and I noticed an Improvement but now with over 500 players I still seems to be left behind. before I use setinterval to click() the button and later started using Mutation observer but still cant get it right. Please Is there a way to send and receive request from the game server faster?

Can someone suggest me a biometric device for fingerprint recognition? [closed]

I want to build an application for a business which wants to track employee attendance via a biometric device. I want my server to retrieve data from the biometric device and display it in a intuitive way in a web based app.

I tried looking for devices but some either stored data locally and data can only be retrieved via a pen drive or they had to connected to a computer to work at all times.

I want the biometric system to be:

  1. Standalone(connected to wifi)
  2. Provides an API through which I can access data

Someone please recommend me some devices.

In ANTD tree drag and drop, how to prevent drop as child if it is a leaf

In ANTD tree if I drag and drop a node below any leaf node then I have two options. Either drop the element as a sibling or drop it as a child or drop as a sibling. To drop as sibling we have to just drop it below any node however if we want to drop as child then move the drop marker a little right and then drop.

I want to disable this feature of dropping as a child.

Dropping as sibling:
enter image description here

Dropping as child:
enter image description here

I tried to search for any prop that might help but not able to find one. I only found the solution to handle this in onDropHandler however this will not stop the tree from dropping as child.

webtorrent – Uncaught (in promise) Error: No server created

I’m experimenting webtorrent library inside a chrome extension that use vite and vue for the UI. I’m looking at the webtorrent documentation and I saw that to add a magnet link a service worker is needed.

According to the chrome mv3 documentation, a service worker is automatically registered from the extension. At the moment I have this code in my vue app to get magnet link file content, but it’s not working. I’m able to log the file info but no way to stream the file.

import WebTorrent from 'import WebTorrent from 'webtorrent/dist/webtorrent.min.js';

const client = new WebTorrent(); 

//this is working fine
watch([enableVideoShare], () => {
  console.log(enableVideoShare.value);
  if(enableVideoShare.value){
    client.seed(file.value, (torrent) => {
      console.log(torrent)
      outcomingMagnetURI.value = torrent.magnetURI;
    });
  }
});

In my background script I have this code at the moment.

import WebTorrent from 'webtorrent/dist/webtorrent.min.js'; //added this but not sure if need to implement the server

console.log('hello world from background');

//
chrome.action.onClicked.addListener((tab) => {
  console.log('tab info', tab);
  chrome.tabs.create({
    active: true,
    url: chrome.runtime.getURL('src/new-tab/index.html')
  });
});

self.onerror = function (message, source, lineno, colno, error) {
  console.info(
    `Error: ${message}nSource: ${source}nLine: ${lineno}nColumn: ${colno}nError object: ${error}`
  )
}

export {}

This is what is logged in console.

index-DcFS_qW1.js:1 Uncaught (in promise) Error: No server created
    at get streamURL (index-DcFS_qW1.js:1:166925)
    at ti.streamTo (index-DcFS_qW1.js:1:167066)
    at index-DcFS_qW1.js:24:9552
    at ni.n (index-DcFS_qW1.js:24:4353)
    at Object.u (index-DcFS_qW1.js:1:11020)
    at index-DcFS_qW1.js:1:12744
    at s.emit (index-DcFS_qW1.js:1:12811)
    at ni._onStore (index-DcFS_qW1.js:1:183681)
    at n (index-DcFS_qW1.js:1:181577)
    at y (index-DcFS_qW1.js:1:43150)
 WebSocket connection to 'wss://tracker.btorrent.xyz/' failed

Can anyone help me to find a solution to correctly manage stream?

How to Redirect and Secure Access to Specific Pages Based on URL Parameters?

So I ve bought a website on hostinger and hosted on github pages (X.info from now on), i have already linked everything and just made the homepage (index from now on) which is a login that give you access to (index_valid :page that shows you a document after id check) which is in the same folder, i would like people to get access with a qrcode that i ve made with python that has a passkey in it in order get access to index_valid but i have some problems.

  1. I’ve created 2 js files, 1 that checks name and pass, where the pass is stored as md5 in the script ( i know it’s not secure idk) the script decode the pass given by the user in md5 and check if has that pass in the md5 passkey list. and this works just fine, but heres the problem, if someone does the login it goes to X.info/index_valid which is free to reach after you know the url, i would like to make it accessable only with the js check.
    2)the qr code. i ll post my code below but i can’t make it work, i just need a js file that has a list of md5 keys, let’s say even 1, and if someone reach the website with ‘X.info/?token=validtoken gets to index_valid page, if they put anything after .info they should be sent to index_failed which is a 403 page basically.
    here’s one of my codes that tries to do point 2 but fails
<pre>// Funzione per calcolare l'MD5 del token
function calculateMD5(token) {
    return CryptoJS.MD5(token).toString();
}

// Funzione per controllare il token e fare il reindirizzamento
function handleFormSubmit(event) {
    event.preventDefault();
    
    const matricola = document.getElementById("matricola").value;
    const token = document.getElementById("token").value;
    
    const validTokenMD5 = "0cf702314bad36f54f0731695f7763b5"; // MD5 di "segreto123"
    
    const tokenHash = calculateMD5(token);
    
    if (tokenHash === validTokenMD5) {
        // Token valido, redirigi a index_valid.html
        window.location.href = "index_valid.html";
    } else {
        // Token non valido, redirigi a index_failed.html
        window.location.href = "index_failed.html";
    }
}

document.getElementById("loginForm").addEventListener("submit", handleFormSubmit);

 <code>

`

where ‘0cf702314bad36f54f0731695f7763b5’ is the md5 for ‘segreto123’
can somebody helps me? I m pretty stuck

I’ve tried several js builds that does’t works, i also tried to make a backend server but the free ones got some limitations (like if the page it’s not reached every x minutes the server goes in stand-by more kindof). so since for what i’m tryin to accomplish here the backend server isn’t really needed that i would love to just have a simple js working subdirectory manager.
Sorry if my Enlish ain’t perfect, tried my best.
Thanks to all

How to retrieve updated trello card description to highlight difference with the old one?

I’m looking for any solution that doesn’t involve using are already mad Trello power-ups to track trello card description updates.

In detail this is what I need:

  • User A update description of the card
  • all member receive the notification (maybe through card activities?)
  • in someway User A should see a diffcheck between the new and old description and check what is added or deleted.

I know I can build a powerup, but maybe you had another idea maybe through butler and payloads? I don’t know, I only have javascript so I thought if I could build a diff checker maybe on a sharepoint space and send to this the two version of the texts?
I’m open to different ideas.

Why might the getImage function not send a request to the backend and instead return cached data?

I have a getImage function that connects to my backend to fetch today’s Astronomical Image of the Day and its details. Since I’m using the Next.js App Router (which defaults to server-side rendering unless specified), I expected the function to fetch the data using SSR. However, the issue is that the function is not hitting the backend but instead seems to be using cached data.
For additional context:
The Information component is a client component.
Below are snippets of my Home page and the getImage function.

import getImage from "../../lib/nasa/astronomicalPic";
import Information from "../../components/home/astronomicalPic";

export default async function Home() {
  async function getData(){
    try{
      const data=await getImage();
      
      return data;
    }
    catch(err){
      console.log(err);
      return null;
    }
  }
    const data = await getData();
    if (!data) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <p className="text-xl font-bold text-red-600">Failed to load data.</p>
      </div>
    );
      
    }
return (
  <main className="flex flex-col items-center justify-center min-h-screen">
    <Information data={data} />
  </main>
);
    

}

const url = process.env.NEXT_PUBLIC_BACKEND_URL;

export default async function getImage() {
  try {
    const res = await fetch(`${url}/nasaImage`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
    });

    if (!res.ok) {
      throw new Error(`Error loading image: ${res.statusText}`);
    }

    const data = await res.json();
    return data;
  } catch (err) {
    console.error(err.message);
    throw new Error("Error loading Astronomical Picture of the Day");
  }
}

I attempted to fetch data from my backend using an async function (getImage) in the Home component. Since the Next.js App Router defaults to server-side rendering unless specified otherwise, I expected the data to be fetched on the server and passed to the component for rendering.

I structured my code to handle the fetched data and conditionally render either the Information component (if data was available) or an error message (if the data was missing). However, instead of hitting the backend to fetch fresh data, it seems to be using cached data, which I wasn’t expecting.

I also tried adding error handling and logging in the getImage function to debug the issue, but I didn’t see any network requests to the backend in the logs or browser’s network tab.

How to Deploy and Install a Subscription Plugin in TON Wallet v4r2 Using TonConnect?

I’m trying to deploy and install a subscription plugin in a TON Wallet v4r2 using the @tonconnect/ui-react library. I’ve set up the payload with opcode 1 and included the necessary contract initialization (StateInit). However, the deployment isn’t working as expected:

  • The TON amount sent with the transaction is returned to the wallet.
  • The subscription plugin isn’t deployed or installed.

Steps Taken:

  • Configured the subscription contract with the required parameters.
  • Compiled the official subscription plugin code using @ton-community/func-js and used result.codeBoc for StateInit.code. Compilation code image
  • Sent the transaction using tonConnectUI.sendTransaction.

Here’s the code I used:

import {
  useTonWallet,
  useTonAddress,
  useTonConnectUI,
} from "@tonconnect/ui-react";
import { Address, beginCell, Cell, toNano } from "@ton/core";

export default function App() {
  const wallet = useTonWallet();
  const walletAddress = useTonAddress(true);
  const [tonConnectUI] = useTonConnectUI();

  const deployAndInstallSubscription = async () => {
    if (!walletAddress) return null;

    const BENEFICIARY = "EQB_tUOR3CwzEsHlq2aDrLF6R6Y_FV70GBldzfBwa8Hyq042";

    // Configuration for the subscription contract
    const config = {
      wallet: Address.parse(walletAddress),
      beneficiary: Address.parse(BENEFICIARY),
      amount: toNano(0.1),
      period: 120, // 2 minutes
      startTime: 120,
      timeout: 3600, // 1 hour
      last_payment_time: 0,
      last_request_time: 0,
      failed_attempts: 0,
      subscription_id: 1,
    };

    // StateInit: Code and Data for the contract
    const stateInit = {
      code: Cell.fromBase64(
        "te6ccgECDwEAAmIAART/APSkE/S88sgLAQIBIAIDAgFIBAUDavIw2zxTNaEnqQT4IyehKKkEAbxRNaD4I7kTsPKe+AByUhC+lFOH8AeOhVOG2zyk4vgjAts8CwwNAgLNBgcBIaDQybZ4E/SI3gQR9IjeBBATCwSP1tngXoaYGY/SAYKYRjgsdOL4QZmemPmEEIMjm6OV1JeAPwGLhBCDq3NbvtnnAphOOC2cdGiEYvhjhBCDq3NbvtnnAVa6TgkECwoKCAJp8Q/SIYQJOIbZ58EsEIMjm6OThACGRlgqgDZ4soAf0BCmW1ZY+JZZ/kuf2AP8EIMjm6OW2eQOCgTwjo0QjF8McIIQdW5rd9s84ArTHzCCEHBsdWeDHrFSELqPSDBTJKEmqQT4IyahJ6kEvvJxCfpEMKYZ+DPQeNch1ws/UmChG76OkjA2+CNwcIIQc3VicydZ2zxQd94QaRBYEEcQNkUTUELbPOA5XwdsIjKCEGRzdHK6CgoNCQEajol/ghBkc3Ry2zzgMAoAaCGzmYIQBAAAAHL7At5w+CdvEYAQyMsFUAXPFiH6AhT0ABPLaRLLH4MGApSBAKAy3skB+wAAMO1E0PpA+kD6ANMf0x/TH9Mf0x/TB9MfMAGAIfpEMCCBOpjbPAGmGfgz0HjXIdcLP6Bw+CWCEHBsdWcigBjIywVQB88WUAT6AhXLahLLHxPLPwH6AssAyXP7AA4AQMhQCs8WUAjPFlAG+gIUyx8Syx/LH8sfyx/LB8sfye1UAFgBphX4M9Ag1wsHgQDRupWBAIjXId7TByGBAN26AoEA3roSsfLgR9M/MKirDw=="
      ),
      data: beginCell()
        .storeAddress(config.wallet)
        .storeAddress(config.beneficiary)
        .storeCoins(config.amount)
        .storeUint(config.period, 32)
        .storeUint(config.startTime, 32)
        .storeUint(config.timeout, 32)
        .storeUint(config.last_payment_time, 32)
        .storeUint(config.last_request_time, 32)
        .storeUint(config.failed_attempts, 8)
        .storeUint(config.subscription_id, 32)
        .endCell(),
    };

    // Build payload
    const payload = beginCell()
      .storeUint(1, 8) // op code
      .storeUint(0, 8) // plugin wc
      .storeCoins(toNano(0.1))
      .storeRef(stateInit.code)
      .storeRef(stateInit.data)
      .endCell();

    // Send transaction with the payload
    await tonConnectUI.sendTransaction({
      validUntil: Date.now() + 5 * 60 * 1000, // Valid for 5 minutes
      messages: [
        {
          address: walletAddress,
          amount: toNano(0.1).toString(),
          payload: payload.toBoc().toString("base64"),
        },
      ],
    });
  };

  return (
    <div>
      <h1>TON Web Subscription</h1>
      {wallet ? (
        <div>
          <p>Wallet connected: {wallet?.account.address}</p>
          <button onClick={deployAndInstallSubscription}>
            Deploy And Install Subscription
          </button>
        </div>
      ) : (
        <p>Please connect your TON wallet.</p>
      )}
    </div>
  );
}

Observed Issue

After inspecting the transaction, I found that the message is sent as an internal message, not an external message. As a result, the plugin isn’t installed or deployed.

Transaction details:

What I Found:

The TonWeb repository includes an example of deploying and installing a subscription plugin using the secret key. Here’s the example code:

import TonWeb from "tonweb";
import { SubscriptionContract } from "tonweb/src/contract/subscription/index";
import { mnemonicToPrivateKey } from "@ton/crypto";

const mnemonicArray = [
  "whale",
  "airport",
  "runway",
  "scissors",
  "pony",
  "elite",
  "false",
  "laugh",
  "purse",
  "letter",
  "gloom",
  "cousin",
  "never",
  "chaos",
  "maid",
  "air",
  "body",
  "candy",
  "amused",
  "service",
  "hammer",
  "kingdom",
  "ability",
  "vanish",
];

export default async function main() {
  const BENEFICIARY = "EQB_tUOR3CwzEsHlq2aDrLF6R6Y_FV70GBldzfBwa8Hyq042";

  // Create testnet tonweb
  const tonweb = new TonWeb(
    new TonWeb.HttpProvider("https://testnet.toncenter.com/api/v2/jsonRPC")
  );

  const keyPair = await mnemonicToPrivateKey(mnemonicArray);

  console.log(keyPair.publicKey);

  const WalletClass = tonweb.wallet.all["v4R2"];

  console.log(`===keyPair.publicKey===`, keyPair.publicKey);

  const wallet = new WalletClass(tonweb.provider, {
    publicKey: keyPair.publicKey,
    wc: 0,
  });

  // Get wallet address

  const walletAddress = await wallet.getAddress();
  const WALLET_ADDRESS = walletAddress.toString(true, true, true);
  console.log("wallet address =", WALLET_ADDRESS);

  // wallet seqno get method

  const seqno = (await wallet.methods.seqno().call()) || 0;
  console.log("wallet seqno = ", seqno);

  const subscription = new SubscriptionContract(tonweb.provider, {
    wc: 0,
    wallet: walletAddress,
    beneficiary: new TonWeb.utils.Address(BENEFICIARY),
    amount: TonWeb.utils.toNano("0.2"), // 1 ton
    startAt: 0,
    period: 2 * 60, // 2 min,
    timeout: 30, // 30 sec
    subscriptionId: 1210,
  });
  console.log(`===subscription===`, subscription);
  const subscriptionAddress = await subscription.getAddress();
  console.log(
    "subscription address=",
    subscriptionAddress.toString(true, true, true)
  );
  console.log(
    `===await subscription.createStateInit();===`,
    await subscription.createStateInit()
  );
  // Deploy and install subscription

  const deployAndInstallPlugin = async () => {
    console.log(
      "deployAndInstallPlugin",
      await wallet.methods
        .deployAndInstallPlugin({
          secretKey: keyPair.secretKey,
          seqno: seqno,
          pluginWc: 0,
          amount: TonWeb.utils.toNano("0.05"), // 1 ton
          stateInit: (await subscription.createStateInit()).stateInit,
          body: subscription.createBody(),
        })
        .send()
    );
  };
  console.log(
    `===await subscription.createStateInit();===`,
    await subscription.createStateInit()
  );
  await deployAndInstallPlugin();
}

I have not found anything helpful that can describe the process how to install the official subscription plugin in wallet v4r2 contract from webapp.

Can someone explain how to install and deploy the official subscription plugin using Tonconnect? So, my webapp users can install a subscription plugin in their wallet V4r2 without requiring the secret key. User will not agree to share their secret key. User will sign the message from user wallet.

Questions:

  • How can I ensure the transaction is sent as an external message with TonConnect?
  • Is there a specific method or configuration required in tonConnectUI.sendTransaction to deploy and install the plugin in Wallet v4r2?

Expected Behavior
The deployAndInstallSubscription function should deploy the subscription and install the plugin in Wallet v4r2.

Actual Behavior
The TON amount sent with the transaction is returned to the wallet without deploying or installing the plugin.

Note
I’ve reviewed the TonConnect and TonWeb documentation but haven’t found any specific example or explanation for deploying plugins directly through a web app.

Parcel “npm run build:js” changes app.js file and not bundle.js

I have a Node.js project and after the Parcel update I’m running into an issue.

In my ./public/js/ folder I have 2 files: bundle.js and bundle.js.map. Previously Parcel was compiling/ bundling the code in these 2 files. However, after the update it’s now changing my app.js file. And I can’t figure out how to adjust parcel to the before-mentioned 2 files.

My package.json looks like this.

{
  ...
  "main": "app.js",
  "scripts": {
    ...
    "watch:js": "parcel watch ./public/js/index.js --dist-dir ./public/js/bundle.js --no-source-maps",
    "build:js": "parcel build ./public/js/index.js --dist-dir ./public/js/bundle.js --no-source-maps"
  },
  ...
  "dependencies": {
    ...
    "parcel": "^2.13.0",
    ...
  },
  "devDependencies": {
  ...
  "parcel-bundler": "^1.12.5",
  ...
  },
  "engines": {
    "node": ">=16.0.0"
  }
}

When I run “npm run watch:js” it adjusts the app.js, which is not in my ./public/js folder at all. See below the file and folder structure of my project.

Folder and file structure

Hopefully I’ve described the issue with sufficient background and info. If not, please let me know! I could not find another question concerning this issue.

Hope someone can help me with this issue.