Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
46ee119
feat: convert to npm workspace; add @wdio/browserstack-service alongs…
AakashHotchandani Jun 13, 2026
8505f85
fix(ci): regenerate package-lock.json to match workspace package.json
AakashHotchandani Jun 15, 2026
52767e8
fix(ci): regenerate package-lock.json via a clean full resolution
AakashHotchandani Jun 15, 2026
67b9cef
fix(ci): include peerDependency closure in lockfile (npm default peer…
AakashHotchandani Jun 15, 2026
254a69d
fix(node18): make telemetry fail-safe + openAsBlob fallback + scope f…
AakashHotchandani Jun 15, 2026
f935a52
chore(review): SHA-pin publish actions, fix core npm metadata, drop i…
AakashHotchandani Jun 15, 2026
47a1c8f
chore(review): SHA-pin publish-workflow actions + correct core npm me…
AakashHotchandani Jun 15, 2026
068e2b5
fix(release): drop setup-node registry-url so it can't shadow OIDC token
AakashHotchandani Jun 15, 2026
ad2648f
refactor: flatten gRPC core into the service package (inline proto)
AakashHotchandani Jun 17, 2026
40411cc
fix: regenerate complete cross-platform package-lock for `npm ci`
AakashHotchandani Jun 17, 2026
7c949da
fix(ci): peer-inclusive lockfile + npm resolution workarounds
AakashHotchandani Jun 18, 2026
041a4c4
fix(browserstack-service): ship @wdio/logger, @wdio/reporter, @wdio/t…
AakashHotchandani Jun 24, 2026
65d7c33
fix(security): resolve Semgrep p/default blocking findings
AakashHotchandani Jun 25, 2026
db30ba0
chore: sync main into migration (semgrep workflow pin f1c3688)
AakashHotchandani Jul 10, 2026
1c6a95f
feat(browserstack-service): port upstream monorepo service changes (9…
AakashHotchandani Jul 10, 2026
e4cf295
chore(release): enable first publish (version floor 9.29.1, minor cha…
AakashHotchandani Jul 10, 2026
62223e6
Merge pull request #39 from browserstack/chore/sync-main-into-migration
kamal-kaur04 Jul 10, 2026
a56cd70
Merge pull request #40 from browserstack/feat/upstream-parity-9.29
kamal-kaur04 Jul 10, 2026
c9f1ed8
chore(release): make publishing manual (hybrid) — push opens Version …
AakashHotchandani Jul 10, 2026
9629eec
Merge pull request #41 from browserstack/chore/release-enablement-9.30
kamal-kaur04 Jul 10, 2026
bf66b27
fix(ci): green PR #37 (test + semgrep) and add the eslint lint gate
AakashHotchandani Jul 10, 2026
7d6f822
fix(browserstack-service): port 2026-07-10 monorepo accessibility/CLI…
AakashHotchandani Jul 13, 2026
7306121
fix(browserstack-service): declare webdriverio as a dependency, not a…
AakashHotchandani Jul 13, 2026
bf5de42
Merge pull request #45 from browserstack/fix/webdriverio-peer-to-dep
AakashHotchandani Jul 13, 2026
2ad7617
Merge pull request #44 from browserstack/port/friday-drift
AakashHotchandani Jul 14, 2026
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
12 changes: 12 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changesets

This repo uses [Changesets](https://github.com/changesets/changesets) to version and publish
**`@wdio/browserstack-service`** on BrowserStack's own cadence (independent of WebdriverIO's
release schedule).

- Add a changeset for any user-facing change: `npm run changeset` (pick patch/minor/major).
- On merge to `main` (the v9 line) or `v8` (the v8 line), the Release workflow opens a
"Version Packages" PR; merging that PR publishes to npm via OIDC trusted publishing.
- The gRPC/protobuf client is generated inline from the bundled `.proto` files at build time
(`buf generate`) — it is no longer a separate workspace package, so `config.json` `ignore` is
empty and there is nothing extra for this pipeline to skip.
14 changes: 14 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we re-use the existing yml file-based approach for this repo as well for maintaining consistency across the SDK repositories.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd lean toward keeping Changesets for this package. It's @wdio-scoped and the upstream WebdriverIO project releases with Changesets, so matching that keeps us consistent with the org we're co-publishing under. The OIDC release.yml is also built around changeset version / changeset publish — it drives the per-branch dist-tag and the "Version Packages" PR flow — so switching to the internal yml approach would mean reworking that pipeline and re-deriving the OIDC wiring.

So here it's consistency-with-wdio vs consistency-with-other-SDK-repos, and for a @wdio/* package I'd favor the former. Happy to discuss if the team feels strongly.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, happy to discuss this over a call.

"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": [],
"snapshot": {
"useCalculatedVersion": true
}
}
5 changes: 5 additions & 0 deletions .changeset/fix-webdriverio-peer-to-dep.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wdio/browserstack-service": patch
---

Declare `webdriverio` as a dependency instead of a peerDependency, matching the package published from the WebdriverIO monorepo. The extraction had moved `webdriverio` into `peerDependencies` (`^9.0.0`); npm then forced the consumer's `webdriverio` to `^9`, which conflicts with any project that also depends on a package peering `webdriverio@"^7 || ^8"` (e.g. `wdio-chromedriver-service@^8`) and surfaced as an `npm ERESOLVE` on install — a failure the monorepo-published package never had. Restores install parity with the upstream package; no user-facing API change.
5 changes: 5 additions & 0 deletions .changeset/port-friday-parity-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wdio/browserstack-service": patch
---

Port the 2026-07-10 monorepo accessibility/CLI fixes to keep the standalone at parity (webdriverio/webdriverio#15380, #15383, #15382, #15381, #15376): skip the accessibility scan for BiDi `window`/`context` commands, route WDIO CLI-flow App Automate sessions to app-accessibility, finalize orphaned test runs on an interrupted exit, coerce stringified boolean accessibility options, and report mocha hooks in the CLI/testHub flow. No user-facing API change.
5 changes: 5 additions & 0 deletions .changeset/port-upstream-service-parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wdio/browserstack-service": minor
---

Publish from the standalone `browserstack/wdio-browserstack-service` repo, at parity with the WebdriverIO monorepo. Includes Load Testing Service (LTS) support, one-to-many Test-Case-ID tagging (`setCustomTestCaseId`), correct test/hook finish on mocha timeouts, per-batch failure isolation in the request queue, accessibility Browser type augmentations, and `yauzl` upgraded to `^3.4.0`. No user-facing API change.
8 changes: 8 additions & 0 deletions .changeset/take-over-publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@wdio/browserstack-service": minor
---

BrowserStack now publishes `@wdio/browserstack-service` from its own repository
(`browserstack/wdio-browserstack-service`) on an independent release cadence, using npm OIDC
trusted publishing. No change for end users — same package name and the same
`services: ['browserstack']` configuration continue to work unchanged.
84 changes: 84 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/** @type {import('eslint').Linter.Config} */
// Ported from the WebdriverIO monorepo (.eslintrc.cjs) so the standalone repo lints
// @wdio/browserstack-service with the same rules it had upstream. ignorePatterns adds the
// build output and the buf-generated gRPC stubs (src/grpc/generated), which are not authored here.
const config = {
root: true,
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'unicorn', 'import'],
extends: ['eslint:recommended'],
env: {
node: true,
es6: true,
},
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
ignorePatterns: [
'**/node_modules/**',
'**/build/**',
'**/generated/**',
'**/*.d.ts',
],
rules: {
quotes: ['error', 'single', { avoidEscape: true }],
camelcase: ['error', { properties: 'never' }],
semi: ['error', 'never'],
indent: [2, 4],
eqeqeq: ['error', 'always'],

'prefer-const': 'error',
'no-multiple-empty-lines': [2, { max: 1, maxEOF: 1 }],
'array-bracket-spacing': ['error', 'never'],
'brace-style': ['error', '1tbs', { allowSingleLine: true }],
'comma-spacing': ['error', { before: false, after: true }],
'no-lonely-if': 'error',
'dot-notation': 'error',
'no-else-return': 'error',
'no-tabs': 'error',
'no-trailing-spaces': [
'error',
{
skipBlankLines: false,
ignoreComments: false,
},
],
'no-var': 'error',
'unicode-bom': ['error', 'never'],
curly: ['error', 'all'],
'object-curly-spacing': ['error', 'always'],
'keyword-spacing': ['error'],
'require-atomic-updates': 0,
'linebreak-style': ['error', 'unix'],
'unicorn/prefer-node-protocol': ['error'],
'import/extensions': ['error', 'ignorePackages'],
'no-restricted-syntax': [
'error',
'IfStatement > ExpressionStatement > AssignmentExpression',
],
'unicorn/prefer-ternary': 'error',
},
overrides: [
{
files: ['*.ts'],
rules: {
// see https://stackoverflow.com/questions/55280555/typescript-eslint-eslint-plugin-error-route-is-defined-but-never-used-no-un
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/consistent-type-imports': 'error',
'no-undef': 'off',
// allow overloads
'no-redeclare': 'off',
},
},
{
files: ['*.test.ts'],
rules: {
'dot-notation': 'off',
},
},
],
}

module.exports = config
57 changes: 57 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: CI

on:
pull_request:
push:
branches:
- main
- v8

permissions:
contents: read

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: 'npm'

- name: Install
run: npm ci

- name: Lint
run: npm run lint

build-test:
name: Build & test (node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: ['18.20', '20', '22']
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'

- name: Install
run: npm ci

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v8 requries --legacy-peer-deps flag along with the npm i command. So, for v8 we would need npm ci --legacy-peer-deps. For main branch, normal npm i would suffice.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, and it's intentional per-line. main/v9 uses plain npm ci — verified: the v9 peer graph resolves cleanly. Adding --legacy-peer-deps here would actually suppress the peerDependency closure and reintroduce the exact npm ci lockfile mismatch that was just fixed on this branch, so it must stay off for v9.

The v8 branch (created later) will set npm ci --legacy-peer-deps in its own ci.yml, since v8's peer ranges need it. No change needed on this (v9) branch.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small correction, if we re-use pnpm used in WDIO v9, then it'd be changed to pnpm ci here for v9 as well

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging a small correctness point on this: pnpm ci isn't actually a pnpm command — it errors with ERR_PNPM_CI_NOT_IMPLEMENTED. The frozen-lockfile equivalent is pnpm install --frozen-lockfile (which pnpm does by default in CI when a lockfile is present).

That said — per the package-manager thread, we're staying on npm for v9 (OIDC publish-path risk, not capability), so this line stays npm ci for now.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


- name: Build
run: npm run build

- name: Test
run: npm test
121 changes: 121 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: Release

# Publishes @wdio/browserstack-service to npm via OIDC Trusted Publishing
# (no long-lived NPM_TOKEN). One-time setup by an @wdio npm org admin on npmjs.com:
# @wdio/browserstack-service -> Settings -> Trusted Publisher -> GitHub Actions
# Organization/user: browserstack
# Repository: wdio-browserstack-service
# Workflow filename: release.yml
# Environment: (leave empty)
# Requires: PUBLIC repo (for provenance), npm >= 11.5.1, Node >= 22.14.0.
Comment on lines +3 to +10

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to handle publishing via GitHub CI? It'd be better to use minion for this if feasible.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the publish step on GitHub Actions by design. The pipeline uses npm OIDC trusted publishing (id-token: write, no NPM_TOKEN) for build provenance and to avoid a long-lived npm automation token — a condition from the @wdio org for taking over @wdio/browserstack-service.

npm Trusted Publishing currently only recognizes GitHub Actions and GitLab CI/CD as trusted publishers, so minion can't be registered as one — publishing from minion would mean falling back to a classic NPM_TOKEN secret and losing provenance. Given the OIDC/provenance requirement, the npm publish needs to live here. (Build/test can still be mirrored to minion if useful — it's specifically the publish step that's constrained.)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AakashHotchandani
We'd need EM Approval for this. None of the other SDKs are published to npm using Github Actions.

#
# The gRPC/protobuf client is generated inline at build time (buf generate); there is no
# separate core package for this workflow to version or publish.
#
# Release model (publishing is MANUAL):
# * push to main / v8 -> opens/updates the "Version Packages" PR only. NEVER publishes;
# merging that PR does NOT publish either.
# * Run workflow (publish) -> publishes the merged version to npm (main -> `latest`,
# v8 -> `v8`) with git tag + GitHub release + provenance.
# * Run workflow (canary) -> snapshot prerelease to the npm `canary` dist-tag.

on:
push:
branches:
- main # v9 line -> opens Version PR (publish target: dist-tag "latest")
- v8 # v8 line -> opens Version PR (publish target: dist-tag "v8" via publishConfig.tag)
workflow_dispatch:
inputs:
publish:
description: 'Publish the merged version to npm (main -> `latest`, v8 -> `v8`). Merge the "Version Packages" PR first so there are no pending changesets.'
type: boolean
default: false
canary:
description: 'Publish a canary prerelease to the npm `canary` dist-tag (validates OIDC + provenance end-to-end; never touches `latest`). Requires at least one pending changeset.'
type: boolean
default: false

# Never run main and v8 releases on top of each other.
concurrency: release-${{ github.ref }}

permissions:
contents: write # commit the "Version Packages" PR + create git tags
pull-requests: write # open the "Version Packages" PR
id-token: write # OIDC for npm trusted publishing + provenance

jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0 # Changesets needs full history/tags

- name: Setup Node
# NOTE: intentionally NO `registry-url:` here. setup-node's registry-url writes
# `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}` into ~/.npmrc; with no
# NODE_AUTH_TOKEN that becomes an empty token line that can shadow OIDC Trusted
# Publishing at `npm publish` time. npm already defaults to registry.npmjs.org and
# publishConfig.access=public handles the scoped publish, so the line isn't needed.
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22 # resolves to >= 22.14 on the runner (OIDC floor)
cache: 'npm'

# Trusted Publishing needs npm >= 11.5.1. Pin to the 11.x line so a future
# npm major can never silently change publish behaviour.
- name: Upgrade npm to an OIDC-capable version
run: npm install -g npm@11

- name: Install
run: npm ci

- name: Build
run: npm run build

- name: Test
run: npm test

# Version PR (push to main / v8): run changesets in VERSION-ONLY mode — open or update the
# "Version Packages" PR (version bump + CHANGELOG). This step NEVER publishes: with no
# `publish:` input, once the Version PR is merged (a push with no pending changesets) the
# action simply no-ops. Publishing is the separate, manual step below.
- name: Open/update the "Version Packages" PR
if: github.event_name == 'push'
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1
with:
version: npm run version # changeset version — manages the Version PR only
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Publish (manual, workflow_dispatch with publish=true): publish the already-versioned
# package to npm — main -> `latest`, v8 -> `v8` (via publishConfig.tag) — with git tag,
# GitHub release, and provenance, over the OIDC trusted publisher (no NPM_TOKEN).
# Merge the "Version Packages" PR first: this publishes only when there are no pending
# changesets. If any remain, the action safely opens/updates the Version PR instead.
- name: Publish to npm
if: github.event_name == 'workflow_dispatch' && inputs.publish
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1
with:
publish: npm run release # changeset publish (honors publishConfig.tag per branch)
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# No NPM_TOKEN: auth is OIDC via id-token: write above.
NPM_CONFIG_PROVENANCE: 'true'

# Canary (manual, workflow_dispatch with canary=true): snapshot-version the pending
# changesets and publish a prerelease to the `canary` dist-tag via the SAME OIDC trusted
# publisher. Validates OIDC + provenance end-to-end without touching `latest`.
# snapshot.useCalculatedVersion=true in .changeset/config.json makes the version
# <next>-canary-<datetime> (e.g. 9.30.0-canary-...). Requires >=1 pending changeset.
- name: Canary publish to `canary` dist-tag
if: github.event_name == 'workflow_dispatch' && inputs.canary
run: |
npx changeset version --snapshot canary
npx changeset publish --no-git-tag --tag canary
env:
NPM_CONFIG_PROVENANCE: 'true'
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
node_modules

# build outputs
dist
generated
build
src/generated
packages/*/dist
packages/*/build
packages/*/src/generated

# test/coverage
coverage
packages/*/coverage

# packed tarballs & logs
*.tgz
*.log
logs

# local/editor
.DS_Store
.env
.env.*
Loading
Loading