Automate to Elevate: Debugging Your Local JS Code Seamlessly on Customer’s Production

Yedidya Schwartz
OwnID Engineering
Published in
7 min readNov 26, 2023
Photo by Nubelson Fernandes on Unsplash

In this blog post, I’ll share how I leveraged the new Chrome API response override feature and rollup.js custom plugins to create a tool enabling the loading of a local version of our SDK for debugging on customer production websites. This automated process allows you to write code on your local machine, with changes reflected immediately on the relevant customer production website, requiring no extra actions.

This enhancement elevated our developer experience to a whole new level.

The instructions here are tailored to our use case, but the general steps are applicable to every frontend component of any application.

Our Product and Its Loading Process

Our product appears on customer websites’ in various variations, mainly on login/registration forms, offering a passwordless login option. End users encounter the product as a widget near the password input, along with various prompt variations.

As detailed in my previously published article series on product loading time improvement, our product loading relies on a code snippet injected into the customer website. A javascript file with all the necessary resources is loaded by the snippet, containing JSONs of translations and configurations, our SDK code, and other required resources.

Chrome API Response Override

Thanks to the new feature introduced in Chrome 117, overriding API responses in Chrome is super simple, a developer just needs to follow these steps:

Right-click on a request in the network tab and choose “Override content.”

Select the folder to configure the overrides path.

Allow access.

Now you can edit the API response of the selected request. If the selected path is /Users/yedidya/chrome_overrides and the selected request is test.com/x/y.js, then Chrome will automatically generate the folder hierarchy for you: /Users/yedidya/chrome_overrides/test.com/x, and will create a new file under folder x, named y.js, with your customized content.

Now let’s dive into the challenge, and later we’ll see how Chrome API override is leveraged as part of the solution.

The Challenge

The loaded javascript file of our product is crafted by a server (our baking-server) that combines many resources from different sources. On the server code, a javascript template with placeholders is created, and placeholders are then replaced with the actual resource.

The output is a large javascript file with all relevant resources merged together.

baking-server javascript output file

Reverse engineering this file to understand where the translation JSON is, where the configurations are, and where the SDK code starts or ends is challenging, especially since the SDK code is minified and uglified.

Our Goal

We aimed to load this javascript file with a different injected SDK code on customer production websites. We wanted the file to contain a specific version of our SDK, which is not minified, not uglified, and can be changed dynamically on-demand — allowing us to examine SDK release candidates on real websites and debug them if needed.

The Solution at a High Level

The solution involves the following 3 steps; once SDK code is running locally in watch mode and a change is made:

  1. A script is triggered and downloads the full original javascript file (with translations, configurations, SDK, etc.) from our CDN, for example: https://cdn.ownid.com/sdk/abcdefg
  2. The SDK code from the downloaded javascript file (uglified and minified) is replaced with the local version of the code (readable and debuggable).
  3. The new Javascript file with the local SDK code is loaded on the customer’s website instead of the original javascript file.

Let’s tackle the steps one by one.

Clues Planting

As I already mentioned, the loaded file in the customer’s website is a large javascript file with all relevant resources merged together. In order to mark the resources so I can tell where every resource is located, I added comments in the baking-server javascript template, before and after each resource injection. This allows me to know which part I need to replace later.

Although the current purpose is to replace the SDK code, I added comments for all injected resources to have the infrastructure to dynamically replace any resource in the future.

So now, the problem is a bit simpler: Once a customer’s website sends a request to load our product to https://cdn.ownid.com/sdk/{client_id}, I need to interfere with the response and dynamically replace the string that lies between /##WEB_SDK_INJECTION_START##/ to /##WEB_SDK_INJECTION_END##/ with my local SDK code.

In the next section, I looked for the point where I can interrupt the code bundling and add some custom steps, to perform the first step from “the solution at a high level” section.

SDK Build Process

Our SDK is a vanilla JS project packed using the rollup.js JavaScript module bundler. The default configuration that’s being used in the build process before production deployment, performs all classic procedures on the code: minification, uglification, styles adjusting, JSON files to ES6 modules conversion, and more.

The command in our package.json that triggers the bundling is:

"build": "rollup -c rollup.config.ts"

I decided to leverage rollup.js custom plugin functionality to trigger my custom code and perform the actions I described above, in the “The solution at a high level” section.

I created a separate rollup.config.ts file with a different name, and a new command to trigger it in the package.json:

"start:inject": "rollup -c rollup-inject.config.ts"

I started to investigate the plugin development docs, and the main challenge was to find the right point in rollup.js build lifecycle where I can trigger the plugin so it will run at the right moment, after the SDK code is ready to be injected into the javascript file.

After investigation, I found Output Generation Hooks, which can provide information about a generated bundle and modify a build once complete. closeBundle seemed to be the right point to trigger my code.

Output Generation Hooks, from rollupjs.org

The new rollup.js configuration contains my custom plugin. It builds the code without any minification or uglification, to make it readable and debuggable after injection.

This is the configuration (“injectSdk” is the plugin reference):

In the next paragraph we’ll dive into the plugin logic, which leverages the Chrome API response override capabilities as part of it.

Plugin Logic

The code is straightforward, involving the following steps:

  1. Build the URL of the customer’s javascript file in our CDN, based on parameters from the .env file
  2. Trigger, in parallel, the javascript file download from the CDN and the local SDK file (that was just built) reading
  3. Replace the minified and uglified SDK from the original javascript file with the locally readable javascript file.
  4. Build the local path of the new javascript file on the developer’s machine, based on the URL and Chrome override path preconfigured in Chrome.
  5. Write the new file to the Chrome override directory.

Once step 5 is completed, and the javascript file abcdefg exists in the path /Users/yedidya/chrome_overrides/cdn.ownid.com/sdk, Chrome knows to override the network request https://cdn.ownid.com/sdk/abcdefg with the local file. The plugin runs on every change, as rollup.js is configured in watch mode. Building the SDK, downloading the javascript file, replacing, and writing again; these actions are super fast, so in less than a second, as a developer, you can see your code changes take effect on the production website.

This is a diagram that compares between the regular loading flow and the local SDK loading flow.

Summary

This article delves into the creation of a tool utilizing the Chrome API response override and rollup.js custom plugins to load a local version of an SDK for debugging on customer production websites.

By automating the process, developers can seamlessly make and test code changes on their local machines, reflecting them instantly on relevant customer production websites. While the instructions are tailored to a specific use case, the outlined steps can be applied to enhance any frontend component in various applications.

Embracing the power of these tools not only streamlines the debugging process but also provides valuable insights into SDK performance on real websites. The combination of Chrome API features and rollup.js custom plugin logic significantly simplifies the workflow, enabling a quick and efficient development cycle.

Happy coding!

--

--

Yedidya Schwartz
OwnID Engineering

Backend Tech Lead | DevOps | AWS Community Builder | AWS Solution Architect