Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/browser/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ See [benchmark details](https://github.com/lightpanda-io/demo)._

## Getting Started

### Configuration
_Environment variables_
- `LIGHTPANDA_EXECUTABLE_PATH` can be specified if you want to use your own version and avoid the binary from being installed on postinstall. The default folder is `~/.cache/lightpanda-node`


### Install
_On installation, the binary corresponding to your platform will be automatically downloaded_
_When installing the package, the binary corresponding to your platform will be automatically downloaded. If your OS is not supported, download will fail_

```bash
yarn add @lightpanda/browser
Expand All @@ -72,7 +77,7 @@ pnpm add @lightpanda/browser

At some point in time, you might want to upgrade Lightpanda browser to a more recent version. To do so, you can run the following command:
```bash
npx @lightpanda/browser
npx @lightpanda/browser upgrade
```

<!-- USAGE EXAMPLES -->
Expand Down
6 changes: 0 additions & 6 deletions packages/browser/cli/install.ts

This file was deleted.

30 changes: 30 additions & 0 deletions packages/browser/cli/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env node

import yargs from 'yargs'
import { hideBin } from 'yargs/helpers'

import { download } from '../src/download'

yargs(hideBin(process.argv))
.command(
'upgrade',
'Upgrade the browser to the latest nightly version',
() => {},
_ => {
download()
},
)
.command(
'$0',
'Default',
() => {},
_ => {
console.info('ℹ️ Please enter a command')
},
)
.option('verbose', {
alias: 'v',
type: 'boolean',
description: 'Run with verbose logging',
})
.parse()
12 changes: 8 additions & 4 deletions packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
"description": "Lightpanda for Node.js",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/types/index.d.ts",
"types": "./dist/index.d.ts",
"type": "module",
"scripts": {
"dev": "tsup index.ts --target node22 --format cjs,esm --dts --watch",
"build:index": "tsup index.ts --target node22 --format cjs,esm --minify terser",
"build:decl": "yarn dlx --package=typescript tsc --emitDeclarationOnly --declaration --declarationDir dist/types",
"build:cli": "tsup cli/install.ts --target node22 --format cjs,esm --minify terser -d dist/cli",
"build:decl": "yarn dlx --package=typescript tsc --emitDeclarationOnly --declaration",
"build:cli": "tsup cli/main.ts --target node22 --format cjs,esm --minify terser -d dist/cli",
"build:postinstall": "tsup scripts/postinstall.ts --target node22 --format cjs,esm --minify terser -d dist/scripts",
"build": "yarn build:index && yarn build:decl && yarn build:cli && yarn build:postinstall",
"postinstall": "node dist/scripts/postinstall.js || true",
"lint": "biome ci . --diagnostic-level=error",
"typecheck": "yarn dlx --package=typescript tsc --noEmit"
},
"bin": {
"lightpanda": "./dist/cli/install.js"
"lightpanda": "./dist/cli/main.js"
},
"repository": {
"type": "git",
Expand All @@ -38,8 +38,12 @@
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@types/node": "22.15.32",
"@types/yargs": "^17.0.33",
"terser": "^5.43.1",
"tsup": "^8.5.0",
"typescript": "^5.8.3"
},
"dependencies": {
"yargs": "^18.0.0"
}
}
32 changes: 19 additions & 13 deletions packages/browser/src/download.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { constants, chmodSync, createWriteStream } from 'node:fs'
import { constants, chmodSync, createWriteStream, existsSync, mkdirSync } from 'node:fs'
import https from 'node:https'
import { arch, exit, platform } from 'node:process'

const FOLDER = 'dist'
const BINARY_NAME = 'lightpanda'
const BINARY_PATH = `${FOLDER}/${BINARY_NAME}`
import { DEFAULT_CACHE_FOLDER, DEFAULT_EXECUTABLE_PATH, USER_EXECUTABLE_PATH } from './utils'

const PLATFORMS = {
darwin: {
Expand All @@ -21,11 +18,15 @@ const PLATFORMS = {
* Download Lightpanda's binary
* @returns {Promise<void>}
*/
export const download = async (binaryPath: string = BINARY_PATH): Promise<void> => {
const path = PLATFORMS?.[platform]?.[arch]
export const download = async (): Promise<void> => {
const platformPath = PLATFORMS?.[platform]?.[arch]

if (!existsSync(DEFAULT_CACHE_FOLDER)) {
mkdirSync(DEFAULT_CACHE_FOLDER, { recursive: true })
}

const get = (url: string, resolve: (value?: unknown) => void, reject: (reason: any) => void) => {
const file = createWriteStream(binaryPath)
const file = createWriteStream(DEFAULT_EXECUTABLE_PATH)

https.get(url, res => {
if (
Expand All @@ -50,20 +51,25 @@ export const download = async (binaryPath: string = BINARY_PATH): Promise<void>
return new Promise((resolve, reject) => get(url, resolve, reject))
}

if (path) {
if (platformPath) {
if (USER_EXECUTABLE_PATH) {
console.info('$LIGHTPANDA_EXECUTABLE_PATH found, skipping binary download…')
exit(0)
}

try {
console.info('⏳ Downloading latest version of Lightpanda browser…')

await downloadBinary(
`https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-${path}`,
`https://github.com/lightpanda-io/browser/releases/download/nightly/lightpanda-${platformPath}`,
)
chmodSync(binaryPath, constants.S_IRWXU)
chmodSync(DEFAULT_EXECUTABLE_PATH, constants.S_IRWXU)

console.info('✅ Download finished!')
console.info('✅ Done!')
exit(0)
} catch (e) {
console.log('error', e)
console.warn(`Lightpanda's failed to download the binary file "${path}".`)
console.warn(`Lightpanda's failed to download the binary file "${platformPath}".`)
exit(1)
}
} else {
Expand Down
5 changes: 3 additions & 2 deletions packages/browser/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { execSync } from 'node:child_process'
import { validateUrl } from './utils'
import { getExecutablePath, validateUrl } from './utils'
/**
* @typedef LightpandaFetchOptions
* @type {object}
Expand Down Expand Up @@ -33,6 +33,7 @@ export const fetch = (url: string, options: LightpandaFetchOptions = defaultOpti

return new Promise<Buffer | string>((resolve, reject) => {
try {
const executablePath = getExecutablePath()
const flags = [
{ flag: '--dump', condition: dump },
{ flag: '--insecure_disable_tls_host_verification', condition: disableHostVerification },
Expand All @@ -41,7 +42,7 @@ export const fetch = (url: string, options: LightpandaFetchOptions = defaultOpti
.map(f => (f.condition ? f.flag : ''))
.join(' ')

const e = execSync(`./lightpanda fetch ${flags} ${url}`)
const e = execSync(`${executablePath} fetch ${flags} ${url}`)

if (dump) {
resolve(e.toString())
Expand Down
5 changes: 3 additions & 2 deletions packages/browser/src/serve.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process'
import { validatePort, validateUrl } from './utils'
import { getExecutablePath, validatePort, validateUrl } from './utils'
/**
* @typedef LightpandaServeOptions
* @type {object}
Expand Down Expand Up @@ -38,6 +38,7 @@ export const serve = (options: LightpandaServeOptions = defaultOptions) => {
}

return new Promise<ChildProcessWithoutNullStreams>((resolve, reject) => {
const executablePath = getExecutablePath()
const flags = [
{ flag: '--host', value: host },
{ flag: '--port', value: port },
Expand All @@ -52,7 +53,7 @@ export const serve = (options: LightpandaServeOptions = defaultOptions) => {
.flatMap(f => (f.value ? [f.flag, !f.flagOnly ? f.value.toString() : ''] : ''))
.filter(f => f !== '')

const process = spawn('./lightpanda', ['serve', ...flags])
const process = spawn(executablePath, ['serve', ...flags])

process.on('spawn', async () => {
console.info("🐼 Running Lightpanda's CDP server…", {
Expand Down
23 changes: 23 additions & 0 deletions packages/browser/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
import os from 'node:os'

export const DEFAULT_CACHE_FOLDER = `${os.homedir()}/.cache/lightpanda-node`
export const BINARY_NAME = 'lightpanda'

export const USER_EXECUTABLE_PATH = process.env.LIGHTPANDA_EXECUTABLE_PATH
export const DEFAULT_EXECUTABLE_PATH = `${DEFAULT_CACHE_FOLDER}/${BINARY_NAME}`

/**
* Validate a URL structure
* @param {string} url URL to validate
*/
export const validateUrl = (url: string): void => {
if (!url || typeof url !== 'string') {
throw new Error(`URL is required and must be a string ${url}`)
Expand All @@ -14,6 +26,10 @@ export const validateUrl = (url: string): void => {
}
}

/**
* Validate a port number
* @param {number} port Port number to validate
*/
export const validatePort = (port: number): void => {
if (!port || typeof port !== 'number') {
throw new Error(`Port is required and must be a number ${port}`)
Expand All @@ -23,3 +39,10 @@ export const validatePort = (port: number): void => {
throw new Error(`Port should be a positive number ${port}`)
}
}

/**
* Get executable path
*/
export const getExecutablePath = () => {
return USER_EXECUTABLE_PATH ?? DEFAULT_EXECUTABLE_PATH
}
Loading