diff --git a/.github/workflows/release_docker.yml b/.github/workflows/release_docker.yml
index 11372166..2c77daf9 100644
--- a/.github/workflows/release_docker.yml
+++ b/.github/workflows/release_docker.yml
@@ -32,7 +32,6 @@ jobs:
         shell: bash
         run: |
           echo "RENDERSCRIPT_VERSION=$(node -e 'console.log(require("./package.json").version)')" >> $GITHUB_OUTPUT
-          echo "PLAYWRIGHT_VERSION=$(node -e 'console.log(require("./package.json").dependencies.playwright)')" >> $GITHUB_OUTPUT
 
       - name: Set up Docker Buildx
         uses: docker/setup-buildx-action@v3
@@ -63,7 +62,6 @@ jobs:
           cache-to: type=gha,mode=max
           build-args: |
             VERSION=${{ steps.env_var.outputs.RENDERSCRIPT_VERSION }}
-            PLAYWRIGHT_VERSION=${{ steps.env_var.outputs.PLAYWRIGHT_VERSION }}
 
       - name: Test Image
         run: ./scripts/test_image.sh ${{ env.COMMIT_SHA }}
@@ -84,4 +82,3 @@ jobs:
           cache-to: type=gha,mode=max
           build-args: |
             VERSION=${{ steps.env_var.outputs.RENDERSCRIPT_VERSION }}
-            PLAYWRIGHT_VERSION=${{ steps.env_var.outputs.PLAYWRIGHT_VERSION }}
diff --git a/.nvmrc b/.nvmrc
index 87ec8842..b8ffd707 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-18.18.2
+22.15.0
diff --git a/Dockerfile b/Dockerfile
index 05b221a7..63dd4670 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,105 +1,52 @@
 # ------------------
-# Build playwright
+# Stage 1: Build
 # ------------------
-FROM ubuntu:jammy as base
+FROM mcr.microsoft.com/playwright:v1.58.2-noble AS builder
 
-# For tzdata
-ARG DEBIAN_FRONTEND=noninteractive
-ARG TZ=America/Los_Angeles
+WORKDIR /app
 
-# === INSTALL Node.js ===
-RUN apt-get update && \
-  # Install node16
-  apt-get install -y curl wget && \
-  curl -sL https://deb.nodesource.com/setup_18.x | bash - && \
-  apt-get install -y nodejs && \
-  # Feature-parity with node.js base images.
-  apt-get install -y --no-install-recommends git openssh-client && \
-  npm install -g yarn && \
-  # clean apt cache
-  rm -rf /var/lib/apt/lists/* && \
-  # Create the pwuser
-  adduser pwuser
+ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true
 
-# === BAKE BROWSERS INTO IMAGE ===
-ARG PLAYWRIGHT_VERSION
-ENV PLAYWRIGHT_VERSION ${PLAYWRIGHT_VERSION}
-ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
-
-# Browsers will be downloaded in `/ms-playwright`.
-RUN mkdir /ms-playwright \
-  && PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true npm install -g playwright@$PLAYWRIGHT_VERSION \
-  && npx playwright install --with-deps chromium \
-  && npx playwright install --with-deps firefox \
-  # Clean cache
-  && rm -rf /var/lib/apt/lists/* \
-  && chmod -R 777 /ms-playwright
-
-
-# ------------------
-# package.json cache
-# ------------------
-FROM apteno/alpine-jq:2022-09-25 AS deps
-
-# To prevent cache invalidation from changes in fields other than dependencies
-COPY package.json /tmp
-RUN jq 'walk(if type == "object" then with_entries(select(.key | test("^jest|prettier|eslint|semantic|dotenv|nodemon") | not)) else . end) | { name, dependencies, devDependencies, packageManager }' < /tmp/package.json > /tmp/deps.json
-
-
-# ------------------
-# New base image
-# ------------------
-FROM base as tmp
-
-ENV IN_DOCKER true
-ENV PLAYWRIGHT_BROWSERS_PATH="/ms-playwright"
-ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD="true"
-
-# Setup the app WORKDIR
-WORKDIR /app/tmp
-
-# Copy and install dependencies separately from the app's code
-# To leverage Docker's cache when no dependency has change
-COPY --from=deps /tmp/deps.json ./package.json
-COPY yarn.lock .yarnrc.yml ./
+# Copy dependency files first for layer caching
+COPY package.json yarn.lock .yarnrc.yml ./
 COPY .yarn .yarn
 
-# Install dev dependencies
-RUN true \
-  && yarn install
+RUN yarn install
 
-# This step will invalidates cache
-COPY . ./
+# Copy source code (invalidates cache on code changes only)
+COPY . .
 
-# Builds the UI, install chrome and remove dev dependencies
-RUN true \
-  && ls -lah /app/tmp \
-  && yarn build \
+# Build, prune dev deps, clean up
+RUN yarn build \
   && yarn workspaces focus --all --production \
   && rm -rf .yarn/
 
+
 # ------------------
-#  New final image that only contains built code
+# Stage 2: Production
 # ------------------
-FROM base as final
+FROM mcr.microsoft.com/playwright:v1.58.2-noble
 
 ARG VERSION
-ENV VERSION ${VERSION:-dev}
+ENV VERSION=${VERSION:-dev}
+ENV NODE_ENV=production
+ENV IN_DOCKER=true
+ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
+ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true
 
-# Autolink repository https://docs.github.com/en/packages/learn-github-packages/connecting-a-repository-to-a-package
+# OCI labels
 LABEL org.opencontainers.image.source=https://github.com/algolia/renderscript
 LABEL org.opencontainers.image.revision=$VERSION
 
-ENV NODE_ENV production
-ENV IN_DOCKER true
-ENV PLAYWRIGHT_BROWSERS_PATH="/ms-playwright"
-ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD="true"
+# Create non-root user
+RUN adduser --disabled-password pwuser
 
-# Do not use root to run the app
 USER pwuser
 
-# Copy install from previous stage
 WORKDIR /app/renderscript
-COPY --from=tmp --chown=pwuser:pwuser /app/tmp /app/renderscript
+COPY --from=builder --chown=pwuser:pwuser /app/dist ./dist
+COPY --from=builder --chown=pwuser:pwuser /app/node_modules ./node_modules
+COPY --from=builder --chown=pwuser:pwuser /app/package.json ./
+COPY --from=builder --chown=pwuser:pwuser /app/public ./public
 
 CMD [ "node", "dist/index.js" ]
diff --git a/esbuild.config.mjs b/esbuild.config.mjs
new file mode 100644
index 00000000..f5692141
--- /dev/null
+++ b/esbuild.config.mjs
@@ -0,0 +1,11 @@
+import { build } from 'esbuild';
+
+await build({
+  entryPoints: ['src/index.ts'],
+  bundle: true,
+  platform: 'node',
+  target: 'node22',
+  outfile: 'dist/index.js',
+  external: ['playwright', 'pino', 'pino-pretty'], // must stay external: playwright for binary resolution, pino for worker_threads
+  format: 'cjs',
+});
diff --git a/jest.setup.ts b/jest.setup.ts
index acc19b03..0b08d855 100644
--- a/jest.setup.ts
+++ b/jest.setup.ts
@@ -1,5 +1,3 @@
-import { request } from 'undici';
-
 import { wait } from './src/helpers/wait';
 
 // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
@@ -10,10 +8,10 @@ export default async function setup() {
   while (curr < max) {
     curr += 1;
     try {
-      const { statusCode } = await request('http://localhost:3000/ready');
-      console.log('API statusCode:', statusCode, `(retries: ${curr})`);
+      const res = await fetch('http://localhost:3000/ready');
+      console.log('API statusCode:', res.status, `(retries: ${curr})`);
 
-      if (statusCode === 200) {
+      if (res.status === 200) {
         console.log('API Ready');
         return;
       }
diff --git a/package.json b/package.json
index 4100e54d..060512c8 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
   "description": "A custom JavaScript rendering engine based on Playwright",
   "main": "dist/index.js",
   "scripts": {
-    "build": "yarn clean && yarn tsc && yarn browser:adblocks",
+    "build": "yarn clean && yarn tsc --noEmit && node esbuild.config.mjs && yarn browser:adblocks",
     "ci:start": "ALLOW_LOCALHOST=true yarn start",
     "clean": "rm -rf dist/",
     "dev": "nodemon",
@@ -35,7 +35,7 @@
     "url": "https://github.com/algolia/renderscript/issues"
   },
   "engines": {
-    "node": "18.18.2"
+    "node": "22.15.0"
   },
   "homepage": "https://github.com/algolia/renderscript#readme",
   "devDependencies": {
@@ -45,12 +45,13 @@
     "@types/csurf": "1.11.5",
     "@types/express": "4.17.21",
     "@types/jest": "29.5.8",
-    "@types/node": "18.18.10",
+    "@types/node": "22.15.3",
     "@types/uuid": "9.0.7",
     "@typescript-eslint/eslint-plugin": "6.11.0",
     "@typescript-eslint/parser": "6.11.0",
     "dotenv": "16.3.1",
     "ejs": "3.1.9",
+    "esbuild": "^0.27.4",
     "eslint": "8.54.0",
     "eslint-config-algolia": "22.0.0",
     "eslint-config-prettier": "9.0.0",
@@ -81,10 +82,9 @@
     "cookie-parser": "1.4.6",
     "csurf": "1.11.0",
     "express": "4.19.2",
-    "hot-shots": "10.0.0",
+    "hot-shots": "14.2.0",
     "pino": "8.16.2",
-    "playwright": "1.49.0",
-    "undici": "5.28.4",
+    "playwright": "1.58.2",
     "uuid": "9.0.1"
   },
   "resolutions": {
diff --git a/scripts/build.sh b/scripts/build.sh
index 119affe9..c4d607a7 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -4,8 +4,7 @@ set -ex
 
 hash=$(git rev-parse HEAD)
 current=$(node -e "console.log(require('./package.json').version)")
-playwright_version=$(node -e 'console.log(require("./package.json").dependencies.playwright)')
-echo "Releasing: $current ; Playwright version: $playwright_version"
+echo "Releasing: $current"
 echo ""
 
 # Build renderscript
@@ -20,6 +19,5 @@ docker buildx build \
   -t "algolia/renderscript:${hash}" \
   -t "algolia/renderscript:latest" \
   --build-arg "VERSION=${current}" \
-  --build-arg "PLAYWRIGHT_VERSION=${playwright_version}" \
   --load \
   .
diff --git a/scripts/update_adblock_hosts.sh b/scripts/update_adblock_hosts.sh
index ba095d08..710f8d3d 100755
--- a/scripts/update_adblock_hosts.sh
+++ b/scripts/update_adblock_hosts.sh
@@ -1,11 +1,13 @@
 #!/bin/sh
 
-# adblock hosts file URL
-URL="https://raw.githubusercontent.com/badmojr/1Hosts/master/Pro/domains.txt"
+# adblock hosts file URL (1Hosts Lite, adblock filter syntax)
+URL="https://raw.githubusercontent.com/badmojr/1Hosts/refs/heads/master/Lite/adblock.txt"
 
 TARGET_DIR="./dist/lib/browser"
 TARGET_FILE="adblock_hosts.txt"
 
+mkdir -p "${TARGET_DIR}"
+
 if curl -o "${TARGET_DIR}/${TARGET_FILE}" "$URL" -s; then
   echo "✅ adblock hosts download successful."
 else
diff --git a/src/__tests__/api.test.ts b/src/__tests__/api.test.ts
index 5b6b812d..374fe6a2 100644
--- a/src/__tests__/api.test.ts
+++ b/src/__tests__/api.test.ts
@@ -80,7 +80,8 @@ describe('POST /render', () => {
 
     expect(JSON.parse(res.body)).toStrictEqual({
       status: 400,
-      error: 'Invalid json: Unexpected end of JSON input',
+      error:
+        'Invalid json: Unterminated string in JSON at position 43 (line 1 column 44)',
       code: 'invalid_json',
     });
   });
diff --git a/src/__tests__/blockedRequests.test.ts b/src/__tests__/blockedRequests.test.ts
index 8df201ef..41d9ff44 100644
--- a/src/__tests__/blockedRequests.test.ts
+++ b/src/__tests__/blockedRequests.test.ts
@@ -36,7 +36,7 @@ describe('adblocker', () => {
     expect(json.metrics.page!.requests).toStrictEqual({
       total: 11,
       pending: 0,
-      blocked: 9,
+      blocked: 6,
     });
     /**
      * @example
diff --git a/src/__tests__/helpers.ts b/src/__tests__/helpers.ts
index 90fb0214..5eea62bf 100644
--- a/src/__tests__/helpers.ts
+++ b/src/__tests__/helpers.ts
@@ -1,8 +1,4 @@
-import type { IncomingHttpHeaders } from 'http';
-
 import type { Cookie } from 'playwright';
-import { request as req } from 'undici';
-import type Dispatcher from 'undici/types/dispatcher';
 
 import type {
   PostLoginParams,
@@ -10,24 +6,41 @@ import type {
 } from '../api/@types/postLogin';
 import type { PostRenderParams } from '../api/@types/postRender';
 
+interface TestResponse {
+  statusCode: number;
+  status: number;
+  headers: Record<string, string>;
+}
+
 export async function request(
   url: string,
-  params?: Parameters<typeof req>[1]
-): Promise<{ res: Dispatcher.ResponseData; body: string }> {
-  const res = await req(url, params);
+  params?: RequestInit
+): Promise<{ res: TestResponse; body: string }> {
+  const raw = await fetch(url, {
+    redirect: 'manual',
+    ...params,
+  });
+  const body = await raw.text();
 
-  let body = '';
-  for await (const chunk of res.body) {
-    body += chunk.toString();
-  }
+  // Convert to a plain object matching the shape tests expect (undici style)
+  const headers: Record<string, string> = {};
+  raw.headers.forEach((value, key) => {
+    headers[key] = value;
+  });
+
+  const res: TestResponse = {
+    statusCode: raw.status,
+    status: raw.status,
+    headers,
+  };
 
   return { res, body };
 }
 
 export async function postRender(
   opts: Partial<PostRenderParams>,
-  headers?: IncomingHttpHeaders
-): Promise<{ res: Dispatcher.ResponseData; body: string }> {
+  headers?: Record<string, string>
+): Promise<{ res: TestResponse; body: string }> {
   return await request('http://localhost:3000/render', {
     method: 'POST',
     headers: {
@@ -43,7 +56,7 @@ export async function postRender(
 
 export async function sendLoginRequest(
   opts: Partial<PostLoginParams>
-): Promise<{ res: Dispatcher.ResponseData; body: string }> {
+): Promise<{ res: TestResponse; body: string }> {
   return await request('http://localhost:3000/login', {
     method: 'POST',
     headers: {
diff --git a/src/helpers/projectRoot.ts b/src/helpers/projectRoot.ts
index 8c1ed9de..2383fef5 100644
--- a/src/helpers/projectRoot.ts
+++ b/src/helpers/projectRoot.ts
@@ -1,3 +1,30 @@
+import * as fs from 'fs';
 import * as path from 'path';
 
-export default path.join(__dirname, '..', '..');
+function hasRuntimeAssets(dir: string): boolean {
+  return (
+    fs.existsSync(path.join(dir, 'package.json')) &&
+    fs.existsSync(path.join(dir, 'public'))
+  );
+}
+
+// Support bundled builds (`dist/index.js`) as well as source/test execution.
+const candidates = [
+  process.cwd(),
+  path.resolve(__dirname, '..'),
+  path.resolve(__dirname, '..', '..'),
+];
+
+function resolveProjectRoot(): string {
+  const projectRoot = candidates.find(hasRuntimeAssets);
+
+  if (projectRoot) {
+    return projectRoot;
+  }
+
+  throw new Error(
+    `Unable to resolve project root from: ${candidates.join(', ')}`
+  );
+}
+
+export default resolveProjectRoot();
diff --git a/src/lib/TasksManager.ts b/src/lib/TasksManager.ts
index 4adbe490..79e3b0d6 100644
--- a/src/lib/TasksManager.ts
+++ b/src/lib/TasksManager.ts
@@ -16,9 +16,12 @@ import type { TaskObject, TaskFinal } from './types';
 
 export const log = mainLog.child({ svc: 'mngr' });
 
+const EAGER_FIREFOX = process.env.EAGER_FIREFOX === 'true';
+
 export class TasksManager {
   #chromium: Browser | null = null;
   #firefox: Browser | null = null;
+  #firefoxLaunchPromise: Promise<void> | null = null;
   #stopping: boolean = true;
   #tasks: Map<string, TaskObject> = new Map();
   #totalRun: number = 0;
@@ -50,17 +53,22 @@ export class TasksManager {
       return { ready: false, reason: 'oldTasks', oldTasks };
     }
 
-    if (this.#chromium && this.#firefox) {
-      return {
-        ready: this.#chromium.isReady && this.#firefox.isReady,
-        reason: `browser(s) not ready: chromium: ${
-          this.#chromium.isReady ? '✅' : '❌'
-        } ; firefox: ${this.#firefox.isReady ? '✅' : '❌'}`,
-        oldTasks,
-      };
+    const chromiumReady = this.#chromium?.isReady ?? false;
+    // Firefox is lazily initialized — only block readiness if it was started but isn't ready
+    const firefoxReady = this.#firefox ? this.#firefox.isReady : true;
+
+    let firefoxStatus = '💤';
+    if (this.#firefox) {
+      firefoxStatus = this.#firefox.isReady ? '✅' : '❌';
     }
 
-    return { ready: false, oldTasks };
+    return {
+      ready: chromiumReady && firefoxReady,
+      reason: `browser(s) not ready: chromium: ${
+        chromiumReady ? '✅' : '❌'
+      } ; firefox: ${firefoxStatus}`,
+      oldTasks,
+    };
   }
 
   get currentBrowsers(): Map<BrowserEngine, Browser | null> {
@@ -81,11 +89,12 @@ export class TasksManager {
   async launch(): Promise<void> {
     const chromium = new Browser('chromium');
     await chromium.create();
-    const firefox = new Browser('firefox');
-    await firefox.create();
-
     this.#chromium = chromium;
-    this.#firefox = firefox;
+
+    if (EAGER_FIREFOX) {
+      await this.#ensureFirefox();
+    }
+
     this.#stopping = false;
     log.info('Ready');
   }
@@ -158,6 +167,19 @@ export class TasksManager {
     }
   }
 
+  async #ensureFirefox(): Promise<void> {
+    if (this.#firefox?.isReady) return;
+    if (!this.#firefoxLaunchPromise) {
+      this.#firefoxLaunchPromise = (async (): Promise<void> => {
+        const firefox = new Browser('firefox');
+        await firefox.create();
+        this.#firefox = firefox;
+        log.info('Firefox ready (lazy init)');
+      })();
+    }
+    await this.#firefoxLaunchPromise;
+  }
+
   /**
    * Actual execution of a task.
    * It will create a browser, a page, launch the task (render, login), close everything.
@@ -169,6 +191,9 @@ export class TasksManager {
     }
 
     const engine: BrowserEngine = task.params.browser || 'chromium';
+    if (engine === 'firefox') {
+      await this.#ensureFirefox();
+    }
     const browser = engine === 'firefox' ? this.#firefox : this.#chromium;
     if (!browser || !browser.isReady) {
       throw new Error('Task can not be executed: no_browser');
diff --git a/src/lib/browser/Adblocker.ts b/src/lib/browser/Adblocker.ts
index 498109ff..3a9b4ab1 100644
--- a/src/lib/browser/Adblocker.ts
+++ b/src/lib/browser/Adblocker.ts
@@ -1,4 +1,5 @@
 import { promises as fs } from 'fs';
+import path from 'path';
 
 import { report } from '../../helpers/errorReporting';
 import { log as mainLog } from '../../helpers/logger';
@@ -13,12 +14,20 @@ export class Adblocker {
 
   async load(): Promise<void> {
     try {
-      const data = await fs.readFile(`${__dirname}/adblock_hosts.txt`, 'utf8');
+      const data = await fs.readFile(
+        path.join(process.cwd(), 'dist/lib/browser/adblock_hosts.txt'),
+        'utf8'
+      );
       const lines = data.split(/[\r\n]+/);
 
       for (const line of lines) {
-        if (!line.startsWith('#')) {
-          this.#hostnames.add(line);
+        // Skip comments (! or #) and empty lines
+        if (!line || line.startsWith('!') || line.startsWith('#')) {
+          continue;
+        }
+        // Parse adblock filter syntax: ||domain.com^
+        if (line.startsWith('||') && line.endsWith('^')) {
+          this.#hostnames.add(line.slice(2, -1));
         }
       }
 
diff --git a/src/lib/browser/Page.ts b/src/lib/browser/Page.ts
index d13c4462..83b37115 100644
--- a/src/lib/browser/Page.ts
+++ b/src/lib/browser/Page.ts
@@ -148,6 +148,11 @@ export class BrowserPage {
     url: string,
     opts: Parameters<Page['goto']>[1]
   ): Promise<Response> {
+    const page = this.#ref;
+    if (!page) {
+      throw new Error('goto_no_page');
+    }
+
     let response: Response | null = null;
 
     function onResponse(res: Response): void {
@@ -156,19 +161,19 @@ export class BrowserPage {
         response = res;
       }
     }
-    this.#ref!.once('response', onResponse);
+    page.once('response', onResponse);
 
     const start = Date.now();
     try {
       // Response can be assigned here or on('response')
-      response = await this.#ref!.goto(url, opts);
+      response = await page.goto(url, opts);
     } catch (err: any) {
       if (!this.redirection && !err.message.includes('ERR_ABORTED')) {
         this.throwIfNotTimeout(err);
       }
     } finally {
       // We remove listener, because we don't want more response
-      this.#ref!.removeListener('response', onResponse);
+      page.removeListener('response', onResponse);
     }
 
     stats.timing('renderscript.page.goto', Date.now() - start, undefined, {
@@ -191,6 +196,11 @@ export class BrowserPage {
     timeout: number;
     waitUntil: Parameters<Page['waitForLoadState']>[0];
   }): Promise<Response | null> {
+    const page = this.#ref;
+    if (!page) {
+      return null;
+    }
+
     let response: Response | null = null;
     function onResponse(res: Response): void {
       // We listen to response because "goto" will throw on timeout but we still want to process the doc in that case
@@ -198,21 +208,19 @@ export class BrowserPage {
         response = res;
       }
     }
-    this.#ref!.once('response', onResponse);
+    page.once('response', onResponse);
 
     try {
-      if (this.#ref) {
-        await this.#ref.waitForLoadState(opts.waitUntil, opts);
-        response = await this.#ref.waitForResponse(
-          (res) => res.status() >= 200 && res.status() < 400,
-          opts
-        );
-      }
+      await page.waitForLoadState(opts.waitUntil, opts);
+      response = await page.waitForResponse(
+        (res) => res.status() >= 200 && res.status() < 400,
+        opts
+      );
     } catch (err: any) {
       this.throwIfNotTimeout(err);
     } finally {
       // We remove listener, because we don't want more response
-      this.#ref!.removeListener('response', onResponse);
+      page.removeListener('response', onResponse);
     }
 
     return response;
diff --git a/src/lib/browser/constants.ts b/src/lib/browser/constants.ts
index 94efa4b5..5f08d5cd 100644
--- a/src/lib/browser/constants.ts
+++ b/src/lib/browser/constants.ts
@@ -63,13 +63,10 @@ export const flags = [
   '--disable-save-password-bubble',
   '--disable-single-click-autofill',
   '--disable-restore-session-state',
-  '--disable-translate',
   '--disable-new-profile-management',
   '--disable-new-avatar-menu',
-  '--disable-infobars',
   '--disable-device-discovery-notifications',
   '--disable-client-side-phishing-detection',
-  '--disable-notifications',
   '--disable-component-extensions-with-background-pages',
   // Disable dev-shm
   // See https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#tips
@@ -85,7 +82,6 @@ export const flags = [
   '--disable-web-resources',
   '--safebrowsing-disable-auto-update',
   '--safebrowsing-disable-download-protection',
-  '--disable-client-side-phishing-detection',
   '--disable-component-update',
   '--disable-default-apps',
 
diff --git a/src/lib/singletons.ts b/src/lib/singletons.ts
index a2b45cf2..14dc200a 100644
--- a/src/lib/singletons.ts
+++ b/src/lib/singletons.ts
@@ -9,8 +9,7 @@ export const adblocker = new Adblocker();
 
 export async function init(): Promise<void> {
   try {
-    await tasksManager.launch();
-    await adblocker.load();
+    await Promise.all([tasksManager.launch(), adblocker.load()]);
   } catch (err: any) {
     report(new Error('Error during launch'), { err });
 
diff --git a/tsconfig.json b/tsconfig.json
index b19f69df..e05257cd 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,16 +6,13 @@
     ],
     "module": "CommonJS",
     "strict": true,
-    "outDir": "dist/",
+    "noEmit": true,
     "types": [
       "node",
       "jest"
     ],
     "esModuleInterop": true,
-    "allowSyntheticDefaultImports": true,
-    "sourceMap": true,
-    "declaration": true,
-    "declarationMap": true
+    "allowSyntheticDefaultImports": true
   },
   "include": [
     "src/**/*"
diff --git a/yarn.lock b/yarn.lock
index abd6aa13..44afc091 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -33,7 +33,7 @@ __metadata:
     "@types/csurf": "npm:1.11.5"
     "@types/express": "npm:4.17.21"
     "@types/jest": "npm:29.5.8"
-    "@types/node": "npm:18.18.10"
+    "@types/node": "npm:22.15.3"
     "@types/uuid": "npm:9.0.7"
     "@typescript-eslint/eslint-plugin": "npm:6.11.0"
     "@typescript-eslint/parser": "npm:6.11.0"
@@ -43,6 +43,7 @@ __metadata:
     csurf: "npm:1.11.0"
     dotenv: "npm:16.3.1"
     ejs: "npm:3.1.9"
+    esbuild: "npm:^0.27.4"
     eslint: "npm:8.54.0"
     eslint-config-algolia: "npm:22.0.0"
     eslint-config-prettier: "npm:9.0.0"
@@ -57,18 +58,17 @@ __metadata:
     eslint-plugin-prettier: "npm:5.0.1"
     eslint-plugin-promise: "npm:6.1.1"
     express: "npm:4.19.2"
-    hot-shots: "npm:10.0.0"
+    hot-shots: "npm:14.2.0"
     jest: "npm:29.7.0"
     nodemon: "npm:3.0.1"
     pino: "npm:8.16.2"
     pino-pretty: "npm:10.2.3"
-    playwright: "npm:1.49.0"
+    playwright: "npm:1.58.2"
     prettier: "npm:3.1.0"
     semantic-release: "npm:22.0.8"
     ts-jest: "npm:29.1.1"
     ts-node: "npm:10.9.2"
     typescript: "npm:5.2.2"
-    undici: "npm:5.28.4"
     uuid: "npm:9.0.1"
   languageName: unknown
   linkType: soft
@@ -533,6 +533,188 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@esbuild/aix-ppc64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/aix-ppc64@npm:0.27.4"
+  conditions: os=aix & cpu=ppc64
+  languageName: node
+  linkType: hard
+
+"@esbuild/android-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/android-arm64@npm:0.27.4"
+  conditions: os=android & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/android-arm@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/android-arm@npm:0.27.4"
+  conditions: os=android & cpu=arm
+  languageName: node
+  linkType: hard
+
+"@esbuild/android-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/android-x64@npm:0.27.4"
+  conditions: os=android & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@esbuild/darwin-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/darwin-arm64@npm:0.27.4"
+  conditions: os=darwin & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/darwin-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/darwin-x64@npm:0.27.4"
+  conditions: os=darwin & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@esbuild/freebsd-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/freebsd-arm64@npm:0.27.4"
+  conditions: os=freebsd & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/freebsd-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/freebsd-x64@npm:0.27.4"
+  conditions: os=freebsd & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-arm64@npm:0.27.4"
+  conditions: os=linux & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-arm@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-arm@npm:0.27.4"
+  conditions: os=linux & cpu=arm
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-ia32@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-ia32@npm:0.27.4"
+  conditions: os=linux & cpu=ia32
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-loong64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-loong64@npm:0.27.4"
+  conditions: os=linux & cpu=loong64
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-mips64el@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-mips64el@npm:0.27.4"
+  conditions: os=linux & cpu=mips64el
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-ppc64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-ppc64@npm:0.27.4"
+  conditions: os=linux & cpu=ppc64
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-riscv64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-riscv64@npm:0.27.4"
+  conditions: os=linux & cpu=riscv64
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-s390x@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-s390x@npm:0.27.4"
+  conditions: os=linux & cpu=s390x
+  languageName: node
+  linkType: hard
+
+"@esbuild/linux-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/linux-x64@npm:0.27.4"
+  conditions: os=linux & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@esbuild/netbsd-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/netbsd-arm64@npm:0.27.4"
+  conditions: os=netbsd & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/netbsd-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/netbsd-x64@npm:0.27.4"
+  conditions: os=netbsd & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@esbuild/openbsd-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/openbsd-arm64@npm:0.27.4"
+  conditions: os=openbsd & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/openbsd-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/openbsd-x64@npm:0.27.4"
+  conditions: os=openbsd & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@esbuild/openharmony-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/openharmony-arm64@npm:0.27.4"
+  conditions: os=openharmony & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/sunos-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/sunos-x64@npm:0.27.4"
+  conditions: os=sunos & cpu=x64
+  languageName: node
+  linkType: hard
+
+"@esbuild/win32-arm64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/win32-arm64@npm:0.27.4"
+  conditions: os=win32 & cpu=arm64
+  languageName: node
+  linkType: hard
+
+"@esbuild/win32-ia32@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/win32-ia32@npm:0.27.4"
+  conditions: os=win32 & cpu=ia32
+  languageName: node
+  linkType: hard
+
+"@esbuild/win32-x64@npm:0.27.4":
+  version: 0.27.4
+  resolution: "@esbuild/win32-x64@npm:0.27.4"
+  conditions: os=win32 & cpu=x64
+  languageName: node
+  linkType: hard
+
 "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
   version: 4.4.0
   resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@@ -582,13 +764,6 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@fastify/busboy@npm:^2.0.0":
-  version: 2.0.0
-  resolution: "@fastify/busboy@npm:2.0.0"
-  checksum: 6a2366d06b82aac1069b8323792f76f7a8fca02533cb3745fcd218d8f0f953dc4dbef057287237414658cd43f8dede0846ef33398999e3dbe54ddaeefec71c0a
-  languageName: node
-  linkType: hard
-
 "@gar/promisify@npm:^1.1.3":
   version: 1.1.3
   resolution: "@gar/promisify@npm:1.1.3"
@@ -1883,12 +2058,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/node@npm:18.18.10":
-  version: 18.18.10
-  resolution: "@types/node@npm:18.18.10"
+"@types/node@npm:22.15.3":
+  version: 22.15.3
+  resolution: "@types/node@npm:22.15.3"
   dependencies:
-    undici-types: "npm:~5.26.4"
-  checksum: 6f5bca390970ac42ed073490127edc9822e030def4f5ec7dd71e2dcf5a9dd622be984ef6676b698db126d986229ef5b02c65dac7cfc09cc2c1d94eaefe32a805
+    undici-types: "npm:~6.21.0"
+  checksum: 6b4ff03c36598432b419980f828281aa16383e2de6eb61f73275495ef8d2cbf8cb5607659b4cae5ff8b2b2ff69913ea07ffcc0be029e4280b6e8bc138dc6629b
   languageName: node
   linkType: hard
 
@@ -3823,6 +3998,95 @@ __metadata:
   languageName: node
   linkType: hard
 
+"esbuild@npm:^0.27.4":
+  version: 0.27.4
+  resolution: "esbuild@npm:0.27.4"
+  dependencies:
+    "@esbuild/aix-ppc64": "npm:0.27.4"
+    "@esbuild/android-arm": "npm:0.27.4"
+    "@esbuild/android-arm64": "npm:0.27.4"
+    "@esbuild/android-x64": "npm:0.27.4"
+    "@esbuild/darwin-arm64": "npm:0.27.4"
+    "@esbuild/darwin-x64": "npm:0.27.4"
+    "@esbuild/freebsd-arm64": "npm:0.27.4"
+    "@esbuild/freebsd-x64": "npm:0.27.4"
+    "@esbuild/linux-arm": "npm:0.27.4"
+    "@esbuild/linux-arm64": "npm:0.27.4"
+    "@esbuild/linux-ia32": "npm:0.27.4"
+    "@esbuild/linux-loong64": "npm:0.27.4"
+    "@esbuild/linux-mips64el": "npm:0.27.4"
+    "@esbuild/linux-ppc64": "npm:0.27.4"
+    "@esbuild/linux-riscv64": "npm:0.27.4"
+    "@esbuild/linux-s390x": "npm:0.27.4"
+    "@esbuild/linux-x64": "npm:0.27.4"
+    "@esbuild/netbsd-arm64": "npm:0.27.4"
+    "@esbuild/netbsd-x64": "npm:0.27.4"
+    "@esbuild/openbsd-arm64": "npm:0.27.4"
+    "@esbuild/openbsd-x64": "npm:0.27.4"
+    "@esbuild/openharmony-arm64": "npm:0.27.4"
+    "@esbuild/sunos-x64": "npm:0.27.4"
+    "@esbuild/win32-arm64": "npm:0.27.4"
+    "@esbuild/win32-ia32": "npm:0.27.4"
+    "@esbuild/win32-x64": "npm:0.27.4"
+  dependenciesMeta:
+    "@esbuild/aix-ppc64":
+      optional: true
+    "@esbuild/android-arm":
+      optional: true
+    "@esbuild/android-arm64":
+      optional: true
+    "@esbuild/android-x64":
+      optional: true
+    "@esbuild/darwin-arm64":
+      optional: true
+    "@esbuild/darwin-x64":
+      optional: true
+    "@esbuild/freebsd-arm64":
+      optional: true
+    "@esbuild/freebsd-x64":
+      optional: true
+    "@esbuild/linux-arm":
+      optional: true
+    "@esbuild/linux-arm64":
+      optional: true
+    "@esbuild/linux-ia32":
+      optional: true
+    "@esbuild/linux-loong64":
+      optional: true
+    "@esbuild/linux-mips64el":
+      optional: true
+    "@esbuild/linux-ppc64":
+      optional: true
+    "@esbuild/linux-riscv64":
+      optional: true
+    "@esbuild/linux-s390x":
+      optional: true
+    "@esbuild/linux-x64":
+      optional: true
+    "@esbuild/netbsd-arm64":
+      optional: true
+    "@esbuild/netbsd-x64":
+      optional: true
+    "@esbuild/openbsd-arm64":
+      optional: true
+    "@esbuild/openbsd-x64":
+      optional: true
+    "@esbuild/openharmony-arm64":
+      optional: true
+    "@esbuild/sunos-x64":
+      optional: true
+    "@esbuild/win32-arm64":
+      optional: true
+    "@esbuild/win32-ia32":
+      optional: true
+    "@esbuild/win32-x64":
+      optional: true
+  bin:
+    esbuild: bin/esbuild
+  checksum: 32b46ec22ef78bae6cc141145022a4c0209852c07151f037fbefccc2033ca54e7f33705f8fca198eb7026f400142f64c2dbc9f0d0ce9c0a638ebc472a04abc4a
+  languageName: node
+  linkType: hard
+
 "escalade@npm:^3.1.1":
   version: 3.1.1
   resolution: "escalade@npm:3.1.1"
@@ -5123,15 +5387,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"hot-shots@npm:10.0.0":
-  version: 10.0.0
-  resolution: "hot-shots@npm:10.0.0"
+"hot-shots@npm:14.2.0":
+  version: 14.2.0
+  resolution: "hot-shots@npm:14.2.0"
   dependencies:
     unix-dgram: "npm:2.x"
   dependenciesMeta:
     unix-dgram:
       optional: true
-  checksum: 8d6f292a9d7ff07946751b8fd622a206379862ed821671d8a1e52357841100059a1b6952f9e663eb7b9fe6ad85d3686c5f6c82a4597c24faa529dd33ecdbe07a
+  checksum: be185993d3588ca0d21507b69653360263a8b1a83bfd2b2a8ad3d9ac5a5d3c0e5dfc83b28ab86244e157870a70aa258f6602248d927ec4023aabb0b5a350e020
   languageName: node
   linkType: hard
 
@@ -8216,27 +8480,27 @@ __metadata:
   languageName: node
   linkType: hard
 
-"playwright-core@npm:1.49.0":
-  version: 1.49.0
-  resolution: "playwright-core@npm:1.49.0"
+"playwright-core@npm:1.58.2":
+  version: 1.58.2
+  resolution: "playwright-core@npm:1.58.2"
   bin:
     playwright-core: cli.js
-  checksum: ef9c708293adab100337ed7fd8e61660be381707fc2b84f07b5f40d1ead44feb6a8e52fef98075e594522229d15a9ad56dd1471689cfa59409bec6447c22944d
+  checksum: 8a98fcf122167e8703d525db2252de0e3da4ab9110ab6ea9951247e52d846310eb25ea2c805e1b7ccb54b4010c44e5adc3a76aae6da02f34324ccc3e76683bb1
   languageName: node
   linkType: hard
 
-"playwright@npm:1.49.0":
-  version: 1.49.0
-  resolution: "playwright@npm:1.49.0"
+"playwright@npm:1.58.2":
+  version: 1.58.2
+  resolution: "playwright@npm:1.58.2"
   dependencies:
     fsevents: "npm:2.3.2"
-    playwright-core: "npm:1.49.0"
+    playwright-core: "npm:1.58.2"
   dependenciesMeta:
     fsevents:
       optional: true
   bin:
     playwright: cli.js
-  checksum: 1fb198d09d388ec46cc2f0fc6b889a8bde8a75066ded82d35f08ba333091ebf3fc4ddf11263a86058a7078c7238ec4f23a86a9f1dc3ebd4f610c9eb07841fb32
+  checksum: d89d6c8a32388911b9aff9ee0f1a90076219f15c804f2b287db048b9e9cde182aea3131fac1959051d25189ed4218ec4272b137c83cd7f9cd24781cbc77edd86
   languageName: node
   linkType: hard
 
@@ -10010,19 +10274,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"undici-types@npm:~5.26.4":
-  version: 5.26.5
-  resolution: "undici-types@npm:5.26.5"
-  checksum: 0097779d94bc0fd26f0418b3a05472410408877279141ded2bd449167be1aed7ea5b76f756562cb3586a07f251b90799bab22d9019ceba49c037c76445f7cddd
-  languageName: node
-  linkType: hard
-
-"undici@npm:5.28.4":
-  version: 5.28.4
-  resolution: "undici@npm:5.28.4"
-  dependencies:
-    "@fastify/busboy": "npm:^2.0.0"
-  checksum: a666a9f5ac4270c659fafc33d78b6b5039a0adbae3e28f934774c85dcc66ea91da907896f12b414bd6f578508b44d5dc206fa636afa0e49a4e1c9e99831ff065
+"undici-types@npm:~6.21.0":
+  version: 6.21.0
+  resolution: "undici-types@npm:6.21.0"
+  checksum: ec8f41aa4359d50f9b59fa61fe3efce3477cc681908c8f84354d8567bb3701fafdddf36ef6bff307024d3feb42c837cf6f670314ba37fc8145e219560e473d14
   languageName: node
   linkType: hard
 
