From 802801bd773362c7c10ecd4c5a7a1d21415ff564 Mon Sep 17 00:00:00 2001
From: Caknoooo
Date: Wed, 26 Nov 2025 14:03:07 +0700
Subject: [PATCH 1/4] feat: change approach crawling with playwright
---
Dockerfile | 53 +-
README.md | 4 +-
docker-compose.yml | 10 +-
docs/en/getting-started.md | 2 +-
docs/en/index.md | 2 +-
docs/id/getting-started.md | 2 +-
docs/id/index.md | 2 +-
docs/public/openapi.json | 2 +-
package-lock.json | 673 +++-----------------
package.json | 5 +-
src/api/openapi.ts | 2 +-
src/api/routes/web.ts | 6 +-
src/cli/services/utils/docker-config.ts | 149 +++--
src/cli/services/utils/docker-operations.ts | 17 +-
src/cli/utils/command-utils.ts | 12 +-
src/services/accessibility.ts | 20 +-
src/services/crawling.ts | 46 +-
17 files changed, 283 insertions(+), 724 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index cc249e1..93c60ca 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,46 +1,57 @@
-FROM node:20-alpine AS builder
-
+# ---------------------------
+# Builder Stage
+# ---------------------------
+FROM node:22-alpine AS builder
WORKDIR /app
-# Install deps
+RUN apk update && apk add --no-cache \
+ bash \
+ chromium \
+ chromium-chromedriver \
+ nss \
+ freetype \
+ harfbuzz \
+ ca-certificates \
+ ttf-freefont \
+ && apk add --no-cache --virtual .build-deps \
+ gcc g++ make python3 && \
+ npm install -g cross-env
+
COPY package*.json ./
-RUN npm ci
+RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
-# Copy source code
-COPY . .
+RUN npm install playwright
-# Build app
-RUN npm run build
+COPY tsconfig*.json ./
+COPY src ./src
-FROM node:20-alpine AS production
+RUN npm run build
+RUN npm run build:cli
+# ---------------------------
+# Runtime Stage
+# ---------------------------
+FROM node:22-alpine AS runtime
WORKDIR /app
-# Install dependencies needed for Puppeteer
-RUN apk add --no-cache \
+RUN apk update && apk add --no-cache \
chromium \
+ chromium-chromedriver \
nss \
freetype \
- freetype-dev \
harfbuzz \
ca-certificates \
ttf-freefont
-ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
- PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser \
+ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \
+ PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium-browser \
NODE_ENV=production
-# Copy package.json dan node_modules hasil build
COPY package*.json ./
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/src/data ./src/data
-# Create necessary directories
RUN mkdir -p logs storage
-# Expose port
-EXPOSE 3001
-
-# Default command
-CMD ["npm", "start"]
\ No newline at end of file
+CMD ["node", "dist/index.js"]
diff --git a/README.md b/README.md
index bc0485a..a0f04be 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-
+
@@ -937,7 +937,7 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
## 🙏 Acknowledgments
- [axe-core](https://github.com/dequelabs/axe-core) for accessibility testing
-- [Puppeteer](https://github.com/puppeteer/puppeteer) for web automation
+- [Playwright](https://github.com/microsoft/playwright) for web automation
- [Express.js](https://expressjs.com/) for the web framework
- [OpenAI](https://openai.com/) for AI-powered accessibility analysis
- [Docker](https://www.docker.com/) for containerization
diff --git a/docker-compose.yml b/docker-compose.yml
index b1945ca..d1872da 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,5 +1,3 @@
-version: '3.9'
-
services:
lenscore-init:
build:
@@ -7,7 +5,7 @@ services:
dockerfile: Dockerfile
volumes:
- node_modules_data:/app/node_modules
- profiles: ['init']
+ profiles: ["init"]
lenscore:
container_name: lenscore-app
@@ -15,7 +13,7 @@ services:
context: .
dockerfile: Dockerfile
ports:
- - '${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}'
+ - "${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}"
environment:
- NODE_ENV=development
- PORT=${LENSCORE_PORT:-3001}
@@ -36,14 +34,14 @@ services:
- ./cache:/app/cache
- ./web:/app/web
- ./storage:/app/storage
- - node_modules_data:/app/node_modules
+ - ${HOME}/.lenscore/web:/app/.lenscore/web
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- - '6379:6379'
+ - "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
diff --git a/docs/en/getting-started.md b/docs/en/getting-started.md
index 7a6fc44..e61a398 100644
--- a/docs/en/getting-started.md
+++ b/docs/en/getting-started.md
@@ -7,7 +7,7 @@ Welcome to LensCore! This guide will help you get up and running with our access
LensCore is an open-source platform that combines:
- **Accessibility Testing** powered by axe-core
-- **Intelligent Web Crawling** with Puppeteer
+- **Intelligent Web Crawling** with Playwright
- **AI-Enhanced Analysis** using OpenAI (optional)
- **Comprehensive Reporting** in HTML and JSON formats
diff --git a/docs/en/index.md b/docs/en/index.md
index a3ac58d..2226aa2 100644
--- a/docs/en/index.md
+++ b/docs/en/index.md
@@ -35,7 +35,7 @@ features:
- icon: ⚡
title: High Performance
- details: Built with TypeScript and Puppeteer for fast, reliable testing at scale
+ details: Built with TypeScript and Playwright for fast, reliable testing at scale
- icon: 🌐
title: Multi-Language
diff --git a/docs/id/getting-started.md b/docs/id/getting-started.md
index 2ac3681..f802cf7 100644
--- a/docs/id/getting-started.md
+++ b/docs/id/getting-started.md
@@ -7,7 +7,7 @@ Selamat datang di LensCore! Panduan ini akan membantu Anda memulai dengan platfo
LensCore adalah platform open-source yang menggabungkan:
- **Testing Aksesibilitas** dengan axe-core
-- **Web Crawling Cerdas** menggunakan Puppeteer
+- **Web Crawling Cerdas** menggunakan Playwright
- **Analisis Berbasis AI** dengan OpenAI (opsional)
- **Pelaporan Komprehensif** dalam format HTML dan JSON
diff --git a/docs/id/index.md b/docs/id/index.md
index eaebe99..80b6218 100644
--- a/docs/id/index.md
+++ b/docs/id/index.md
@@ -35,7 +35,7 @@ features:
- icon: ⚡
title: Performa Tinggi
- details: Dibangun dengan TypeScript dan Puppeteer untuk testing yang cepat dan reliable dalam skala besar
+ details: Dibangun dengan TypeScript dan Playwright untuk testing yang cepat dan reliable dalam skala besar
- icon: 🌐
title: Multi-Bahasa
diff --git a/docs/public/openapi.json b/docs/public/openapi.json
index 117e8e3..084cd67 100644
--- a/docs/public/openapi.json
+++ b/docs/public/openapi.json
@@ -103,7 +103,7 @@
"networkidle0",
"networkidle2"
],
- "description": "Puppeteer navigation wait condition",
+ "description": "Playwright navigation wait condition",
"example": "networkidle2"
}
},
diff --git a/package-lock.json b/package-lock.json
index 85e3c8f..3e5d11b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7,6 +7,7 @@
"": {
"name": "@accesstime/lenscore",
"version": "0.1.68",
+ "hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@google-cloud/storage": "^7.7.0",
@@ -27,7 +28,7 @@
"marked": "^16.4.1",
"openai": "^6.5.0",
"ora": "^5.4.1",
- "puppeteer": "^24.15.0",
+ "playwright": "^1.57.0",
"sharp": "^0.33.0",
"uuid": "^9.0.1",
"winston": "^3.11.0",
@@ -330,6 +331,7 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
@@ -501,6 +503,7 @@
"version": "7.28.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -3356,27 +3359,6 @@
"@noble/hashes": "^1.1.5"
}
},
- "node_modules/@puppeteer/browsers": {
- "version": "2.10.12",
- "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.12.tgz",
- "integrity": "sha512-mP9iLFZwH+FapKJLeA7/fLqOlSUwYpMwjR1P5J23qd4e7qGJwecJccJqHYrjw33jmIZYV4dtiTHPD/J+1e7cEw==",
- "license": "Apache-2.0",
- "dependencies": {
- "debug": "^4.4.3",
- "extract-zip": "^2.0.1",
- "progress": "^2.0.3",
- "proxy-agent": "^6.5.0",
- "semver": "^7.7.3",
- "tar-fs": "^3.1.1",
- "yargs": "^17.7.2"
- },
- "bin": {
- "browsers": "lib/cjs/main-cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz",
@@ -3872,12 +3854,6 @@
"node": ">= 10"
}
},
- "node_modules/@tootallnate/quickjs-emscripten": {
- "version": "0.23.0",
- "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz",
- "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==",
- "license": "MIT"
- },
"node_modules/@types/babel__core": {
"version": "7.20.5",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
@@ -4348,16 +4324,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@types/yauzl": {
- "version": "2.10.3",
- "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
- "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@types/node": "*"
- }
- },
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "8.46.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz",
@@ -5036,6 +5002,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
"license": "Python-2.0"
},
"node_modules/array-flatten": {
@@ -5067,18 +5034,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/ast-types": {
- "version": "0.13.4",
- "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz",
- "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==",
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.0.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/async": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
@@ -5155,20 +5110,6 @@
"node": ">=4"
}
},
- "node_modules/b4a": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz",
- "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==",
- "license": "Apache-2.0",
- "peerDependencies": {
- "react-native-b4a": "*"
- },
- "peerDependenciesMeta": {
- "react-native-b4a": {
- "optional": true
- }
- }
- },
"node_modules/babel-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@@ -5302,97 +5243,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/bare-events": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.1.tgz",
- "integrity": "sha512-oxSAxTS1hRfnyit2CL5QpAOS5ixfBjj6ex3yTNvXyY/kE719jQ/IjuESJBK2w5v4wwQRAHGseVJXx9QBYOtFGQ==",
- "license": "Apache-2.0",
- "peerDependencies": {
- "bare-abort-controller": "*"
- },
- "peerDependenciesMeta": {
- "bare-abort-controller": {
- "optional": true
- }
- }
- },
- "node_modules/bare-fs": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.0.tgz",
- "integrity": "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "bare-events": "^2.5.4",
- "bare-path": "^3.0.0",
- "bare-stream": "^2.6.4",
- "bare-url": "^2.2.2",
- "fast-fifo": "^1.3.2"
- },
- "engines": {
- "bare": ">=1.16.0"
- },
- "peerDependencies": {
- "bare-buffer": "*"
- },
- "peerDependenciesMeta": {
- "bare-buffer": {
- "optional": true
- }
- }
- },
- "node_modules/bare-os": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz",
- "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==",
- "license": "Apache-2.0",
- "optional": true,
- "engines": {
- "bare": ">=1.14.0"
- }
- },
- "node_modules/bare-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz",
- "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "bare-os": "^3.0.1"
- }
- },
- "node_modules/bare-stream": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz",
- "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "streamx": "^2.21.0"
- },
- "peerDependencies": {
- "bare-buffer": "*",
- "bare-events": "*"
- },
- "peerDependenciesMeta": {
- "bare-buffer": {
- "optional": true
- },
- "bare-events": {
- "optional": true
- }
- }
- },
- "node_modules/bare-url": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz",
- "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==",
- "license": "Apache-2.0",
- "optional": true,
- "dependencies": {
- "bare-path": "^3.0.0"
- }
- },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -5423,15 +5273,6 @@
"baseline-browser-mapping": "dist/cli.js"
}
},
- "node_modules/basic-ftp": {
- "version": "5.0.5",
- "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz",
- "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==",
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- }
- },
"node_modules/bignumber.js": {
"version": "9.3.1",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
@@ -5647,15 +5488,6 @@
"isarray": "^1.0.0"
}
},
- "node_modules/buffer-crc32": {
- "version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
@@ -5729,6 +5561,7 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -5910,19 +5743,6 @@
"node": ">= 6"
}
},
- "node_modules/chromium-bidi": {
- "version": "10.5.1",
- "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-10.5.1.tgz",
- "integrity": "sha512-rlj6OyhKhVTnk4aENcUme3Jl9h+cq4oXu4AzBcvr8RMmT6BR4a3zSNT9dbIfXr9/BS6ibzRyDhowuw4n2GgzsQ==",
- "license": "Apache-2.0",
- "dependencies": {
- "mitt": "^3.0.1",
- "zod": "^3.24.1"
- },
- "peerDependencies": {
- "devtools-protocol": "*"
- }
- },
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -5983,6 +5803,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
@@ -5997,6 +5818,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
@@ -6275,6 +6097,7 @@
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.1",
@@ -6400,15 +6223,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/data-uri-to-buffer": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz",
- "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==",
- "license": "MIT",
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -6487,20 +6301,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/degenerator": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz",
- "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==",
- "license": "MIT",
- "dependencies": {
- "ast-types": "^0.13.4",
- "escodegen": "^2.1.0",
- "esprima": "^4.0.1"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -6581,12 +6381,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/devtools-protocol": {
- "version": "0.0.1521046",
- "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1521046.tgz",
- "integrity": "sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==",
- "license": "BSD-3-Clause"
- },
"node_modules/dezalgo": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz",
@@ -6815,6 +6609,7 @@
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -6824,6 +6619,7 @@
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
@@ -6920,6 +6716,7 @@
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -6944,27 +6741,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/escodegen": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
- "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "esprima": "^4.0.1",
- "estraverse": "^5.2.0",
- "esutils": "^2.0.2"
- },
- "bin": {
- "escodegen": "bin/escodegen.js",
- "esgenerate": "bin/esgenerate.js"
- },
- "engines": {
- "node": ">=6.0"
- },
- "optionalDependencies": {
- "source-map": "~0.6.1"
- }
- },
"node_modules/eslint": {
"version": "9.39.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz",
@@ -7137,6 +6913,7 @@
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
"license": "BSD-2-Clause",
"bin": {
"esparse": "bin/esparse.js",
@@ -7176,6 +6953,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
@@ -7192,6 +6970,7 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.10.0"
@@ -7224,15 +7003,6 @@
"node": ">=0.4.x"
}
},
- "node_modules/events-universal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
- "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
- "license": "Apache-2.0",
- "dependencies": {
- "bare-events": "^2.7.0"
- }
- },
"node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -7357,41 +7127,6 @@
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT"
},
- "node_modules/extract-zip": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
- "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
- "license": "BSD-2-Clause",
- "dependencies": {
- "debug": "^4.1.1",
- "get-stream": "^5.1.0",
- "yauzl": "^2.10.0"
- },
- "bin": {
- "extract-zip": "cli.js"
- },
- "engines": {
- "node": ">= 10.17.0"
- },
- "optionalDependencies": {
- "@types/yauzl": "^2.9.1"
- }
- },
- "node_modules/extract-zip/node_modules/get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "license": "MIT",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -7399,12 +7134,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/fast-fifo": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
- "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
- "license": "MIT"
- },
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -7511,15 +7240,6 @@
"bser": "2.1.1"
}
},
- "node_modules/fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
- "license": "MIT",
- "dependencies": {
- "pend": "~1.2.0"
- }
- },
"node_modules/fecha": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
@@ -7791,6 +7511,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
@@ -7869,20 +7590,6 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
- "node_modules/get-uri": {
- "version": "6.0.5",
- "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz",
- "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==",
- "license": "MIT",
- "dependencies": {
- "basic-ftp": "^5.0.2",
- "data-uri-to-buffer": "^6.0.2",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/git-raw-commits": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz",
@@ -8273,19 +7980,6 @@
"node": ">= 0.8"
}
},
- "node_modules/http-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
@@ -8364,6 +8058,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -8495,15 +8190,6 @@
"url": "https://opencollective.com/ioredis"
}
},
- "node_modules/ip-address": {
- "version": "10.0.1",
- "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
- "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
- "license": "MIT",
- "engines": {
- "node": ">= 12"
- }
- },
"node_modules/ipaddr.js": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -8533,6 +8219,7 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/is-binary-path": {
@@ -9439,12 +9126,14 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@@ -9486,6 +9175,7 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true,
"license": "MIT"
},
"node_modules/json-schema-traverse": {
@@ -9617,6 +9307,7 @@
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
"license": "MIT"
},
"node_modules/locate-path": {
@@ -10096,6 +9787,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/ms": {
@@ -10154,15 +9846,6 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"license": "MIT"
},
- "node_modules/netmask": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz",
- "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==",
- "license": "MIT",
- "engines": {
- "node": ">= 0.4.0"
- }
- },
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -10489,42 +10172,11 @@
"node": ">=6"
}
},
- "node_modules/pac-proxy-agent": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz",
- "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==",
- "license": "MIT",
- "dependencies": {
- "@tootallnate/quickjs-emscripten": "^0.23.0",
- "agent-base": "^7.1.2",
- "debug": "^4.3.4",
- "get-uri": "^6.0.1",
- "http-proxy-agent": "^7.0.0",
- "https-proxy-agent": "^7.0.6",
- "pac-resolver": "^7.0.1",
- "socks-proxy-agent": "^8.0.5"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/pac-resolver": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz",
- "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==",
- "license": "MIT",
- "dependencies": {
- "degenerator": "^5.0.0",
- "netmask": "^2.0.2"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
@@ -10537,6 +10189,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
@@ -10652,12 +10305,6 @@
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"license": "MIT"
},
- "node_modules/pend": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
- "license": "MIT"
- },
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
@@ -10669,6 +10316,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
@@ -10763,6 +10411,50 @@
"node": ">=8"
}
},
+ "node_modules/playwright": {
+ "version": "1.57.0",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz",
+ "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "playwright-core": "1.57.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.57.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz",
+ "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==",
+ "license": "Apache-2.0",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/playwright/node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -10866,15 +10558,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/prompts": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
@@ -10913,40 +10596,6 @@
"node": ">= 0.10"
}
},
- "node_modules/proxy-agent": {
- "version": "6.5.0",
- "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz",
- "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "^4.3.4",
- "http-proxy-agent": "^7.0.1",
- "https-proxy-agent": "^7.0.6",
- "lru-cache": "^7.14.1",
- "pac-proxy-agent": "^7.1.0",
- "proxy-from-env": "^1.1.0",
- "socks-proxy-agent": "^8.0.5"
- },
- "engines": {
- "node": ">= 14"
- }
- },
- "node_modules/proxy-agent/node_modules/lru-cache": {
- "version": "7.18.3",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
- "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/proxy-from-env": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
- "license": "MIT"
- },
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
@@ -10954,16 +10603,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/pump": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
- "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -10974,45 +10613,6 @@
"node": ">=6"
}
},
- "node_modules/puppeteer": {
- "version": "24.27.0",
- "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.27.0.tgz",
- "integrity": "sha512-eEcAFGxmHRSrk74DVkFAMAwfj4l3Ak8avBuA2bZaAoocY1+Fb9WLS3I7jlOc/tIOU7EmGLiDdVP08R44wADpHw==",
- "hasInstallScript": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@puppeteer/browsers": "2.10.12",
- "chromium-bidi": "10.5.1",
- "cosmiconfig": "^9.0.0",
- "devtools-protocol": "0.0.1521046",
- "puppeteer-core": "24.27.0",
- "typed-query-selector": "^2.12.0"
- },
- "bin": {
- "puppeteer": "lib/cjs/puppeteer/node/cli.js"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/puppeteer-core": {
- "version": "24.27.0",
- "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.27.0.tgz",
- "integrity": "sha512-yubwj2XXmTM3wRIpbhO5nCjbByPgpFHlgrsD4IK+gMPqO7/a5FfnoSXDKjmqi8A2M1Ewusz0rTI/r+IN0GU0MA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@puppeteer/browsers": "2.10.12",
- "chromium-bidi": "10.5.1",
- "debug": "^4.4.3",
- "devtools-protocol": "0.0.1521046",
- "typed-query-selector": "^2.12.0",
- "webdriver-bidi-protocol": "0.3.8",
- "ws": "^8.18.3"
- },
- "engines": {
- "node": ">=18"
- }
- },
"node_modules/pure-rand": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
@@ -11197,6 +10797,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -11260,6 +10861,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -11813,44 +11415,6 @@
"node": ">=8"
}
},
- "node_modules/smart-buffer": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
- "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
- "license": "MIT",
- "engines": {
- "node": ">= 6.0.0",
- "npm": ">= 3.0.0"
- }
- },
- "node_modules/socks": {
- "version": "2.8.7",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
- "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
- "license": "MIT",
- "dependencies": {
- "ip-address": "^10.0.1",
- "smart-buffer": "^4.2.0"
- },
- "engines": {
- "node": ">= 10.0.0",
- "npm": ">= 3.0.0"
- }
- },
- "node_modules/socks-proxy-agent": {
- "version": "8.0.5",
- "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
- "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
- "license": "MIT",
- "dependencies": {
- "agent-base": "^7.1.2",
- "debug": "^4.3.4",
- "socks": "^2.8.3"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -11981,17 +11545,6 @@
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
"license": "MIT"
},
- "node_modules/streamx": {
- "version": "2.23.0",
- "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
- "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
- "license": "MIT",
- "dependencies": {
- "events-universal": "^1.0.0",
- "fast-fifo": "^1.3.2",
- "text-decoder": "^1.1.0"
- }
- },
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
@@ -12210,31 +11763,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/tar-fs": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz",
- "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==",
- "license": "MIT",
- "dependencies": {
- "pump": "^3.0.0",
- "tar-stream": "^3.1.5"
- },
- "optionalDependencies": {
- "bare-fs": "^4.0.1",
- "bare-path": "^3.0.0"
- }
- },
- "node_modules/tar-stream": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
- "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
- "license": "MIT",
- "dependencies": {
- "b4a": "^1.6.4",
- "fast-fifo": "^1.2.0",
- "streamx": "^2.15.0"
- }
- },
"node_modules/teeny-request": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
@@ -12329,15 +11857,6 @@
"node": "*"
}
},
- "node_modules/text-decoder": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz",
- "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==",
- "license": "Apache-2.0",
- "dependencies": {
- "b4a": "^1.6.4"
- }
- },
"node_modules/text-extensions": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz",
@@ -12590,17 +12109,11 @@
"node": ">= 0.6"
}
},
- "node_modules/typed-query-selector": {
- "version": "2.12.0",
- "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz",
- "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==",
- "license": "MIT"
- },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "devOptional": true,
+ "dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -13465,12 +12978,6 @@
"defaults": "^1.0.3"
}
},
- "node_modules/webdriver-bidi-protocol": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.8.tgz",
- "integrity": "sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==",
- "license": "Apache-2.0"
- },
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@@ -13638,27 +13145,6 @@
"dev": true,
"license": "ISC"
},
- "node_modules/ws": {
- "version": "8.18.3",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
- "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
- "license": "MIT",
- "engines": {
- "node": ">=10.0.0"
- },
- "peerDependencies": {
- "bufferutil": "^4.0.1",
- "utf-8-validate": ">=5.0.2"
- },
- "peerDependenciesMeta": {
- "bufferutil": {
- "optional": true
- },
- "utf-8-validate": {
- "optional": true
- }
- }
- },
"node_modules/xml2js": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
@@ -13685,6 +13171,7 @@
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
@@ -13701,6 +13188,7 @@
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
@@ -13719,21 +13207,12 @@
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
- "node_modules/yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
- "license": "MIT",
- "dependencies": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
- },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index b743306..b637083 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@accesstime/lenscore",
- "version": "0.1.68",
+ "version": "0.1.77",
"description": "Open-source accessibility testing and web crawling platform",
"main": "dist/index.js",
"bin": {
@@ -24,6 +24,7 @@
"format:check": "prettier -c .",
"typecheck": "tsc --noEmit",
"prepare": "husky",
+ "postinstall": "playwright install chromium",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
@@ -56,7 +57,7 @@
"marked": "^16.4.1",
"openai": "^6.5.0",
"ora": "^5.4.1",
- "puppeteer": "^24.15.0",
+ "playwright": "^1.57.0",
"sharp": "^0.33.0",
"uuid": "^9.0.1",
"winston": "^3.11.0",
diff --git a/src/api/openapi.ts b/src/api/openapi.ts
index d3f6986..b1d779d 100644
--- a/src/api/openapi.ts
+++ b/src/api/openapi.ts
@@ -99,7 +99,7 @@ export const openApiSpec = {
'networkidle0',
'networkidle2',
],
- description: 'Puppeteer navigation wait condition',
+ description: 'Playwright navigation wait condition',
example: 'networkidle2',
},
},
diff --git a/src/api/routes/web.ts b/src/api/routes/web.ts
index 40f55ae..6f0702b 100644
--- a/src/api/routes/web.ts
+++ b/src/api/routes/web.ts
@@ -12,14 +12,16 @@ const router = Router();
*/
function findWebOutputDir(): string {
const possiblePaths = [
+ // Docker container - mounted from ~/.lenscore/web
+ path.join('/app', '.lenscore', 'web', 'output'),
+ // Docker container - from app directory
+ path.join('/app', 'web', 'output'),
// User's home directory (preferred for global usage)
path.join(os.homedir(), '.lenscore', 'web', 'output'),
// Current working directory with .lenscore
path.join(process.cwd(), '.lenscore', 'web', 'output'),
// Development mode - from source
path.join(process.cwd(), 'web', 'output'),
- // Docker container - from app directory
- path.join('/app', 'web', 'output'),
];
// Try to add global install path if available
diff --git a/src/cli/services/utils/docker-config.ts b/src/cli/services/utils/docker-config.ts
index 5c1925d..839db90 100644
--- a/src/cli/services/utils/docker-config.ts
+++ b/src/cli/services/utils/docker-config.ts
@@ -84,7 +84,7 @@ export class DockerConfigService {
- ./cache:/app/cache
- ./web:/app/web
- ./storage:/app/storage
- - node_modules_data:/app/node_modules
+ - \${HOME}/.lenscore/web:/app/.lenscore/web
depends_on:
- redis
@@ -102,45 +102,63 @@ volumes:
}
private getDockerfileContent(): string {
- return `FROM node:20-alpine AS builder
-
+ return `# ---------------------------
+# Builder Stage
+# ---------------------------
+FROM node:22-alpine AS builder
WORKDIR /app
+RUN apk update && apk add --no-cache \\
+ bash \\
+ chromium \\
+ chromium-chromedriver \\
+ nss \\
+ freetype \\
+ harfbuzz \\
+ ca-certificates \\
+ ttf-freefont \\
+ && apk add --no-cache --virtual .build-deps \\
+ gcc g++ make python3 && \\
+ npm install -g cross-env
+
COPY package*.json ./
+RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
-RUN npm install
+RUN npm install playwright
-COPY . .
+COPY tsconfig*.json ./
+COPY src ./src
RUN npm run build
+RUN npm run build:cli
-FROM node:20-alpine AS production
-
+# ---------------------------
+# Runtime Stage
+# ---------------------------
+FROM node:22-alpine AS runtime
WORKDIR /app
-RUN apk add --no-cache \\
- chromium \\
- nss \\
- freetype \\
- freetype-dev \\
- harfbuzz \\
- ca-certificates \\
- ttf-freefont
-
-ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \\
- PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser \\
+RUN apk update && apk add --no-cache \\
+ chromium \\
+ chromium-chromedriver \\
+ nss \\
+ freetype \\
+ harfbuzz \\
+ ca-certificates \\
+ ttf-freefont
+
+ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \\
+ PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium-browser \\
NODE_ENV=production
COPY package*.json ./
-RUN npm install --production
-
+COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
+COPY --from=builder /app/src/data ./src/data
RUN mkdir -p logs storage
-EXPOSE 3001
-
-CMD ["npm", "start"]`;
+CMD ["node", "dist/index.js"]`;
}
private async setupPackageFiles(lenscoreDir: string): Promise {
@@ -152,13 +170,38 @@ CMD ["npm", "start"]`;
JSON.stringify(packageJsonContent, null, 2)
);
+ const possiblePackageDirs: string[] = [
+ process.cwd(),
+ path.resolve(__filename, '../../../../'),
+ ];
+
try {
- const packageDir = await this.findPackageDirectory();
- await this.copyConfigFiles(packageDir, lenscoreDir);
- await this.copySourceFiles(packageDir, lenscoreDir);
- await this.copyWebTemplates(packageDir, lenscoreDir);
- } catch (error) {
- console.warn(`⚠️ Package setup warning: ${error}`);
+ const packagePath = require.resolve('@accesstime/lenscore');
+ possiblePackageDirs.push(
+ path.resolve(packagePath, '../..'),
+ path.dirname(packagePath)
+ );
+ } catch {
+ //
+ }
+
+ let packageDirFound = false;
+ for (const packageDir of possiblePackageDirs) {
+ try {
+ const packageJsonPath = path.join(packageDir, 'package.json');
+ await fs.access(packageJsonPath);
+ await this.copyConfigFiles(packageDir, lenscoreDir);
+ await this.copySourceFiles(packageDir, lenscoreDir);
+ await this.copyWebTemplates(packageDir, lenscoreDir);
+ packageDirFound = true;
+ break;
+ } catch {
+ //
+ }
+ }
+
+ if (!packageDirFound) {
+ console.warn('⚠️ Could not find package directory, some files may be missing');
}
}
@@ -214,7 +257,7 @@ CMD ["npm", "start"]`;
marked: '^16.4.1',
openai: '^6.5.0',
ora: '^5.4.1',
- puppeteer: '^24.15.0',
+ playwright: '^1.48.0',
sharp: '^0.33.0',
uuid: '^9.0.1',
winston: '^3.11.0',
@@ -245,34 +288,6 @@ CMD ["npm", "start"]`;
};
}
- private async findPackageDirectory(): Promise {
- const possiblePackageDirs: string[] = [
- path.resolve(__filename, '../../../../'),
- ];
-
- // Try to add global install paths if available
- try {
- const packagePath = require.resolve('@accesstime/lenscore');
- possiblePackageDirs.push(
- path.resolve(packagePath, '../..'),
- path.dirname(packagePath)
- );
- } catch {
- // Package not found, skip this path (development mode)
- }
-
- for (const dir of possiblePackageDirs) {
- try {
- const packageJsonPath = path.join(dir, 'package.json');
- await fs.access(packageJsonPath);
- return dir;
- } catch {
- //
- }
- }
-
- throw new Error('Could not find package directory');
- }
private async copyConfigFiles(
packageDir: string,
@@ -283,18 +298,24 @@ CMD ["npm", "start"]`;
try {
const srcPath = path.join(packageDir, tsconfigFile);
const destPath = path.join(lenscoreDir, tsconfigFile);
+ await fs.access(srcPath);
await fs.copyFile(srcPath, destPath);
} catch {
//
}
}
- try {
- const packageLockSrc = path.join(packageDir, 'package-lock.json');
- const packageLockDest = path.join(lenscoreDir, 'package-lock.json');
- await fs.copyFile(packageLockSrc, packageLockDest);
- } catch {
- //
+ const packageLockDest = path.join(lenscoreDir, 'package-lock.json');
+
+ for (const possibleDir of [packageDir, process.cwd(), path.resolve(__filename, '../../../../')]) {
+ try {
+ const packageLockSrc = path.join(possibleDir, 'package-lock.json');
+ await fs.access(packageLockSrc);
+ await fs.copyFile(packageLockSrc, packageLockDest);
+ console.log(`✅ Copied package-lock.json from ${possibleDir}`);
+ } catch {
+ //
+ }
}
}
diff --git a/src/cli/services/utils/docker-operations.ts b/src/cli/services/utils/docker-operations.ts
index 2a822d0..2975c60 100644
--- a/src/cli/services/utils/docker-operations.ts
+++ b/src/cli/services/utils/docker-operations.ts
@@ -118,8 +118,21 @@ export class DockerOperationsService {
try {
const portInUse = await this.validationService.validatePort(this.port);
if (portInUse) {
- spinner.succeed('Port already in use - services likely running');
- return;
+ try {
+ const healthResponse = await fetch(`http://localhost:${this.port}/api/health`, {
+ method: 'GET',
+ signal: AbortSignal.timeout(3000),
+ });
+ if (healthResponse.ok) {
+ spinner.succeed('Port already in use - services likely running');
+ return;
+ }
+ } catch {
+ spinner.text = 'Port in use but service not responding, rebuilding...';
+ await this.stop();
+ await this.build();
+ return;
+ }
}
const imageExists = await this.validationService.validateImage();
diff --git a/src/cli/utils/command-utils.ts b/src/cli/utils/command-utils.ts
index e36c5ee..2ff7480 100644
--- a/src/cli/utils/command-utils.ts
+++ b/src/cli/utils/command-utils.ts
@@ -28,7 +28,17 @@ export class CommandUtils {
await dockerService.ensureServicesReady();
spinner.text = 'Waiting for LensCore to be ready...';
- await client.waitForReady();
+ await client.waitForReady(60000);
+ } else {
+ spinner.text = 'Verifying LensCore is fully ready...';
+ try {
+ await client.waitForReady(10000);
+ } catch {
+ spinner.text = 'Service responding but may need restart, rebuilding...';
+ const dockerService = await this.getDockerService();
+ await dockerService.ensureServicesReady();
+ await client.waitForReady(60000);
+ }
}
spinner.succeed('LensCore ready');
diff --git a/src/services/accessibility.ts b/src/services/accessibility.ts
index ca48c5c..9d17d24 100644
--- a/src/services/accessibility.ts
+++ b/src/services/accessibility.ts
@@ -1,4 +1,4 @@
-import puppeteer, { Browser } from 'puppeteer';
+import { chromium, Browser } from 'playwright';
import { v4 as uuidv4 } from 'uuid';
import fs from 'fs/promises';
import {
@@ -24,10 +24,18 @@ export class AccessibilityService {
}
async initialize(): Promise {
- this.browser = await puppeteer.launch({
+ const executablePath = process.env['PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH'];
+
+ const launchOptions: any = {
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
- });
+ };
+
+ if (executablePath) {
+ launchOptions.executablePath = executablePath;
+ }
+
+ this.browser = await chromium.launch(launchOptions);
}
async close(): Promise {
@@ -234,7 +242,11 @@ export class AccessibilityService {
}
} catch (error) {
clearTimeout(timeoutId);
- logger.error('Test accessibility error:', { error });
+ logger.error('Test accessibility error:', {
+ error: error instanceof Error ? error.message : String(error),
+ stack: error instanceof Error ? error.stack : undefined,
+ url: request.url,
+ });
return this.getMockResult(request);
}
}
diff --git a/src/services/crawling.ts b/src/services/crawling.ts
index e970eda..223dca3 100644
--- a/src/services/crawling.ts
+++ b/src/services/crawling.ts
@@ -1,4 +1,4 @@
-import puppeteer, { Browser } from 'puppeteer';
+import { chromium, Browser } from 'playwright';
import * as cheerio from 'cheerio';
import { CrawlRequest, CrawlResponse, CrawlResult, CrawlRules } from '../types';
import { env } from '../utils/env';
@@ -17,7 +17,9 @@ export class CrawlingService {
async initialize(): Promise {
try {
- this.browser = await puppeteer.launch({
+ const executablePath = process.env['PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH'];
+
+ const launchOptions: any = {
headless: true,
args: [
'--no-sandbox',
@@ -32,7 +34,13 @@ export class CrawlingService {
'--disable-renderer-backgrounding',
],
timeout: 30000,
- });
+ };
+
+ if (executablePath) {
+ launchOptions.executablePath = executablePath;
+ }
+
+ this.browser = await chromium.launch(launchOptions);
logger.info('Browser initialized successfully');
} catch (error) {
@@ -136,14 +144,11 @@ export class CrawlingService {
page = await this.browser!.newPage();
page.setDefaultTimeout(Math.min(timeout, 15000));
- await page.setViewport({ width: 1280, height: 720 });
- await page.setUserAgent(
- 'Mozilla/5.0 (compatible; LensCore/1.0; +https://github.com/accesslens/lenscore)'
- );
-
- if (request.headers) {
- await page.setExtraHTTPHeaders(request.headers);
- }
+ await page.setViewportSize({ width: 1280, height: 720 });
+ await page.setExtraHTTPHeaders({
+ 'User-Agent': 'Mozilla/5.0 (compatible; LensCore/1.0; +https://github.com/accesslens/lenscore)',
+ ...(request.headers || {}),
+ });
const pageController = new AbortController();
const pageTimeoutId = setTimeout(
@@ -156,17 +161,24 @@ export class CrawlingService {
try {
const response = await page.goto(url, {
waitUntil:
- request.waitUntil ||
- (env.CRAWL_WAIT_UNTIL as
- | 'domcontentloaded'
- | 'networkidle0'
- | 'networkidle2'),
+ request.waitUntil === 'networkidle0'
+ ? 'networkidle'
+ : request.waitUntil === 'networkidle2'
+ ? 'networkidle'
+ : request.waitUntil === 'domcontentloaded'
+ ? 'domcontentloaded'
+ : (env.CRAWL_WAIT_UNTIL === 'networkidle0' ||
+ env.CRAWL_WAIT_UNTIL === 'networkidle2'
+ ? 'networkidle'
+ : env.CRAWL_WAIT_UNTIL === 'domcontentloaded'
+ ? 'domcontentloaded'
+ : 'load'),
timeout: Math.min(timeout, 15000),
});
clearTimeout(pageTimeoutId);
- const statusCode = response?.status() || 0;
+ const statusCode = response?.status() ?? 0;
if (statusCode >= 400) {
logger.warn('HTTP error status', { url, statusCode });
From 45536932c367deae259fe4ec955997c99eee5892 Mon Sep 17 00:00:00 2001
From: Caknoooo
Date: Wed, 26 Nov 2025 14:04:33 +0700
Subject: [PATCH 2/4] style: running formatter
---
docker-compose.yml | 6 ++---
src/cli/services/utils/docker-config.ts | 13 +++++++---
src/cli/services/utils/docker-operations.ts | 14 +++++++----
src/cli/utils/command-utils.ts | 3 ++-
src/services/accessibility.ts | 8 +++---
src/services/crawling.ts | 27 +++++++++++----------
6 files changed, 41 insertions(+), 30 deletions(-)
diff --git a/docker-compose.yml b/docker-compose.yml
index d1872da..8550a5c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,7 +5,7 @@ services:
dockerfile: Dockerfile
volumes:
- node_modules_data:/app/node_modules
- profiles: ["init"]
+ profiles: ['init']
lenscore:
container_name: lenscore-app
@@ -13,7 +13,7 @@ services:
context: .
dockerfile: Dockerfile
ports:
- - "${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}"
+ - '${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}'
environment:
- NODE_ENV=development
- PORT=${LENSCORE_PORT:-3001}
@@ -41,7 +41,7 @@ services:
redis:
image: redis:7-alpine
ports:
- - "6379:6379"
+ - '6379:6379'
volumes:
- redis_data:/data
command: redis-server --appendonly yes
diff --git a/src/cli/services/utils/docker-config.ts b/src/cli/services/utils/docker-config.ts
index 839db90..0810126 100644
--- a/src/cli/services/utils/docker-config.ts
+++ b/src/cli/services/utils/docker-config.ts
@@ -201,7 +201,9 @@ CMD ["node", "dist/index.js"]`;
}
if (!packageDirFound) {
- console.warn('⚠️ Could not find package directory, some files may be missing');
+ console.warn(
+ '⚠️ Could not find package directory, some files may be missing'
+ );
}
}
@@ -288,7 +290,6 @@ CMD ["node", "dist/index.js"]`;
};
}
-
private async copyConfigFiles(
packageDir: string,
lenscoreDir: string
@@ -306,8 +307,12 @@ CMD ["node", "dist/index.js"]`;
}
const packageLockDest = path.join(lenscoreDir, 'package-lock.json');
-
- for (const possibleDir of [packageDir, process.cwd(), path.resolve(__filename, '../../../../')]) {
+
+ for (const possibleDir of [
+ packageDir,
+ process.cwd(),
+ path.resolve(__filename, '../../../../'),
+ ]) {
try {
const packageLockSrc = path.join(possibleDir, 'package-lock.json');
await fs.access(packageLockSrc);
diff --git a/src/cli/services/utils/docker-operations.ts b/src/cli/services/utils/docker-operations.ts
index 2975c60..0919e13 100644
--- a/src/cli/services/utils/docker-operations.ts
+++ b/src/cli/services/utils/docker-operations.ts
@@ -119,16 +119,20 @@ export class DockerOperationsService {
const portInUse = await this.validationService.validatePort(this.port);
if (portInUse) {
try {
- const healthResponse = await fetch(`http://localhost:${this.port}/api/health`, {
- method: 'GET',
- signal: AbortSignal.timeout(3000),
- });
+ const healthResponse = await fetch(
+ `http://localhost:${this.port}/api/health`,
+ {
+ method: 'GET',
+ signal: AbortSignal.timeout(3000),
+ }
+ );
if (healthResponse.ok) {
spinner.succeed('Port already in use - services likely running');
return;
}
} catch {
- spinner.text = 'Port in use but service not responding, rebuilding...';
+ spinner.text =
+ 'Port in use but service not responding, rebuilding...';
await this.stop();
await this.build();
return;
diff --git a/src/cli/utils/command-utils.ts b/src/cli/utils/command-utils.ts
index 2ff7480..ea0300f 100644
--- a/src/cli/utils/command-utils.ts
+++ b/src/cli/utils/command-utils.ts
@@ -34,7 +34,8 @@ export class CommandUtils {
try {
await client.waitForReady(10000);
} catch {
- spinner.text = 'Service responding but may need restart, rebuilding...';
+ spinner.text =
+ 'Service responding but may need restart, rebuilding...';
const dockerService = await this.getDockerService();
await dockerService.ensureServicesReady();
await client.waitForReady(60000);
diff --git a/src/services/accessibility.ts b/src/services/accessibility.ts
index 9d17d24..6ee50b4 100644
--- a/src/services/accessibility.ts
+++ b/src/services/accessibility.ts
@@ -1,4 +1,4 @@
-import { chromium, Browser } from 'playwright';
+import { chromium, Browser, LaunchOptions } from 'playwright';
import { v4 as uuidv4 } from 'uuid';
import fs from 'fs/promises';
import {
@@ -25,8 +25,8 @@ export class AccessibilityService {
async initialize(): Promise {
const executablePath = process.env['PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH'];
-
- const launchOptions: any = {
+
+ const launchOptions: LaunchOptions = {
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
};
@@ -34,7 +34,7 @@ export class AccessibilityService {
if (executablePath) {
launchOptions.executablePath = executablePath;
}
-
+
this.browser = await chromium.launch(launchOptions);
}
diff --git a/src/services/crawling.ts b/src/services/crawling.ts
index 223dca3..7040f73 100644
--- a/src/services/crawling.ts
+++ b/src/services/crawling.ts
@@ -1,4 +1,4 @@
-import { chromium, Browser } from 'playwright';
+import { chromium, Browser, LaunchOptions } from 'playwright';
import * as cheerio from 'cheerio';
import { CrawlRequest, CrawlResponse, CrawlResult, CrawlRules } from '../types';
import { env } from '../utils/env';
@@ -18,8 +18,8 @@ export class CrawlingService {
async initialize(): Promise {
try {
const executablePath = process.env['PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH'];
-
- const launchOptions: any = {
+
+ const launchOptions: LaunchOptions = {
headless: true,
args: [
'--no-sandbox',
@@ -39,7 +39,7 @@ export class CrawlingService {
if (executablePath) {
launchOptions.executablePath = executablePath;
}
-
+
this.browser = await chromium.launch(launchOptions);
logger.info('Browser initialized successfully');
@@ -146,7 +146,8 @@ export class CrawlingService {
await page.setViewportSize({ width: 1280, height: 720 });
await page.setExtraHTTPHeaders({
- 'User-Agent': 'Mozilla/5.0 (compatible; LensCore/1.0; +https://github.com/accesslens/lenscore)',
+ 'User-Agent':
+ 'Mozilla/5.0 (compatible; LensCore/1.0; +https://github.com/accesslens/lenscore)',
...(request.headers || {}),
});
@@ -164,15 +165,15 @@ export class CrawlingService {
request.waitUntil === 'networkidle0'
? 'networkidle'
: request.waitUntil === 'networkidle2'
- ? 'networkidle'
- : request.waitUntil === 'domcontentloaded'
- ? 'domcontentloaded'
- : (env.CRAWL_WAIT_UNTIL === 'networkidle0' ||
- env.CRAWL_WAIT_UNTIL === 'networkidle2'
- ? 'networkidle'
- : env.CRAWL_WAIT_UNTIL === 'domcontentloaded'
+ ? 'networkidle'
+ : request.waitUntil === 'domcontentloaded'
? 'domcontentloaded'
- : 'load'),
+ : env.CRAWL_WAIT_UNTIL === 'networkidle0' ||
+ env.CRAWL_WAIT_UNTIL === 'networkidle2'
+ ? 'networkidle'
+ : env.CRAWL_WAIT_UNTIL === 'domcontentloaded'
+ ? 'domcontentloaded'
+ : 'load',
timeout: Math.min(timeout, 15000),
});
From 46d1cdff9687a2213e234306268a514b7abc8404 Mon Sep 17 00:00:00 2001
From: Caknoooo
Date: Fri, 28 Nov 2025 09:05:56 +0700
Subject: [PATCH 3/4] refactor: change approach for docker
---
.dockerignore | 8 +
Dockerfile | 9 +-
docker-compose.yml | 8 +-
package.json | 2 +-
src/api/routes/web.ts | 2 +-
src/cli/services/utils/docker-config.ts | 246 +++++-------------------
6 files changed, 62 insertions(+), 213 deletions(-)
diff --git a/.dockerignore b/.dockerignore
index 3ce09d2..37255b2 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -8,3 +8,11 @@ coverage
.cache
.DS_Store
*.log
+node_modules
+dist
+tests
+.vscode
+.idea
+*.swp
+*.swo
+*~
diff --git a/Dockerfile b/Dockerfile
index 93c60ca..5e8efb4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,12 +18,9 @@ RUN apk update && apk add --no-cache \
npm install -g cross-env
COPY package*.json ./
-RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
+RUN npm ci
-RUN npm install playwright
-
-COPY tsconfig*.json ./
-COPY src ./src
+COPY . .
RUN npm run build
RUN npm run build:cli
@@ -54,4 +51,4 @@ COPY --from=builder /app/src/data ./src/data
RUN mkdir -p logs storage
-CMD ["node", "dist/index.js"]
+CMD ["npm", "start"]
diff --git a/docker-compose.yml b/docker-compose.yml
index 8550a5c..fb4a738 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,7 +5,7 @@ services:
dockerfile: Dockerfile
volumes:
- node_modules_data:/app/node_modules
- profiles: ['init']
+ profiles: ["init"]
lenscore:
container_name: lenscore-app
@@ -13,7 +13,7 @@ services:
context: .
dockerfile: Dockerfile
ports:
- - '${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}'
+ - "${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}"
environment:
- NODE_ENV=development
- PORT=${LENSCORE_PORT:-3001}
@@ -34,14 +34,14 @@ services:
- ./cache:/app/cache
- ./web:/app/web
- ./storage:/app/storage
- - ${HOME}/.lenscore/web:/app/.lenscore/web
+ - ./.lenscore/web:/app/.lenscore/web
depends_on:
- redis
redis:
image: redis:7-alpine
ports:
- - '6379:6379'
+ - "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
diff --git a/package.json b/package.json
index b637083..5467595 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@accesstime/lenscore",
- "version": "0.1.77",
+ "version": "0.1.68",
"description": "Open-source accessibility testing and web crawling platform",
"main": "dist/index.js",
"bin": {
diff --git a/src/api/routes/web.ts b/src/api/routes/web.ts
index 6f0702b..38b64d5 100644
--- a/src/api/routes/web.ts
+++ b/src/api/routes/web.ts
@@ -12,7 +12,7 @@ const router = Router();
*/
function findWebOutputDir(): string {
const possiblePaths = [
- // Docker container - mounted from ~/.lenscore/web
+ // Docker container - mounted from ./.lenscore/web (project directory)
path.join('/app', '.lenscore', 'web', 'output'),
// Docker container - from app directory
path.join('/app', 'web', 'output'),
diff --git a/src/cli/services/utils/docker-config.ts b/src/cli/services/utils/docker-config.ts
index 0810126..2bfa998 100644
--- a/src/cli/services/utils/docker-config.ts
+++ b/src/cli/services/utils/docker-config.ts
@@ -1,4 +1,3 @@
-/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable no-console */
import path from 'path';
import { promises as fs } from 'fs';
@@ -49,127 +48,50 @@ export class DockerConfigService {
const composePath = path.join(lenscoreDir, 'docker-compose.yml');
const dockerfilePath = path.join(lenscoreDir, 'Dockerfile');
- const dockerComposeContent = this.getDockerComposeContent();
- const dockerfileContent = this.getDockerfileContent();
+ const possiblePackageDirs: string[] = [
+ process.cwd(),
+ path.resolve(__filename, '../../../../'),
+ ];
- await fs.writeFile(composePath, dockerComposeContent);
- await fs.writeFile(dockerfilePath, dockerfileContent);
- }
+ try {
+ const packagePath = require.resolve('@accesstime/lenscore');
+ possiblePackageDirs.push(
+ path.resolve(packagePath, '../..'),
+ path.dirname(packagePath)
+ );
+ } catch {
+ //
+ }
- private getDockerComposeContent(): string {
- return `services:
- lenscore-init:
- build:
- context: .
- dockerfile: Dockerfile
- volumes:
- - node_modules_data:/app/node_modules
- profiles: ['init']
-
- lenscore:
- container_name: lenscore-app
- build:
- context: .
- dockerfile: Dockerfile
- ports:
- - '\${LENSCORE_PORT:-3001}:\${LENSCORE_PORT:-3001}'
- environment:
- - NODE_ENV=development
- - PORT=\${LENSCORE_PORT:-3001}
- - CACHE_TYPE=redis
- - REDIS_HOST=redis
- - REDIS_PORT=6379
- volumes:
- - ./logs:/app/logs
- - ./cache:/app/cache
- - ./web:/app/web
- - ./storage:/app/storage
- - \${HOME}/.lenscore/web:/app/.lenscore/web
- depends_on:
- - redis
-
- redis:
- image: redis:7-alpine
- ports:
- - '6379:6379'
- volumes:
- - redis_data:/data
- command: redis-server --appendonly yes
-
-volumes:
- redis_data:
- node_modules_data:`;
- }
+ let dockerFilesCopied = false;
+ for (const packageDir of possiblePackageDirs) {
+ try {
+ const srcComposePath = path.join(packageDir, 'docker-compose.yml');
+ const srcDockerfilePath = path.join(packageDir, 'Dockerfile');
+
+ await fs.access(srcComposePath);
+ await fs.access(srcDockerfilePath);
+
+ await fs.copyFile(srcComposePath, composePath);
+ await fs.copyFile(srcDockerfilePath, dockerfilePath);
+
+ console.log(`✅ Copied Docker files from ${packageDir}`);
+ dockerFilesCopied = true;
+ break;
+ } catch {
+ //
+ }
+ }
- private getDockerfileContent(): string {
- return `# ---------------------------
-# Builder Stage
-# ---------------------------
-FROM node:22-alpine AS builder
-WORKDIR /app
-
-RUN apk update && apk add --no-cache \\
- bash \\
- chromium \\
- chromium-chromedriver \\
- nss \\
- freetype \\
- harfbuzz \\
- ca-certificates \\
- ttf-freefont \\
- && apk add --no-cache --virtual .build-deps \\
- gcc g++ make python3 && \\
- npm install -g cross-env
-
-COPY package*.json ./
-RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
-
-RUN npm install playwright
-
-COPY tsconfig*.json ./
-COPY src ./src
-
-RUN npm run build
-RUN npm run build:cli
-
-# ---------------------------
-# Runtime Stage
-# ---------------------------
-FROM node:22-alpine AS runtime
-WORKDIR /app
-
-RUN apk update && apk add --no-cache \\
- chromium \\
- chromium-chromedriver \\
- nss \\
- freetype \\
- harfbuzz \\
- ca-certificates \\
- ttf-freefont
-
-ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \\
- PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium-browser \\
- NODE_ENV=production
-
-COPY package*.json ./
-COPY --from=builder /app/node_modules ./node_modules
-COPY --from=builder /app/dist ./dist
-COPY --from=builder /app/src/data ./src/data
-
-RUN mkdir -p logs storage
-
-CMD ["node", "dist/index.js"]`;
+ if (!dockerFilesCopied) {
+ throw new Error(
+ 'Could not find Dockerfile or docker-compose.yml in package directory. Please ensure the package is properly installed.'
+ );
+ }
}
private async setupPackageFiles(lenscoreDir: string): Promise {
const packageJsonPath = path.join(lenscoreDir, 'package.json');
- const packageJsonContent = await this.getPackageJsonContent();
-
- await fs.writeFile(
- packageJsonPath,
- JSON.stringify(packageJsonContent, null, 2)
- );
-
const possiblePackageDirs: string[] = [
process.cwd(),
path.resolve(__filename, '../../../../'),
@@ -188,8 +110,12 @@ CMD ["node", "dist/index.js"]`;
let packageDirFound = false;
for (const packageDir of possiblePackageDirs) {
try {
- const packageJsonPath = path.join(packageDir, 'package.json');
- await fs.access(packageJsonPath);
+ const srcPackageJsonPath = path.join(packageDir, 'package.json');
+ await fs.access(srcPackageJsonPath);
+
+ await fs.copyFile(srcPackageJsonPath, packageJsonPath);
+ console.log(`✅ Copied package.json from ${packageDir}`);
+
await this.copyConfigFiles(packageDir, lenscoreDir);
await this.copySourceFiles(packageDir, lenscoreDir);
await this.copyWebTemplates(packageDir, lenscoreDir);
@@ -201,94 +127,12 @@ CMD ["node", "dist/index.js"]`;
}
if (!packageDirFound) {
- console.warn(
- '⚠️ Could not find package directory, some files may be missing'
- );
- }
- }
-
- private async getPackageJsonContent(): Promise {
- try {
- const currentFile = __filename;
- const packageDir = path.resolve(currentFile, '../../../../');
- const originalPackageJsonPath = path.join(packageDir, 'package.json');
- const originalPackageJson = await fs.readFile(
- originalPackageJsonPath,
- 'utf8'
+ throw new Error(
+ 'Could not find package.json in package directory. Please ensure the package is properly installed.'
);
- const packageJsonContent = JSON.parse(originalPackageJson);
-
- packageJsonContent.scripts = {
- start: 'node dist/index.js',
- build: 'tsc',
- 'build:cli': 'tsc -p tsconfig.cli.json',
- };
-
- return packageJsonContent;
- } catch {
- return this.getDefaultPackageJson();
}
}
- private getDefaultPackageJson(): any {
- return {
- name: 'lenscore',
- version: '1.0.0',
- main: 'dist/index.js',
- scripts: {
- start: 'node dist/index.js',
- build: 'tsc',
- 'build:cli': 'tsc -p tsconfig.cli.json',
- },
- dependencies: {
- '@google-cloud/storage': '^7.7.0',
- '@types/inquirer': '^9.0.9',
- '@types/ioredis': '^4.28.10',
- 'aws-sdk': '^2.1490.0',
- 'axe-core': '^4.8.2',
- chalk: '^4.1.2',
- cheerio: '^1.0.0',
- commander: '^11.1.0',
- cors: '^2.8.5',
- dotenv: '^16.3.1',
- express: '^4.18.2',
- handlebars: '^4.7.8',
- helmet: '^7.1.0',
- inquirer: '^12.10.0',
- ioredis: '^5.8.1',
- marked: '^16.4.1',
- openai: '^6.5.0',
- ora: '^5.4.1',
- playwright: '^1.48.0',
- sharp: '^0.33.0',
- uuid: '^9.0.1',
- winston: '^3.11.0',
- zod: '^3.22.4',
- },
- devDependencies: {
- '@types/cors': '^2.8.17',
- '@types/express': '^4.17.21',
- '@types/handlebars': '^4.0.40',
- '@types/jest': '^29.5.8',
- '@types/marked': '^5.0.2',
- '@types/multer': '^1.4.11',
- '@types/node': '^20.10.5',
- '@types/supertest': '^2.0.16',
- '@types/uuid': '^9.0.8',
- '@typescript-eslint/eslint-plugin': '^8.15.0',
- '@typescript-eslint/parser': '^8.15.0',
- eslint: '^9.15.0',
- globals: '^13.24.0',
- jest: '^29.7.0',
- nodemon: '^3.0.2',
- prettier: '^3.1.1',
- supertest: '^7.1.3',
- 'ts-jest': '^29.1.1',
- tsx: '^4.6.2',
- typescript: '^5.3.3',
- },
- };
- }
private async copyConfigFiles(
packageDir: string,
From cadb2bd248a93a8ea70aa3a9ebfb3f1d5675d049 Mon Sep 17 00:00:00 2001
From: Caknoooo
Date: Fri, 28 Nov 2025 09:06:17 +0700
Subject: [PATCH 4/4] style: run formatter
---
docker-compose.yml | 6 +++---
src/cli/services/utils/docker-config.ts | 1 -
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/docker-compose.yml b/docker-compose.yml
index fb4a738..983b28c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,7 +5,7 @@ services:
dockerfile: Dockerfile
volumes:
- node_modules_data:/app/node_modules
- profiles: ["init"]
+ profiles: ['init']
lenscore:
container_name: lenscore-app
@@ -13,7 +13,7 @@ services:
context: .
dockerfile: Dockerfile
ports:
- - "${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}"
+ - '${LENSCORE_PORT:-3001}:${LENSCORE_PORT:-3001}'
environment:
- NODE_ENV=development
- PORT=${LENSCORE_PORT:-3001}
@@ -41,7 +41,7 @@ services:
redis:
image: redis:7-alpine
ports:
- - "6379:6379"
+ - '6379:6379'
volumes:
- redis_data:/data
command: redis-server --appendonly yes
diff --git a/src/cli/services/utils/docker-config.ts b/src/cli/services/utils/docker-config.ts
index 2bfa998..8c00e47 100644
--- a/src/cli/services/utils/docker-config.ts
+++ b/src/cli/services/utils/docker-config.ts
@@ -133,7 +133,6 @@ export class DockerConfigService {
}
}
-
private async copyConfigFiles(
packageDir: string,
lenscoreDir: string