Compare commits

..

68 Commits
1.4.0 ... 1.5.2

Author SHA1 Message Date
f56f934514 build: update tvapp2 sub package 2025-06-01 11:51:06 -07:00
c9aaad376b build(deps): bump user-agents from 1.1.556 to 1.1.557 2025-06-01 11:28:57 -07:00
3ebd2478fe build(s6-overlay): add new build structure 2025-06-01 11:22:52 -07:00
b3fbaa1d97 build(run): update logic for root tvapp2 script 2025-06-01 11:21:01 -07:00
94da974373 fix(build): add compatibility for detecting commands for older operating systems 2025-06-01 11:09:23 -07:00
fd76d3ce59 ci: reintegrate root run tvapp2 script 2025-06-01 10:55:17 -07:00
7c5420d279 build( re-integrate root plugins file 2025-06-01 10:48:17 -07:00
c2ab89457b build: update packages 2025-06-01 10:16:20 -07:00
8f87fae452 feat: add git hash to webui footer 2025-06-01 10:07:07 -07:00
d5c9c3550a feat: web ui now opens links in new window 2025-06-01 10:06:48 -07:00
b654cc3469 feat: add git hash reporting 2025-06-01 10:04:51 -07:00
fdb99d57d6 feat: show tvapp2 version as console message 2025-06-01 10:04:36 -07:00
e3e611d47b feat: add tracking for daddylive 2025-06-01 10:04:08 -07:00
b8ef37188c feat: try http first for MoveOnJoy 2025-06-01 10:03:42 -07:00
f8d68789fc feat: add child_process support 2025-06-01 10:03:06 -07:00
e0c9df41d7 fix: change fl2.moveonjoy to fl6.moveonjoy 2025-06-01 09:51:42 -07:00
1363ed4f8a build: update dockerfile to add GIT_SHA1 2025-06-01 09:47:36 -07:00
3e914b7a99 docs(readme): update 2025-06-01 09:46:31 -07:00
d690256369 ci: update workflows 2025-06-01 09:44:36 -07:00
18c37feed8 feat: container startup message now shows public and docker ip assignment 2025-05-13 20:04:11 -07:00
d21a8721cf ci: update release workflow, remove v from docker-compose file 2025-05-13 01:59:31 -07:00
d0192d9a72 ci: update release workflow 2025-05-13 01:28:15 -07:00
684963bc2b ci: update hash digest algorithm 2025-05-13 01:17:01 -07:00
220b995f28 ci: update release workflow 2025-05-13 01:15:13 -07:00
998d706bf7 ci: update release workflow 2025-05-13 01:02:01 -07:00
f4a394bd3b feat: add notice every 30 minutes for next data refresh
every 30 minutes, console will print date on when next data refresh is
2025-05-12 16:57:48 -07:00
898bbe4827 docs(readme): update env variable list 2025-05-12 13:33:52 -07:00
66b69d5629 build: bump version from 1.5.0 to 1.5.1 2025-05-12 12:55:35 -07:00
818729d6ed feat: add automatic cron support; resync every 3 days
refresh iptv data every 3rd day at midnight
2025-05-12 12:55:08 -07:00
33a2a90eb1 build(deps): bump user-agents from 1.1.529 to 1.1.537 2025-05-12 12:54:04 -07:00
2ff6c193c9 build(deps): add package node-cron 2025-05-12 12:51:33 -07:00
647ce980b4 chore: update log level trace 2025-05-04 20:04:37 -07:00
f557c5aff6 feat: add status check for iptv services 2025-05-04 18:11:28 -07:00
2e67915bb6 refactor: add logging to promises 2025-05-04 16:00:03 -07:00
cce142d636 fix: page not found error on initial loading of /channels 2025-05-04 15:28:18 -07:00
e398e1acc4 refactor: log category labels 2025-05-04 13:29:44 -07:00
5c6756d98f feat: add new log type log.verbose with id 6 2025-05-04 13:29:02 -07:00
f0ccd04718 feat: add env var WEB_FOLDER 2025-05-04 13:27:12 -07:00
fe0545efc0 fix(deps): update dependency playwright to v1.52.0 2025-05-04 02:12:41 -07:00
15bfc1c35e fix(deps): update dependency user-agents to v1.1.529 2025-05-04 02:11:28 -07:00
a0635658bd chore(deps): update dependency @stylistic/eslint-plugin-js to v4 2025-05-04 02:09:56 -07:00
cde7a1ab40 fix: undefined variables 2025-05-04 01:52:13 -07:00
c8d68bdb2a feat: add detailed logging system 2025-05-04 01:47:01 -07:00
d18e67881c docs(CONTRIBUTING): remove <sub><sup> 2025-05-03 16:04:26 -07:00
cdfccdd022 build(dockerfile): add package curl to apk add 2025-05-03 16:01:13 -07:00
44f6a002bf docs(readme): add env var WEB_PROXY_HEADER 2025-05-03 16:00:41 -07:00
b85db52c77 docs(readme): update 2025-05-03 15:34:55 -07:00
3ebc986132 ci: update labels-create workflow 2025-05-03 15:14:15 -07:00
5973508f80 ci: update issues workflow 2025-05-03 04:13:41 -07:00
9a9e4ebedf fix: invalid api key should stop request once detected 2025-05-02 16:58:42 -07:00
a78e3201cb fix: ensure env var streamQuality casing is not sensitive 2025-05-02 16:57:36 -07:00
fbdeea47eb ci: update labels-create workflow 2025-05-02 04:04:01 -07:00
761a4f187e ci: update labels-clean workflow 2025-05-02 04:02:26 -07:00
77bb68e3a9 ci: update issues-stable workflow 2025-05-02 04:01:19 -07:00
189d913567 ci: update issues-scan workflow 2025-05-02 03:54:42 -07:00
2e9fdcf9bd ci: update workflow issues-new 2025-05-02 03:45:57 -07:00
f57a46cd29 ci: add workflow history-clean 2025-05-02 03:41:47 -07:00
305abb3e47 ci: update deploy-clean workflow 2025-05-02 03:39:36 -07:00
renovate[bot]
ef904032d7 chore(deps): lock file maintenance (#49)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-21 06:05:29 +00:00
renovate[bot]
55c1058832 chore(deps): lock file maintenance (#46)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-14 05:50:08 +00:00
879a9a658c docs: add docker run page 2025-04-12 00:56:33 -07:00
893679c843 feat: report client ip in logs for any connections made to server 2025-04-11 13:17:18 -07:00
f935b184d7 feat: add env variable API_KEY for restart / resync triggering 2025-04-11 13:03:19 -07:00
010a440e3e refactor: change toast notification lifetime from 4 to 10 seconds 2025-04-11 13:02:35 -07:00
51023a0e14 refactor: move api endpoint restart to api/restart 2025-04-11 13:01:22 -07:00
55d5cae85f docs: add main install page 2025-04-11 12:59:06 -07:00
d564f064d6 feat: add env variable WEB_PROXY_HEADER to override proxy header 2025-04-11 12:58:41 -07:00
1750b6ff11 docs: add healthcheck page 2025-04-11 07:45:38 -07:00
370 changed files with 44005 additions and 42660 deletions

View File

@@ -14,6 +14,9 @@ body:
4. Before creating this bug report, ensure you updated your applications to the latest versions. 4. Before creating this bug report, ensure you updated your applications to the latest versions.
Check your configurations to ensure there are no typos or errors. Check your configurations to ensure there are no typos or errors.
Docker users should attempt to re-pull the TVApp2 image to ensure caching is not the cause of an issue. Docker users should attempt to re-pull the TVApp2 image to ensure caching is not the cause of an issue.
5. To get detailed logs of the issue, set the environment variable:
`LOG_LEVEL=5`
Restart the docker container and you should get more detailed logs.
<br /> <br />
@@ -126,9 +129,9 @@ body:
- type: textarea - type: textarea
id: docker-compose id: docker-compose
attributes: attributes:
label: docker-compose.yml label: docker-compose.yml / Run command
description: | description: |
Copy / paste your `docker-compose.yml` file here Copy / paste your `docker-compose.yml` file or run command here
- type: textarea - type: textarea
id: logs id: logs
@@ -137,6 +140,7 @@ body:
description: | description: |
Paste your docker logs here. Paste your docker logs here.
You can get your docker logs by opening terminal and running `docker logs tvapp2` You can get your docker logs by opening terminal and running `docker logs tvapp2`
To get detailed logs, set the environment variable `LOG_LEVEL=5` and restart the container.
- type: textarea - type: textarea
id: screenshots id: screenshots

View File

@@ -12,15 +12,16 @@
# secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/ # secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/
# secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/ # secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/
# secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token # secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token
# secrets.ORG_BINARYNINJA_TOKEN org github personal access token (fine-grained) # secrets.ORG_TOKEN org github personal access token (fine-grained)
# secrets.ORG_BINARYNINJA_TOKEN_CL org github personal access token (classic) # secrets.ORG_TOKEN_CL org github personal access token (classic)
# secrets.ORG_BINARYNINJA_DOCKERHUB_TOKEN org dockerhub secret # secrets.ORG_DOCKERHUB_TOKEN org dockerhub secret
# secrets.ORG_BINARYNINJA_GITEA_TOKEN org gitea personal access token (classic) with package:write permission # secrets.ORG_GITEA_TOKEN org gitea personal access token (classic) with package:write permission
# secrets.BINARYSERV_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK # secrets.BOT_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
# secrets.BINARYSERV_GPG_PASSPHRASE bot gpg private key passphrase # secrets.BOT_GPG_PASSPHRASE bot gpg private key passphrase
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_RELEASES discord webhook to report release notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_RELEASES discord webhook to report release notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKFLOWS discord webhook to report workflow notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_WORKFLOWS discord webhook to report workflow notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_UPDATES discord webhook to report activity notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_UPDATES discord webhook to report activity notifications from github to discord
#
# #
# @local these workflows can be tested locally through the use of `act` # @local these workflows can be tested locally through the use of `act`
# https://github.com/nektos/act # https://github.com/nektos/act
@@ -32,14 +33,21 @@
# act -W .github/workflows/deploy-clean.yml -s TOKEN_CL=XXXXXXXXXX --pull=false # act -W .github/workflows/deploy-clean.yml -s TOKEN_CL=XXXXXXXXXX --pull=false
# # # #
name: "⚙️ Deploy Clean" # #
run-name: "⚙️ Deploy Clean"
name: '🧹 Deployments Clean'
run-name: '🧹 Deployments Clean'
# # # #
# triggers # triggers
# # # #
on: on:
# #
# Trigger > Workflow Dispatch
# #
workflow_dispatch: workflow_dispatch:
inputs: inputs:
@@ -78,67 +86,6 @@ env:
BOT_NAME_2: BinaryServ BOT_NAME_2: BinaryServ
BOT_NAME_DEPENDABOT: dependabot[bot] BOT_NAME_DEPENDABOT: dependabot[bot]
BOT_NAME_RENOVATE: renovate[bot] BOT_NAME_RENOVATE: renovate[bot]
LABELS_JSON: |
[
{ "name": "AC Changes Made", "color": "8F1784", "description": "Requested changes have been made and are pending a re-scan" },
{ "name": "AC Changes Required", "color": "8F1784", "description": "Requires changes to be made to the package before being accepted" },
{ "name": "AC Failed", "color": "a61f2d", "description": "Autocheck failed to run through a complete cycle, requires investigation" },
{ "name": "AC Needs Rebase", "color": "8F1784", "description": "Due to the permissions on the requesting repo, this pull request must be rebased by the author" },
{ "name": "AC Passed", "color": "146b4a", "description": "Ready to be reviewed" },
{ "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
{ "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
{ "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" },
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by EuropaServ for a prolonged period of inactivity" },
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" },
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" },
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" },
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
{ "name": "Type ◦ Bug", "color": "9a2c2c", "description": "Something isn't working" },
{ "name": "Type ◦ Dependency", "color": "243759", "description": "Item is associated to dependency" },
{ "name": "Type ◦ Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
{ "name": "Type ◦ Feature", "color": "3c4e93", "description": "Feature request" },
{ "name": "Type ◦ Git Action", "color": "030406", "description": "GitHub Action / workflow" },
{ "name": "Type ◦ Pull Request", "color": "8F1784", "description": "Normal pull request" },
{ "name": "Type ◦ Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
{ "name": "Type ◦ Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
{ "name": "Build ◦ Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
{ "name": "Build ◦ Linux", "color": "c7ca4a", "description": "Specific to Linux" },
{ "name": "Build ◦ MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
{ "name": "Build ◦ Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
{ "name": "Build ◦ Web", "color": "c7ca4a", "description": "Specific to web" },
{ "name": "Build ◦ Windows", "color": "c7ca4a", "description": "Specific to Windows" },
{ "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
{ "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
{ "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
{ "name": " Customization", "color": "E3F0FC", "description": "Customizations: plugins, themes, configs" },
{ "name": " Design", "color": "FA70DE", "description": "Design related queries" },
{ "name": " Dist", "color": "FA70DE", "description": "Installers and other forms of software distribution" },
{ "name": " Enterprise", "color": "11447a", "description": "Issues about collaboration, administration, and so on" },
{ "name": " Hardware", "color": "5a7503", "description": "YubiKey, other tokens, biometrics" },
{ "name": " Import/Export", "color": "F5FFCC", "description": "Import from and export to different file formats" },
{ "name": " Improvement", "color": "185c98", "description": "Enhance an existing feature" },
{ "name": " Performance", "color": "006b75", "description": "Web and desktop performance issues" },
{ "name": " Plugin Request", "color": "FCE9CA", "description": "Requested changes should be implemented as a plugin" },
{ "name": " Security", "color": "F75D39", "description": "Security issues" },
{ "name": " Self-Hosting", "color": "fad8c7", "description": "Self-hosting installations and configs" },
{ "name": " Storage", "color": "5319e7", "description": "Storage providers: Dropbox, Google, WebDAV, etc." },
{ "name": " Updater", "color": "1BADDE", "description": "Auto-updater issues" },
{ "name": " UX", "color": "1BADDE", "description": "UX and usability" },
{ "name": " Website", "color": "fef2c0", "description": "Website related issues" },
{ "name": "⚠ Urgent", "color": "a8740e", "description": "Requires urgent attention" },
{ "name": "⚠ Announcement", "color": "DB4712", "description": "Announcements" },
{ "name": "📰 Progress Report", "color": "392297", "description": "Development updates" },
{ "name": "📦 Release", "color": "277542", "description": "Release announcements" },
{ "name": "✔️ Poll", "color": "972255", "description": "Community polls" },
{ "name": "❔ Question", "color": "FFFFFF", "description": "All questions" }
]
# # # #
# jobs # jobs
@@ -146,10 +93,13 @@ env:
jobs: jobs:
cleanup: cleanup:
name: >-
🧹 Deployments Clean
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
runs-on: apollo-x64 runs-on: apollo-x64
timeout-minutes: 20 timeout-minutes: 5
permissions: write-all permissions: write-all
steps: steps:
# # # #
@@ -160,6 +110,7 @@ jobs:
🕛 Get Timestamp 🕛 Get Timestamp
id: task_cleanup_set_timestamp id: task_cleanup_set_timestamp
run: | run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
@@ -202,6 +153,7 @@ jobs:
git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV echo 'EOF' >> $GITHUB_ENV
# # # #
# Cleanup Notify Github Success # Cleanup Notify Github Success
# # # #

View File

@@ -280,6 +280,7 @@ jobs:
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV # 02-25-2025 12:49 echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV # 02-25-2025 12:49
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV # 20250225 echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV # 20250225
echo "NOW_DOCKER_TS=$(date -u +'%FT%T.%3NZ')" >> $GITHUB_ENV # 2025-02-25T12:50:11.569Z echo "NOW_DOCKER_TS=$(date -u +'%FT%T.%3NZ')" >> $GITHUB_ENV # 2025-02-25T12:50:11.569Z
echo "GITHUB_SHA1=`git rev-parse HEAD`" >> ${GITHUB_ENV}
# # # #
# Release Dockerhub Install Dependencies # Release Dockerhub Install Dependencies
@@ -421,6 +422,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=dockerhub org.opencontainers.image.registry=dockerhub
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
annotations: | annotations: |
org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }} org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }}
org.opencontainers.image.version=${{ env.IMAGE_VERSION }} org.opencontainers.image.version=${{ env.IMAGE_VERSION }}
@@ -431,6 +434,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=dockerhub org.opencontainers.image.registry=dockerhub
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
# # # #
# Release Dockerhub Build and Push Amd64 # Release Dockerhub Build and Push Amd64
@@ -455,6 +460,7 @@ jobs:
RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }} RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}
VERSION=${{ env.IMAGE_VERSION }} VERSION=${{ env.IMAGE_VERSION }}
BUILDDATE=${{ env.NOW_DOCKER_LABEL }} BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
GIT_SHA1=${{ env.GITHUB_SHA1 }}
# # # #
# Release Dockerhub Export Digest Amd64 # Release Dockerhub Export Digest Amd64
@@ -507,6 +513,7 @@ jobs:
RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }} RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}
VERSION=${{ env.IMAGE_VERSION }} VERSION=${{ env.IMAGE_VERSION }}
BUILDDATE=${{ env.NOW_DOCKER_LABEL }} BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
GIT_SHA1=${{ env.GITHUB_SHA1 }}
# # # #
# Release Dockerhub Export Digest Arm64 # Release Dockerhub Export Digest Arm64
@@ -536,22 +543,6 @@ jobs:
if-no-files-found: error if-no-files-found: error
retention-days: 10 retention-days: 10
# #
# Release Dockerhub Push Manifest
# #
- name: '📦 Push Manifest'
id: task_release_dh_manifest
uses: int128/docker-manifest-create-action@v2
with:
tags: |
${{ steps.task_release_dh_meta.outputs.tags }}
sources: |
${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_dh_push_amd64.outputs.digest }}
${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_dh_push_arm64.outputs.digest }}
index-annotations: |
${{ steps.task_release_dh_meta.outputs.labels }}
# # # #
# Release Dockerhub Checkpoint Amd64 # Release Dockerhub Checkpoint Amd64
# # # #
@@ -560,23 +551,24 @@ jobs:
id: task_release_dh_checkpoint id: task_release_dh_checkpoint
run: | run: |
echo "" echo ""
echo "[ GITHUB ] ---------------------------------------------------------------------------------------------" echo "---- [ GITHUB ] ----------------------------------------------------------------------------------------"
echo "github.actor.............................. ${{ github.actor }}" echo "github.actor.............................. ${{ github.actor }}"
echo "github.ref ............................... ${{ github.ref }}" echo "github.ref ............................... ${{ github.ref }}"
echo "github.ref_name .......................... ${{ github.ref_name }}" echo "github.ref_name .......................... ${{ github.ref_name }}"
echo "github.event_name ........................ ${{ github.event_name }}" echo "github.event_name ........................ ${{ github.event_name }}"
echo "github.repository_owner .................. ${{ github.repository_owner }}" echo "github.repository_owner .................. ${{ github.repository_owner }}"
echo "github.repository ........................ ${{ github.repository }}" echo "github.repository ........................ ${{ github.repository }}"
echo "" echo "github.sha ............................... ${{ github.sha }}"
echo "[ INPUTS ] ---------------------------------------------------------------------------------------------" echo -e ""
echo "---- [ INPUTS ] ----------------------------------------------------------------------------------------"
echo "inputs.IMAGE_NAME ........................ ${{ inputs.IMAGE_NAME }}" echo "inputs.IMAGE_NAME ........................ ${{ inputs.IMAGE_NAME }}"
echo "inputs.IMAGE_VERSION ..................... ${{ inputs.IMAGE_VERSION }}" echo "inputs.IMAGE_VERSION ..................... ${{ inputs.IMAGE_VERSION }}"
echo "inputs.IMAGE_DOCKERHUB_AUTHOR ............ ${{ inputs.IMAGE_DOCKERHUB_AUTHOR }}" echo "inputs.IMAGE_DOCKERHUB_AUTHOR ............ ${{ inputs.IMAGE_DOCKERHUB_AUTHOR }}"
echo "inputs.IMAGE_DOCKERHUB_USERNAME .......... ${{ inputs.IMAGE_DOCKERHUB_USERNAME }}" echo "inputs.IMAGE_DOCKERHUB_USERNAME .......... ${{ inputs.IMAGE_DOCKERHUB_USERNAME }}"
echo "inputs.DEV_RELEASE ....................... ${{ inputs.DEV_RELEASE }}" echo "inputs.DEV_RELEASE ....................... ${{ inputs.DEV_RELEASE }}"
echo "inputs.DRY_RUN ........................... ${{ inputs.DRY_RUN }}" echo "inputs.DRY_RUN ........................... ${{ inputs.DRY_RUN }}"
echo "" echo -e ""
echo "[ ENV ] ------------------------------------------------------------------------------------------------" echo "---- [ ENV ] -------------------------------------------------------------------------------------------"
echo "env.IMAGE_NAME ........................... ${{ env.IMAGE_NAME }}" echo "env.IMAGE_NAME ........................... ${{ env.IMAGE_NAME }}"
echo "env.IMAGE_VERSION ........................ ${{ env.IMAGE_VERSION }}" echo "env.IMAGE_VERSION ........................ ${{ env.IMAGE_VERSION }}"
echo "env.IMAGE_VERSION_1DIGIT ................. ${{ env.IMAGE_VERSION_1DIGIT }}" echo "env.IMAGE_VERSION_1DIGIT ................. ${{ env.IMAGE_VERSION_1DIGIT }}"
@@ -591,22 +583,39 @@ jobs:
echo "env.REGISTRY_REPO_ORG_AUTHOR_LC .......... ${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}" echo "env.REGISTRY_REPO_ORG_AUTHOR_LC .......... ${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}"
echo "env.REGISTRY_REPO_AUTHOR_LC .............. ${{ env.REGISTRY_REPO_AUTHOR_LC }}" echo "env.REGISTRY_REPO_AUTHOR_LC .............. ${{ env.REGISTRY_REPO_AUTHOR_LC }}"
echo "env.DOCKER_SHA ........................... ${{ env.DOCKER_SHA }}" echo "env.DOCKER_SHA ........................... ${{ env.DOCKER_SHA }}"
echo "" echo "env.GITHUB_SHA1 .......................... ${{ env.GITHUB_SHA1 }}"
echo "[ DOCKER IMAGES ] --------------------------------------------------------------------------------------" echo -e ""
echo "---- [ DOCKER IMAGES ] ---------------------------------------------------------------------------------"
echo "registry ................................. Dockerhub" echo "registry ................................. Dockerhub"
echo "tags ..................................... ${{ steps.task_release_dh_meta.outputs.tags }}" echo "tags ..................................... ${{ steps.task_release_dh_meta.outputs.tags }}"
echo "labels ................................... ${{ steps.task_release_dh_meta.outputs.labels }}" echo "labels ................................... ${{ steps.task_release_dh_meta.outputs.labels }}"
echo "digest ................................... ${{ steps.task_release_dh_push_amd64.outputs.digest }}" echo "digest ................................... ${{ steps.task_release_dh_push_amd64.outputs.digest }}"
echo "" echo -e ""
echo "(release) tags ........................... ${{ steps.task_release_dh_meta.outputs.tags }}" echo "(release) tags ........................... ${{ steps.task_release_dh_meta.outputs.tags }}"
echo "(release) labels ......................... ${{ steps.task_release_dh_meta.outputs.labels }}" echo "(release) labels ......................... ${{ steps.task_release_dh_meta.outputs.labels }}"
echo "" echo -e ""
echo "[ DOCKER DIGESTS ] -------------------------------------------------------------------------------------" echo "---- [ DOCKER DIGESTS ] --------------------------------------------------------------------------------"
echo "docker image id (amd64) .................. ${{ steps.task_release_dh_push_amd64.outputs.imageid }}" echo "docker image id (amd64) .................. ${{ steps.task_release_dh_push_amd64.outputs.imageid }}"
echo "docker digest (amd64) .................... ${{ steps.task_release_dh_push_amd64.outputs.digest }}" echo "docker digest (amd64) .................... ${{ steps.task_release_dh_push_amd64.outputs.digest }}"
echo "docker image id (arm64) .................. ${{ steps.task_release_dh_push_arm64.outputs.imageid }}" echo "docker image id (arm64) .................. ${{ steps.task_release_dh_push_arm64.outputs.imageid }}"
echo "docker digest (arm64) .................... ${{ steps.task_release_dh_push_arm64.outputs.digest }}" echo "docker digest (arm64) .................... ${{ steps.task_release_dh_push_arm64.outputs.digest }}"
# #
# Release Dockerhub Push Manifest
# #
- name: '📦 Push Manifest'
id: task_release_dh_manifest
uses: int128/docker-manifest-create-action@v2
with:
tags: |
${{ steps.task_release_dh_meta.outputs.tags }}
sources: |
${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_dh_push_amd64.outputs.digest }}
${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_dh_push_arm64.outputs.digest }}
index-annotations: |
${{ steps.task_release_dh_meta.outputs.labels }}
# # # #
# Release Dockerhub Get Weekly Commits # Release Dockerhub Get Weekly Commits
# # # #

View File

@@ -292,6 +292,7 @@ jobs:
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV # 02-25-2025 12:49 echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV # 02-25-2025 12:49
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV # 20250225 echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV # 20250225
echo "NOW_DOCKER_TS=$(date -u +'%FT%T.%3NZ')" >> $GITHUB_ENV # 2025-02-25T12:50:11.569Z echo "NOW_DOCKER_TS=$(date -u +'%FT%T.%3NZ')" >> $GITHUB_ENV # 2025-02-25T12:50:11.569Z
echo "GITHUB_SHA1=`git rev-parse HEAD`" >> ${GITHUB_ENV}
# # # #
# Release Gitea Install Dependencies # Release Gitea Install Dependencies
@@ -437,6 +438,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=gitea org.opencontainers.image.registry=gitea
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
annotations: | annotations: |
org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }} org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }}
org.opencontainers.image.version=${{ env.IMAGE_VERSION }} org.opencontainers.image.version=${{ env.IMAGE_VERSION }}
@@ -447,6 +450,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=gitea org.opencontainers.image.registry=gitea
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
# # # #
# Release Gitea Meta arm64 # Release Gitea Meta arm64
@@ -485,6 +490,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=gitea org.opencontainers.image.registry=gitea
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
annotations: | annotations: |
org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }} org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }}
org.opencontainers.image.version=${{ env.IMAGE_VERSION }} org.opencontainers.image.version=${{ env.IMAGE_VERSION }}
@@ -495,6 +502,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=gitea org.opencontainers.image.registry=gitea
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
# # # #
# Release Gitea Meta Release # Release Gitea Meta Release
@@ -579,6 +588,7 @@ jobs:
RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }} RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}
VERSION=${{ env.IMAGE_VERSION }} VERSION=${{ env.IMAGE_VERSION }}
BUILDDATE=${{ env.NOW_DOCKER_LABEL }} BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
GIT_SHA1=${{ env.GITHUB_SHA1 }}
# # # #
# Release Gitea Export Digest Amd64 # Release Gitea Export Digest Amd64
@@ -631,6 +641,7 @@ jobs:
RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }} RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}
VERSION=${{ env.IMAGE_VERSION }} VERSION=${{ env.IMAGE_VERSION }}
BUILDDATE=${{ env.NOW_DOCKER_LABEL }} BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
GIT_SHA1=${{ env.GITHUB_SHA1 }}
# # # #
# Release Gitea Export Digest Arm64 # Release Gitea Export Digest Arm64
@@ -660,22 +671,6 @@ jobs:
if-no-files-found: error if-no-files-found: error
retention-days: 10 retention-days: 10
# #
# Release Gitea Push Manifest
# #
- name: '📦 Push Manifest'
id: task_release_gi_manifest
uses: int128/docker-manifest-create-action@v2
with:
tags: |
${{ steps.task_release_gi_meta_release.outputs.tags }}
sources: |
${{ env.IMAGE_GITEA_WEBSITE }}/${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_gi_push_amd64.outputs.digest }}
${{ env.IMAGE_GITEA_WEBSITE }}/${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_gi_push_arm64.outputs.digest }}
index-annotations: |
${{ steps.task_release_gi_meta_release.outputs.labels }}
# # # #
# Release Gitea Checkpoint # Release Gitea Checkpoint
# # # #
@@ -684,15 +679,16 @@ jobs:
id: task_release_gi_checkpoint id: task_release_gi_checkpoint
run: | run: |
echo "" echo ""
echo "[ GITHUB ] ---------------------------------------------------------------------------------------------" echo "---- [ GITHUB ] ----------------------------------------------------------------------------------------"
echo "github.actor.............................. ${{ github.actor }}" echo "github.actor.............................. ${{ github.actor }}"
echo "github.ref ............................... ${{ github.ref }}" echo "github.ref ............................... ${{ github.ref }}"
echo "github.ref_name .......................... ${{ github.ref_name }}" echo "github.ref_name .......................... ${{ github.ref_name }}"
echo "github.event_name ........................ ${{ github.event_name }}" echo "github.event_name ........................ ${{ github.event_name }}"
echo "github.repository_owner .................. ${{ github.repository_owner }}" echo "github.repository_owner .................. ${{ github.repository_owner }}"
echo "github.repository ........................ ${{ github.repository }}" echo "github.repository ........................ ${{ github.repository }}"
echo "" echo "github.sha ............................... ${{ github.sha }}"
echo "[ INPUTS ] ---------------------------------------------------------------------------------------------" echo -e ""
echo "---- [ INPUTS ] ----------------------------------------------------------------------------------------"
echo "inputs.IMAGE_NAME ........................ ${{ inputs.IMAGE_NAME }}" echo "inputs.IMAGE_NAME ........................ ${{ inputs.IMAGE_NAME }}"
echo "inputs.IMAGE_VERSION ..................... ${{ inputs.IMAGE_VERSION }}" echo "inputs.IMAGE_VERSION ..................... ${{ inputs.IMAGE_VERSION }}"
echo "inputs.IMAGE_GITEA_USERNAME .............. ${{ inputs.IMAGE_GITEA_AUTHOR }}" echo "inputs.IMAGE_GITEA_USERNAME .............. ${{ inputs.IMAGE_GITEA_AUTHOR }}"
@@ -700,8 +696,8 @@ jobs:
echo "inputs.IMAGE_GITEA_WEBSITE ............... ${{ inputs.IMAGE_GITEA_WEBSITE }}" echo "inputs.IMAGE_GITEA_WEBSITE ............... ${{ inputs.IMAGE_GITEA_WEBSITE }}"
echo "inputs.DEV_RELEASE ....................... ${{ inputs.DEV_RELEASE }}" echo "inputs.DEV_RELEASE ....................... ${{ inputs.DEV_RELEASE }}"
echo "inputs.DRY_RUN ........................... ${{ inputs.DRY_RUN }}" echo "inputs.DRY_RUN ........................... ${{ inputs.DRY_RUN }}"
echo "" echo -e ""
echo "[ ENV ] ------------------------------------------------------------------------------------------------" echo "---- [ ENV ] -------------------------------------------------------------------------------------------"
echo "env.IMAGE_NAME ........................... ${{ env.IMAGE_NAME }}" echo "env.IMAGE_NAME ........................... ${{ env.IMAGE_NAME }}"
echo "env.IMAGE_VERSION ........................ ${{ env.IMAGE_VERSION }}" echo "env.IMAGE_VERSION ........................ ${{ env.IMAGE_VERSION }}"
echo "env.IMAGE_VERSION_1DIGIT ................. ${{ env.IMAGE_VERSION_1DIGIT }}" echo "env.IMAGE_VERSION_1DIGIT ................. ${{ env.IMAGE_VERSION_1DIGIT }}"
@@ -717,8 +713,9 @@ jobs:
echo "env.REGISTRY_REPO_ORG_AUTHOR_LC .......... ${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}" echo "env.REGISTRY_REPO_ORG_AUTHOR_LC .......... ${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}"
echo "env.REGISTRY_REPO_AUTHOR_LC .............. ${{ env.REGISTRY_REPO_AUTHOR_LC }}" echo "env.REGISTRY_REPO_AUTHOR_LC .............. ${{ env.REGISTRY_REPO_AUTHOR_LC }}"
echo "env.DOCKER_SHA ........................... ${{ env.DOCKER_SHA }}" echo "env.DOCKER_SHA ........................... ${{ env.DOCKER_SHA }}"
echo "" echo "env.GITHUB_SHA1 .......................... ${{ env.GITHUB_SHA1 }}"
echo "[ DOCKER IMAGES ] --------------------------------------------------------------------------------------" echo -e ""
echo "---- [ DOCKER IMAGES ] ---------------------------------------------------------------------------------"
echo "registry ................................. Gitea" echo "registry ................................. Gitea"
echo "(amd64) tags ............................. ${{ steps.task_release_gi_meta_amd64.outputs.tags }}" echo "(amd64) tags ............................. ${{ steps.task_release_gi_meta_amd64.outputs.tags }}"
echo "(amd64) labels ........................... ${{ steps.task_release_gi_meta_amd64.outputs.labels }}" echo "(amd64) labels ........................... ${{ steps.task_release_gi_meta_amd64.outputs.labels }}"
@@ -730,13 +727,29 @@ jobs:
echo "" echo ""
echo "(release) tags ........................... ${{ steps.task_release_gi_meta_release.outputs.tags }}" echo "(release) tags ........................... ${{ steps.task_release_gi_meta_release.outputs.tags }}"
echo "(release) labels ......................... ${{ steps.task_release_gi_meta_release.outputs.labels }}" echo "(release) labels ......................... ${{ steps.task_release_gi_meta_release.outputs.labels }}"
echo "" echo -e ""
echo "[ DOCKER DIGESTS ] -------------------------------------------------------------------------------------" echo "---- [ DOCKER DIGESTS ] --------------------------------------------------------------------------------"
echo "docker image id (amd64) .................. ${{ steps.task_release_gi_push_amd64.outputs.imageid }}" echo "docker image id (amd64) .................. ${{ steps.task_release_gi_push_amd64.outputs.imageid }}"
echo "docker digest (amd64) .................... ${{ steps.task_release_gi_push_amd64.outputs.digest }}" echo "docker digest (amd64) .................... ${{ steps.task_release_gi_push_amd64.outputs.digest }}"
echo "docker image id (arm64) .................. ${{ steps.task_release_gi_push_arm64.outputs.imageid }}" echo "docker image id (arm64) .................. ${{ steps.task_release_gi_push_arm64.outputs.imageid }}"
echo "docker digest (arm64) .................... ${{ steps.task_release_gi_push_arm64.outputs.digest }}" echo "docker digest (arm64) .................... ${{ steps.task_release_gi_push_arm64.outputs.digest }}"
# #
# Release Gitea Push Manifest
# #
- name: '📦 Push Manifest'
id: task_release_gi_manifest
uses: int128/docker-manifest-create-action@v2
with:
tags: |
${{ steps.task_release_gi_meta_release.outputs.tags }}
sources: |
${{ env.IMAGE_GITEA_WEBSITE }}/${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_gi_push_amd64.outputs.digest }}
${{ env.IMAGE_GITEA_WEBSITE }}/${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}@${{ steps.task_release_gi_push_arm64.outputs.digest }}
index-annotations: |
${{ steps.task_release_gi_meta_release.outputs.labels }}
# # # #
# Release Gitea Get Weekly Commits # Release Gitea Get Weekly Commits
# # # #

View File

@@ -277,6 +277,7 @@ jobs:
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV # 02-25-2025 12:49 echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV # 02-25-2025 12:49
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV # 20250225 echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV # 20250225
echo "NOW_DOCKER_TS=$(date -u +'%FT%T.%3NZ')" >> $GITHUB_ENV # 2025-02-25T12:50:11.569Z echo "NOW_DOCKER_TS=$(date -u +'%FT%T.%3NZ')" >> $GITHUB_ENV # 2025-02-25T12:50:11.569Z
echo "GITHUB_SHA1=`git rev-parse HEAD`" >> ${GITHUB_ENV}
# # # #
# Release Github Install Dependencies # Release Github Install Dependencies
@@ -419,6 +420,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=github org.opencontainers.image.registry=github
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
annotations: | annotations: |
org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }} org.opencontainers.image.created=${{ env.NOW_DOCKER_TS }}
org.opencontainers.image.version=${{ env.IMAGE_VERSION }} org.opencontainers.image.version=${{ env.IMAGE_VERSION }}
@@ -429,6 +432,8 @@ jobs:
org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }} org.opencontainers.image.development=${{ inputs.DEV_RELEASE == true && 'true' || 'false' }}
org.opencontainers.image.registry=github org.opencontainers.image.registry=github
org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}" org.tvapp2.image.build-version="Version:- ${{ env.IMAGE_VERSION }} Date:- ${{ env.NOW_DOCKER_LABEL }}"
org.tvapp2.image.build-release="${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}"
org.tvapp2.image.build-sha1=${GITHUB_SHA1}
# # # #
# Release Github Build and Push Amd64 # Release Github Build and Push Amd64
@@ -458,6 +463,7 @@ jobs:
RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }} RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}
VERSION=${{ env.IMAGE_VERSION }} VERSION=${{ env.IMAGE_VERSION }}
BUILDDATE=${{ env.NOW_DOCKER_LABEL }} BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
GIT_SHA1=${{ env.GITHUB_SHA1 }}
# # # #
# Release Github Export Digest Amd64 # Release Github Export Digest Amd64
@@ -515,6 +521,7 @@ jobs:
RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }} RELEASE=${{ inputs.DEV_RELEASE == true && 'development' || 'stable' }}
VERSION=${{ env.IMAGE_VERSION }} VERSION=${{ env.IMAGE_VERSION }}
BUILDDATE=${{ env.NOW_DOCKER_LABEL }} BUILDDATE=${{ env.NOW_DOCKER_LABEL }}
GIT_SHA1=${{ env.GITHUB_SHA1 }}
# # # #
# Release Github Export Digest Arm64 # Release Github Export Digest Arm64
@@ -544,6 +551,63 @@ jobs:
if-no-files-found: error if-no-files-found: error
retention-days: 10 retention-days: 10
# #
# Release Github Checkpoint
# #
- name: '⚠️ Checkpoint'
id: task_release_gh_checkpoint
run: |
echo ""
echo "---- [ GITHUB ] ----------------------------------------------------------------------------------------"
echo "github.actor.............................. ${{ github.actor }}"
echo "github.ref ............................... ${{ github.ref }}"
echo "github.ref_name .......................... ${{ github.ref_name }}"
echo "github.event_name ........................ ${{ github.event_name }}"
echo "github.repository_owner .................. ${{ github.repository_owner }}"
echo "github.repository ........................ ${{ github.repository }}"
echo "github.sha ............................... ${{ github.sha }}"
echo -e ""
echo "---- [ INPUTS ] ----------------------------------------------------------------------------------------"
echo "inputs.IMAGE_NAME ........................ ${{ inputs.IMAGE_NAME }}"
echo "inputs.IMAGE_VERSION ..................... ${{ inputs.IMAGE_VERSION }}"
echo "inputs.IMAGE_GHCR_AUTHOR ................. ${{ inputs.IMAGE_GHCR_AUTHOR }}"
echo "inputs.IMAGE_GHCR_USERNAME ............... ${{ inputs.IMAGE_GHCR_USERNAME }}"
echo "inputs.DEV_RELEASE ....................... ${{ inputs.DEV_RELEASE }}"
echo "inputs.DRY_RUN ........................... ${{ inputs.DRY_RUN }}"
echo -e ""
echo "---- [ ENV ] -------------------------------------------------------------------------------------------"
echo "env.IMAGE_NAME ........................... ${{ env.IMAGE_NAME }}"
echo "env.IMAGE_VERSION ........................ ${{ env.IMAGE_VERSION }}"
echo "env.IMAGE_VERSION_1DIGIT ................. ${{ env.IMAGE_VERSION_1DIGIT }}"
echo "env.IMAGE_VERSION_2DIGIT ................. ${{ env.IMAGE_VERSION_2DIGIT }}"
echo "env.IMAGE_GHCR_AUTHOR .................... ${{ env.IMAGE_GHCR_AUTHOR }}"
echo "env.IMAGE_GHCR_USERNAME .................. ${{ env.IMAGE_GHCR_USERNAME }}"
echo "env.NOW .................................. ${{ env.NOW }}"
echo "env.NOW_SHORT ............................ ${{ env.NOW_SHORT }}"
echo "env.NOW_LONG ............................. ${{ env.NOW_LONG }}"
echo "env.NOW_DOCKER_LABEL ..................... ${{ env.NOW_DOCKER_LABEL }}"
echo "env.NOW_DOCKER_TS ........................ ${{ env.NOW_DOCKER_TS }}"
echo "env.REGISTRY_REPO_ORG_AUTHOR_LC .......... ${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}"
echo "env.REGISTRY_REPO_AUTHOR_LC .............. ${{ env.REGISTRY_REPO_AUTHOR_LC }}"
echo "env.DOCKER_SHA ........................... ${{ env.DOCKER_SHA }}"
echo "env.GITHUB_SHA1 .......................... ${{ env.GITHUB_SHA1 }}"
echo -e ""
echo "---- [ DOCKER IMAGES ] ---------------------------------------------------------------------------------"
echo "registry ................................. Github GHCR"
echo "tags ..................................... ${{ steps.task_release_gh_meta.outputs.tags }}"
echo "labels ................................... ${{ steps.task_release_gh_meta.outputs.labels }}"
echo "digest ................................... ${{ steps.task_release_gh_push_amd64.outputs.digest }}"
echo -e ""
echo "(release) tags ........................... ${{ steps.task_release_gh_meta.outputs.tags }}"
echo "(release) labels ......................... ${{ steps.task_release_gh_meta.outputs.labels }}"
echo -e ""
echo "---- [ DOCKER DIGESTS ] --------------------------------------------------------------------------------"
echo "docker image id (amd64) .................. ${{ steps.task_release_gh_push_amd64.outputs.imageid }}"
echo "docker digest (amd64) .................... ${{ steps.task_release_gh_push_amd64.outputs.digest }}"
echo "docker image id (arm64) .................. ${{ steps.task_release_gh_push_arm64.outputs.imageid }}"
echo "docker digest (arm64) .................... ${{ steps.task_release_gh_push_arm64.outputs.digest }}"
# # # #
# Release Github Push Manifest # Release Github Push Manifest
# # # #
@@ -561,62 +625,6 @@ jobs:
index-annotations: | index-annotations: |
${{ steps.task_release_gh_meta.outputs.labels }} ${{ steps.task_release_gh_meta.outputs.labels }}
# #
# Release Github Checkpoint
# #
- name: '⚠️ Checkpoint'
id: task_release_gh_checkpoint
run: |
echo ""
echo "[ GITHUB ] ---------------------------------------------------------------------------------------------"
echo "github.actor.............................. ${{ github.actor }}"
echo "github.ref ............................... ${{ github.ref }}"
echo "github.ref_name .......................... ${{ github.ref_name }}"
echo "github.event_name ........................ ${{ github.event_name }}"
echo "github.repository_owner .................. ${{ github.repository_owner }}"
echo "github.repository ........................ ${{ github.repository }}"
echo ""
echo "[ INPUTS ] ---------------------------------------------------------------------------------------------"
echo "inputs.IMAGE_NAME ........................ ${{ inputs.IMAGE_NAME }}"
echo "inputs.IMAGE_VERSION ..................... ${{ inputs.IMAGE_VERSION }}"
echo "inputs.IMAGE_GHCR_AUTHOR ................. ${{ inputs.IMAGE_GHCR_AUTHOR }}"
echo "inputs.IMAGE_GHCR_USERNAME ............... ${{ inputs.IMAGE_GHCR_USERNAME }}"
echo "inputs.DEV_RELEASE ....................... ${{ inputs.DEV_RELEASE }}"
echo "inputs.DRY_RUN ........................... ${{ inputs.DRY_RUN }}"
echo ""
echo "[ ENV ] ------------------------------------------------------------------------------------------------"
echo "env.IMAGE_NAME ........................... ${{ env.IMAGE_NAME }}"
echo "env.IMAGE_VERSION ........................ ${{ env.IMAGE_VERSION }}"
echo "env.IMAGE_VERSION_1DIGIT ................. ${{ env.IMAGE_VERSION_1DIGIT }}"
echo "env.IMAGE_VERSION_2DIGIT ................. ${{ env.IMAGE_VERSION_2DIGIT }}"
echo "env.IMAGE_GHCR_AUTHOR .................... ${{ env.IMAGE_GHCR_AUTHOR }}"
echo "env.IMAGE_GHCR_USERNAME .................. ${{ env.IMAGE_GHCR_USERNAME }}"
echo "env.NOW .................................. ${{ env.NOW }}"
echo "env.NOW_SHORT ............................ ${{ env.NOW_SHORT }}"
echo "env.NOW_LONG ............................. ${{ env.NOW_LONG }}"
echo "env.NOW_DOCKER_LABEL ..................... ${{ env.NOW_DOCKER_LABEL }}"
echo "env.NOW_DOCKER_TS ........................ ${{ env.NOW_DOCKER_TS }}"
echo "env.DOCKER_IMAGE ......................... ${{ env.DOCKER_IMAGE }}"
echo "env.REGISTRY_REPO_ORG_AUTHOR_LC .......... ${{ env.REGISTRY_REPO_ORG_AUTHOR_LC }}"
echo "env.REGISTRY_REPO_AUTHOR_LC .............. ${{ env.REGISTRY_REPO_AUTHOR_LC }}"
echo "env.DOCKER_SHA ........................... ${{ env.DOCKER_SHA }}"
echo ""
echo "[ DOCKER IMAGES ] --------------------------------------------------------------------------------------"
echo "registry ................................. Github GHCR"
echo "tags ..................................... ${{ steps.task_release_gh_meta.outputs.tags }}"
echo "labels ................................... ${{ steps.task_release_gh_meta.outputs.labels }}"
echo "digest ................................... ${{ steps.task_release_gh_push_amd64.outputs.digest }}"
echo ""
echo "(release) tags ........................... ${{ steps.task_release_gh_meta.outputs.tags }}"
echo "(release) labels ......................... ${{ steps.task_release_gh_meta.outputs.labels }}"
echo ""
echo "[ DOCKER DIGESTS ] -------------------------------------------------------------------------------------"
echo "docker image id (amd64) .................. ${{ steps.task_release_gh_push_amd64.outputs.imageid }}"
echo "docker digest (amd64) .................... ${{ steps.task_release_gh_push_amd64.outputs.digest }}"
echo "docker image id (arm64) .................. ${{ steps.task_release_gh_push_arm64.outputs.imageid }}"
echo "docker digest (arm64) .................... ${{ steps.task_release_gh_push_arm64.outputs.digest }}"
# # # #
# Release Github Get Weekly Commits # Release Github Get Weekly Commits
# # # #

230
.github/workflows/history-clean.yml vendored Normal file
View File

@@ -0,0 +1,230 @@
# #
# @type github workflow
# @author Aetherinox
# @url https://github.com/Aetherinox
# @usage cleans all commit history for a repository
# edit the 'environment:' to determine which deployment to keep clean
# - can be ran manually
#
# @secrets secrets.SELF_TOKEN self github personal access token (fine-grained)
# secrets.SELF_TOKEN_CL self github personal access token (classic)
# secrets.NPM_TOKEN self npmjs access token
# secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/
# secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/
# secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token
# secrets.ORG_TOKEN org github personal access token (fine-grained)
# secrets.ORG_TOKEN_CL org github personal access token (classic)
# secrets.ORG_DOCKERHUB_TOKEN org dockerhub secret
# secrets.ORG_GITEA_TOKEN org gitea personal access token (classic) with package:write permission
# secrets.BOT_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
# secrets.BOT_GPG_PASSPHRASE bot gpg private key passphrase
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_RELEASES discord webhook to report release notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_WORKFLOWS discord webhook to report workflow notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_UPDATES discord webhook to report activity notifications from github to discord
#
#
# @local these workflows can be tested locally through the use of `act`
# https://github.com/nektos/act
# Extract act to folder
# Add system env var with path to act.exe
# Run the commands:
# git pull https://github.com/username/repo
# act -W .github/workflows/history-clean.yml -P ubuntu-latest=catthehacker/ubuntu:full-22.04
# act -W .github/workflows/history-clean.yml -s TOKEN_CL=XXXXXXXXXX --pull=false
# #
name: '🧹 History Clean'
run-name: '🧹 History Clean'
# #
# triggers
# #
on:
# #
# Trigger > Workflow Dispatch
# #
workflow_dispatch:
inputs:
# #
# Commit Label
#
# the label to use when repository is cleaned
# #
COMMIT_LABEL:
description: '🏷️ Commit Label'
required: true
default: 'cleanup'
type: string
# #
# Main Branch
#
# main branch re-recreate
# #
BRANCH_MAIN:
description: '🌳 Main Branch'
required: true
default: 'main'
type: string
# #
# Deployment Environment Name
#
# this is the name of the deployment item
# #
DEPLOYMENT_ENV:
description: '📦 Deployment Environment'
required: true
default: 'orion'
type: string
# #
# environment variables
# #
env:
COMMIT_LABEL: ${{ github.event.inputs.COMMIT_LABEL || 'cleanup' }}
BRANCH_MAIN: ${{ github.event.inputs.BRANCH_MAIN || 'main' }}
DEPLOYMENT_ENV: ${{ github.event.inputs.DEPLOYMENT_ENV || 'orion' }}
BOT_NAME_1: EuropaServ
BOT_NAME_2: BinaryServ
BOT_NAME_DEPENDABOT: dependabot[bot]
BOT_NAME_RENOVATE: renovate[bot]
# #
# jobs
# #
jobs:
history-clean:
name: >-
🧹 History Clean
# runs-on: ubuntu-latest
runs-on: apollo-x64
timeout-minutes: 5
steps:
# #
# History Clean Set TImestamps
# #
- name: >-
🕛 Get Timestamp
id: task_history_clean_set_timestamp
run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
# #
# History Clean Checkout
# #
- name: >-
✅ Checkout
id: task_history_clean_gh_checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# #
# History Clean Git Identify
# #
- name: >-
🪪 Configure Git Identity
id: task_history_clean_git_ident
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
# #
# History Clean Pre-Commit
# #
- name: >-
📦 Commit Pre-commit
id: task_history_clean_commit_pre
run: |
now=$(date -u '+%m/%d/%Y %H:%M')
commit_label="${{ env.COMMIT_LABEL }}" >> $GITHUB_ENV
echo -e "$commit_label"
commit_message="\\\`️️🧹 $commit_label 🧹\\\` \\\`$now UTC\\\`" >> $GITHUB_ENV
echo -e "$commit_message"
echo "COMMIT_MESSAGE=$(echo $commit_message)" >> $GITHUB_ENV
echo "NOW=$(echo $now)" >> $GITHUB_ENV
# #
# History Clean Pre-Commit Debug
# #
- name: >-
📦 Commit Pre-commit Debug
id: task_history_clean_commit_debug
run: |
echo -e "―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――"
echo -e " Printing Values"
echo -e "―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――"
echo -e " env.COMMIT_LABEL .................... ${{ env.COMMIT_LABEL }}"
echo -e " env.COMMIT_MESSAGE .................. ${{ env.COMMIT_MESSAGE }}"
echo -e " env.NOW ............................. ${{ env.NOW }}"
# #
# History Clean Start
# #
- name: >-
🧹 Clean Repo History
id: task_history_clean_history
run: |
# Create a new orphan branch
git checkout --orphan temp-branch
# Add all files to the new branch
git add -A
# Commit the files to the new branch
git commit -m "${{ env.COMMIT_MESSAGE }}"
# Delete the old main branch
git branch -D ${{ env.BRANCH_MAIN }}
# Rename the new orphan branch to main
git branch -m ${{ env.BRANCH_MAIN }}
# Force push the new main branch to the remote repository
git push -f origin ${{ env.BRANCH_MAIN }}
# #
# History Clean References
# #
- name: >-
🗑️ Clean References
id: task_history_clean_references
run: |
# Remove remote-tracking references to deleted branches (optional)
git fetch origin --prune
git reflog expire --expire=now --all
git gc --prune=now --aggressive
# #
# History Clean Commit
# #
- name: >-
📦 Commit Execute
id: task_history_clean_commit_execute
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: ${{ env.COMMIT_MESSAGE }}

View File

@@ -17,15 +17,15 @@
# secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/ # secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/
# secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/ # secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/
# secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token # secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token
# secrets.ORG_BINARYNINJA_TOKEN org github personal access token (fine-grained) # secrets.ORG_TOKEN org github personal access token (fine-grained)
# secrets.ORG_BINARYNINJA_TOKEN_CL org github personal access token (classic) # secrets.ORG_TOKEN_CL org github personal access token (classic)
# secrets.ORG_BINARYNINJA_DOCKERHUB_TOKEN org dockerhub secret # secrets.ORG_DOCKERHUB_TOKEN org dockerhub secret
# secrets.ORG_BINARYNINJA_GITEA_TOKEN org gitea personal access token (classic) with package:write permission # secrets.ORG_GITEA_TOKEN org gitea personal access token (classic) with package:write permission
# secrets.BINARYSERV_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK # secrets.BOT_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
# secrets.BINARYSERV_GPG_PASSPHRASE bot gpg private key passphrase # secrets.BOT_GPG_PASSPHRASE bot gpg private key passphrase
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_RELEASES discord webhook to report release notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_RELEASES discord webhook to report release notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKFLOWS discord webhook to report workflow notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_WORKFLOWS discord webhook to report workflow notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_UPDATES discord webhook to report activity notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_UPDATES discord webhook to report activity notifications from github to discord
# #
# @local these workflows can be tested locally through the use of `act` # @local these workflows can be tested locally through the use of `act`
# https://github.com/nektos/act # https://github.com/nektos/act
@@ -37,8 +37,8 @@
# act -W .github/workflows/issues-new.yml -s TOKEN_CL=XXXXXXXXXX --pull=false # act -W .github/workflows/issues-new.yml -s TOKEN_CL=XXXXXXXXXX --pull=false
# # # #
name: "🎫 Issue New" name: '🎫 Issue New'
run-name: "🎫 Issue New ${{ github.event.issue.number }}: ${{ github.event.issue.title }}" run-name: '🎫 Issue New ${{ github.event.issue.number }}: ${{ github.event.issue.title }}'
# # # #
# triggers # triggers
@@ -65,14 +65,14 @@ env:
PREFIX_INTERNAL: "Internal" PREFIX_INTERNAL: "Internal"
PREFIX_URGENT: "⚠ Urgent" PREFIX_URGENT: "⚠ Urgent"
LABEL_BUG: "Type Bug" LABEL_BUG: "Type Bug"
LABEL_DEPENDENCY: "Type Dependency" LABEL_DEPENDENCY: "Type Dependency"
LABEL_DOCS: "Type Docs" LABEL_DOCS: "Type Docs"
LABEL_FEATURE: "Type Feature" LABEL_FEATURE: "Type Feature"
LABEL_GIT: "Type Git Action" LABEL_GIT: "Type Git Action"
LABEL_PR: "Type Pull Request" LABEL_PR: "Type Pull Request"
LABEL_ROADMAP: "Type Roadmap" LABEL_ROADMAP: "Type Roadmap"
LABEL_INTERNAL: "Type Internal" LABEL_INTERNAL: "Type Internal"
LABEL_URGENT: "⚠ Urgent" LABEL_URGENT: "⚠ Urgent"
ASSIGN_USER: Aetherinox ASSIGN_USER: Aetherinox
@@ -91,32 +91,32 @@ env:
{ "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, { "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
{ "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, { "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
{ "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, { "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, { "name": "Status Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, { "name": "Status Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, { "name": "Status Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, { "name": "Status Denied", "color": "ba4058", "description": "Pull request has been denied" },
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, { "name": "Status Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, { "name": "Status Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, { "name": "Status No Action", "color": "030406", "description": "Closed without any action being taken" },
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, { "name": "Status Pending", "color": "984b12", "description": "Pending pull request" },
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, { "name": "Status Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, { "name": "Status Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, { "name": "Status Review", "color": "9e1451", "description": "Currently pending review" },
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, { "name": "Status Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
{ "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" }, { "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" },
{ "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" }, { "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" },
{ "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, { "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
{ "name": "Type Feature", "color": "3c4e93", "description": "Feature request" }, { "name": "Type Feature", "color": "3c4e93", "description": "Feature request" },
{ "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" }, { "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" },
{ "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" }, { "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" },
{ "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, { "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
{ "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, { "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
{ "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, { "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
{ "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" }, { "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" },
{ "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, { "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
{ "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, { "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
{ "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" }, { "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" },
{ "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" }, { "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" },
{ "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, { "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
{ "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, { "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
{ "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, { "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
@@ -159,10 +159,25 @@ jobs:
job-labels-create: job-labels-create:
name: >- name: >-
🎫 Labels Verify Existing 🎫 Labels Verify Existing
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 4 runs-on: apollo-x64
timeout-minutes: 5
steps: steps:
# #
# Ping Developer Set TImestamps
# #
- name: >-
🕛 Get Timestamp
id: task_label_set_timestamp
run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
# # # #
# [ Create Labels ] Start # [ Create Labels ] Start
# # # #
@@ -193,7 +208,7 @@ jobs:
id: task_label_create_verify id: task_label_create_verify
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const labels = JSON.parse( process.env.LABELS_JSON ); const labels = JSON.parse( process.env.LABELS_JSON );
for ( const label of labels ) for ( const label of labels )
@@ -205,7 +220,7 @@ jobs:
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
name: label.name, name: label.name,
description: label.description || '', description: label.description || 'No Description',
color: label.color color: label.color
}); });
} }
@@ -231,8 +246,9 @@ jobs:
🏷️ Labels Assign 🏷️ Labels Assign
needs: needs:
- job-labels-create - job-labels-create
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 4 runs-on: apollo-x64
timeout-minutes: 5
permissions: permissions:
contents: 'read' contents: 'read'
id-token: 'write' id-token: 'write'
@@ -241,6 +257,8 @@ jobs:
# # # #
# Assign > Get Issue Title # Assign > Get Issue Title
#
# this step simply gets the title set for a new issue created by a user and sets it as an env
# # # #
- name: >- - name: >-
@@ -248,7 +266,7 @@ jobs:
uses: actions/github-script@v7 uses: actions/github-script@v7
id: task_get_title id: task_get_title
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
let iss_title = `${ context.payload.issue.title }`; let iss_title = `${ context.payload.issue.title }`;
@@ -260,7 +278,22 @@ jobs:
# # # #
# Labels > Bugs # Labels > Bugs
# #
# Title of issue is carried over from the previous step. # this step scans the title and body of a new submitted issue for certain keywords.
# this step will then re-name the issue with a title which contains the type of issue at the front
# with whatever label is set for PREFIX_BUG; title change will look like the following:
# - EuropaServ changed the title
# [-] test issue found bug here
# [+] 🐛 Bug: test issue found bug here
# - EuropaServ added [ Type Bug ]
#
# There are four main types of tickets:
# 1. 🐛 Bug
# 2. 💡 Feature
# 3. 🗺️ Roadmap
# 4. ⚠ Urgent
#
# If the ticket is classified as a "Bug", the title of the ticket will be re-named to include `🐛 Bug` at the beginning
# of the issue name, but then that title will be carried over to the next type of ticket to check the title again.
# # # #
- name: >- - name: >-
@@ -268,9 +301,8 @@ jobs:
uses: actions/github-script@v7 uses: actions/github-script@v7
id: task_issues_bugs id: task_issues_bugs
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const issueLabels = await github.rest.issues.listLabelsOnIssue( const issueLabels = await github.rest.issues.listLabelsOnIssue(
{ {
owner: context.repo.owner, owner: context.repo.owner,
@@ -305,18 +337,16 @@ jobs:
/* /*
Bugs Bugs
*/
const words = [ "bug", "broke", "issue", "fail" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
/*
Find regex based phrases Find regex based phrases
Regex: Regex:
https://regex101.com/r/Z99Gnq/2 https://regex101.com/r/Z99Gnq/2
*/ */
const words = [ "bug", "broke", "issue", "fail", "wont work" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
const findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:problem\swith)|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm; const findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:problem\swith)|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm;
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) ); const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) ); const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
@@ -352,6 +382,7 @@ jobs:
console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) ) console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
// change LBL per category
add_labels.push( `${ bug_lbl }` ); add_labels.push( `${ bug_lbl }` );
console.log( `Adding Tag ....................... ${ bug_lbl }` ) console.log( `Adding Tag ....................... ${ bug_lbl }` )
@@ -360,15 +391,44 @@ jobs:
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` ) if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ iss_author }` ) core.info( `Skipping: Detected ${ iss_author }` )
// Rename title to contain Bug: if ( iss_author === `${{ env.BOT_NAME_RENOVATE }}` )
// Make sure issue / pr title doesnt already contain a beginning title tag core.info( `Skipping: Detected ${ iss_author }` )
/*
Rename title to contain Bug:
if bug title or body contains keyword hinting at the issue being about a bug; change the title of the issue
Make sure issue / pr title doesnt already contain a beginning title tag
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{ {
console.log( "Renaming Title" ) console.log( "Renaming Title" )
console.log( `Old Title: .................. ${ iss_title }` ) console.log( `Old Title: .................. ${ iss_title }` )
const title = context.payload.issue.title /*
If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
original: Bug: CMD windows opens then closes, can't run the script.
new: 🐛 Bug: CMD windows opens then closes, can't run the script.
*/
const removeBeginning1 = bug_tag.substring(3); // "Bug:"
let removeBeginning2 = bug_tag.substring(0); // "🐛 Bug:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Bug:"
if ( iss_title.startsWith(removeBeginning1) ) // "Bug:"
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
}
else if ( iss_title.startsWith(removeBeginning2) ) // "🐛Bug:"
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = iss_title;
let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' ); let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' );
@@ -390,7 +450,22 @@ jobs:
# # # #
# Labels > Features # Labels > Features
# #
# Title of issue is carried over from the previous step. # this step scans the title and body of a new submitted issue for certain keywords.
# this step will then re-name the issue with a title which contains the type of issue at the front
# with whatever label is set for PREFIX_FEATURE; title change will look like the following:
# - EuropaServ changed the title
# [-] Add ability to create X
# [+] 💡 Feature: Add ability to create X
# - EuropaServ added [ Type Feature ]
#
# There are four main types of tickets:
# 1. 🐛 Bug
# 2. 💡 Feature
# 3. 🗺️ Roadmap
# 4. ⚠ Urgent
#
# If the ticket is classified as a "Feature", the title of the ticket will be re-named to include `💡 Feature` at the beginning
# of the issue name, but then that title will be carried over to the next type of ticket to check the title again.
# # # #
- name: >- - name: >-
@@ -398,9 +473,8 @@ jobs:
uses: actions/github-script@v7 uses: actions/github-script@v7
id: task_issues_features id: task_issues_features
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const issueLabels = await github.rest.issues.listLabelsOnIssue( const issueLabels = await github.rest.issues.listLabelsOnIssue(
{ {
owner: context.repo.owner, owner: context.repo.owner,
@@ -412,6 +486,7 @@ jobs:
let iss_title = `${{ steps.task_issues_bugs.outputs.issue_title }}` || `${ context.payload.issue.title }`; let iss_title = `${{ steps.task_issues_bugs.outputs.issue_title }}` || `${ context.payload.issue.title }`;
let iss_body = `${ context.payload.issue.body }`; let iss_body = `${ context.payload.issue.body }`;
let iss_author = `${ context.payload.issue.user.login }`;
const iss_title_lc = iss_title.toLowerCase( ); const iss_title_lc = iss_title.toLowerCase( );
@@ -434,19 +509,16 @@ jobs:
/* /*
Features Features
*/
const words = [ "feature", "request", "add support" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
/*
Find regex based phrases Find regex based phrases
Regex: Regex:
https://regex101.com/r/fR1Hm6/1 https://regex101.com/r/fR1Hm6/3
*/ */
const findWordList = /^(?:(?:request|include|see)\s*(?:an?|the?)\s*(?:feature|addon|addition|plugin))|(?:(?:add|see|get)\s*support\s*(?:for|with|of))|(?:can\s*we\s*get\s*(?:the|a)\s*(?:ability|feature))|(?:💡 Feature:)$/igm; const words = [ "feature", "request", "add", "addition", "enhance", "create" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
const findWordList = /(?:(?:add|enjoy|would|like|can|request|include|see|could|have)\s*?(?:the|liked?|i|we|an?|the|you?)\s*?(?:to|have|an|get|ability|request|add|feature|functionality|addon|addition|plugin|create))|(?:(?:add|see|get)\s*?support\s*?(?:for|with|of))|(?:can\s*we\s*get\s*?(?:the|a)\s*?(?:ability|feature))|(?:💡 Feature:)$/igm;
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) ); const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) ); const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
@@ -491,15 +563,44 @@ jobs:
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` ) if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ iss_author }` ) core.info( `Skipping: Detected ${ iss_author }` )
// Rename title to contain Feature: if ( iss_author === `${{ env.BOT_NAME_RENOVATE }}` )
// Make sure issue / pr title doesnt already contain a beginning title tag core.info( `Skipping: Detected ${ iss_author }` )
/*
Rename title to contain Feature:
if feature title or body contains keyword hinting at the issue being about a feature; change the title of the issue
Make sure issue / pr title doesnt already contain a beginning title tag
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{ {
console.log( "Renaming Title" ) console.log( "Renaming Title" )
console.log( `Old Title: .................. ${ iss_title }` ) console.log( `Old Title: .................. ${ iss_title }` )
const title = context.payload.issue.title /*
If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
original: Feature: CMD windows opens then closes, can't run the script.
new: 💡 Feature: CMD windows opens then closes, can't run the script.
*/
const removeBeginning1 = feat_tag.substring(3); // "Feature:"
let removeBeginning2 = feat_tag.substring(0); // "💡 Feature:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Feature:"
if ( iss_title.startsWith(removeBeginning1) ) // "Feature:"
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
}
else if ( iss_title.startsWith(removeBeginning2) ) // "💡 Feature:"
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = iss_title;
let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' ); let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' );
@@ -520,17 +621,32 @@ jobs:
console.log( "\n\n" ) console.log( "\n\n" )
# # # #
# Labels > Urgent # Labels > Roadmap
# #
# Title of issue is carried over from the previous step. # this step scans the title and body of a new submitted issue for certain keywords.
# this step will then re-name the issue with a title which contains the type of issue at the front
# with whatever label is set for PREFIX_ROADMAP; title change will look like the following:
# - EuropaServ changed the title
# [-] roadmap new feature
# [+] 🗺️ Roadmap: new feature
# - EuropaServ added [ Type Roadmap ]
#
# There are four main types of tickets:
# 1. 🐛 Bug
# 2. 💡 Feature
# 3. 🗺️ Roadmap
# 4. ⚠ Urgent
#
# If the ticket is classified as a "Roadmap", the title of the ticket will be re-named to include `🗺️ Roadmap` at the beginning
# of the issue name, but then that title will be carried over to the next type of ticket to check the title again.
# # # #
- name: >- - name: >-
🏷️ ${{ env.PREFIX_URGENT }} Assignment 🏷️ ${{ env.PREFIX_ROADMAP }} Assignment
uses: actions/github-script@v7 uses: actions/github-script@v7
id: task_issues_urgent id: task_issues_roadmap
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const issueLabels = await github.rest.issues.listLabelsOnIssue( const issueLabels = await github.rest.issues.listLabelsOnIssue(
{ {
@@ -543,11 +659,191 @@ jobs:
let iss_title = `${{ steps.task_issues_features.outputs.issue_title }}` || `${ context.payload.issue.title }`; let iss_title = `${{ steps.task_issues_features.outputs.issue_title }}` || `${ context.payload.issue.title }`;
let iss_body = `${ context.payload.issue.body }`; let iss_body = `${ context.payload.issue.body }`;
let iss_author = `${ context.payload.issue.user.login }`;
const iss_title_lc = iss_title.toLowerCase( );
console.log( "Road Title .................... " + iss_title )
console.log( "Road Output ................... " + `${{ steps.task_issues_features.outputs.issue_title }}` )
console.log( "Road Payload .................. " + `${ context.payload.issue.title }` )
/*
Tags
*/
const bug_tag = `${{ env.PREFIX_BUG }}:`;
const bug_lbl = `${{ env.LABEL_BUG }}`;
const feat_tag = `${{ env.PREFIX_FEATURE }}:`;
const feat_lbl = `${{ env.LABEL_FEATURE }}`;
const urgn_tag = `${{ env.PREFIX_URGENT }}:`;
const urgn_lbl = `${{ env.LABEL_URGENT }}`;
const road_tag = `${{ env.PREFIX_ROADMAP }}:`;
const road_lbl = `${{ env.LABEL_ROADMAP }}`;
/*
Roadmap
Find regex based phrases
Roadmap requires headers #Summary and #Proposal | #Objective
Regex:
https://regex101.com/r/ucajBZ/1
*/
const words = [ "roadmap", "road map", "planned" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
const findWordList = /#\s*Summary[\S\s]+#\s*(?:Proposal|Objective)[^\]]+/igm;
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
/*
Do not change a title if the item starts with a PR: #
Regex:
https://regex101.com/r/JOrqbN/1
*/
const road_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
const road_bFoundPRTitle = Boolean( road_findPRTitle.test( iss_title ) );
console.log( "Title Lowercase ............... " + iss_title_lc )
console.log( "Startswith " + road_tag.toLowerCase( ) + "............ " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle )
console.log( "Title Includes Regex .......... " + bFoundMatchTitle )
console.log( "Body Includes Regex ........... " + bFoundMatchBody )
console.log( "\n" )
/*
- Check if issue title matches the issue label "Roadmap:"
- Check if title contains word in words
*/
// change TAG per category
if ( iss_title_lc.startsWith( road_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody )
{
console.log( "⚠️ " + road_tag + " ---------------------------------------" )
console.log( "Already starts with " + bug_tag + " ......... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) )
console.log( "Already starts with " + feat_tag + " ..... " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) )
console.log( "Already starts with " + urgn_tag + " ...... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
console.log( "Already starts with " + road_tag + " ..... " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
// change LBL per category
add_labels.push( `${ road_lbl }` );
console.log( `Adding Tag ....................... ${ road_lbl }` )
console.log( "\n" )
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ iss_author }` )
if ( iss_author === `${{ env.BOT_NAME_RENOVATE }}` )
core.info( `Skipping: Detected ${ iss_author }` )
/*
Rename title to contain Roadmap:
if roadmap title or body contains keyword hinting at the issue being about roadmap; change the title of the issue
Make sure issue / pr title doesnt already contain a beginning title tag
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{
console.log( "Renaming Title" )
console.log( `Old Title: .................. ${ iss_title }` )
/*
If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
original: Roadmap: CMD windows opens then closes, can't run the script.
new: 🗺️ Roadmap: CMD windows opens then closes, can't run the script.
*/
const removeBeginning1 = road_tag.substring(3); // "Roadmap:"
let removeBeginning2 = road_tag.substring(0); // "🗺️ Roadmap:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Roadmap:"
if ( iss_title.startsWith(removeBeginning1) ) // "Roadmap:"
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
}
else if ( iss_title.startsWith(removeBeginning2) ) // "🗺️ Roadmap:"
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = iss_title;
let title_new = title.replace( /^\s?broad(.*?)\s?map\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?planned\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?broadmap\s*(.*?)\b/gi, '' );
iss_title = `${ road_tag } ${ title_new }`; // change TAG per category
}
console.log( `New Title: .................... ${ iss_title }` )
await github.rest.issues.update(
{
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
title: `${ iss_title }`, labels: add_labels
} );
}
core.setOutput( 'issue_title', iss_title )
console.log( "\n\n" )
# #
# Labels > Urgent
#
# this step scans the title and body of a new submitted issue for certain keywords.
# this step will then re-name the issue with a title which contains the type of issue at the front
# with whatever label is set for PREFIX_URGENT; title change will look like the following:
# - EuropaServ changed the title
# [-] emergency
# [+] ⚠ Urgent: emergency
# - EuropaServ added [ Type Urgent ]
#
# this tag can be mixed with 🐛 Bug so that a ticket gets both tags
# - EuropaServ changed the title
# [-] emergency
# [+] 🐛 Bug: urgent issue
# - EuropaServ added [ Type Bug ], [ Type Urgent ]
#
# There are four main types of tickets:
# 1. 🐛 Bug
# 2. 💡 Feature
# 3. 🗺️ Roadmap
# 4. ⚠ Urgent
#
# If the ticket is classified as a "Urgent", the title of the ticket will be re-named to include `⚠ Urgent` at the beginning
# of the issue name, but then that title will be carried over to the next type of ticket to check the title again.
# #
- name: >-
🏷️ ${{ env.PREFIX_URGENT }} Assignment
uses: actions/github-script@v7
id: task_issues_urgent
with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: |
const issueLabels = await github.rest.issues.listLabelsOnIssue(
{
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
let add_labels = issueLabels.data.map( label => label.name );
let iss_title = `${{ steps.task_issues_urgent.outputs.issue_title }}` || `${ context.payload.issue.title }`;
let iss_body = `${ context.payload.issue.body }`;
let iss_author = `${ context.payload.issue.user.login }`;
const iss_title_lc = iss_title.toLowerCase( ); const iss_title_lc = iss_title.toLowerCase( );
console.log( "Urgn Title .................... " + iss_title ) console.log( "Urgn Title .................... " + iss_title )
console.log( "Urgn Output ................... " + `${{ steps.task_issues_features.outputs.issue_title }}` ) console.log( "Urgn Output ................... " + `${{ steps.task_issues_urgent.outputs.issue_title }}` )
console.log( "Urgn Payload .................. " + `${ context.payload.issue.title }` ) console.log( "Urgn Payload .................. " + `${ context.payload.issue.title }` )
/* /*
@@ -565,18 +861,15 @@ jobs:
/* /*
Urgent Urgent
*/
const words = [ "urgent", "urgency", "emergency", "important", "critical" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
/*
Find regex based phrases Find regex based phrases
Regex: Regex:
https://regex101.com/r/eE9tJX/2 https://regex101.com/r/eE9tJX/2
*/ */
const words = [ "urgent", "urgency", "emergency", "important", "critical" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
const findWordList = /(?:(?:this)?is\s*a?n?\s*?(?:emergency|urgent|important|vital|acute|crucial|grave|pressing|serious|top.?priority|high.?priority))|(?:reply|respond|answer|write|address)\s*(?:immediate|quick|asap|urgent|now|fast|(?:as)?\s*(?:soon|quick|immediate|fast))(?:ly)?|(?:need\s*(?:help|support|fixed|answer|reply|response)!)|(?:emergency|critical|urgen(?:t|cy)|high.?priority)/igm; const findWordList = /(?:(?:this)?is\s*a?n?\s*?(?:emergency|urgent|important|vital|acute|crucial|grave|pressing|serious|top.?priority|high.?priority))|(?:reply|respond|answer|write|address)\s*(?:immediate|quick|asap|urgent|now|fast|(?:as)?\s*(?:soon|quick|immediate|fast))(?:ly)?|(?:need\s*(?:help|support|fixed|answer|reply|response)!)|(?:emergency|critical|urgen(?:t|cy)|high.?priority)/igm;
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) ); const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) ); const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
@@ -622,21 +915,50 @@ jobs:
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` ) if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ iss_author }` ) core.info( `Skipping: Detected ${ iss_author }` )
// Rename title to contain Urgent: if ( iss_author === `${{ env.BOT_NAME_RENOVATE }}` )
// Make sure issue / pr title doesnt already contain a beginning title tag core.info( `Skipping: Detected ${ iss_author }` )
/*
Rename title to contain Urgent:
if urgent title or body contains keyword hinting at the issue being about urgent; change the title of the issue
Make sure issue / pr title doesnt already contain a beginning title tag
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{ {
console.log( "Renaming Title" ) console.log( "Renaming Title" )
console.log( `Old Title: .................. ${ iss_title }` ) console.log( `Old Title: .................. ${ iss_title }` )
const title = context.payload.issue.title /*
If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
original: Urgent: CMD windows opens then closes, can't run the script.
new: ⚠ Urgent: CMD windows opens then closes, can't run the script.
*/
const removeBeginning1 = urgn_tag.substring(3); // "Urgent:"
let removeBeginning2 = urgn_tag.substring(0); // "⚠ Urgent:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Urgent:"
if ( iss_title.startsWith(removeBeginning1) ) // "Urgent:"
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
}
else if ( iss_title.startsWith(removeBeginning2) ) // "⚠ Urgent:"
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = context.payload.issue.title;
let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' ); let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' );
iss_title = `${ urgn_tag } ${ title_new }`; // change TAG per category iss_title = `${ urgn_tag } ${ title_new }`;
} }
console.log( `New Title: ...................... ${ iss_title }` ) console.log( `New Title: ...................... ${ iss_title }` )
@@ -651,137 +973,6 @@ jobs:
core.setOutput( 'issue_title', iss_title ) core.setOutput( 'issue_title', iss_title )
console.log( "\n\n" ) console.log( "\n\n" )
# #
# Labels > Roadmap
#
# Title of issue is carried over from the previous step.
# #
- name: >-
🏷️ ${{ env.PREFIX_ROADMAP }} Assignment
uses: actions/github-script@v7
id: task_issues_roadmap
with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
script: |
const issueLabels = await github.rest.issues.listLabelsOnIssue(
{
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number
});
let add_labels = issueLabels.data.map( label => label.name );
let iss_title = `${{ steps.task_issues_urgent.outputs.issue_title }}` || `${ context.payload.issue.title }`;
let iss_body = `${ context.payload.issue.body }`;
const iss_title_lc = iss_title.toLowerCase( );
console.log( "Road Title .................... " + iss_title )
console.log( "Road Output ................... " + `${{ steps.task_issues_urgent.outputs.issue_title }}` )
console.log( "Road Payload .................. " + `${ context.payload.issue.title }` )
/*
Tags
*/
const bug_tag = `${{ env.PREFIX_BUG }}:`;
const bug_lbl = `${{ env.LABEL_BUG }}`;
const feat_tag = `${{ env.PREFIX_FEATURE }}:`;
const feat_lbl = `${{ env.LABEL_FEATURE }}`;
const urgn_tag = `${{ env.PREFIX_URGENT }}:`;
const urgn_lbl = `${{ env.LABEL_URGENT }}`;
const road_tag = `${{ env.PREFIX_ROADMAP }}:`;
const road_lbl = `${{ env.LABEL_ROADMAP }}`;
/*
Roadmap
*/
const words = [ "roadmap", "road map", "planned" ];
const bTriggerWordInTitle = words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
/*
Find regex based phrases
Roadmap requires headers #Summary and #Proposal | #Objective
Regex:
https://regex101.com/r/ucajBZ/1
*/
const findWordList = /#\s*Summary[\S\s]+#\s*(?:Proposal|Objective)[^\]]+/igm;
const bFoundMatchTitle = Boolean( findWordList.test( iss_title ) );
const bFoundMatchBody = Boolean( findWordList.test( iss_body ) );
/*
Do not change a title if the item starts with a PR: #
Regex:
https://regex101.com/r/JOrqbN/1
*/
const road_findPRTitle = /^PR\s?#?(?:[0-9]*:)/igm;
const road_bFoundPRTitle = Boolean( road_findPRTitle.test( iss_title ) );
console.log( "Title Lowercase ............... " + iss_title_lc )
console.log( "Startswith " + road_tag.toLowerCase( ) + "............ " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
console.log( "Title Includes Keyword ........ " + bTriggerWordInTitle )
console.log( "Title Includes Regex .......... " + bFoundMatchTitle )
console.log( "Body Includes Regex ........... " + bFoundMatchBody )
console.log( "\n" )
/*
- Check if issue title matches the issue label "Roadmap:"
- Check if title contains word in words
*/
// change TAG per category
if ( iss_title_lc.startsWith( road_tag.toLowerCase( ) ) || bTriggerWordInTitle || bFoundMatchTitle || bFoundMatchBody )
{
console.log( "⚠️ " + road_tag + " ---------------------------------------" )
console.log( "Already starts with " + bug_tag + " ...... " + iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) )
console.log( "Already starts with " + feat_tag + " .. " + iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) )
console.log( "Already starts with " + urgn_tag + " ... " + iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) )
console.log( "Already starts with " + road_tag + " .. " + iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
// change LBL per category
add_labels.push( `${ road_lbl }` );
console.log( `Adding Tag .................... ${ road_lbl }` )
console.log( "\n" )
if ( iss_author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ iss_author }` )
// Rename title to contain Roadmap:
// Make sure issue / pr title doesnt already contain a beginning title tag
if ( iss_author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{
console.log( "Renaming Title" )
console.log( `Old Title: .................. ${ iss_title }` )
const title = context.payload.issue.title
let title_new = title.replace( /^\s?broad(.*?)\s?map\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?planned\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?broadmap\s*(.*?)\b/gi, '' );
iss_title = `${ road_tag } ${ title_new }`; // change TAG per category
}
console.log( `New Title: .................... ${ iss_title }` )
await github.rest.issues.update(
{
owner: context.repo.owner, repo: context.repo.repo, issue_number: context.issue.number,
title: `${ iss_title }`, labels: add_labels
} );
}
core.setOutput( 'issue_title', iss_title )
console.log( "\n\n" )
# # # #
# Job > Phrase Search # Job > Phrase Search
# #
@@ -793,8 +984,9 @@ jobs:
🏷️ Labels Phrase Search 🏷️ Labels Phrase Search
needs: needs:
- job-labels-create - job-labels-create
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 4 runs-on: apollo-x64
timeout-minutes: 5
permissions: permissions:
contents: 'read' contents: 'read'
id-token: 'write' id-token: 'write'
@@ -802,7 +994,7 @@ jobs:
steps: steps:
# # # #
# [ Search Phrase ] Checkout # Phrase Search > Checkout
# # # #
- name: >- - name: >-
@@ -813,14 +1005,16 @@ jobs:
fetch-depth: 0 fetch-depth: 0
# # # #
# [ Search Phrase ] Search # Phrase Search > Search
#
# if an issue message contains certain keywords; have our bot reply with helpful links and information.
# # # #
- name: >- - name: >-
👄 Search Phrases 👄 Search Phrases
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const fs = require( 'fs' ); const fs = require( 'fs' );
const iss_title = `${ context.payload.issue.title }`; const iss_title = `${ context.payload.issue.title }`;
@@ -879,8 +1073,9 @@ jobs:
job-assign-assignees: job-assign-assignees:
name: >- name: >-
✍️ Issue Assignees ✍️ Issue Assignees
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 4 runs-on: apollo-x64
timeout-minutes: 5
needs: [ job-assign-labels ] needs: [ job-assign-labels ]
# disable # disable
# #
@@ -901,7 +1096,7 @@ jobs:
✍️ Set Assignees ✍️ Set Assignees
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const assignees = [ `${{ github.repository_owner }}` ]; const assignees = [ `${{ github.repository_owner }}` ];

View File

@@ -2,7 +2,7 @@
# @type github workflow # @type github workflow
# @author Aetherinox # @author Aetherinox
# @url https://github.com/Aetherinox # @url https://github.com/Aetherinox
# @desc pull request auto-scan # @usage pull request auto-scan
# scans all of the files related to a particular pull request # scans all of the files related to a particular pull request
# if the code in the files being submitted contains code that is forbidden, # if the code in the files being submitted contains code that is forbidden,
# a report is generated and posted as a comment in the PR. # a report is generated and posted as a comment in the PR.
@@ -16,15 +16,15 @@
# secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/ # secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/
# secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/ # secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/
# secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token # secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token
# secrets.ORG_BINARYNINJA_TOKEN org github personal access token (fine-grained) # secrets.ORG_TOKEN org github personal access token (fine-grained)
# secrets.ORG_BINARYNINJA_TOKEN_CL org github personal access token (classic) # secrets.ORG_TOKEN_CL org github personal access token (classic)
# secrets.ORG_BINARYNINJA_DOCKERHUB_TOKEN org dockerhub secret # secrets.ORG_DOCKERHUB_TOKEN org dockerhub secret
# secrets.ORG_BINARYNINJA_GITEA_TOKEN org gitea personal access token (classic) with package:write permission # secrets.ORG_GITEA_TOKEN org gitea personal access token (classic) with package:write permission
# secrets.BINARYSERV_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK # secrets.BOT_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
# secrets.BINARYSERV_GPG_PASSPHRASE bot gpg private key passphrase # secrets.BOT_GPG_PASSPHRASE bot gpg private key passphrase
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_RELEASES discord webhook to report release notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_RELEASES discord webhook to report release notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKFLOWS discord webhook to report workflow notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_WORKFLOWS discord webhook to report workflow notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_UPDATES discord webhook to report activity notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_UPDATES discord webhook to report activity notifications from github to discord
# #
# @local these workflows can be tested locally through the use of `act` # @local these workflows can be tested locally through the use of `act`
# https://github.com/nektos/act # https://github.com/nektos/act
@@ -36,8 +36,8 @@
# act -W .github/workflows/issues-scan.yml -s TOKEN_CL=XXXXXXXXXX --pull=false # act -W .github/workflows/issues-scan.yml -s TOKEN_CL=XXXXXXXXXX --pull=false
# # # #
name: "🎫 Issues Scan" name: '🎫 PR Scan'
run-name: "🎫 Issues Scan" run-name: '🎫 PR Scan'
# # # #
# triggers # triggers
@@ -61,9 +61,9 @@ env:
LABEL_CHECK_SECURITY_ERR: AC Security Warning LABEL_CHECK_SECURITY_ERR: AC Security Warning
LABEL_CHECK_STATUS_CHGMADE: AC Changes Made LABEL_CHECK_STATUS_CHGMADE: AC Changes Made
LABEL_CHECK_SCAN_SKIPPED: AC Skipped Scan LABEL_CHECK_SCAN_SKIPPED: AC Skipped Scan
LABEL_TYPE_PR: Type Pull Request LABEL_TYPE_PR: Type Pull Request
LABEL_TYPE_DEPENDENCY: Type Dependency LABEL_TYPE_DEPENDENCY: Type Dependency
LABEL_TYPE_GITACTION: Type Git Action LABEL_TYPE_GITACTION: Type Git Action
ASSIGN_USER: Aetherinox ASSIGN_USER: Aetherinox
BOT_NAME_1: EuropaServ BOT_NAME_1: EuropaServ
@@ -81,32 +81,32 @@ env:
{ "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, { "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
{ "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, { "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
{ "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, { "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, { "name": "Status Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, { "name": "Status Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, { "name": "Status Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, { "name": "Status Denied", "color": "ba4058", "description": "Pull request has been denied" },
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by EuropaServ for a prolonged period of inactivity" }, { "name": "Status Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, { "name": "Status Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, { "name": "Status No Action", "color": "030406", "description": "Closed without any action being taken" },
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, { "name": "Status Pending", "color": "984b12", "description": "Pending pull request" },
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, { "name": "Status Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, { "name": "Status Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, { "name": "Status Review", "color": "9e1451", "description": "Currently pending review" },
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, { "name": "Status Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
{ "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" }, { "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" },
{ "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" }, { "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" },
{ "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, { "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
{ "name": "Type Feature", "color": "3c4e93", "description": "Feature request" }, { "name": "Type Feature", "color": "3c4e93", "description": "Feature request" },
{ "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" }, { "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" },
{ "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" }, { "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" },
{ "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, { "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
{ "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, { "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
{ "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, { "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
{ "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" }, { "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" },
{ "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, { "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
{ "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, { "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
{ "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" }, { "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" },
{ "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" }, { "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" },
{ "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, { "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
{ "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, { "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
{ "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, { "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
@@ -140,14 +140,17 @@ env:
jobs: jobs:
# # # #
# Job Autoscan # Job PR Scan
#
# automatically scan a pull request once it is submitted
# # # #
job-pr-autoscan: job-pr-scan:
name: 🎫 Issues Autoscan name: >-
🎫 Issues Autoscan
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
runs-on: apollo-x64 runs-on: apollo-x64
timeout-minutes: 7 timeout-minutes: 5
permissions: permissions:
contents: read contents: read
actions: read actions: read
@@ -156,27 +159,32 @@ jobs:
steps: steps:
# # # #
# Cleanup Set Env Variables # PR Scan Set Env Variables
# # # #
- name: >- - name: >-
🕛 Get Timestamp 🕛 Get Timestamp
id: task_autocheck_set_timestamp id: task_prscan_set_timestamp
run: | run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
# # # #
# PR Scan Labels Verify Existing
#
# check if repo has all of the needed issue / pr labels; create label if not exists
#
# action needed if using 'pull_request' and 'issue_comment' # action needed if using 'pull_request' and 'issue_comment'
# to get the pull request, you would normally use ${{ github.event.number }} # to get the pull request, you would normally use ${{ github.event.number }}
# however this isnt available for 'issue_comment' # however this isnt available for 'issue_comment'
# # # #
- name: >- - name: >-
🏷️ Verify Existing Labels 🎫 Labels Verify Existing
id: task_autocheck_labels_verify id: task_prscan_labels_verify
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
@@ -191,7 +199,7 @@ jobs:
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
name: label.name, name: label.name,
description: label.description || '', description: label.description || 'No Description',
color: label.color color: label.color
}); });
} }
@@ -209,13 +217,15 @@ jobs:
} }
# # # #
# set issue number # PR Scan Assign Pull Request ID to variable
#
# get id (number) for pr when submitted
# # # #
- name: >- - name: >-
#️⃣ Issue number Set #️⃣ Pull-Request ID Set
id: task_prscan_issue_num_set
uses: actions/github-script@v7 uses: actions/github-script@v7
id: task_autocheck_issue_num_set
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
@@ -244,41 +254,43 @@ jobs:
result-encoding: string result-encoding: string
# # # #
# print issue number # PR Scan Pull-Request ID Print
#
# prints the pr number detected
# # # #
- name: >- - name: >-
#️⃣ Issue number Print #️⃣ Pull-Request ID Print
id: task_autocheck_issue_num_get id: task_prscan_issue_num_get
run: | run: |
echo '${{ steps.task_autocheck_issue_num_set.outputs.result }}' echo '${{ steps.task_prscan_issue_num_set.outputs.result }}'
# # # #
# checkout # PR Scan Checkout
# # # #
- name: >- - name: >-
☑️ Checkout ☑️ Checkout
id: task_autoscan_checkout id: task_prscan_checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
if: | if: |
( github.event_name == 'pull_request_target' ) || ( github.event_name == 'pull_request' ) || ( github.event_name == 'issue_comment' && contains( github.event.comment.html_url, '/pull/' ) && contains( github.event.comment.body, '/rescan' ) ) ( github.event_name == 'pull_request_target' ) || ( github.event_name == 'pull_request' ) || ( github.event_name == 'issue_comment' && contains( github.event.comment.html_url, '/pull/' ) && contains( github.event.comment.body, '/rescan' ) )
with: with:
token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }} token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
fetch-depth: 0 fetch-depth: 0
ref: "refs/pull/${{ steps.task_autocheck_issue_num_set.outputs.result }}/merge" ref: "refs/pull/${{ steps.task_prscan_issue_num_set.outputs.result }}/merge"
# # # #
# nodejs # PR Scan Setup NodeJS
# # # #
- name: >- - name: >-
⚙️ Setup Node ⚙️ Setup Node
id: task_autocheck_nodejs id: task_prscan_nodejs
uses: actions/setup-node@v4 uses: actions/setup-node@v4
# # # #
# get list of changed files # PR Scan Get List of Changed Files
# #
# Effortlessly track all changed files and directories relative to a target branch, # Effortlessly track all changed files and directories relative to a target branch,
# the current branch (preceding commit or the last remote commit), multiple branches, # the current branch (preceding commit or the last remote commit), multiple branches,
@@ -288,49 +300,49 @@ jobs:
- name: >- - name: >-
📄 Get changed files 📄 Get changed files
id: task_autocheck_changed_files_get id: task_prscan_changed_files_get
uses: tj-actions/changed-files@v46 uses: tj-actions/changed-files@v46
with: with:
separator: "," separator: ","
# # # #
# list of changed files # PR Scan List All Changed Files
# # # #
- name: >- - name: >-
📄 List all added files 📄 List all added files
id: task_autocheck_added_files_get id: task_prscan_added_files_get
run: | run: |
for file in ${CHANGED_FILES}; do for file in ${CHANGED_FILES}; do
echo "$file was changed" echo "$file was changed"
done done
env: env:
ADDED_FILES: ${{ steps.task_autocheck_changed_files_get.outputs.added_files }} ADDED_FILES: ${{ steps.task_prscan_changed_files_get.outputs.added_files }}
MODIFIED_FILES: ${{ steps.task_autocheck_changed_files_get.outputs.modified_files }} MODIFIED_FILES: ${{ steps.task_prscan_changed_files_get.outputs.modified_files }}
CHANGED_FILES: ${{ steps.task_autocheck_changed_files_get.outputs.all_changed_files }} CHANGED_FILES: ${{ steps.task_prscan_changed_files_get.outputs.all_changed_files }}
COUNT_ADDED: ${{ steps.task_autocheck_changed_files_get.outputs.added_files_count }} COUNT_ADDED: ${{ steps.task_prscan_changed_files_get.outputs.added_files_count }}
COUNT_MODIFIED: ${{ steps.task_autocheck_changed_files_get.outputs.modified_files_count }} COUNT_MODIFIED: ${{ steps.task_prscan_changed_files_get.outputs.modified_files_count }}
COUNT_DELETED: ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} COUNT_DELETED: ${{ steps.task_prscan_changed_files_get.outputs.deleted_files_count }}
COUNT_RENAMED: ${{ steps.task_autocheck_changed_files_get.outputs.renamed_files_count }} COUNT_RENAMED: ${{ steps.task_prscan_changed_files_get.outputs.renamed_files_count }}
COUNT_COPIED: ${{ steps.task_autocheck_changed_files_get.outputs.copied_files_count }} COUNT_COPIED: ${{ steps.task_prscan_changed_files_get.outputs.copied_files_count }}
# # # #
# List directories # PR Scan List Directories / File Structure
# # # #
- name: >- - name: >-
📂 List Directories 📂 List Directories
id: task_autocheck_dirs_list id: task_prscan_dirs_list
run: | run: |
ls ls
# # # #
# Run autocheck # PR Scan Autocheck
# # # #
- name: >- - name: >-
☑️ Run Autocheck ☑️ Run Autocheck
id: task_autocheck_run id: task_prscan_run
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL }}
@@ -358,8 +370,8 @@ jobs:
"number": 32 "number": 32
}, },
"repo": { "repo": {
"owner": "TheBinaryNinja", "owner": "Aetherinox",
"repo": "TVApp2" "repo": "TheRepoName"
}, },
"payload": { "payload": {
"action": "synchronize", "action": "synchronize",
@@ -368,19 +380,19 @@ jobs:
"number": 32, "number": 32,
"organization": { "organization": {
"avatar_url": "https://avatars.githubusercontent.com/u/200161462?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/200161462?v=4",
"events_url": "https://api.github.com/orgs/TheBinaryNinja/events", "events_url": "https://api.github.com/orgs/Aetherinox/events",
"hooks_url": "https://api.github.com/orgs/TheBinaryNinja/hooks", "hooks_url": "https://api.github.com/orgs/Aetherinox/hooks",
"issues_url": "https://api.github.com/orgs/TheBinaryNinja/issues", "issues_url": "https://api.github.com/orgs/Aetherinox/issues",
"login": "TheBinaryNinja", "login": "Aetherinox",
"members_url": "https://api.github.com/orgs/TheBinaryNinja/members{/member}", "members_url": "https://api.github.com/orgs/Aetherinox/members{/member}",
"public_members_url": "https://api.github.com/orgs/TheBinaryNinja/public_members{/member}", "public_members_url": "https://api.github.com/orgs/Aetherinox/public_members{/member}",
"repos_url": "https://api.github.com/orgs/TheBinaryNinja/repos", "repos_url": "https://api.github.com/orgs/Aetherinox/repos",
"url": "https://api.github.com/orgs/TheBinaryNinja" "url": "https://api.github.com/orgs/Aetherinox"
}, },
"pull_request": { "pull_request": {
"created_at": "2025-03-17T23:32:22Z", "created_at": "2025-03-17T23:32:22Z",
"updated_at": "2025-03-17T23:32:22Z", "updated_at": "2025-03-17T23:32:22Z",
"url": "https://api.github.com/repos/TheBinaryNinja/tvapp2", "url": "https://api.github.com/repos/Aetherinox/TheRepoName",
"title": "Test PR Workflow", "title": "Test PR Workflow",
"head": { "head": {
"ref": "main" "ref": "main"
@@ -399,16 +411,16 @@ jobs:
"id": 7821944832, "id": 7821944832,
"name": "AC Passed", "name": "AC Passed",
"node_id": "LA_kwDONW-GkM8AAAAB0jloAA", "node_id": "LA_kwDONW-GkM8AAAAB0jloAA",
"url": "https://api.github.com/repos/TheBinaryNinja/tvapp2/labels/AC%20%E2%80%BA%20Passed" "url": "https://api.github.com/repos/Aetherinox/TheRepoName/labels/AC%20%E2%80%BA%20Passed"
}, },
{ {
"color": "8F1784", "color": "8F1784",
"default": false, "default": false,
"description": "Normal pull request", "description": "Normal pull request",
"id": 7821944963, "id": 7821944963,
"name": "Type Pull Request", "name": "Type Pull Request",
"node_id": "LA_kwDONW-GkM8AAAAB0jlogw", "node_id": "LA_kwDONW-GkM8AAAAB0jlogw",
"url": "https://api.github.com/repos/TheBinaryNinja/tvapp2/labels/Type%20%E2%97%A6%20Pull%20Request" "url": "https://api.github.com/repos/Aetherinox/TheRepoName/labels/Type%20%E2%97%A6%20Pull%20Request"
} }
] ]
}, },
@@ -417,7 +429,7 @@ jobs:
"sha": "c938f7a21247f69b29cf352d0c6890a63f260d47", "sha": "c938f7a21247f69b29cf352d0c6890a63f260d47",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"workflow": "🎫 Issues Scan", "workflow": "🎫 Issues Scan",
"action": "task_autocheck_run", "action": "task_prscan_run",
"actor": "renovate[bot]", "actor": "renovate[bot]",
"job": "job-pr-autoscan", "job": "job-pr-autoscan",
"runNumber": 45, "runNumber": 45,
@@ -454,7 +466,7 @@ jobs:
labels.push(name); labels.push(name);
}); });
const files_List = `${{ steps.task_autocheck_changed_files_get.outputs.all_changed_files }}` || '' const files_List = `${{ steps.task_prscan_changed_files_get.outputs.all_changed_files }}` || ''
const files_Array = files_List.split(',') const files_Array = files_List.split(',')
const branch_ref = `${ ct.payload.pull_request.head.ref }` const branch_ref = `${ ct.payload.pull_request.head.ref }`
@@ -507,11 +519,11 @@ jobs:
| Author | [ ` + ct.payload.pull_request.user.login + ` ](https://github.com/` + ct.repo.owner + `/) | | Author | [ ` + ct.payload.pull_request.user.login + ` ](https://github.com/` + ct.repo.owner + `/) |
| Repo | [ ` + ct.repo.repo + ` ](https://github.com/` + ct.repo.owner + `/` + ct.repo.repo + `) | | Repo | [ ` + ct.repo.repo + ` ](https://github.com/` + ct.repo.owner + `/` + ct.repo.repo + `) |
| Branch | [ ` + ct.payload.pull_request.head.ref + `](https://github.com/` + ct.repo.owner + `/` + ct.repo.repo + `/tree/` + ct.payload.pull_request.head.ref + `) ⇁ [ ` + ct.payload.pull_request.base.ref + `](https://github.com/` + ct.repo.owner + `/` + ct.repo.repo + `/tree/` + ct.payload.pull_request.base.ref + `) | | Branch | [ ` + ct.payload.pull_request.head.ref + `](https://github.com/` + ct.repo.owner + `/` + ct.repo.repo + `/tree/` + ct.payload.pull_request.head.ref + `) ⇁ [ ` + ct.payload.pull_request.base.ref + `](https://github.com/` + ct.repo.owner + `/` + ct.repo.repo + `/tree/` + ct.payload.pull_request.base.ref + `) |
| Added Files | ${{ steps.task_autocheck_changed_files_get.outputs.added_files_count }} | | Added Files | ${{ steps.task_prscan_changed_files_get.outputs.added_files_count }} |
| Modified Files | ${{ steps.task_autocheck_changed_files_get.outputs.all_modified_files_count }} | | Modified Files | ${{ steps.task_prscan_changed_files_get.outputs.all_modified_files_count }} |
| Renamed Files | ${{ steps.task_autocheck_changed_files_get.outputs.renamed_files_count }} | | Renamed Files | ${{ steps.task_prscan_changed_files_get.outputs.renamed_files_count }} |
| Copied Files | ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} | | Copied Files | ${{ steps.task_prscan_changed_files_get.outputs.deleted_files_count }} |
| Deleted Files | ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} | | Deleted Files | ${{ steps.task_prscan_changed_files_get.outputs.deleted_files_count }} |
`; `;
message.push ( md_table ); message.push ( md_table );
@@ -960,12 +972,13 @@ jobs:
- name: >- - name: >-
🕛 Get Weekly Commit List 🕛 Get Weekly Commit List
id: task_autocheck_set_weekly_commit_list id: task_prscan_set_weekly_commit_list
run: | run: |
echo 'WEEKLY_COMMITS<<EOF' >> $GITHUB_ENV echo 'WEEKLY_COMMITS<<EOF' >> $GITHUB_ENV
git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV echo 'EOF' >> $GITHUB_ENV
# # # #
# Autoscan Notify Github Success # Autoscan Notify Github Success
# # # #
@@ -1004,11 +1017,11 @@ jobs:
- Status: `${{ github.event.pull_request.state }}` - Status: `${{ github.event.pull_request.state }}`
### Scan Results ### Scan Results
- Added Files: ${{ steps.task_autocheck_changed_files_get.outputs.added_files_count }} - Added Files: ${{ steps.task_prscan_added_files_get.outputs.added_files_count }}
- Modified Files: ${{ steps.task_autocheck_changed_files_get.outputs.all_modified_files_count }} - Modified Files: ${{ steps.task_prscan_added_files_get.outputs.all_modified_files_count }}
- Renamed Files: ${{ steps.task_autocheck_changed_files_get.outputs.renamed_files_count }} - Renamed Files: ${{ steps.task_prscan_added_files_get.outputs.renamed_files_count }}
- Copied Files: ${{ steps.task_autocheck_changed_files_get.outputs.copied_files_count }} - Copied Files: ${{ steps.task_prscan_added_files_get.outputs.copied_files_count }}
- Deleted Files: ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} - Deleted Files: ${{ steps.task_prscan_added_files_get.outputs.deleted_files_count }}
embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }} embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }}
embed-footer-text: "Completed at ${{ env.NOW }} UTC" embed-footer-text: "Completed at ${{ env.NOW }} UTC"
@@ -1055,11 +1068,11 @@ jobs:
- Status: `${{ github.event.pull_request.state }}` - Status: `${{ github.event.pull_request.state }}`
### Scan Results ### Scan Results
- Added Files: ${{ steps.task_autocheck_changed_files_get.outputs.added_files_count }} - Added Files: ${{ steps.task_prscan_added_files_get.outputs.added_files_count }}
- Modified Files: ${{ steps.task_autocheck_changed_files_get.outputs.all_modified_files_count }} - Modified Files: ${{ steps.task_prscan_added_files_get.outputs.all_modified_files_count }}
- Renamed Files: ${{ steps.task_autocheck_changed_files_get.outputs.renamed_files_count }} - Renamed Files: ${{ steps.task_prscan_added_files_get.outputs.renamed_files_count }}
- Copied Files: ${{ steps.task_autocheck_changed_files_get.outputs.copied_files_count }} - Copied Files: ${{ steps.task_prscan_added_files_get.outputs.copied_files_count }}
- Deleted Files: ${{ steps.task_autocheck_changed_files_get.outputs.deleted_files_count }} - Deleted Files: ${{ steps.task_prscan_added_files_get.outputs.deleted_files_count }}
embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }} embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }}
embed-footer-text: "Completed at ${{ env.NOW }} UTC" embed-footer-text: "Completed at ${{ env.NOW }} UTC"

View File

@@ -3,14 +3,13 @@
# @author Aetherinox # @author Aetherinox
# @url https://github.com/Aetherinox # @url https://github.com/Aetherinox
# @usage creates repository labels if they are not yet installed # @usage creates repository labels if they are not yet installed
# issues marked as stale after 30 days, given tag Status 𐄂 Stale # issues marked as stale after 30 days, given tag Status Stale
# inactive issues closed after 180 days, given tag Status 𐄂 Locked # inactive issues closed after 180 days, given tag Status Locked
# inactive pr closed after 365 days, given tag Status 𐄂 Locked # inactive pr closed after 365 days, given tag Status Locked
# issues marked stale after 30 days, given tag Status 𐄂 Stale # issues marked stale after 30 days, given tag Status Stale
# issues marked closed 7 days after being marked stale, given tag Status 𐄂 Autoclosed # issues marked closed 7 days after being marked stale, given tag Status Autoclosed
# #
# @notes This Github action must be activated manually. This workflow script will do the following: # @notes This Github action must be activated manually. This workflow script will do the following:
#
# - Scan issues / pull requests and make sure they have properly assigned labels: # - Scan issues / pull requests and make sure they have properly assigned labels:
# - `Bug` # - `Bug`
# - `Feature` # - `Feature`
@@ -28,15 +27,15 @@
# secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/ # secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/
# secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/ # secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/
# secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token # secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token
# secrets.ORG_BINARYNINJA_TOKEN org github personal access token (fine-grained) # secrets.ORG_TOKEN org github personal access token (fine-grained)
# secrets.ORG_BINARYNINJA_TOKEN_CL org github personal access token (classic) # secrets.ORG_TOKEN_CL org github personal access token (classic)
# secrets.ORG_BINARYNINJA_DOCKERHUB_TOKEN org dockerhub secret # secrets.ORG_DOCKERHUB_TOKEN org dockerhub secret
# secrets.ORG_BINARYNINJA_GITEA_TOKEN org gitea personal access token (classic) with package:write permission # secrets.ORG_GITEA_TOKEN org gitea personal access token (classic) with package:write permission
# secrets.BINARYSERV_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK # secrets.BOT_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
# secrets.BINARYSERV_GPG_PASSPHRASE bot gpg private key passphrase # secrets.BOT_GPG_PASSPHRASE bot gpg private key passphrase
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_RELEASES discord webhook to report release notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_RELEASES discord webhook to report release notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKFLOWS discord webhook to report workflow notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_WORKFLOWS discord webhook to report workflow notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_UPDATES discord webhook to report activity notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_UPDATES discord webhook to report activity notifications from github to discord
# #
# @local these workflows can be tested locally through the use of `act` # @local these workflows can be tested locally through the use of `act`
# https://github.com/nektos/act # https://github.com/nektos/act
@@ -48,8 +47,8 @@
# act -W .github/workflows/issues-stale.yml -s TOKEN_CL=XXXXXXXXXX --pull=false # act -W .github/workflows/issues-stale.yml -s TOKEN_CL=XXXXXXXXXX --pull=false
# # # #
name: "🎫 Issues Stale" name: '🎫 Issues Stale'
run-name: "🎫 Issues Stale" run-name: '🎫 Issues Stale'
# # # #
# triggers # triggers
@@ -75,14 +74,21 @@ env:
PREFIX_INTERNAL: "Internal" PREFIX_INTERNAL: "Internal"
PREFIX_URGENT: "⚠ Urgent" PREFIX_URGENT: "⚠ Urgent"
LABEL_BUG: "Type Bug" LABEL_BUG: "Type Bug"
LABEL_DEPENDENCY: "Type Dependency" LABEL_DEPENDENCY: "Type Dependency"
LABEL_DOCS: "Type Docs" LABEL_DOCS: "Type Docs"
LABEL_FEATURE: "Type Feature" LABEL_FEATURE: "Type Feature"
LABEL_GIT: "Type Git Action" LABEL_GIT: "Type Git Action"
LABEL_PR: "Type Pull Request" LABEL_PR: "Type Pull Request"
LABEL_ROADMAP: "Type Roadmap" LABEL_ROADMAP: "Type Roadmap"
LABEL_INTERNAL: "Type Internal" LABEL_INTERNAL: "Type Internal"
LABEL_LOCKED: "Status Locked"
LABEL_STALE: "Status Stale"
LABEL_AUTOCLOSE: "Status Autoclosed"
LABEL_ACCEPTED: "Status Accepted"
LABEL_REVIEW: "Status Review"
LABEL_PENDING: "Status Pending"
LABEL_AC_REVIEW: "AC Review Required"
LABEL_URGENT: "⚠ Urgent" LABEL_URGENT: "⚠ Urgent"
ASSIGN_USER: Aetherinox ASSIGN_USER: Aetherinox
@@ -101,32 +107,32 @@ env:
{ "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, { "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
{ "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, { "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
{ "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, { "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, { "name": "Status Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, { "name": "Status Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, { "name": "Status Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, { "name": "Status Denied", "color": "ba4058", "description": "Pull request has been denied" },
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, { "name": "Status Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, { "name": "Status Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, { "name": "Status No Action", "color": "030406", "description": "Closed without any action being taken" },
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, { "name": "Status Pending", "color": "984b12", "description": "Pending pull request" },
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, { "name": "Status Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, { "name": "Status Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, { "name": "Status Review", "color": "9e1451", "description": "Currently pending review" },
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, { "name": "Status Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
{ "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" }, { "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" },
{ "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" }, { "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" },
{ "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, { "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
{ "name": "Type Feature", "color": "3c4e93", "description": "Feature request" }, { "name": "Type Feature", "color": "3c4e93", "description": "Feature request" },
{ "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" }, { "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" },
{ "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" }, { "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" },
{ "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, { "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
{ "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, { "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
{ "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, { "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
{ "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" }, { "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" },
{ "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, { "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
{ "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, { "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
{ "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" }, { "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" },
{ "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" }, { "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" },
{ "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, { "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
{ "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, { "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
{ "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, { "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
@@ -169,41 +175,62 @@ jobs:
job-labels-create: job-labels-create:
name: >- name: >-
🎫 Labels Verify Existing 🎫 Labels Verify Existing
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 4 runs-on: apollo-x64
timeout-minutes: 5
steps: steps:
# # # #
# [ Create Labels ] Start # Issues (Stale) Labels Create Start
# # # #
- name: >- - name: >-
✅ Start ✅ Start
id: task_label_create_start id: task_labels_create_start
run: | run: |
echo "Assigning labels and assignees" echo "Assigning labels and assignees"
# # # #
# [ Create Labels ] Checkout # Issues (Stale) Labels Create Set Env Variables
# #
- name: >-
🕛 Get Timestamp
id: task_labels_create_set_timestamp
run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
# #
# Issues (Stale) Labels Create Checkout
# # # #
- name: >- - name: >-
☑️ Checkout ☑️ Checkout
id: task_label_create_checkout id: task_labels_create_checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
# # # #
# [ Create Labels ] Verify Existing Labels # Issues (Stale) Labels Create Verify Existing
#
# check if repo has all of the needed issue / pr labels; create label if not exists
#
# action needed if using 'pull_request' and 'issue_comment'
# to get the pull request, you would normally use ${{ github.event.number }}
# however this isnt available for 'issue_comment'
# # # #
- name: >- - name: >-
🏷️ Verify Existing Labels 🎫 Labels Verify Existing
id: task_label_create_verify id: task_labels_create_verify
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const labels = JSON.parse( process.env.LABELS_JSON ); const labels = JSON.parse( process.env.LABELS_JSON );
for ( const label of labels ) for ( const label of labels )
@@ -215,7 +242,7 @@ jobs:
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
name: label.name, name: label.name,
description: label.description || '', description: label.description || 'No Description',
color: label.color color: label.color
}); });
} }
@@ -233,7 +260,7 @@ jobs:
} }
# # # #
# Job [ Check Labels ] # Issues (Stale) Labels Assign Missing
# #
# Runs through all submissions to check for ones that have not been properly labeled # Runs through all submissions to check for ones that have not been properly labeled
# - Bug # - Bug
@@ -251,25 +278,29 @@ jobs:
steps: steps:
# # # #
# [ Check Labels ] Checkout # Issues (Stale) Labels Assign Missing Checkout
# # # #
- name: "☑️ Prepare"
- name: >-
☑️ Checkout
id: task_issues_nolabel_prepare id: task_issues_nolabel_prepare
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
# # # #
# [ Check Labels ] Check # Issues (Stale) Labels Assign Missing Check
# Check if repo has labels currently added to issues #
# Check if repo has labels to use
# # # #
- name: 🏷️ Checking Issues - name: >-
🎫 Labels Check
id: task_issues_nolabel_run id: task_issues_nolabel_run
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
/* /*
@@ -289,7 +320,6 @@ jobs:
hours = hours % 12; hours = hours % 12;
hours = hours ? hours : 12; hours = hours ? hours : 12;
minutes = minutes.toString( ).padStart( 2, '0' ); minutes = minutes.toString( ).padStart( 2, '0' );
let mergeTime = month + '.' + day + '.' + year + ' ' + hours + ':' + minutes + ' ' + x; let mergeTime = month + '.' + day + '.' + year + ' ' + hours + ':' + minutes + ' ' + x;
return mergeTime; return mergeTime;
@@ -336,8 +366,8 @@ jobs:
let iss_body = `${ issue.body }`; let iss_body = `${ issue.body }`;
const iss_body_lc = iss_body.toLowerCase( ); const iss_body_lc = iss_body.toLowerCase( );
console.log( ` └── 📁 ` + iss_title ); console.log( ` └── 📁 ` + iss_title + ` #${ issue.number }`);
console.log( ` └── 📄 Issue #${ issue.number } last updated on ${ date_UpdateHuman }` ); console.log( ` └── 📄 last updated on ${ date_UpdateHuman }` );
console.log( ` └── 📄 ${add_labels}` ); console.log( ` └── 📄 ${add_labels}` );
console.log( `\n\n` ) console.log( `\n\n` )
@@ -345,8 +375,8 @@ jobs:
Keywords Keywords
*/ */
const bug_words = [ "bug", "broke", "issue", "fail" ]; const bug_words = [ "bug", "broke", "issue", "fail", "wont work" ];
const feat_words = [ "feature", "request", "add support" ]; const feat_words = [ "feature", "request", "add", "addition", "enhance", "create" ];
const urgn_words = [ "urgent", "urgency", "emergency", "important", "critical" ]; const urgn_words = [ "urgent", "urgency", "emergency", "important", "critical" ];
const road_words = [ "roadmap", "road map", "planned" ]; const road_words = [ "roadmap", "road map", "planned" ];
@@ -365,17 +395,14 @@ jobs:
/* /*
Label > Bugs Label > Bugs
*/
const bug_bIncWordT = bug_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
/*
Find regex based phrases Find regex based phrases
Regex: Regex:
https://regex101.com/r/Z99Gnq/2 https://regex101.com/r/Z99Gnq/2
*/ */
const bug_bIncWordT = bug_words.some( s => s.includes( iss_title_lc ) || iss_title_lc.includes( s ) );
const bug_findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm; const bug_findWordList = /^\b(?:I?\s*have\s*(?:a|an)\s*(?:issue|problem|bug))|(?:will\s*not\s*work)|(?:it\s*is\s*(?:broken|broke|stuck))|(?:found\s*(?:an?|the)\s*(?:bug|issue))|(?:can\s*I\s*fix\s*the\s*(?:bug|issue))|(?:(?:does not|doesn'?t|don'?t|won'?t|can'?t|can\s?not|will\s*not)\s*(?:work|load|function))|(?:it\s*(?:will\s?not|won'?t|can\s?not|can'?t))\s*(?:get|find)\s*the\s*(?:website|site|webpage|page)|(?:the\s*(?:window|frame)\s*is\s*(?:blank|white|empty|missing))\b$/igm;
const bug_bFoundMatchTitle = Boolean( bug_findWordList.test( iss_title ) ); const bug_bFoundMatchTitle = Boolean( bug_findWordList.test( iss_title ) );
const bug_bFoundMatchBody = Boolean( bug_findWordList.test( iss_body ) ); const bug_bFoundMatchBody = Boolean( bug_findWordList.test( iss_body ) );
@@ -403,16 +430,49 @@ jobs:
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ author }` ) core.info( `Skipping: Detected ${ author }` )
// Rename title to contain Bug: if ( author === `${{ env.BOT_NAME_RENOVATE }}` )
core.info( `Skipping: Detected ${ author }` )
/*
Rename title to contain Bug:
if bug title or body contains keyword hinting at the issue being about a bug; change the title of the issue
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !bug_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{ {
const title = issue.title;
/*
If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
original: Bug: CMD windows opens then closes, can't run the script.
new: 🐛 Bug: CMD windows opens then closes, can't run the script.
*/
const removeBeginning1 = bug_tag.substring(3); // "Bug:"
let removeBeginning2 = bug_tag.substring(0); // "🐛 Bug:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Bug:"
if ( iss_title.startsWith(removeBeginning1) )
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
}
else if ( iss_title.startsWith(removeBeginning2) )
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = iss_title;
let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' ); let title_new = title.replace( /^\s?bug\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?fail\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?issue\s*(.*?)\b/gi, '' );
iss_title = `${ bug_tag } ${ title_new }`; iss_title = `${ bug_tag } ${ title_new }`;
} }
console.log( `New Title: ...................... ${ iss_title }` )
await github.rest.issues.update( await github.rest.issues.update(
{ {
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
@@ -433,7 +493,7 @@ jobs:
https://regex101.com/r/fR1Hm6/1 https://regex101.com/r/fR1Hm6/1
*/ */
const feat_findWordList = /^(?:(?:request|include|see)\s*(?:an?|the?)\s*(?:feature|addon|addition|plugin))|(?:(?:add|see|get)\s*support\s*(?:for|with|of))|(?:can\s*we\s*get\s*(?:the|a)\s*(?:ability|feature))|(?:💡 Feature:)$/igm; const feat_findWordList = /(?:(?:add|enjoy|would|like|can|request|include|see|could|have)\s*?(?:the|liked?|i|we|an?|the|you?)\s*?(?:to|have|an|get|ability|request|add|feature|functionality|addon|addition|plugin|create))|(?:(?:add|see|get)\s*?support\s*?(?:for|with|of))|(?:can\s*we\s*get\s*?(?:the|a)\s*?(?:ability|feature))|(?:💡 Feature:)$/igm;
const feat_bFoundMatchTitle = Boolean( feat_findWordList.test( iss_title ) ); const feat_bFoundMatchTitle = Boolean( feat_findWordList.test( iss_title ) );
const feat_bFoundMatchBody = Boolean( feat_findWordList.test( iss_body ) ); const feat_bFoundMatchBody = Boolean( feat_findWordList.test( iss_body ) );
@@ -460,17 +520,49 @@ jobs:
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ author }` ) core.info( `Skipping: Detected ${ author }` )
// Rename title to contain Feature: if ( author === `${{ env.BOT_NAME_RENOVATE }}` )
core.info( `Skipping: Detected ${ author }` )
/*
Rename title to contain Feature:
if feature title or body contains keyword hinting at the issue being about a feature; change the title of the issue
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !feat_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{ {
const title = issue.title;
/*
If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
original: Feature: CMD windows opens then closes, can't run the script.
new: 💡 Feature: CMD windows opens then closes, can't run the script.
*/
const removeBeginning1 = feat_tag.substring(3); // "Feature:"
let removeBeginning2 = feat_tag.substring(0); // "💡 Feature:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Feature:"
if ( iss_title.startsWith(removeBeginning1) )
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
}
else if ( iss_title.startsWith(removeBeginning2) )
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = iss_title;
let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' ); let title_new = title.replace( /^\s?feature\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?request\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?add(.*?)\s?feature\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?add(.*?)\s?support\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?add(.*?)\s?support\s*(.*?)\b/gi, '' );
iss_title = `${ feat_tag } ${ title_new }`; iss_title = `${ feat_tag } ${ title_new }`; // change TAG per category
} }
console.log( `New Title: ...................... ${ iss_title }` )
await github.rest.issues.update( await github.rest.issues.update(
{ {
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
@@ -518,10 +610,40 @@ jobs:
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ author }` ) core.info( `Skipping: Detected ${ author }` )
// Rename title to contain Urgent: if ( author === `${{ env.BOT_NAME_RENOVATE }}` )
core.info( `Skipping: Detected ${ author }` )
/*
Rename title to contain Urgent:
if urgent title or body contains keyword hinting at the issue being about urgent; change the title of the issue
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !urgn_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{ {
const title = issue.title;
/*
If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
original: Urgent: CMD windows opens then closes, can't run the script.
new: ⚠ Urgent: CMD windows opens then closes, can't run the script.
*/
const removeBeginning1 = urgn_tag.substring(3); // "Urgent:"
let removeBeginning2 = urgn_tag.substring(0); // "⚠ Urgent:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Urgent:"
if ( iss_title.startsWith(removeBeginning1) )
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
}
else if ( iss_title.startsWith(removeBeginning2) )
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = iss_title;
let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' ); let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' ); title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' );
@@ -530,6 +652,8 @@ jobs:
iss_title = `${ urgn_tag } ${ title_new }`; iss_title = `${ urgn_tag } ${ title_new }`;
} }
console.log( `New Title: ...................... ${ iss_title }` )
await github.rest.issues.update( await github.rest.issues.update(
{ {
owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number,
@@ -578,17 +702,47 @@ jobs:
if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` ) if ( author === `${{ env.BOT_NAME_DEPENDABOT }}` )
core.info( `Skipping: Detected ${ author }` ) core.info( `Skipping: Detected ${ author }` )
// Rename title to contain Roadmap: if ( author === `${{ env.BOT_NAME_RENOVATE }}` )
core.info( `Skipping: Detected ${ author }` )
/*
Rename title to contain Roadmap:
if roadmap title or body contains keyword hinting at the issue being about roadmap; change the title of the issue
@ref https://jsfiddle.net/aetherinox/wj17x8mp/2/
*/
if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) ) if ( author !== `${{ env.BOT_NAME_DEPENDABOT }}` && !road_bFoundPRTitle && !iss_title_lc.startsWith( bug_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( feat_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( urgn_tag.toLowerCase( ) ) && !iss_title_lc.startsWith( road_tag.toLowerCase( ) ) )
{ {
const title = issue.title;
let title_new = title.replace( /^\s?emergency\s*(.*?)\b/gi, '' ); /*
title_new = title.replace( /^\s?urgent\s*(.*?)\b/gi, '' ); If a user creates an issue starting with our tag; strip it and add ours with the emoji instead
title_new = title.replace( /^\s?urgency\s*(.*?)\b/gi, '' ); original: Roadmap: CMD windows opens then closes, can't run the script.
title_new = title.replace( /^\s?important\s*(.*?)\b/gi, '' ); new: 🗺️ Roadmap: CMD windows opens then closes, can't run the script.
title_new = title.replace( /^\s?critical\s*(.*?)\b/gi, '' ); */
iss_title = `${ road_tag } ${ title_new }`;
const removeBeginning1 = road_tag.substring(3); // "Roadmap:"
let removeBeginning2 = road_tag.substring(0); // "🗺️ Roadmap:"
removeBeginning2 = removeBeginning2.replace(/\s/g, '') // "Roadmap:"
if ( iss_title.startsWith(removeBeginning1) )
{
iss_title = iss_title.slice(removeBeginning1.length);
iss_title = iss_title.trim();
} }
else if ( iss_title.startsWith(removeBeginning2) )
{
iss_title = iss_title.slice(removeBeginning2.length);
iss_title = iss_title.trim();
}
const title = iss_title;
let title_new = title.replace( /^\s?broad(.*?)\s?map\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?planned\s*(.*?)\b/gi, '' );
title_new = title.replace( /^\s?broadmap\s*(.*?)\b/gi, '' );
iss_title = `${ road_tag } ${ title_new }`; // change TAG per category
}
console.log( `New Title: ...................... ${ iss_title }` )
await github.rest.issues.update( await github.rest.issues.update(
{ {
@@ -607,14 +761,15 @@ jobs:
} }
# # # #
# Job [ Stale Issues ] # Issues (Stale) Stale
# # # #
job-issues-stale: job-issues-stale:
name: >- name: >-
💤 Check Stale 💤 Scan Check Stale
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 4 runs-on: apollo-x64
timeout-minutes: 5
needs: needs:
- job-labels-create - job-labels-create
- job-issues-nolabel - job-issues-nolabel
@@ -625,14 +780,15 @@ jobs:
steps: steps:
# # # #
# [ Stale Issues ] Check Condition # Stale Check Condition
# # # #
- name: "💤 Stale Check Condition" - name: >-
💤 Stale Check Condition
id: task_issues_inactive_stale
uses: actions/stale@v9 uses: actions/stale@v9
id: task_issues_stale_run
with: with:
repo-token: ${{ secrets.ADMINSERV_TOKEN_CL }} repo-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
stale-issue-message: | stale-issue-message: |
⚠️ It looks like there hasn't been any recent updates on this issue. If you created this issue and no longer consider it open, then please login to github and close the issue. ⚠️ It looks like there hasn't been any recent updates on this issue. If you created this issue and no longer consider it open, then please login to github and close the issue.
@@ -641,40 +797,42 @@ jobs:
--- ---
<sub>I am a bot reaching out to you with an automated response.</sub> <sub>I am a bot reaching out to you with an automated response.</sub>
stale-issue-label: 'Status 𐄂 Stale' stale-issue-label: '${{ env.LABEL_STALE }}'
close-issue-label: 'Status 𐄂 Autoclosed' close-issue-label: '${{ env.LABEL_AUTOCLOSE }}'
exempt-issue-labels: 'Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Pull Request,Type ◦ Roadmap' exempt-issue-labels: '${{ env.LABEL_ACCEPTED }},${{ env.LABEL_REVIEW }},${{ env.LABEL_PENDING }},${{ env.LABEL_BUG }},${{ env.LABEL_DEPENDENCY }},${{ env.LABEL_DOCS }},${{ env.LABEL_FEATURE }},${{ env.LABEL_GIT }},${{ env.LABEL_PR }},${{ env.LABEL_ROADMAP }}'
days-before-stale: 60 days-before-stale: 120
days-before-close: 7 days-before-close: 7
days-before-pr-stale: -1 days-before-pr-stale: -1
days-before-pr-close: -1 days-before-pr-close: -1
# # # #
# Job [ Lock Issues ] # Issues (Stale) Lock
# # # #
job-issues-lock: job-issues-lock:
name: >- name: >-
🔒 Check Inactive 🔒 Scan Lock Inactive
runs-on: ubuntu-latest # runs-on: ubuntu-latest
timeout-minutes: 4 runs-on: apollo-x64
timeout-minutes: 5
needs: needs:
- job-labels-create - job-labels-create
- job-issues-nolabel - job-issues-nolabel
steps: steps:
# # # #
# [ Lock Issues ] Look for inactives # Inactive Lock
# # # #
- name: "🔒 Lock Inactives" - name: >-
🔒 Inactive Lock
id: task_issues_inactive_lock
uses: dessant/lock-threads@v5 uses: dessant/lock-threads@v5
id: task_issues_lock_run
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
exclude-any-issue-labels: 'AC Review Required,Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Roadmap,Type ◦ Internal' exclude-any-issue-labels: '${{ env.LABEL_AC_REVIEW }},${{ env.LABEL_ACCEPTED }},${{ env.LABEL_REVIEW }},${{ env.LABEL_PENDING }},${{ env.LABEL_BUG }},${{ env.LABEL_DEPENDENCY }},${{ env.LABEL_DOCS }},${{ env.LABEL_FEATURE }},${{ env.LABEL_GIT }},${{ env.LABEL_ROADMAP }},${{ env.LABEL_INTERNAL }}'
add-issue-labels: 'Status 𐄂 Locked' add-issue-labels: '${{ env.LABEL_LOCKED }}'
issue-inactive-days: '60' issue-inactive-days: '120'
issue-lock-reason: 'resolved' issue-lock-reason: 'resolved'
issue-comment: > issue-comment: >
⚠️ This **issue** has been automatically locked since there has not been any recent activity after it was closed. ⚠️ This **issue** has been automatically locked since there has not been any recent activity after it was closed.
@@ -684,8 +842,8 @@ jobs:
--- ---
<sub>I am a bot reaching out to you with an automated response.</sub> <sub>I am a bot reaching out to you with an automated response.</sub>
exclude-any-pr-labels: 'AC Review Required,Status 𐄂 Accepted,Status 𐄂 Review,Status 𐄂 Pending,Type ◦ Bug,Type ◦ Dependency,Type ◦ Docs,Type ◦ Feature,Type ◦ Git Action,Type ◦ Roadmap,Type ◦ Internal' exclude-any-pr-labels: '${{ env.LABEL_AC_REVIEW }},${{ env.LABEL_ACCEPTED }},${{ env.LABEL_REVIEW }},${{ env.LABEL_PENDING }},${{ env.LABEL_BUG }},${{ env.LABEL_DEPENDENCY }},${{ env.LABEL_DOCS }},${{ env.LABEL_FEATURE }},${{ env.LABEL_GIT }},${{ env.LABEL_ROADMAP }},${{ env.LABEL_INTERNAL }}'
add-pr-labels: 'Status 𐄂 Locked' add-pr-labels: '${{ env.LABEL_LOCKED }}'
pr-inactive-days: '365' pr-inactive-days: '365'
pr-lock-reason: 'resolved' pr-lock-reason: 'resolved'
pr-comment: > pr-comment: >

View File

@@ -13,15 +13,15 @@
# secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/ # secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/
# secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/ # secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/
# secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token # secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token
# secrets.ORG_BINARYNINJA_TOKEN org github personal access token (fine-grained) # secrets.ORG_TOKEN org github personal access token (fine-grained)
# secrets.ORG_BINARYNINJA_TOKEN_CL org github personal access token (classic) # secrets.ORG_TOKEN_CL org github personal access token (classic)
# secrets.ORG_BINARYNINJA_DOCKERHUB_TOKEN org dockerhub secret # secrets.ORG_DOCKERHUB_TOKEN org dockerhub secret
# secrets.ORG_BINARYNINJA_GITEA_TOKEN org gitea personal access token (classic) with package:write permission # secrets.ORG_GITEA_TOKEN org gitea personal access token (classic) with package:write permission
# secrets.BINARYSERV_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK # secrets.BOT_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
# secrets.BINARYSERV_GPG_PASSPHRASE bot gpg private key passphrase # secrets.BOT_GPG_PASSPHRASE bot gpg private key passphrase
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_RELEASES discord webhook to report release notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_RELEASES discord webhook to report release notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKFLOWS discord webhook to report workflow notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_WORKFLOWS discord webhook to report workflow notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_UPDATES discord webhook to report activity notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_UPDATES discord webhook to report activity notifications from github to discord
# #
# @local these workflows can be tested locally through the use of `act` # @local these workflows can be tested locally through the use of `act`
# https://github.com/nektos/act # https://github.com/nektos/act
@@ -33,8 +33,8 @@
# act -W .github/workflows/labels-clean.yml -s TOKEN_CL=XXXXXXXXXX --pull=false # act -W .github/workflows/labels-clean.yml -s TOKEN_CL=XXXXXXXXXX --pull=false
# # # #
name: "🎫 Labels Remove" name: '🧹 Labels Clean'
run-name: "🎫 Labels Remove" run-name: '🧹 Labels Clean'
# # # #
# triggers # triggers
@@ -73,32 +73,32 @@ env:
{ "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, { "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
{ "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, { "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
{ "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, { "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, { "name": "Status Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, { "name": "Status Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, { "name": "Status Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, { "name": "Status Denied", "color": "ba4058", "description": "Pull request has been denied" },
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, { "name": "Status Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, { "name": "Status Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, { "name": "Status No Action", "color": "030406", "description": "Closed without any action being taken" },
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, { "name": "Status Pending", "color": "984b12", "description": "Pending pull request" },
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, { "name": "Status Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, { "name": "Status Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, { "name": "Status Review", "color": "9e1451", "description": "Currently pending review" },
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, { "name": "Status Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
{ "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" }, { "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" },
{ "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" }, { "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" },
{ "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, { "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
{ "name": "Type Feature", "color": "3c4e93", "description": "Feature request" }, { "name": "Type Feature", "color": "3c4e93", "description": "Feature request" },
{ "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" }, { "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" },
{ "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" }, { "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" },
{ "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, { "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
{ "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, { "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
{ "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, { "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
{ "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" }, { "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" },
{ "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, { "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
{ "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, { "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
{ "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" }, { "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" },
{ "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" }, { "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" },
{ "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, { "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
{ "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, { "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
{ "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, { "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
@@ -137,12 +137,12 @@ jobs:
# This job removes all existing labels # This job removes all existing labels
# # # #
issues-labels-remove: issues-labels-clean:
name: >- name: >-
🎫 Labels Remove 🧹 Labels Clean
# runs-on: ubuntu-latest # runs-on: ubuntu-latest
runs-on: apollo-x64 runs-on: apollo-x64
timeout-minutes: 4 timeout-minutes: 3
permissions: permissions:
contents: 'read' contents: 'read'
id-token: 'write' id-token: 'write'
@@ -167,6 +167,7 @@ jobs:
🕛 Get Timestamp 🕛 Get Timestamp
id: task_label_set_timestamp id: task_label_set_timestamp
run: | run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
@@ -192,7 +193,7 @@ jobs:
id: task_label_remove_run id: task_label_remove_run
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const targetOwner = context.repo.owner; const targetOwner = context.repo.owner;
const targetRepo = context.repo.repo; const targetRepo = context.repo.repo;
@@ -249,73 +250,3 @@ jobs:
echo 'WEEKLY_COMMITS<<EOF' >> $GITHUB_ENV echo 'WEEKLY_COMMITS<<EOF' >> $GITHUB_ENV
git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV git log --format="[\`%h\`](${{ github.server_url }}/${{ github.repository }}/commit/%H) %s - %an" --since=7.days >> $GITHUB_ENV
echo 'EOF' >> $GITHUB_ENV echo 'EOF' >> $GITHUB_ENV
# #
# Labels Notify Github Success
# #
- name: >-
🔔 Send Discord Webhook Message (Success)
id: task_label_notify_discord_success
uses: tsickert/discord-webhook@v7.0.0
if: success()
with:
username: 'Io'
avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_WORKFLOWS }}
embed-title: "⚙️ ${{ github.workflow_ref }}"
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'
embed-description: |
## 🎫 Labels Clean ${{ job.status == 'success' && '✅' || '❌' }}
A **successful** workflow has been ran to wipe all labels from your repository.
**${{ steps.task_label_remove_run.outputs.result }}** labels have been removed.
- Labels: `${{ steps.task_label_remove_run.outputs.result }} created`
- Workflow: `${{ github.workflow }} (#${{github.run_number}})`
- Runner: `${{ runner.name }}`
- Triggered By: `${{ github.actor }}`
- Status: `${{ job.status == 'success' && '✅ Successful' || '❌ Failed' }}`
embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }}
embed-footer-text: "Completed at ${{ env.NOW }} UTC"
embed-timestamp: "${{ env.NOW_LONG }}"
embed-author-name: "${{ github.repository_owner }}"
embed-author-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
embed-author-icon-url: "https://avatars.githubusercontent.com/u/200161462"
# #
# Labels Notify Github Failure
# #
- name: >-
🔔 Send Discord Webhook Message (Failure)
id: task_label_notify_discord_failure
uses: tsickert/discord-webhook@v7.0.0
if: failure()
with:
username: 'Io'
avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_WORKFLOWS }}
embed-title: "⚙️ ${{ github.workflow_ref }}"
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'
embed-description: |
## 🎫 Labels Clean ${{ job.status == 'success' && '✅' || '❌' }}
A **failed** attempt was made to run this workflow. No new labels have been added to your repository.
- Labels: `${{ steps.task_label_remove_run.outputs.result }} created`
- Workflow: `${{ github.workflow }} (#${{github.run_number}})`
- Runner: `${{ runner.name }}`
- Triggered By: `${{ github.actor }}`
- Status: `${{ job.status == 'success' && '✅ Successful' || '❌ Failed' }}`
embed-color: ${{ job.status == 'success' && '5763719' || '15418782' }}
embed-footer-text: "Completed at ${{ env.NOW }} UTC"
embed-timestamp: "${{ env.NOW_LONG }}"
embed-author-name: "${{ github.repository_owner }}"
embed-author-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
embed-author-icon-url: "https://avatars.githubusercontent.com/u/200161462"

View File

@@ -4,34 +4,21 @@
# @url https://github.com/Aetherinox # @url https://github.com/Aetherinox
# @usage manually activated workflow to create issue labels # @usage manually activated workflow to create issue labels
# #
# @notes This Github action must be activated manually. This workflow script will do the following:
#
# - Scan issues / pull requests and make sure they have properly assigned labels:
# - `Bug`
# - `Feature`
# - `Urgent`
# - `Roadmap`
#
# - Workflow script will then scan each pr or issue and mark them as `Stale`
# if they haven't had any replies in 30 days.
#
# - Workflow will `autoclose` pr or issues which haven't had action in `365 days`.
#
# @secrets secrets.SELF_TOKEN self github personal access token (fine-grained) # @secrets secrets.SELF_TOKEN self github personal access token (fine-grained)
# secrets.SELF_TOKEN_CL self github personal access token (classic) # secrets.SELF_TOKEN_CL self github personal access token (classic)
# secrets.NPM_TOKEN self npmjs access token # secrets.NPM_TOKEN self npmjs access token
# secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/ # secrets.PYPI_API_TOKEN self Pypi API token (production site) - https://pypi.org/
# secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/ # secrets.PYPI_API_TEST_TOKEN self Pypi API token (test site) - https://test.pypi.org/
# secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token # secrets.SELF_DOCKERHUB_TOKEN self Dockerhub token
# secrets.ORG_BINARYNINJA_TOKEN org github personal access token (fine-grained) # secrets.ORG_TOKEN org github personal access token (fine-grained)
# secrets.ORG_BINARYNINJA_TOKEN_CL org github personal access token (classic) # secrets.ORG_TOKEN_CL org github personal access token (classic)
# secrets.ORG_BINARYNINJA_DOCKERHUB_TOKEN org dockerhub secret # secrets.ORG_DOCKERHUB_TOKEN org dockerhub secret
# secrets.ORG_BINARYNINJA_GITEA_TOKEN org gitea personal access token (classic) with package:write permission # secrets.ORG_GITEA_TOKEN org gitea personal access token (classic) with package:write permission
# secrets.BINARYSERV_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK # secrets.BOT_GPG_KEY_ASC bot gpg private key (armored) | BEGIN PGP PRIVATE KEY BLOCK
# secrets.BINARYSERV_GPG_PASSPHRASE bot gpg private key passphrase # secrets.BOT_GPG_PASSPHRASE bot gpg private key passphrase
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_RELEASES discord webhook to report release notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_RELEASES discord webhook to report release notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKFLOWS discord webhook to report workflow notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_WORKFLOWS discord webhook to report workflow notifications from github to discord
# secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_UPDATES discord webhook to report activity notifications from github to discord # secrets.DISCORD_WEBHOOK_CHAN_GITHUB_UPDATES discord webhook to report activity notifications from github to discord
# #
# @local these workflows can be tested locally through the use of `act` # @local these workflows can be tested locally through the use of `act`
# https://github.com/nektos/act # https://github.com/nektos/act
@@ -43,8 +30,8 @@
# act -W .github/workflows/labels-create.yml -s TOKEN_CL=XXXXXXXXXX --pull=false # act -W .github/workflows/labels-create.yml -s TOKEN_CL=XXXXXXXXXX --pull=false
# # # #
name: "🎫 Labels Create" name: '🎫 Labels Create'
run-name: "🎫 Labels Create" run-name: '🎫 Labels Create'
# # # #
# triggers # triggers
@@ -74,32 +61,32 @@ env:
{ "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" }, { "name": "AC Review Required", "color": "8F1784", "description": "PR needs to be reviewed by another person, after the requested changes have been made" },
{ "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" }, { "name": "AC Security Warning", "color": "761620", "description": "Does not conform to developer policies, or includes potentially dangerous code" },
{ "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" }, { "name": "AC Skipped Scan", "color": "8F1784", "description": "Author has skipped code scan" },
{ "name": "Status 𐄂 Duplicate", "color": "75536b", "description": "Issue or pull request already exists" }, { "name": "Status Duplicate", "color": "75536b", "description": "Issue or pull request already exists" },
{ "name": "Status 𐄂 Accepted", "color": "2e7539", "description": "This pull request has been accepted" }, { "name": "Status Accepted", "color": "2e7539", "description": "This pull request has been accepted" },
{ "name": "Status 𐄂 Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" }, { "name": "Status Autoclosed", "color": "3E0915", "description": "Originally stale and was autoclosed for no activity" },
{ "name": "Status 𐄂 Denied", "color": "ba4058", "description": "Pull request has been denied" }, { "name": "Status Denied", "color": "ba4058", "description": "Pull request has been denied" },
{ "name": "Status 𐄂 Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" }, { "name": "Status Locked", "color": "550F45", "description": "Automatically locked by AdminServ for a prolonged period of inactivity" },
{ "name": "Status 𐄂 Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" }, { "name": "Status Need Info", "color": "2E3C4C", "description": "Not enough information to resolve" },
{ "name": "Status 𐄂 No Action", "color": "030406", "description": "Closed without any action being taken" }, { "name": "Status No Action", "color": "030406", "description": "Closed without any action being taken" },
{ "name": "Status 𐄂 Pending", "color": "984b12", "description": "Pending pull request" }, { "name": "Status Pending", "color": "984b12", "description": "Pending pull request" },
{ "name": "Status 𐄂 Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" }, { "name": "Status Released", "color": "1b6626", "description": "Issues or PR has been implemented and is now live" },
{ "name": "Status 𐄂 Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" }, { "name": "Status Reopened", "color": "8a6f14", "description": "A previously closed PR which has been re-opened" },
{ "name": "Status 𐄂 Review", "color": "9e1451", "description": "Currently pending review" }, { "name": "Status Review", "color": "9e1451", "description": "Currently pending review" },
{ "name": "Status 𐄂 Stale", "color": "928282", "description": "Has not had any activity in over 30 days" }, { "name": "Status Stale", "color": "928282", "description": "Has not had any activity in over 30 days" },
{ "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" }, { "name": "Type Bug", "color": "9a2c2c", "description": "Something isn't working" },
{ "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" }, { "name": "Type Dependency", "color": "243759", "description": "Item is associated to dependency" },
{ "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" }, { "name": "Type Docs", "color": "0e588d", "description": "Improvements or modifications to docs" },
{ "name": "Type Feature", "color": "3c4e93", "description": "Feature request" }, { "name": "Type Feature", "color": "3c4e93", "description": "Feature request" },
{ "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" }, { "name": "Type Git Action", "color": "030406", "description": "GitHub Action / workflow" },
{ "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" }, { "name": "Type Pull Request", "color": "8F1784", "description": "Normal pull request" },
{ "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" }, { "name": "Type Roadmap", "color": "8F1784", "description": "Feature or bug currently planned for implementation" },
{ "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" }, { "name": "Type Internal", "color": "A51994", "description": "Assigned items are for internal developer use" },
{ "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" }, { "name": "Build Desktop", "color": "c7ca4a", "description": "Specific to desktop" },
{ "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" }, { "name": "Build Linux", "color": "c7ca4a", "description": "Specific to Linux" },
{ "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" }, { "name": "Build MacOS", "color": "c7ca4a", "description": "Specific to MacOS" },
{ "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" }, { "name": "Build Mobile", "color": "c7ca4a", "description": "Specific to mobile" },
{ "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" }, { "name": "Build Web", "color": "c7ca4a", "description": "Specific to web" },
{ "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" }, { "name": "Build Windows", "color": "c7ca4a", "description": "Specific to Windows" },
{ "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" }, { "name": " API", "color": "F99B50", "description": "Plugin API, CLI, browser JS API" },
{ "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" }, { "name": " Auto-type", "color": "9141E0", "description": "Auto-type functionality in desktop apps" },
{ "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" }, { "name": " Browser", "color": "9141E0", "description": "Browser plugins and passing data to <=> from app" },
@@ -142,7 +129,9 @@ jobs:
issues-labels-create: issues-labels-create:
name: >- name: >-
🎫 Labels Create 🎫 Labels Create
runs-on: ubuntu-latest # runs-on: ubuntu-latest
runs-on: apollo-x64
timeout-minutes: 3
permissions: permissions:
contents: 'read' contents: 'read'
id-token: 'write' id-token: 'write'
@@ -167,6 +156,7 @@ jobs:
🕛 Get Timestamp 🕛 Get Timestamp
id: task_label_set_timestamp id: task_label_set_timestamp
run: | run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
@@ -192,7 +182,7 @@ jobs:
id: task_label_verify_existing id: task_label_verify_existing
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
github-token: ${{ secrets.ADMINSERV_TOKEN_CL }} github-token: ${{ secrets.ADMINSERV_TOKEN_CL || github.token }}
script: | script: |
const labels = JSON.parse( process.env.LABELS_JSON ); const labels = JSON.parse( process.env.LABELS_JSON );
let result = Object.keys(labels).length; let result = Object.keys(labels).length;
@@ -250,7 +240,7 @@ jobs:
with: with:
username: 'Io' username: 'Io'
avatar-url: 'https://i.imgur.com/8BVDkla.jpg' avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_WORKFLOWS }} webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKfLOWS }}
embed-title: "⚙️ ${{ github.workflow_ref }}" embed-title: "⚙️ ${{ github.workflow_ref }}"
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg' embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'
@@ -286,7 +276,7 @@ jobs:
with: with:
username: 'Io' username: 'Io'
avatar-url: 'https://i.imgur.com/8BVDkla.jpg' avatar-url: 'https://i.imgur.com/8BVDkla.jpg'
webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_TVAPP2_WORKFLOWS }} webhook-url: ${{ secrets.DISCORD_WEBHOOK_CHAN_GITHUB_TVAPP2_WORKfLOWS }}
embed-title: "⚙️ ${{ github.workflow_ref }}" embed-title: "⚙️ ${{ github.workflow_ref }}"
embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}" embed-url: "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg' embed-thumbnail-url: 'https://i.imgur.com/zDIzE8T.jpg'

View File

@@ -77,23 +77,23 @@ on:
type: boolean type: boolean
# # # #
# ENABLE: released version will be marked as pre-release # true released version will be marked as a development build and will have the v1.x.x-development tag instead of -latest
# DISABLE: release version will be marked as stable / normal release # false release version will be marked with -latest docker tag
# # # #
PRERELEASE: RC_RELEASE:
description: '🧪 Build RC (Pre-release)' description: '🧪 Build RC (Pre-release)'
required: true required: true
default: false default: false
type: boolean type: boolean
# # # #
# Release Candidate version number # only needed if env variable `RC_ONLY` = true
# this will be added to the end of your released app in the releases page. # sets the version number for the release candidate
# e.g: tvapp2-v1.0.0-rc.1 # e.g: noxenv-v1.0.0-rc.1
# # # #
VERSION_RC: RC_VERSION:
description: '🧪 RC (Pre-release) Ver (tvapp2-rc.v1)' description: '🧪 RC (Pre-release) Ver (tvapp2-rc.v1)'
required: false required: false
type: string type: string
@@ -105,15 +105,17 @@ on:
env: env:
PROJECT_NAME: ${{ github.event.inputs.PROJECT_NAME || 'tvapp2' }} PROJECT_NAME: ${{ github.event.inputs.PROJECT_NAME || 'tvapp2' }}
CHANGELOG_MODE_COMMIT: true CHANGELOG_MODE_COMMIT: ${{ github.event.inputs.CHANGELOG_MODE_COMMIT || true }}
SHOW_UNCATEGORIZED: false SHOW_UNCATEGORIZED: ${{ github.event.inputs.SHOW_UNCATEGORIZED || false }}
PRERELEASE: false RC_RELEASE: ${{ github.event.inputs.RC_RELEASE || false }}
VERSION_RC: '1' RC_VERSION: ${{ github.event.inputs.RC_VERSION || '1' }}
ASSIGN_USER: Aetherinox ASSIGN_USER: Aetherinox
BOT_NAME_1: EuropaServ BOT_NAME_1: EuropaServ
BOT_NAME_2: BinaryServ BOT_NAME_2: BinaryServ
BOT_NAME_DEPENDABOT: dependabot[bot] BOT_NAME_DEPENDABOT: dependabot[bot]
BOT_NAME_RENOVATE: renovate[bot] BOT_NAME_RENOVATE: renovate[bot]
GPG_KEY_BASE64: ${{ secrets.ADMINSERV_GPG_KEY_B64 }}
GPG_KEY_PASSPHRASE: ${{ secrets.ADMINSERV_GPG_PASSPHRASE }}
# # # #
# Jobs # Jobs
@@ -207,6 +209,20 @@ jobs:
packages: write packages: write
steps: steps:
# #
# Release Set Env Variables
# #
- name: >-
🕛 Get Timestamp
id: task_release_label_set_timestamp
run: |
echo "YEAR=$(date +'%Y')" >> $GITHUB_ENV
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV
# # # #
# Release Checkout # Release Checkout
# # # #
@@ -218,17 +234,22 @@ jobs:
fetch-depth: 0 fetch-depth: 0
# # # #
# Release Set Env Variables # Release Start
# # # #
- name: >- - name: >-
🕛 Get Timestamp ✅ Start
id: task_release_label_set_timestamp id: task_release_start
run: | run: |
echo "NOW=$(date +'%m-%d-%Y %H:%M:%S')" >> $GITHUB_ENV echo "―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――"
echo "NOW_SHORT=$(date +'%m-%d-%Y')" >> $GITHUB_ENV echo " Starting Documentation Build script"
echo "NOW_LONG=$(date +'%m-%d-%Y %H:%M')" >> $GITHUB_ENV echo " Runner .............. ${{ runner.name }}"
echo "NOW_DOCKER_LABEL=$(date +'%Y%m%d')" >> $GITHUB_ENV echo " Workflow ............ ${{ github.workflow }} (#${{ github.workflow_ref }})"
echo " Run Number .......... #${{ github.run_number }}"
echo " Run ID .............. https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
echo " Triggered By ........ ${{ github.actor }}"
echo " Time ................ ${{ env.NOW_LONG }}"
echo "―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――"
# # # #
# Release Print Version Debug # Release Print Version Debug
@@ -288,15 +309,17 @@ jobs:
# Release Build Stable # Release Build Stable
# # # #
- name: '🔨 Build Stable ( ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip )' - name: '🔨 Build Stable ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip'
id: task_release_build_st id: task_release_build_st
if: ${{ startsWith( inputs.PRERELEASE, false ) }} if: |
startsWith( inputs.RC_RELEASE, false ) ||
startsWith( env.RC_RELEASE, false )
run: | run: |
echo Building STABLE Package ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip echo Building STABLE Package ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip
zip -r ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip Dockerfile Dockerfile.aarch64 docker-compose.yml docker-entrypoint.sh root/ tvapp2/package.json README.md LICENSE zip -r ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip Dockerfile Dockerfile.aarch64 docker-compose.yml docker-entrypoint.sh root/ tvapp2/package.json README.md LICENSE
echo Building STABLE Package ${{ env.PROJECT_NAME }}-v${{ env.PACKAGE_VERSION }}-docker-compose.zip echo Building STABLE Package ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-docker-compose.zip
zip -r ${{ env.PROJECT_NAME }}-v${{ env.PACKAGE_VERSION }}-docker-compose.zip docker-compose.yml README.md LICENSE zip -r ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-docker-compose.zip docker-compose.yml README.md LICENSE
env: env:
NODE_AUTH_TOKEN: ${{ secrets.ADMINSERV_TOKEN_CL }} NODE_AUTH_TOKEN: ${{ secrets.ADMINSERV_TOKEN_CL }}
@@ -304,15 +327,17 @@ jobs:
# Release Build Release Candidate # Release Build Release Candidate
# # # #
- name: '🔨 Build RC ( ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}.zip )' - name: '🔨 Build Release Candidate ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}.zip'
id: task_release_build_rc id: task_release_build_rc
if: ${{ startsWith( inputs.PRERELEASE, true ) }} if: |
startsWith( inputs.RC_RELEASE, true ) ||
startsWith( env.RC_RELEASE, true )
run: | run: |
echo Building PRE-RELEASE Package ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}.zip echo Building PRE-RELEASE Package ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}.zip
zip -r ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}.zip Dockerfile Dockerfile.aarch64 docker-compose.yml docker-entrypoint.sh root/ tvapp2/package.json README.md LICENSE zip -r ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}.zip Dockerfile Dockerfile.aarch64 docker-compose.yml docker-entrypoint.sh root/ tvapp2/package.json README.md LICENSE
echo Building PRE-RELEASE Package ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}-docker-compose.zip echo Building PRE-RELEASE Package ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}-docker-compose.zip
zip -r ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}-docker-compose.zip docker-compose.yml README.md LICENSE zip -r ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}-docker-compose.zip docker-compose.yml README.md LICENSE
env: env:
NODE_AUTH_TOKEN: ${{ secrets.ADMINSERV_TOKEN_CL }} NODE_AUTH_TOKEN: ${{ secrets.ADMINSERV_TOKEN_CL }}
@@ -346,39 +371,93 @@ jobs:
echo "Tag already present: ${{ env.TAG_EXISTS }}" echo "Tag already present: ${{ env.TAG_EXISTS }}"
echo "Tag already present: ${{ steps.task_release_tag_create.outputs.tag_exists }}" echo "Tag already present: ${{ steps.task_release_tag_create.outputs.tag_exists }}"
# #
# Release GPG Import Key (No Passphrase)
#
# requires your GPG private key, converted to base64 binary .gpg (not armored .asc)
# #
- name: '🪪 GPG Import Signing Key W/o Passphrase'
id: task_release_gpg_import_nopass
if: env.GPG_KEY_BASE64 != '' && env.GPG_KEY_PASSPHRASE == ''
run: |
echo $GPG_KEY_BASE64 | base64 -di | gpg --import
# #
# Release GPG Import Key (With Passphrase)
#
# requires your GPG private key, converted to base64 binary .gpg (not armored .asc)
# #
- name: '🪪 GPG Import Signing Key w/ Passphrase'
id: task_release_gpg_import_withpass
if: env.GPG_KEY_BASE64 != '' && env.GPG_KEY_PASSPHRASE != ''
run: |
echo "$GPG_KEY_BASE64" | base64 -di > /tmp/signing-key.gpg
echo "$GPG_KEY_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg
(echo "$GPG_KEY_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1)
# # # #
# Release Checksum Stable # Release Checksum Stable
# # # #
- name: '🆔 Checksum Stable' - name: '🆔 Checksum Stable'
id: task_release_checksum_st_set id: task_release_checksum_st_set
if: ${{ startsWith( inputs.PRERELEASE, false ) }} if: |
startsWith( inputs.RC_RELEASE, false ) ||
startsWith( env.RC_RELEASE, false )
run: | run: |
filename_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip" filename_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip"
sha256="$(shasum --algorithm 256 ${filename_zip} | awk '{ print $1 }')"
shasum --algorithm 256 ${filename_zip} > SHA256SUMS.txt # get sha1 and sha256 for .zip and .gz files
find . -maxdepth 1 \( -name '*.zip' -o -name '*.gz' \) -printf '%P\n' | xargs -r sha1sum | gpg --digest-algo sha256 --clearsign > sha1sum.txt.asc
find . -maxdepth 1 \( -name '*.zip' -o -name '*.gz' \) -printf '%P\n' | xargs -r sha256sum | gpg --digest-algo sha256 --clearsign > sha256sum.txt.asc
# get sha1sum; assign to variable
sha1sum="$(shasum --algorithm 1 ${filename_zip} | awk '{ print $1 }')"
echo "SHA1SUM=${sha1sum}" >> $GITHUB_ENV
# get sha256sum; assign to variable
sha256sum="$(shasum --algorithm 256 ${filename_zip} | awk '{ print $1 }')"
echo "SHA256SUM=${sha256sum}" >> $GITHUB_ENV
# no longer needed, replaced by find . command
# shasum --algorithm 256 ${filename_zip} > SHA256SUMS.txt
echo "FILE_ZIP=${filename_zip}" >> $GITHUB_ENV echo "FILE_ZIP=${filename_zip}" >> $GITHUB_ENV
echo "SHA256SUM=${sha256}" >> $GITHUB_ENV
filename_compose_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-docker-compose.zip" filename_compose_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-docker-compose.zip"
sha256_compose="$(shasum --algorithm 256 ${filename_compose_zip} | awk '{ print $1 }')" sha256sum_compose="$(shasum --algorithm 256 ${filename_compose_zip} | awk '{ print $1 }')"
echo "FILE_COMPOSE_ZIP=${filename_compose_zip}" >> $GITHUB_ENV echo "FILE_COMPOSE_ZIP=${filename_compose_zip}" >> $GITHUB_ENV
# # # #
# Release Checksum Release Candidate # Release Checksum Release Candidate
# # # #
- name: '🆔 Checksum RC' - name: '🆔 Checksum Release Candidate'
id: task_release_checksum_rc_set id: task_release_checksum_rc_set
if: ${{ startsWith( inputs.PRERELEASE, true ) }} if: |
startsWith( inputs.RC_RELEASE, true ) ||
startsWith( env.RC_RELEASE, true )
run: | run: |
filename_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}.zip" filename_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}.zip"
sha256="$(shasum --algorithm 256 ${filename_zip} | awk '{ print $1 }')"
shasum --algorithm 256 ${filename_zip} > SHA256SUMS.txt
echo "FILE_ZIP=${filename_zip}" >> $GITHUB_ENV
echo "SHA256SUM=${sha256}" >> $GITHUB_ENV
filename_compose_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}-docker-compose.zip" # get sha1 and sha256 for .zip and .gz files
find . -maxdepth 1 \( -name '*.zip' -o -name '*.gz' \) -printf '%P\n' | xargs -r sha1sum | gpg --digest-algo sha256 --clearsign > sha1sum.txt.asc
find . -maxdepth 1 \( -name '*.zip' -o -name '*.gz' \) -printf '%P\n' | xargs -r sha256sum | gpg --digest-algo sha256 --clearsign > sha256sum.txt.asc
# get sha1sum; assign to variable
sha1sum="$(shasum --algorithm 1 ${filename_zip} | awk '{ print $1 }')"
echo "SHA1SUM=${sha1sum}" >> $GITHUB_ENV
# get sha256sum; assign to variable
sha256sum="$(shasum --algorithm 256 ${filename_zip} | awk '{ print $1 }')"
echo "SHA256SUM=${sha256sum}" >> $GITHUB_ENV
# no longer needed, replaced by find . command
# shasum --algorithm 256 ${filename_zip} > SHA256SUMS.txt
echo "FILE_ZIP=${filename_zip}" >> $GITHUB_ENV
filename_compose_zip="${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}-docker-compose.zip"
sha256_compose="$(shasum --algorithm 256 ${filename_compose_zip} | awk '{ print $1 }')" sha256_compose="$(shasum --algorithm 256 ${filename_compose_zip} | awk '{ print $1 }')"
echo "FILE_COMPOSE_ZIP=${filename_compose_zip}" >> $GITHUB_ENV echo "FILE_COMPOSE_ZIP=${filename_compose_zip}" >> $GITHUB_ENV
@@ -404,31 +483,13 @@ jobs:
avatarSize: 42 avatarSize: 42
# # # #
# Release Checksum Add (Stable) # Release Changelog Generate Tags
# # #
# generates a changelog from the github api. requires a PREVIOUS_TAG in order to figure
- name: '📦 Zip Add Checksum Stable' # out the changes made between the two versions.
id: task_release_zip_st #
if: ${{ startsWith( inputs.PRERELEASE, false ) }} # outputs:
run: | # ${{ steps.changelog.outputs.changelog }}
echo Zipping STABLE Package .zip ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip
zip -jr ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip SHA256SUMS.txt
ls
# #
# Release Checksum Add (Release Candidate)
# #
- name: '📦 Zip Add Checksum RC'
id: task_release_zip_rc
if: ${{ startsWith( inputs.PRERELEASE, true ) }}
run: |
echo Zipping PRE-RELEASE Package .zip ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}.zip
zip -jr ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}.zip SHA256SUMS.txt
ls
# #
# Release Generate Tags
# # # #
- name: '📝 Changelog Pre Setup (Categorized Commits)' - name: '📝 Changelog Pre Setup (Categorized Commits)'
@@ -543,14 +604,56 @@ jobs:
echo "$CHANGELOG_UNCATEGORIZED" echo "$CHANGELOG_UNCATEGORIZED"
# # # #
# Release List Tree # Release Verbose List Tree
# # # #
- name: '⚙️ Debug Tree Listing' - name: '⚙️ Verbose Tree Listing'
id: task_release_debug_tree id: task_release_verbose_tree
run: | run: |
tree tree
# #
# Release Verbose Changelog Print Categorized
# #
- name: '⚙️ Verbose Changelog Categorized'
id: task_release_changelog_verbose_categorized
if: |
startsWith( inputs.SHOW_UNCATEGORIZED, false ) ||
startsWith( env.SHOW_UNCATEGORIZED, false )
env:
CHANGELOG_CATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
CHANGELOG_UNCATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
run: |
echo
echo "---- CHANGELOG [ Categorized ] -----------------------------------------------"
echo
echo "$CHANGELOG_CATEGORIZED"
echo
echo "---- CHANGELOG ---------------------------------------------------------------"
echo
# #
# Release Verbose Changelog Print Uncategorized
# #
- name: '⚙️ Verbose Changelog Uncategorized'
id: task_release_changelog_verbose_uncategorized
if: |
startsWith( inputs.SHOW_UNCATEGORIZED, true ) ||
startsWith( env.SHOW_UNCATEGORIZED, true )
env:
CHANGELOG_CATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
CHANGELOG_UNCATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
run: |
echo
echo "---- CHANGELOG [ Uncategorized ] ---------------------------------------------"
echo
echo "$CHANGELOG_UNCATEGORIZED"
echo
echo "---- CHANGELOG ---------------------------------------------------------------"
echo
# # # #
# Release Post Release (Stable) # Release Post Release (Stable)
# #
@@ -563,10 +666,12 @@ jobs:
- name: '🏳️ Post Stable' - name: '🏳️ Post Stable'
id: task_release_bundle_st id: task_release_bundle_st
if: | if: |
startsWith( inputs.PRERELEASE, false ) || startsWith( inputs.RC_RELEASE, false ) ||
startsWith( env.PRERELEASE, false ) startsWith( env.RC_RELEASE, false )
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
env: env:
CHANGELOG_CATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
CHANGELOG_UNCATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
GITHUB_TOKEN: ${{ secrets.ADMINSERV_TOKEN_CL }} GITHUB_TOKEN: ${{ secrets.ADMINSERV_TOKEN_CL }}
with: with:
token: ${{ secrets.ADMINSERV_TOKEN_CL }} token: ${{ secrets.ADMINSERV_TOKEN_CL }}
@@ -576,9 +681,10 @@ jobs:
draft: false draft: false
generate_release_notes: false generate_release_notes: false
files: | files: |
${{ env.PROJECT_NAME }}-v${{ env.PACKAGE_VERSION }}-docker-compose.zip ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-docker-compose.zip
${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip
SHA256SUMS.txt sha1sum.txt.asc
sha256sum.txt.asc
prerelease: false prerelease: false
body: | body: |
${{ steps.task_release_changelog_categorized.outputs.changelog }} ${{ steps.task_release_changelog_categorized.outputs.changelog }}
@@ -593,14 +699,16 @@ jobs:
# ${{ steps.task_release_bundle_st.outputs.id # ${{ steps.task_release_bundle_st.outputs.id
# # # #
- name: "🏳️ Post Release Candidate" - name: '🏳️ Post Release Candidate'
id: task_release_bundle_rc id: task_release_bundle_rc
if: | if: |
startsWith( inputs.PRERELEASE, true ) || startsWith( inputs.RC_RELEASE, true ) ||
startsWith( env.PRERELEASE, true ) startsWith( env.RC_RELEASE, true )
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v2
env: env:
GITHUB_TOKEN: ${{ secrets.ADMINSERV_TOKEN }} CHANGELOG_CATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
CHANGELOG_UNCATEGORIZED: ${{ steps.task_release_changelog_categorized.outputs.changelog }}
GITHUB_TOKEN: ${{ secrets.ADMINSERV_TOKEN_CL }}
with: with:
token: ${{ secrets.ADMINSERV_TOKEN }} token: ${{ secrets.ADMINSERV_TOKEN }}
name: v${{ env.PACKAGE_VERSION }} name: v${{ env.PACKAGE_VERSION }}
@@ -609,9 +717,10 @@ jobs:
draft: false draft: false
generate_release_notes: false generate_release_notes: false
files: | files: |
${{ env.PROJECT_NAME }}-v${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}-docker-compose.zip ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}-docker-compose.zip
${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.VERSION_RC }}.zip ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}.zip
SHA256SUMS.txt sha1sum.txt.asc
sha256sum.txt.asc
prerelease: false prerelease: false
body: | body: |
> [!WARNING] > [!WARNING]
@@ -655,19 +764,37 @@ jobs:
echo "---- CHANGELOG ---------------------------------------------------------------" echo "---- CHANGELOG ---------------------------------------------------------------"
# # # #
# Release Upload Artifacts Release Files # Release Upload Artifacts Release Files Stable
# # # #
- name: >- - name: >-
📋 Upload Artifacts ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip 📋 Upload Artifacts Stable ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip
id: task_release_artifact id: task_release_artifact_stable
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
if: always() if: |
startsWith( inputs.RC_RELEASE, false ) ||
startsWith( env.RC_RELEASE, false )
with: with:
name: "${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}" name: "${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}"
path: ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip path: ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip
retention-days: 30 retention-days: 30
# #
# Release Upload Artifacts Release Files Release Candidate
# #
- name: >-
📋 Upload Artifacts RC ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}.zip
id: task_release_artifact_rc
uses: actions/upload-artifact@v4
if: |
startsWith( inputs.RC_RELEASE, true ) ||
startsWith( env.RC_RELEASE, true )
with:
name: "${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}"
path: ${{ env.PROJECT_NAME }}-${{ env.PACKAGE_VERSION }}-rc.${{ inputs.RC_VERSION }}.zip
retention-days: 30
# # # #
# Job Complete # Job Complete
# # # #

View File

@@ -169,17 +169,17 @@ Our repositories make use of the following commit tags:
| Type | Description | | Type | Description |
| --- | --- | | --- | --- |
| `feat` | <sup><sub>Introduce new feature</sub></sup> | | `feat` | Introduce new feature |
| `fix` | <sup><sub>Bug fix</sub></sup> | | `fix` | Bug fix |
| `chore` | <sup><sub>Includes technical or preventative maintenance task that is necessary for managing the app or repo, such as updating grunt tasks, but is not tied to any specific feature. Usually done for maintenance purposes.<br/>E.g: Edit .gitignore, .prettierrc, .prettierignore, .gitignore, eslint.config.js file</sub></sup> | | `chore` | Includes technical or preventative maintenance task that is necessary for managing the app or repo, such as updating grunt tasks, but is not tied to any specific feature. Usually done for maintenance purposes.<br/>E.g: Edit .gitignore, .prettierrc, .prettierignore, .gitignore, eslint.config.js file |
| `revert` | <sup><sub>Revert a previous commit</sub></sup> | | `revert` | Revert a previous commit |
| `style` | <sup><sub>Update / reformat style of source code. Does not change the way app is implemented. Changes that do not affect the meaning of the code<br />E.g: white-space, formatting, missing semi-colons, change tabs to spaces, etc)</sub></sup> | | `style` | Update / reformat style of source code. Does not change the way app is implemented. Changes that do not affect the meaning of the code<br />E.g: white-space, formatting, missing semi-colons, change tabs to spaces, etc) |
| `docs` | <sup><sub>Change website or markdown documents. Does not mean changes to the documentation generator script itself, only the documents created from the generator. <br/>E.g: documentation, readme.md or markdown | | `docs` | Change website or markdown documents. Does not mean changes to the documentation generator script itself, only the documents created from the generator. <br/>E.g: documentation, readme.md or markdown |
| `build` | <sup><sub>Changes to the build / compilation / packaging process or auxiliary tools such as doc generation<br />E.g: create new build tasks, update release script, etc.</sub></sup> | | `build` | Changes to the build / compilation / packaging process or auxiliary tools such as doc generation<br />E.g: create new build tasks, update release script, etc. |
| `refactor` | <sup><sub>Change to production code that leads to no behavior difference,<br/>E.g: split files, rename variables, rename package, improve code style, etc.</sub></sup> | | `refactor` | Change to production code that leads to no behavior difference,<br/>E.g: split files, rename variables, rename package, improve code style, etc. |
| `test` | <sup><sub>Add or refactor tests, no production code change. Changes the suite of automated tests for the app.</sub></sup> | | `test` | Add or refactor tests, no production code change. Changes the suite of automated tests for the app. |
| `ci` | <sup><sub>Changes related to Continuous Integration (usually `yml` and other configuration files).</sub></sup> | | `ci` | Changes related to Continuous Integration (usually `yml` and other configuration files). |
| `perf` | <sup><sub>Performance improvement of algorithms or execution time of the app. Does not change an existing feature.</sub></sup> | | `perf` | Performance improvement of algorithms or execution time of the app. Does not change an existing feature. |
<br /> <br />

View File

@@ -9,14 +9,31 @@
# https://git.binaryninja.net/BinaryNinja/tvapp2 # https://git.binaryninja.net/BinaryNinja/tvapp2
# https://github.com/aetherinox/docker-base-alpine # https://github.com/aetherinox/docker-base-alpine
# #
# you can build your own image by running # build your own image by running
# amd64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 -t tvapp2:1.0.0-amd64 -f Dockerfile . # amd64 docker build --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 -t tvapp2:latest -t tvapp2:1.5.0 -t tvapp2:1.5.0-amd64 -f Dockerfile .
# arm64 docker build --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:1.0.0-arm64 -f Dockerfile.aarch64 . # arm64 docker build --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 -t tvapp2:1.5.0-arm64 -f Dockerfile.aarch64 .
# #
# if you prefer to use `docker buildx` # OR; build using `docker buildx`
# create docker buildx create --driver docker-container --name container --bootstrap --use # create docker buildx create --driver docker-container --name container --bootstrap --use
# amd64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/amd64 --output type=docker --output type=docker . # amd64 docker buildx build --build-arg ARCH=amd64 --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 --build-arg RELEASE=stable --tag ghcr.io/thebinaryninja/tvapp2:1.5.0-amd64 --attest type=provenance,disabled=true --attest type=sbom,disabled=true --file Dockerfile --platform linux/amd64 --output type=docker --allow network.host --network host --no-cache --pull --push .
# arm64 docker buildx build --no-cache --pull --build-arg VERSION=1.0.0 --build-arg BUILDDATE=20250218 -t tvapp2:latest -t tvapp2:1.0.0 --platform=linux/arm64 --output type=docker --output type=docker . # arm64 docker buildx build --build-arg ARCH=arm64 --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 --build-arg RELEASE=stable --tag ghcr.io/thebinaryninja/tvapp2:1.5.0-arm64 --attest type=provenance,disabled=true --attest type=sbom,disabled=true --file Dockerfile --platform linux/arm64 --output type=docker --allow network.host --network host --no-cache --pull --push .
#
# OR; build single amd64 image
# create docker buildx create --driver docker-container --name container --bootstrap --use
# amd64 docker buildx build --build-arg ARCH=amd64 --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 --build-arg RELEASE=stable --tag ghcr.io/thebinaryninja/tvapp2:1.5.0 --tag ghcr.io/thebinaryninja/tvapp2:1.5 --tag ghcr.io/thebinaryninja/tvapp2:1 --tag ghcr.io/thebinaryninja/tvapp2:latest --attest type=provenance,disabled=true --attest type=sbom,disabled=true --file Dockerfile --platform linux/amd64 --output type=docker --allow network.host --network host --no-cache --push .
#
# OR; build official image (publish)
# create docker buildx create --driver docker-container --name container --bootstrap --use
# amd64-stable docker buildx build --build-arg ARCH=amd64 --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 --build-arg RELEASE=stable --tag ghcr.io/thebinaryninja/tvapp2:1.5.0-amd64 --attest type=provenance,disabled=true --attest type=sbom,disabled=true --file Dockerfile --platform linux/amd64 --output type=docker --allow network.host --network host --no-cache --pull --push .
# arm64-stable docker buildx build --build-arg ARCH=arm64 --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 --build-arg RELEASE=stable --tag ghcr.io/thebinaryninja/tvapp2:1.5.0-arm64 --attest type=provenance,disabled=true --attest type=sbom,disabled=true --file Dockerfile --platform linux/arm64 --output type=docker --allow network.host --network host --no-cache --pull --push .
# amd64-dev docker buildx build --build-arg ARCH=amd64 --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 --build-arg RELEASE=development --tag ghcr.io/thebinaryninja/tvapp2:development-amd64 --attest type=provenance,disabled=true --attest type=sbom,disabled=true --file Dockerfile --platform linux/amd64 --output type=docker --allow network.host --network host --no-cache --pull --push .
# arm64-dev docker buildx build --build-arg ARCH=arm64 --build-arg VERSION=1.5.0 --build-arg BUILDDATE=20260812 --build-arg RELEASE=development --tag ghcr.io/thebinaryninja/tvapp2:development-arm64 --attest type=provenance,disabled=true --attest type=sbom,disabled=true --file Dockerfile --platform linux/arm64 --output type=docker --allow network.host --network host --no-cache --pull --push .
# amd64-stable-hash docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.5.0-amd64
# arm64-stable-hash docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.5.0-arm64
# amd64-dev-hash docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:development-amd64
# arm64-dev-hash docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:development-arm64
# merge-stable docker buildx imagetools create --tag ghcr.io/thebinaryninja/tvapp2:1.5.0 --tag ghcr.io/thebinaryninja/tvapp2:1.5 --tag ghcr.io/thebinaryninja/tvapp2:1 --tag ghcr.io/thebinaryninja/tvapp2:latest sha256:0abe1b1c119959b3b1ccc23c56a7ee2c4c908c6aaef290d4ab2993859d807a3b sha256:e68b9de8669eac64d4e4d2a8343c56705e05e9a907cf0b542343f9b536d9c473
# merge-dev docker buildx imagetools create --tag ghcr.io/thebinaryninja/tvapp2:development sha256:8f36385a28c8f6eb7394d903c9a7a2765b06f94266b32628389ee9e3e3d7e69d sha256:c719ccb034946e3f0625003f25026d001768794e38a1ba8aafc9146291d548c5
# # # #
# # # #
@@ -28,16 +45,19 @@
# # # #
ARG ARCH=amd64 ARG ARCH=amd64
FROM --platform=linux/${ARCH} ghcr.io/aetherinox/alpine-base:3.21 ARG ALPINE_VERSION=3.21
FROM --platform=linux/${ARCH} ghcr.io/aetherinox/alpine-base:${ALPINE_VERSION}
# # # #
# Set Args # Set Args
# # # #
ARG ARCH=amd64 ARG ARCH=amd64
ARG ALPINE_VERSION=3.21
ARG BUILDDATE ARG BUILDDATE
ARG VERSION ARG VERSION
ARG RELEASE ARG RELEASE
ARG GIT_SHA1=0000000000000000000000000000000000000000
# # # #
# Set Labels # Set Labels
@@ -60,6 +80,10 @@ LABEL org.opencontainers.image.registry="local"
LABEL org.opencontainers.image.release="${RELEASE}" LABEL org.opencontainers.image.release="${RELEASE}"
LABEL org.tvapp2.image.maintainers="Aetherinox, iFlip721, Optx" LABEL org.tvapp2.image.maintainers="Aetherinox, iFlip721, Optx"
LABEL org.tvapp2.image.build-version="Version:- ${VERSION} Date:- ${BUILDDATE}" LABEL org.tvapp2.image.build-version="Version:- ${VERSION} Date:- ${BUILDDATE}"
LABEL org.tvapp2.image.build-version-alpine="${ALPINE_VERSION}"
LABEL org.tvapp2.image.build-architecture="${ARCH}"
LABEL org.tvapp2.image.build-release="${RELEASE}"
LABEL org.tvapp2.image.build-sha1="${GIT_SHA1}"
# # # #
# Set Env Var # Set Env Var
@@ -74,14 +98,17 @@ ENV URL_REPO="https://git.binaryninja.net/binaryninja/"
ENV WEB_IP="0.0.0.0" ENV WEB_IP="0.0.0.0"
ENV WEB_PORT=4124 ENV WEB_PORT=4124
ENV WEB_ENCODING="deflate, br" ENV WEB_ENCODING="deflate, br"
ENV WEB_PROXY_HEADER="x-forwarded-for"
ENV STREAM_QUALITY="hd" ENV STREAM_QUALITY="hd"
ENV FILE_URL="urls.txt" ENV FILE_URL="urls.txt"
ENV FILE_M3U="playlist.m3u8" ENV FILE_M3U="playlist.m3u8"
ENV FILE_EPG="xmltv.xml" ENV FILE_EPG="xmltv.xml"
ENV FILE_TAR="xmltv.xml.gz" ENV FILE_TAR="xmltv.xml.gz"
ENV HEALTH_TIMER=600000 ENV HEALTH_TIMER=600000
ENV TASK_CRON_SYNC="0 0 */3 * *"
ENV LOG_LEVEL=4 ENV LOG_LEVEL=4
ENV TZ="Etc/UTC" ENV TZ="Etc/UTC"
ENV GIT_SHA1=${GIT_SHA1:-0000000000000000000000000000000000000000}
# # # #
# Install # Install
@@ -90,8 +117,10 @@ ENV TZ="Etc/UTC"
RUN \ RUN \
apk add --no-cache \ apk add --no-cache \
wget \ wget \
curl \
bash \ bash \
nano \ nano \
git \
npm \ npm \
openssl openssl

266
README.md
View File

@@ -7,7 +7,7 @@
<div align="center"> <div align="center">
<img src="docs/img/screenshots/01.png" height="320"> <img src="docs/img/screenshots/01.gif" height="450">
<br /> <br />
<br /> <br />
@@ -70,6 +70,10 @@
- [Environment Variables](#environment-variables-1) - [Environment Variables](#environment-variables-1)
- [Mountable Volumes](#mountable-volumes-1) - [Mountable Volumes](#mountable-volumes-1)
- [Docker health check](#docker-health-check) - [Docker health check](#docker-health-check)
- [Logging](#logging)
- [Trace (7)](#trace-7)
- [Verbose (6)](#verbose-6)
- [Debug (5)](#debug-5)
- [Traefik Integration](#traefik-integration) - [Traefik Integration](#traefik-integration)
- [Labels](#labels) - [Labels](#labels)
- [Dynamic.yml](#dynamicyml) - [Dynamic.yml](#dynamicyml)
@@ -150,21 +154,46 @@ For the [environment variables](#environment-variables), you may specify these i
#### Environment Variables #### Environment Variables
The following is a list of environment variables you can declare within your `docker-compose.yml` or docker run command:
<br />
> [!CAUTION]
> Do **not** add `"` quotation marks to environment variables.
>
> ✔️ Correct
> ```yml
> environment:
> - TASK_CRON_SYNC=*/60 * * * *
> ```
>
> ❌ Incorrect
> ```yml
> environment:
> - TASK_CRON_SYNC="*/60 * * * *"
> ```
<br />
| Env Var | Default | Description | | Env Var | Default | Description |
| --- | --- | --- | | --- | --- | --- |
| `TZ` | `Etc/UTC` | Timezone for error / log reporting | | `TZ` | `Etc/UTC` | Timezone for error / log reporting |
| `WEB_IP` | `0.0.0.0` | IP to use for webserver | | `WEB_IP` | `0.0.0.0` | IP to use for webserver |
| `WEB_PORT` | `4124` | Port to use for webserver | | `WEB_PORT` | `4124` | Port to use for webserver |
| `WEB_ENCODING` | `deflate, br` | Defines the HTTP `Accept-Encoding` request and response header. This value specifies what content encoding the sender can understand<br /><br />Gzip compression can be enabled by specifying `'gzip, deflate, br'` | | `WEB_FOLDER` | `www` | Internal container folder to keep TVApp2 web files in. <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> |
| `WEB_ENCODING` | `deflate, br` | Defines the HTTP `Accept-Encoding` request and response header. This value specifies what content encoding the sender can understand<br /><br />Gzip compression can be enabled by specifying `'gzip, deflate, br'`, however, [it may break Jellyfin users](#build-error-err-27-jellyfinlivetvguideguidemanager-error-getting-programs-for-channel-xxxxxxxxxxxxxxx-source-2-systemxmlxmlexception--hexadecimal-value-0x1f-is-an-invalid-character-line-1-position-1). |
| `WEB_PROXY_HEADER` | `x-forwarded-for` | Defines the header to look for when finding a client's IP address. Used to get a client's IP when behind a reverse proxy or Cloudflare |
| `URL_REPO` | `https://git.binaryninja.net/BinaryNinja/` | Determines where the data files will be downloaded from. Do not change this or you will be unable to get M3U and EPG data. | | `URL_REPO` | `https://git.binaryninja.net/BinaryNinja/` | Determines where the data files will be downloaded from. Do not change this or you will be unable to get M3U and EPG data. |
| `FILE_URL` | `urls.txt` | Filename for `urls.txt` cache file | | `FILE_URL` | `urls.txt` | Filename for `urls.txt` cache file |
| `FILE_M3U` | `playlist.m3u8` | Filename for M3U playlist file | | `FILE_M3U` | `playlist.m3u8` | Filename for M3U playlist file |
| `FILE_EPG` | `xmltv.xml` | Filename for XML guide data file | | `FILE_EPG` | `xmltv.xml` | Filename for XML guide data file |
| `FILE_GZP` | `xmltv.xml.gz` | Filename for XML compressed as gzip .gz | | `FILE_GZP` | `xmltv.xml.gz` | Filename for XML compressed as gzip .gz |
| `STREAM_QUALITY` | `hd` | Stream quality<br />Can be either `hd` or `sd` | | `STREAM_QUALITY` | `hd` | Stream quality<br />Can be either `hd` or `sd` |
| `TASK_CRON_SYNC` | `0 0 */3 * *` | Defines how often to refresh the M3U and XML IPTV data |
| `HEALTH_TIMER` | `600000` | How often (in milliseconds) to run a health check |
| `DIR_BUILD` | `/usr/src/app` | Path inside container where TVApp2 will be built. <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> | | `DIR_BUILD` | `/usr/src/app` | Path inside container where TVApp2 will be built. <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> |
| `DIR_RUN` | `/usr/bin/app` | Path inside container where TVApp2 will be placed after it is built <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> | | `DIR_RUN` | `/usr/bin/app` | Path inside container where TVApp2 will be placed after it is built <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> |
| `LOG_LEVEL` | `4` | Level of logging to display in console<br/>`6` Trace <sup><sub>& below</sub></sup><br />`5` Debug <sup><sub>& below</sub></sup><br />`4` Info <sup><sub>& below</sub></sup><br />`3` Notice <sup><sub>& below</sub></sup><br />`2` Warn <sup><sub>& below</sub></sup><br />`1` Error <sup><sub>only</sub></sup> | | `LOG_LEVEL` | `4` | Level of logging to display in console<br/>`7` Trace <sup><sub>& below</sub></sup><br />`6` Verbose <sup><sub>& below</sub></sup><br />`5` Debug <sup><sub>& below</sub></sup><br />`4` Info <sup><sub>& below</sub></sup><br />`3` Notice <sup><sub>& below</sub></sup><br />`2` Warn <sup><sub>& below</sub></sup><br />`1` Error <sup><sub>only</sub></sup> |
<br /> <br />
<br /> <br />
@@ -313,7 +342,7 @@ subgraph GRAPH_TVAPP ["Build tvapp2:latest"]
Dockerfile.aarch64**`"] Dockerfile.aarch64**`"]
obj_step12["`&gt; docker build &bsol; obj_step12["`&gt; docker build &bsol;
--build-arg VERSION=1.0.0 &bsol; --build-arg VERSION=1.0.0 &bsol;
--build-arg BUILDDATE=20250225 &bsol; --build-arg BUILDDATE=20260812 &bsol;
-t tvapp:latest &bsol; -t tvapp:latest &bsol;
-t tvapp:1.0.0-amd64 &bsol; -t tvapp:1.0.0-amd64 &bsol;
-f Dockerfile . &bsol;`"] -f Dockerfile . &bsol;`"]
@@ -335,7 +364,7 @@ direction TB
Dockerfile.aarch64**`"] Dockerfile.aarch64**`"]
obj_step22["`&gt; docker build &bsol; obj_step22["`&gt; docker build &bsol;
--build-arg VERSION=3.20 &bsol; --build-arg VERSION=3.20 &bsol;
--build-arg BUILDDATE=20250225 &bsol; --build-arg BUILDDATE=20260812 &bsol;
-t docker-alpine-base:latest &bsol; -t docker-alpine-base:latest &bsol;
-t docker-alpine-base:3.20-amd64 &bsol; -t docker-alpine-base:3.20-amd64 &bsol;
-f Dockerfile . &bsol;`"] -f Dockerfile . &bsol;`"]
@@ -360,7 +389,7 @@ This repository offers two types of docker image; `stable` and `development`. Yo
| Build | Tags | | Build | Tags |
| ------------------------- | ----------------------------------------------------------------------------- | | ------------------------- | ----------------------------------------------------------------------------- |
| `Stable` | `🔖 tvapp2:latest` <br /> `🔖 tvapp2:1.2.0` <br /> `🔖 tvapp2:1.2` <br /> `🔖 tvapp2:1` | | `Stable` | `🔖 tvapp2:latest` <br /> `🔖 tvapp2:1.5.2` <br /> `🔖 tvapp2:1.5` <br /> `🔖 tvapp2:1` |
| `Development` | `🔖 tvapp2:development` | | `Development` | `🔖 tvapp2:development` |
<br /> <br />
@@ -395,7 +424,11 @@ If you cloned the files from the official repository [🔆 gitea:binaryninja/tva
> [!CAUTION] > [!CAUTION]
> Be careful using the command to change **ALL** files. You should **NOT** change the files in your `📁 .git` folder, otherwise you will corrupt your git indexes. > Be careful using the command to change **ALL** files. You should **NOT** change the files in your `📁 .git` folder, otherwise you will corrupt your git indexes.
> >
> If you accidentally run `🗔 dos2unix` on your `📁 .git` folder, do NOT push anything to git. Pull a new copy from the repo. > If you accidentally run `🗔 dos2unix` on your `📁 .git` folder, do NOT push anything to git. Pull a new copy from the repo or reset your local files back to the remote:
>
> ```shell
> git reset --hard origin/main
> ```
<br /> <br />
@@ -432,7 +465,8 @@ sudo chmod +x ./root/etc/s6-overlay/s6-rc.d/init-adduser/run \
./root/etc/s6-overlay/s6-rc.d/init-permissions/run \ ./root/etc/s6-overlay/s6-rc.d/init-permissions/run \
./root/etc/s6-overlay/s6-rc.d/init-samples/run \ ./root/etc/s6-overlay/s6-rc.d/init-samples/run \
./root/etc/s6-overlay/s6-rc.d/init-version-checks/run \ ./root/etc/s6-overlay/s6-rc.d/init-version-checks/run \
./root/etc/s6-overlay/s6-rc.d/svc-cron/run ./root/etc/s6-overlay/s6-rc.d/svc-cron/run \
./root/etc/services.d/tvapp2/run
``` ```
<br /> <br />
@@ -443,7 +477,7 @@ After completing the steps above; we will now build the [🔆 gitea:binaryninja/
<br /> <br />
Before you build the TVApp2 image; open the `📄 Dockerfile` and ensure you are pulling the correct Alpine base image. This instruction is located near the top of the `📄 Dockerfile`: Open the `📄 Dockerfile` and ensure you are pulling the correct Alpine base image. This code is located near the top of the `📄 Dockerfile`:
```dockerfile ```dockerfile
ARG ARCH=amd64 ARG ARCH=amd64
@@ -539,7 +573,7 @@ Waiting for authentication in the browser…
<br /> <br />
Once you are finished in your browser, you can return to your Linux terminal, and it should bring you back to where you can type a command. You can now verify again if you are signed in: Once finished in your browser, return to your Linux terminal, and it should bring you back to where you can type a command. You can now verify again if you are signed in:
```shell ```shell
docker info | grep Username docker info | grep Username
@@ -567,17 +601,19 @@ Creates the TVApp2 `amd64` docker image:
# Build tvapp2 amd64 # Build tvapp2 amd64
docker buildx build \ docker buildx build \
--build-arg ARCH=amd64 \ --build-arg ARCH=amd64 \
--build-arg VERSION=1.2.0 \ --build-arg VERSION=1.5.2 \
--build-arg BUILDDATE=20250407 \ --build-arg BUILDDATE=20260812 \
--tag ghcr.io/thebinaryninja/tvapp2:1.2.0 \ --build-arg RELEASE=stable \
--tag ghcr.io/thebinaryninja/tvapp2:1.2 \ --tag ghcr.io/thebinaryninja/tvapp2:1.5.2 \
--tag ghcr.io/thebinaryninja/tvapp2:2 \ --tag ghcr.io/thebinaryninja/tvapp2:1.5 \
--tag ghcr.io/thebinaryninja/tvapp2:1 \
--tag ghcr.io/thebinaryninja/tvapp2:latest \ --tag ghcr.io/thebinaryninja/tvapp2:latest \
--attest type=provenance,disabled=true \ --attest type=provenance,disabled=true \
--attest type=sbom,disabled=true \ --attest type=sbom,disabled=true \
--output type=docker \
--builder default \
--file Dockerfile \ --file Dockerfile \
--platform linux/amd64 \ --platform linux/amd64 \
--output type=docker \
--allow network.host \ --allow network.host \
--network host \ --network host \
--no-cache \ --no-cache \
@@ -595,17 +631,19 @@ Creates the TVApp2 `arm64` docker image:
# Build tvapp2 arm64 # Build tvapp2 arm64
docker buildx build \ docker buildx build \
--build-arg ARCH=arm64 \ --build-arg ARCH=arm64 \
--build-arg VERSION=1.2.0 \ --build-arg VERSION=1.5.2 \
--build-arg BUILDDATE=20250407 \ --build-arg BUILDDATE=20260812 \
--tag ghcr.io/thebinaryninja/tvapp2:1.2.0 \ --build-arg RELEASE=stable \
--tag ghcr.io/thebinaryninja/tvapp2:1.2 \ --tag ghcr.io/thebinaryninja/tvapp2:1.5.2 \
--tag ghcr.io/thebinaryninja/tvapp2:1.5 \
--tag ghcr.io/thebinaryninja/tvapp2:1 \ --tag ghcr.io/thebinaryninja/tvapp2:1 \
--tag ghcr.io/thebinaryninja/tvapp2:latest \ --tag ghcr.io/thebinaryninja/tvapp2:latest \
--attest type=provenance,disabled=true \ --attest type=provenance,disabled=true \
--attest type=sbom,disabled=true \ --attest type=sbom,disabled=true \
--output type=docker \
--builder default \
--file Dockerfile \ --file Dockerfile \
--platform linux/arm64 \ --platform linux/arm64 \
--output type=docker \
--allow network.host \ --allow network.host \
--network host \ --network host \
--no-cache \ --no-cache \
@@ -727,8 +765,8 @@ docker run --privileged --rm tonistiigi/binfmt --install all
Once the emulator is installed; we will now build two images. When building these two images; we will ensure the `--tag` value is different for each one, by adding the architecture to the end. This ensures we don't overwrite one image with the newer one. We need to have two seperate docker images with two different tags. Once the emulator is installed; we will now build two images. When building these two images; we will ensure the `--tag` value is different for each one, by adding the architecture to the end. This ensures we don't overwrite one image with the newer one. We need to have two seperate docker images with two different tags.
- `--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64` - `--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64`
- `--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64` - `--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64`
<br /> <br />
@@ -739,14 +777,14 @@ Once the emulator is installed; we will now build two images. When building thes
> >
> | Registry | Tag | > | Registry | Tag |
> | --- | --- | > | --- | --- |
> | Dockerhub | `--tag thebinaryninja/tvapp2:1.2.0-amd64`<br>`--tag thebinaryninja/tvapp2:1.2.0-arm64` | > | Dockerhub | `--tag thebinaryninja/tvapp2:1.5.2-amd64`<br>`--tag thebinaryninja/tvapp2:1.5.2-arm64` |
> | Github (GHCR) | `--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64`<br>`--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64` | > | Github (GHCR) | `--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64`<br>`--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64` |
> | Registry v2 | `--tag registry.domain.lan/thebinaryninja/tvapp2:1.2.0-amd64`<br>`--tag registry.domain.lan/thebinaryninja/tvapp2:1.2.0-arm64` | > | Registry v2 | `--tag registry.domain.lan/thebinaryninja/tvapp2:1.5.2-amd64`<br>`--tag registry.domain.lan/thebinaryninja/tvapp2:1.5.2-arm64` |
> | Gitea | `--tag git.binaryninja.net/binaryninja/tvapp2:1.2.0-amd64`<br>`--tag git.binaryninja.net/binaryninja/tvapp2:1.2.0-arm64` | > | Gitea | `--tag git.binaryninja.net/binaryninja/tvapp2:1.5.2-amd64`<br>`--tag git.binaryninja.net/binaryninja/tvapp2:1.5.2-arm64` |
<br /> <br />
After we built these two images and push them to a registry online, we will merge them into a single docker image which contains both arcitectures. After we build these two images and push them to a registry online; merge them into a single docker image which contains both arcitectures.
<br /> <br />
@@ -763,14 +801,16 @@ Creates the TVApp2 **Stable** release `amd64` docker image:
# Build Tvapp2 amd64 - (stable release) # Build Tvapp2 amd64 - (stable release)
docker buildx build \ docker buildx build \
--build-arg ARCH=amd64 \ --build-arg ARCH=amd64 \
--build-arg VERSION=1.2.0 \ --build-arg VERSION=1.5.2 \
--build-arg BUILDDATE=20250407 \ --build-arg BUILDDATE=20260812 \
--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64 \ --build-arg RELEASE=stable \
--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64 \
--attest type=provenance,disabled=true \ --attest type=provenance,disabled=true \
--attest type=sbom,disabled=true \ --attest type=sbom,disabled=true \
--output type=docker \
--builder default \
--file Dockerfile \ --file Dockerfile \
--platform linux/amd64 \ --platform linux/amd64 \
--output type=docker \
--allow network.host \ --allow network.host \
--network host \ --network host \
--no-cache \ --no-cache \
@@ -789,14 +829,16 @@ Creates the TVApp2 **Stable** release `arm64` docker image:
# Build Tvapp2 arm64 - (stable release) # Build Tvapp2 arm64 - (stable release)
docker buildx build \ docker buildx build \
--build-arg ARCH=arm64 \ --build-arg ARCH=arm64 \
--build-arg VERSION=1.2.0 \ --build-arg VERSION=1.5.2 \
--build-arg BUILDDATE=20250407 \ --build-arg BUILDDATE=20260812 \
--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64 \ --build-arg RELEASE=stable \
--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64 \
--attest type=provenance,disabled=true \ --attest type=provenance,disabled=true \
--attest type=sbom,disabled=true \ --attest type=sbom,disabled=true \
--output type=docker \
--builder default \
--file Dockerfile \ --file Dockerfile \
--platform linux/arm64 \ --platform linux/arm64 \
--output type=docker \
--allow network.host \ --allow network.host \
--network host \ --network host \
--no-cache \ --no-cache \
@@ -815,14 +857,16 @@ Creates the TVApp2 **Development** release `amd64` docker image:
# Build Tvapp2 amd64 - (development release) # Build Tvapp2 amd64 - (development release)
docker buildx build \ docker buildx build \
--build-arg ARCH=amd64 \ --build-arg ARCH=amd64 \
--build-arg VERSION=1.2.0 \ --build-arg VERSION=1.5.2 \
--build-arg BUILDDATE=20250407 \ --build-arg BUILDDATE=20260812 \
--build-arg RELEASE=development \
--tag ghcr.io/thebinaryninja/tvapp2:development-amd64 \ --tag ghcr.io/thebinaryninja/tvapp2:development-amd64 \
--attest type=provenance,disabled=true \ --attest type=provenance,disabled=true \
--attest type=sbom,disabled=true \ --attest type=sbom,disabled=true \
--output type=docker \
--builder default \
--file Dockerfile \ --file Dockerfile \
--platform linux/amd64 \ --platform linux/amd64 \
--output type=docker \
--allow network.host \ --allow network.host \
--network host \ --network host \
--no-cache \ --no-cache \
@@ -841,14 +885,16 @@ Creates the TVApp2 **Development** release `arm64` docker image:
# Build Tvapp2 arm64 - (development release) # Build Tvapp2 arm64 - (development release)
docker buildx build \ docker buildx build \
--build-arg ARCH=arm64 \ --build-arg ARCH=arm64 \
--build-arg VERSION=1.2.0 \ --build-arg VERSION=1.5.2 \
--build-arg BUILDDATE=20250407 \ --build-arg BUILDDATE=20260812 \
--build-arg RELEASE=development \
--tag ghcr.io/thebinaryninja/tvapp2:development-arm64 \ --tag ghcr.io/thebinaryninja/tvapp2:development-arm64 \
--attest type=provenance,disabled=true \ --attest type=provenance,disabled=true \
--attest type=sbom,disabled=true \ --attest type=sbom,disabled=true \
--output type=docker \
--builder default \
--file Dockerfile \ --file Dockerfile \
--platform linux/arm64 \ --platform linux/arm64 \
--output type=docker \
--allow network.host \ --allow network.host \
--network host \ --network host \
--no-cache \ --no-cache \
@@ -861,14 +907,14 @@ docker buildx build \
After completing the `docker buildx` commands above; you should now have a few new images. Each image should have its own separate docker tags which do not conflict. If you decided to not build the **development** releases below; that is fine. After completing the `docker buildx` commands above; you should now have a few new images. Each image should have its own separate docker tags which do not conflict. If you decided to not build the **development** releases below; that is fine.
- `--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64` - `--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64`
- `--tag ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64` - `--tag ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64`
- `--tag ghcr.io/thebinaryninja/tvapp2:development-amd64` - `--tag ghcr.io/thebinaryninja/tvapp2:development-amd64`
- `--tag ghcr.io/thebinaryninja/tvapp2:development-arm64` - `--tag ghcr.io/thebinaryninja/tvapp2:development-arm64`
<br /> <br />
Next, we need to take these two images, and merge them into one so that both architectures are available without having to push separate images. You need to obtain the SHA256 hash digest for the `amd64` and `arm64` images. You can go to the registry where you uploaded the images and then copy them. Or you can run the following commands: Next, we need to take these two images, and merge them into one so that both architectures are available without having to push separate images. You need to obtain the SHA256 hash digest for the `amd64` and `arm64` images. You can go to the registry where you uploaded the images and then copy them. Or you can run the commands specified below depending on which release type you want:
<br /> <br />
<br /> <br />
@@ -888,19 +934,20 @@ You can also get the hash digests by running the commands:
<br /> <br />
```shell ```shell
$ docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64 $ docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64
Name: ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64 Name: ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64
MediaType: application/vnd.docker.distribution.manifest.v2+json MediaType: application/vnd.docker.distribution.manifest.v2+json
Digest: sha256:0abe1b1c119959b3b1ccc23c56a7ee2c4c908c6aaef290d4ab2993859d807a3b Digest: sha256:0abe1b1c119959b3b1ccc23c56a7ee2c4c908c6aaef290d4ab2993859d807a3b
$ docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64 $ docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64
Name: ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64 Name: ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64
MediaType: application/vnd.docker.distribution.manifest.v2+json MediaType: application/vnd.docker.distribution.manifest.v2+json
Digest: sha256:e68b9de8669eac64d4e4d2a8343c56705e05e9a907cf0b542343f9b536d9c473 Digest: sha256:e68b9de8669eac64d4e4d2a8343c56705e05e9a907cf0b542343f9b536d9c473
``` ```
<br />
<br /> <br />
**Development Release** **Development Release**
@@ -942,14 +989,14 @@ Digest: sha256:c719ccb034946e3f0625003f25026d001768794e38a1ba8aafc9146291d548
> ```shell > ```shell
> $ docker images --all --no-trunc | grep thebinaryninja > $ docker images --all --no-trunc | grep thebinaryninja
> >
> ghcr.io/thebinaryninja/tvapp2 1.2.0-arm64 sha256:48520ca15fed6483d2d5b79993126c311f833002345b0e12b8eceb5bf9def966 42 minutes ago 46MB > ghcr.io/thebinaryninja/tvapp2 1.5.2-arm64 sha256:48520ca15fed6483d2d5b79993126c311f833002345b0e12b8eceb5bf9def966 42 minutes ago 46MB
> >
> ghcr.io/thebinaryninja/tvapp2 1.2.0-amd64 sha256:54a9b7d390199532d5667fae67120d77e2f459bd6108b27ce94e0cfec8f3c41f 43 minutes ago 45MB > ghcr.io/thebinaryninja/tvapp2 1.5.2-amd64 sha256:54a9b7d390199532d5667fae67120d77e2f459bd6108b27ce94e0cfec8f3c41f 43 minutes ago 45MB
> ``` > ```
> >
> To get the correct sha256 digest, use: > To get the correct sha256 digest, use:
> - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64` > - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64`
> - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64` > - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64`
> - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:development-amd64` > - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:development-amd64`
> - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:development-arm64` > - `docker buildx imagetools inspect ghcr.io/thebinaryninja/tvapp2:development-arm64`
> >
@@ -969,8 +1016,8 @@ For the **stable** releases, use:
# # # #
docker buildx imagetools create \ docker buildx imagetools create \
--tag ghcr.io/thebinaryninja/tvapp2:1.2.0 \ --tag ghcr.io/thebinaryninja/tvapp2:1.5.2 \
--tag ghcr.io/thebinaryninja/tvapp2:1.2 \ --tag ghcr.io/thebinaryninja/tvapp2:1.5 \
--tag ghcr.io/thebinaryninja/tvapp2:1 \ --tag ghcr.io/thebinaryninja/tvapp2:1 \
--tag ghcr.io/thebinaryninja/tvapp2:latest \ --tag ghcr.io/thebinaryninja/tvapp2:latest \
sha256:0abe1b1c119959b3b1ccc23c56a7ee2c4c908c6aaef290d4ab2993859d807a3b \ sha256:0abe1b1c119959b3b1ccc23c56a7ee2c4c908c6aaef290d4ab2993859d807a3b \
@@ -978,9 +1025,9 @@ docker buildx imagetools create \
[+] Building 0.2s (4/4) FINISHED [+] Building 0.2s (4/4) FINISHED
=> [internal] pushing ghcr.io/thebinaryninja/tvapp2:latest 0.2s => [internal] pushing ghcr.io/thebinaryninja/tvapp2:latest 0.2s
=> [internal] pushing ghcr.io/thebinaryninja/tvapp2:1.2 0.2s
=> [internal] pushing ghcr.io/thebinaryninja/tvapp2:1 0.2s => [internal] pushing ghcr.io/thebinaryninja/tvapp2:1 0.2s
=> [internal] pushing ghcr.io/thebinaryninja/tvapp2:1.2.0 0.2s => [internal] pushing ghcr.io/thebinaryninja/tvapp2:1.5 0.2s
=> [internal] pushing ghcr.io/thebinaryninja/tvapp2:1.5.2 0.2s
``` ```
<br /> <br />
@@ -1026,8 +1073,8 @@ In this example, we take the existing two files we created earlier, and merge th
```shell ```shell
# Example 1 (using tag) # Example 1 (using tag)
docker manifest create ghcr.io/thebinaryninja/tvapp2:latest \ docker manifest create ghcr.io/thebinaryninja/tvapp2:latest \
--amend ghcr.io/thebinaryninja/tvapp2:1.2.0-amd64 \ --amend ghcr.io/thebinaryninja/tvapp2:1.5.2-amd64 \
--amend ghcr.io/thebinaryninja/tvapp2:1.2.0-arm64 --amend ghcr.io/thebinaryninja/tvapp2:1.5.2-arm64
# Example 2 (using sha256 hash) # Example 2 (using sha256 hash)
docker manifest create ghcr.io/thebinaryninja/tvapp2:latest \ docker manifest create ghcr.io/thebinaryninja/tvapp2:latest \
@@ -1068,7 +1115,7 @@ To build the project, `🗔 cd` into the project folder and run the build comman
```shell ```shell
cd /home/docker/tvapp2/ cd /home/docker/tvapp2/
npm run docker:build:amd64 --VERSION=1.2.0 --BUILDDATE=20250407 npm run docker:build:amd64 --VERSION=1.5.2 --BUILDDATE=20260812
``` ```
<br /> <br />
@@ -1093,7 +1140,7 @@ The run command above has several variables you must specify:
| Variable | Description | | Variable | Description |
| --- | --- | | --- | --- |
| `--VERSION=1.X.X` | The version to assign to the docker image | | `--VERSION=1.X.X` | The version to assign to the docker image |
| `--BUILDDATE=20250407` | The date to assign to the docker image. <br /> Date format: `YYYYMMDD` | | `--BUILDDATE=20260812` | The date to assign to the docker image. <br /> Date format: `YYYYMMDD` |
| `--ARCH=amd64` | Architecture for image<br /> <sub><sup>Options:</sup></sub> `amd64`, `arm64` | | `--ARCH=amd64` | Architecture for image<br /> <sub><sup>Options:</sup></sub> `amd64`, `arm64` |
<br /> <br />
@@ -1192,16 +1239,20 @@ This docker container contains the following env variables:
| `TZ` | `Etc/UTC` | Timezone for error / log reporting | | `TZ` | `Etc/UTC` | Timezone for error / log reporting |
| `WEB_IP` | `0.0.0.0` | IP to use for webserver | | `WEB_IP` | `0.0.0.0` | IP to use for webserver |
| `WEB_PORT` | `4124` | Port to use for webserver | | `WEB_PORT` | `4124` | Port to use for webserver |
| `WEB_ENCODING` | `deflate, br` | Defines the HTTP `Accept-Encoding` request and response header. This value specifies what content encoding the sender can understand<br /><br />Gzip compression can be enabled by specifying `'gzip, deflate, br'` | | `WEB_FOLDER` | `www` | Internal container folder to keep TVApp2 web files in. <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> |
| `WEB_ENCODING` | `deflate, br` | Defines the HTTP `Accept-Encoding` request and response header. This value specifies what content encoding the sender can understand<br /><br />Gzip compression can be enabled by specifying `'gzip, deflate, br'`, however, [it may break Jellyfin users](#build-error-err-27-jellyfinlivetvguideguidemanager-error-getting-programs-for-channel-xxxxxxxxxxxxxxx-source-2-systemxmlxmlexception--hexadecimal-value-0x1f-is-an-invalid-character-line-1-position-1). |
| `WEB_PROXY_HEADER` | `x-forwarded-for` | Defines the header to look for when finding a client's IP address. Used to get a client's IP when behind a reverse proxy or Cloudflare |
| `URL_REPO` | `https://git.binaryninja.net/BinaryNinja/` | Determines where the data files will be downloaded from. Do not change this or you will be unable to get M3U and EPG data. | | `URL_REPO` | `https://git.binaryninja.net/BinaryNinja/` | Determines where the data files will be downloaded from. Do not change this or you will be unable to get M3U and EPG data. |
| `FILE_URL` | `urls.txt` | Filename for `urls.txt` cache file | | `FILE_URL` | `urls.txt` | Filename for `urls.txt` cache file |
| `FILE_M3U` | `playlist.m3u8` | Filename for M3U playlist file | | `FILE_M3U` | `playlist.m3u8` | Filename for M3U playlist file |
| `FILE_EPG` | `xmltv.xml` | Filename for XML guide data file | | `FILE_EPG` | `xmltv.xml` | Filename for XML guide data file |
| `FILE_GZP` | `xmltv.xml.gz` | Filename for XML compressed as gzip .gz | | `FILE_GZP` | `xmltv.xml.gz` | Filename for XML compressed as gzip .gz |
| `STREAM_QUALITY` | `hd` | Stream quality<br />Can be either `hd` or `sd` | | `STREAM_QUALITY` | `hd` | Stream quality<br />Can be either `hd` or `sd` |
| `TASK_CRON_SYNC` | `0 0 */3 * *` | Defines how often to refresh the M3U and XML IPTV data |
| `HEALTH_TIMER` | `600000` | How often (in milliseconds) to run a health check |
| `DIR_BUILD` | `/usr/src/app` | Path inside container where TVApp2 will be built. <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> | | `DIR_BUILD` | `/usr/src/app` | Path inside container where TVApp2 will be built. <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> |
| `DIR_RUN` | `/usr/bin/app` | Path inside container where TVApp2 will be placed after it is built <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> | | `DIR_RUN` | `/usr/bin/app` | Path inside container where TVApp2 will be placed after it is built <br /><br /> <sup>⚠️ This should not be used unless you know what you're doing</sup> |
| `LOG_LEVEL` | `4` | Level of logging to display in console<br/>`6` Trace <sup><sub>& below</sub></sup><br />`5` Debug <sup><sub>& below</sub></sup><br />`4` Info <sup><sub>& below</sub></sup><br />`3` Notice <sup><sub>& below</sub></sup><br />`2` Warn <sup><sub>& below</sub></sup><br />`1` Error <sup><sub>only</sub></sup> | | `LOG_LEVEL` | `4` | Level of logging to display in console<br/>`7` Trace <sup><sub>& below</sub></sup><br />`6` Verbose <sup><sub>& below</sub></sup><br />`5` Debug <sup><sub>& below</sub></sup><br />`4` Info <sup><sub>& below</sub></sup><br />`3` Notice <sup><sub>& below</sub></sup><br />`2` Warn <sup><sub>& below</sub></sup><br />`1` Error <sup><sub>only</sub></sup> |
<br /> <br />
<br /> <br />
@@ -1262,6 +1313,92 @@ health check:
<br /> <br />
### Logging
This docker container includes numerous log levels you can switch between in order to see exactly what the docker container is doing during operations.
Out-of-box, the default value is set to `4`:
```yml
environment:
- TZ=Etc/UTC
- LOG_LEVEL=4
```
<br />
The default level of `info (4)` will give you all messages related to running your container. It should be enough for everyday users. This log level includes:
- All informative messages
- All errors
- All warnings
- All notices
<br />
However, this container supports three levels with high verbosity:
- **Trace**: 7
- **Verbose**: 6
- **Debug**: 5
<br />
#### Trace (7)
This **LOG_LEVEL** will output anything that you can see with **debug** `LOG_LEVEL=5`; but with the addition that each log output to your console will show a complete traceback of how the log was triggered.
<br />
> [!NOTE]
> Users should be cautioned that using `LOG_LEVEL=7` will generate a large amount of logs to your console, especially when you initiate watching an IPTV channel.
<br />
```shell
Trace: tvapp2 → .gzp [requests] ⚙️ <msg> Requesting to create compressed gzip from uncompressed XML data <src> /usr/bin/app/www/xmltv.xml <dest> /usr/bin/app/www/xmltv.xml.gz
at Log.debug (file:///usr/bin/app/index.js:182:21)
at getGzip (file:///usr/bin/app/index.js:658:13)
at initialize (file:///usr/bin/app/index.js:1840:15)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async file:///usr/bin/app/index.js:2288:5
```
<br />
#### Verbose (6)
This **LOG_LEVEL** will output anything that you can see with **debug** `LOG_LEVEL=5`; as well as even deeper logs; such as every environment variable your TVApp2 container has assigned. You should not need to use this log level unless you are troubleshooting an issue or if instructed to do so by the developers.
```shell
tvapp2 → .env [assigner] 📣 <name> npm_config_user_agent <value> npm/10.9.1 node/v22.13.1 linux x64 workspaces/false
tvapp2 → .env [assigner] 📣 <name> HOSTNAME <value> tvapp2
tvapp2 → .env [assigner] 📣 <name> SHLVL <value> 4
tvapp2 → .env [assigner] 📣 <name> HOME <value> /root
tvapp2 → .env [assigner] 📣 <name> OLDPWD <value> /run/s6/legacy-services/tvapp2
```
<br />
#### Debug (5)
This **LOG_LEVEL** will output many of the steps that this container takes to create new XML guide and M3U playlist files. It will also output the environment variables you have set associated with the docker image itself.
<br />
```shell
tvapp2 → .net [assigner] ⚙️ <name> IP_CONTAINER <value> 172.18.0.7
tvapp2 → .net [assigner] ⚙️ <name> IP_GATEWAY <value> 172.18.0.1
tvapp2 → .env [assigner] ⚙️ <name> RELEASE <value> stable
tvapp2 → .env [assigner] ⚙️ <name> WEB_IP <value> 0.0.0.0
tvapp2 → .env [assigner] ⚙️ <name> WEB_PORT <value> 4124
tvapp2 → .env [assigner] ⚙️ <name> WEB_FOLDER <value> www
tvapp2 → file [requests] ⚙️ <msg> Requesting to download external file <src> https://git.binaryninja.net/binaryninja//XMLTV-EPG/raw/branch/main/xmltv.1.xml <dest> /usr/bin/app/www/xmltv.xml
```
<br />
--- ---
<br /> <br />
@@ -1862,6 +1999,8 @@ cd /path/to/tvapp2
find ./ -type f | grep -Ev '.git|*.jpg|*.jpeg|*.png' | sudo xargs dos2unix -- find ./ -type f | grep -Ev '.git|*.jpg|*.jpeg|*.png' | sudo xargs dos2unix --
``` ```
<br />
> [!WARNING] > [!WARNING]
> Do not run `🗔 dos2unix` on your `📁 .git` folder or you will corrupt your git indexes and will be unable to push commits. > Do not run `🗔 dos2unix` on your `📁 .git` folder or you will corrupt your git indexes and will be unable to push commits.
> >
@@ -1888,7 +2027,6 @@ If you receive any of the above errors; this means that you have not set your `r
find ./ -name 'run' -exec sudo chmod +x {} \; find ./ -name 'run' -exec sudo chmod +x {} \;
``` ```
<br />
<br /> <br />
After you have set these permissions, re-build your docker image using `docker build` or `docker buildx`. Then spin the container up. After you have set these permissions, re-build your docker image using `docker build` or `docker buildx`. Then spin the container up.

View File

@@ -28,6 +28,60 @@ This section outlines all releases of TVApp2, including the version of the relea
<br /> <br />
### <!-- md:version stable- --> 1.4.0 <small>Apr 10, 2025</small> { id="1.4.0" }
- `feat`: add new environment variable `HEALTH_TIMER` _(See feature below)_
- `feat`: new health check icon in top right; triggers every `10 minutes` and reports back to the web interface as a toast notification
- health check shows timer until next health check is performed.
- `feat`: added bootstramp toast notifications and modals
- `feat`: new modal dialog when when user triggers a re-sync of the m3u and epg data
- `feat`: footer of web interface how discloses which build is being ran; `stable` or `development`
- `refactor`: console logging system overhauled with new syntax for how logs are displayed
- `refactor`: additional logs added for `LOG_LEVEL=5` or higher
<br />
---
<br />
### <!-- md:version stable- --> 1.3.0 <small>Apr 9, 2025</small> { id="1.3.0" }
- `feat`: new button to upper-right side of header which allows you to **force re-sync** your m3u and xml data.
- `feat`: new api endpoint `/api/resync`; utilized to resync M3U and EPG data
- `feat`: new api endpoint `/api/health`; utilized in your `docker-compose.yml` health check
- `feat`: new env variable `WEB_ENCODING`; allows you to customize the HTTP `Accept-Encoding` request and response header
- `feat`: new env variable `IP_GATEWAY`; stores assigned container gateway ip
- `feat`: new env variable `IP_CONTAINER`; stores assigned container ip
- `refactor`: env variables re-named
- `FILE_TAR``FILE_GZP`
- `refactor`: errors and success messages now use api endpoint; lists timestamp, error code, etc.
- `build`: bump bootstrap from `v4` to `v5`
- `fix`: bug in Jellyfin which caused EPG data syncing to error out for hosts which cannot support gzip compression
- Error: _[ERR] [27] Jellyfin.LiveTv.Guide.GuideManager: Error getting programs for channel XXXXXXXXXXXXXXX (Source 2) System.Xml.XmlException: '', hexadecimal value 0x1F, is an invalid character. Line 1, position 1._
<br />
---
<br />
### <!-- md:version stable- --> 1.2.0 <small>Apr 5, 2025</small> { id="1.2.0" }
- `feat`: add support for additional mime types and default file type
- `style`: rename env variable `FILE_PLAYLIST` to `FILE_M3U`
- `build`: add new env vars to `Dockerfile`
- `FILE_TAR`
- `FILE_URL`
- `refactor`: m3u and epg files now stored in `www` folder
- `fix`: html template links pointing to old repo
<br />
---
<br />
### <!-- md:version stable- --> 1.1.0 <small>Mar 25, 2025</small> { id="1.1.0" } ### <!-- md:version stable- --> 1.1.0 <small>Mar 25, 2025</small> { id="1.1.0" }
- `feat`: new interface & theme for web ui - `feat`: new interface & theme for web ui
@@ -66,6 +120,10 @@ This section outlines all releases of TVApp2, including the version of the relea
<br /> <br />
---
<br />
### <!-- md:version stable- --> 1.0.0 <small>Feb 24, 2025</small> { id="1.0.0" } ### <!-- md:version stable- --> 1.0.0 <small>Feb 24, 2025</small> { id="1.0.0" }
- Initial release - Initial release

View File

@@ -42,10 +42,21 @@ All channels contain multiple sources so that you have a reliable streaming expe
<br /> <br />
## Associated Links ## Image Sources
Check out the following websites for additional resources for the TVApp2 docker image below. This project contains several repositories which all share the same code; use them as backups:
- [🔀 dockerhub:thebinaryninja/tvapp2](https://hub.docker.com/r/thebinaryninja/tvapp2)
- [🔀 github:thebinaryninja/tvapp2](https://github.com/thebinaryninja/tvapp2)
- [🔀 gitea:git.binaryninja.net/binaryninja/tvapp2](https://git.binaryninja.net/binaryninja/tvapp2)
<br />
| Pull URL | Registry | Arch | Version |
| --- | --- | --- | --- |
| `ghcr.io/thebinaryninja/tvapp2:latest`<br />`ghcr.io/thebinaryninja/tvapp2:development` | Github | amd64<br/>arm64 | [![Github][github-docker-version-img]][github-docker-version-uri] |
| `thebinaryninja/tvapp2:latest`<br />`thebinaryninja/tvapp2:development` | Dockerhub | amd64<br/>arm64 | [![Dockerhub][dockerhub-docker-version-img]][dockerhub-docker-version-uri] |
| `git.binaryninja.net/binaryninja/tvapp2:latest`<br />`git.binaryninja.net/binaryninja/tvapp2:development` | Gitea | amd64<br/>arm64 | [![Gitea][gitea-docker-version-img]][gitea-docker-version-uri] |
<br /> <br />
@@ -64,6 +75,13 @@ The following is a small list of the features available with the TVApp2 containe
- Channel playlists can be downloaded as a `.m38u` or a compressed `.gzip` archive. - Channel playlists can be downloaded as a `.m38u` or a compressed `.gzip` archive.
- Compressed gzip compatible with 3rd party apps like Cabernet and Jellyfin. - Compressed gzip compatible with 3rd party apps like Cabernet and Jellyfin.
- Tracking statistics which show the last update time, size, and a description for each file's purpose. - Tracking statistics which show the last update time, size, and a description for each file's purpose.
- API endpoints:
- **Resync All Files**
- http://127.0.0.1:4124/api/resync
- http://127.0.0.1:4124/api/restart
- **Health check**
- http://127.0.0.1:4124/api/health
- http://127.0.0.1:4124/api/status
- Direct access to download each of the generated files, including multiple easy-to-remember URLs for each file type. - Direct access to download each of the generated files, including multiple easy-to-remember URLs for each file type.
- **M3U Playlist**: - **M3U Playlist**:
- http://127.0.0.1:4124/playlist - http://127.0.0.1:4124/playlist

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -175,6 +175,63 @@ services:
<br /> <br />
## WEB_ENCODING
<!-- md:control env -->
<!-- md:version stable-1.3.0 -->
<!-- md:default `deflate, br` -->
The `WEB_ENCODING` environment variable allows you to customize the HTTP `Accept-Encoding` request and response header. This value specifies what content encoding the sender can understand when sending these requests.
Most users will not need to modify this value unless you are running Jellyfin and receive the following error when attempting to sync EPG data between Jellyfin and the TVApp2 container:
!!! warning "Jellyfin Error"
```
[ERR] [27] Jellyfin.LiveTv.Guide.GuideManager: Error getting programs for channel
XXXXXXXXXXXXXXX (Source 2) System.Xml.XmlException: '', hexadecimal value 0x1F,
is an invalid character. Line 1, position 1.
```
If you receive the above error and you have already customized this environment variable to include `gzip`, you must remove it from your accepted encoders string to fix the error.
=== "Old"
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- WEB_ENCODING: 'gzip, deflate, br'
```
=== "New"
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- WEB_ENCODING: 'deflate, br'
```
<br />
## URL_REPO ## URL_REPO
<!-- md:control env --> <!-- md:control env -->
<!-- md:version stable-1.0.0 --> <!-- md:version stable-1.0.0 -->
@@ -202,19 +259,19 @@ services:
``` ```
1. :warning: It is highly recommended that you do not change this value 1. :warning: It is highly recommended that you do not change this value
otherwise you will not be able to download the latest M3U playlists and EPG otherwise you will not be able to download the latest M3U playlists
guide data. and EPG guide data.
<br /> <br />
## DIR_BUILD ## FILE_URL
<!-- md:control env --> <!-- md:control env -->
<!-- md:version stable-1.0.0 --> <!-- md:version stable-1.2.0 -->
<!-- md:default `/usr/src/app` --> <!-- md:default `urls.txt` -->
<!-- md:flag dangerous -->
The `DIR_BUILD` environment variable specifies what local folder will be utilized The `FILE_URL` environment variable allows you to specify what the name of the downloaded `urls.txt` cache file. This file is downloaded when you first spin up the TVApp2 container.
by the TVApp2 docker container when the container builds the app.
There should be no need to utilize this environment variable unless you have a specific reason.
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" } ``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services: services:
@@ -229,85 +286,22 @@ services:
- ./config:/config - ./config:/config
- ./app:/usr/bin/app - ./app:/usr/bin/app
environment: environment:
- DIR_BUILD=/usr/src/app # (1) - FILE_URL=urls.txt # (1)
``` ```
1. :warning: You should not change this unless you are an advanced user. 1. :warning: There is really no reason to modify this environment variable
unless you have a specific purpose.
<br /> <br />
## DIR_RUN ## FILE_M3U
<!-- md:control env -->
<!-- md:version stable-1.0.0 -->
<!-- md:default `/usr/src/app` -->
<!-- md:flag dangerous -->
The `DIR_RUN` environment variable specifies what local folder will be utilized
by the TVApp2 docker container when the container has built the app and placed it
into a production folder.
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- DIR_RUN=/usr/bin/app # (1)
```
1. :warning: You should not change this unless you are an advanced user.
<br />
## STREAM_QUALITY
<!-- md:control env -->
<!-- md:version stable-1.1.0 -->
<!-- md:default `hd` -->
The `STREAM_QUALITY` environment variable specifies the default stream quality that will
be used when you initiate a new channel to view.
Available Options:
* `hd` High Definition
* `sd` Standard Definition
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- STREAM_QUALITY=hd # (1)
```
1. :information: This environment variable has the following options:
- hd
- sd
<br />
## FILE_PLAYLIST
<!-- md:control env --> <!-- md:control env -->
<!-- md:version stable-1.1.0 --> <!-- md:version stable-1.1.0 -->
<!-- md:default `playlist.m3u8` --> <!-- md:default `playlist.m3u8` -->
The `FILE_PLAYLIST` environment variable specifies the filename that will be utilized when The `FILE_M3U` environment variable allows you to specify what the name of the downloaded `playlist.m3u8` file will be. This file is downloaded when you first spin up the TVApp2 container, and contains a list of what channels you can pick from.
your .m3u playlist file is generated.
There should be no need to utilize this environment variable unless you have a specific reason.
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" } ``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services: services:
@@ -322,11 +316,12 @@ services:
- ./config:/config - ./config:/config
- ./app:/usr/bin/app - ./app:/usr/bin/app
environment: environment:
- FILE_PLAYLIST=playlist.m3u8 # (1) - FILE_M3U=playlist.m3u8 # (1)
``` ```
1. :information: Changing this file only changes the filename locally; it does 1. :warning: There is really no reason to modify this environment variable
not affect the server-side fetching mechanism. unless you have a specific purpose.
<br /> <br />
@@ -389,6 +384,100 @@ services:
<br /> <br />
## STREAM_QUALITY
<!-- md:control env -->
<!-- md:version stable-1.1.0 -->
<!-- md:default `hd` -->
The `STREAM_QUALITY` environment variable specifies the default stream quality that will
be used when you initiate a new channel to view.
Available Options:
* `hd` High Definition
* `sd` Standard Definition
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- STREAM_QUALITY=hd # (1)
```
1. :information: This environment variable has the following options:
- hd
- sd
<br />
## DIR_BUILD
<!-- md:control env -->
<!-- md:version stable-1.0.0 -->
<!-- md:default `/usr/src/app` -->
<!-- md:flag dangerous -->
The `DIR_BUILD` environment variable specifies what local folder will be utilized
by the TVApp2 docker container when the container builds the app.
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- DIR_BUILD=/usr/src/app # (1)
```
1. :warning: You should not change this unless you are an advanced user.
<br />
## DIR_RUN
<!-- md:control env -->
<!-- md:version stable-1.0.0 -->
<!-- md:default `/usr/src/app` -->
<!-- md:flag dangerous -->
The `DIR_RUN` environment variable specifies what local folder will be utilized
by the TVApp2 docker container when the container has built the app and placed it
into a production folder.
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- DIR_RUN=/usr/bin/app # (1)
```
1. :warning: You should not change this unless you are an advanced user.
<br />
## LOG_LEVEL ## LOG_LEVEL
<!-- md:control env --> <!-- md:control env -->
<!-- md:version stable-1.1.0 --> <!-- md:version stable-1.1.0 -->
@@ -417,13 +506,16 @@ when being output to your console.
1. :information: The default log level is `4` (info). 1. :information: The default log level is `4` (info).
=== "Logging Options" === "Log Levels"
| Log Level | Name | Description | | Log Level | Name | Description |
| --------------- | ----------- | ------------------------------------------------------------------------------- | | --------------- | ----------- | ------------------------------------------------------------------------------- |
| `6` | Trace | Displays all possible logs in console, along with anything below this line. | | `6` | :material-checkbox-blank-circle:{ style="color: var(--md-loglevel-trace-color) " } Trace | Displays all possible logs in console, along with anything below this line. |
| `5` | Debug | Displays debug / developer logs, along with anything below this line. | | `5` | :material-checkbox-blank-circle:{ style="color: var(--md-loglevel-debug-color) " } Debug | Displays debug / developer logs, along with anything below this line. |
| `4` | Info | Displays informative logs, along with anything below this line. | | `4` | :material-checkbox-blank-circle:{ style="color: var(--md-loglevel-info-color) " } Info | Displays informative logs, along with anything below this line. |
| `3` | Notice | Displays important notices, along with anything below this line. | | `3` | :material-checkbox-blank-circle:{ style="color: var(--md-loglevel-notice-color) " } Notice | Displays important notices, along with anything below this line. |
| `2` | Warm | Displays warnings, along with anything below this line. | | `2` | :material-checkbox-blank-circle:{ style="color: var(--md-loglevel-warn-color) " } Warn | Displays warnings, along with anything below this line. |
| `1` | Error | Displays only errors, none of the log levels above this line will be shown | | `1` | :material-checkbox-blank-circle:{ style="color: var(--md-loglevel-error-color) " } Error | Displays only errors, none of the log levels above this line will be shown |
<br />
<br />

View File

@@ -13,108 +13,97 @@ The TVApp2 docker image provides a few different paths that you can mount to you
<br /> <br />
## 📁 /usr/bin/app ## 📁 /usr/bin/app
<!-- md:control env -->
<!-- md:version stable-1.0.0 -->
<!-- md:default `Etc/UTC` -->
The `TZ` environment variable specifies the timezone that your docker container will <!-- md:control volume -->
utilize. This is useful for syncing your local time with console outputs such as <!-- md:version stable-1.0.0 -->
our logging system.
The mountable volume `/usr/bin/app` is where TVApp2 files will be placed once the app has been built when your docker container spins up. The files in this folder include:
| File | Description |
| --- | --- |
| `📁 node_modules` | List of all NodeJS packages utilized by TVApp2 |
| `📁 www` | Main storage folder for TVApp2. Contains website files and M3U / EPG synced files |
| `📄 package.json` | NodeJS package file |
| `📄 playlist.m3u8` | Generated playlist containing channels |
| `📄 urls.txt` | List containing cached URLs utilized by TVApp2 |
| `📄 xmltv.xml` | EPG guide data in uncompressed XML format |
| `📄 xmltv.xml.gz` | EPG guide data in compressed GZ archive |
| `📄 index.js` | Main source code file for TVApp2 |
<br />
=== "Example" === "Example"
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" } ``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="7" }
services: services:
tvapp2: tvapp2:
container_name: tvapp2 container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- /etc/timezone:/etc/timezone:ro - ./app:/usr/bin/app # (1)
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- TZ=Etc/UTC # (1)
``` ```
1. :information: Changing this env variable will change the time for anything 1. :information: Changing this env variable will change the time for anything
related to the TVApp2 docker container. related to the TVApp2 docker container.
=== "Timezones" <br />
``` yaml This folder path can be changed by specifying a new path with the environment variable `DIR_RUN`
Etc/UTC
Africa/Cairo === "Example"
Africa/Johannesburg
Africa/Lagos ``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="7" }
America/Argentina/Buenos_Aires services:
America/Bogota tvapp2:
America/Caracas container_name: tvapp2
America/Chicago image: ghcr.io/thebinaryninja/tvapp2:latest
America/El_Salvador restart: unless-stopped
America/Juneau volumes:
America/Lima - ./app:/usr/bin/app # (1) (2)
America/Los_Angeles
America/Mexico_City
America/New_York
America/Phoenix
America/Santiago
America/Sao_Paulo
America/Toronto
America/Vancouver
Asia/Almaty
Asia/Ashkhabad
Asia/Bahrain
Asia/Bangkok
Asia/Chongqing
Asia/Dubai
Asia/Ho_Chi_Minh
Asia/Hong_Kong
Asia/Jakarta
Asia/Jerusalem
Asia/Kathmandu
Asia/Kolkata
Asia/Kuwait
Asia/Muscat
Asia/Qatar
Asia/Riyadh
Asia/Seoul
Asia/Shanghai
Asia/Singapore
Asia/Taipei
Asia/Tehran
Asia/Tokyo
Atlantic/Reykjavik
Australia/ACT
Australia/Adelaide
Australia/Brisbane
Australia/Sydney
Europe/Athens
Europe/Belgrade
Europe/Berlin
Europe/Copenhagen
Europe/Helsinki
Europe/Istanbul
Europe/London
Europe/Luxembourg
Europe/Madrid
Europe/Moscow
Europe/Paris
Europe/Riga
Europe/Rome
Europe/Stockholm
Europe/Tallinn
Europe/Vilnius
Europe/Warsaw
Europe/Zurich
Pacific/Auckland
Pacific/Chatham
Pacific/Fakaofo
Pacific/Honolulu
Pacific/Norfolk
US/Mountain
``` ```
1. :information: Changing this env variable will change the folder within the docker container which stores the fully built TVApp2 files.
2. This should not be used unless you know what you're doing
<br />
---
<br />
## 📁 /config
<!-- md:control volume -->
<!-- md:version stable-1.0.0 -->
The mountable volume `/config` defines where the TVApp2 application will store SSL certificates related to the TVApp2 web interface being ran using https instead of http. The files in this folder include:
| File | Description |
| --- | --- |
| `📁 keys` | Folder which stores the SSL cert and keys |
| `📄 keys/cert.crt` | SSL public certificate |
| `📄 keys/key.crt` | SSL private key |
<br />
=== "Example"
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="7" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- ./config:/config # (1) (2)
```
1. :information: Changing this env variable will change the folder within the docker container which stores the fully built TVApp2 files.
2. This should not be used unless you know what you're doing
<br />
<br /> <br />

View File

@@ -0,0 +1,125 @@
---
title: "Install: docker-run"
tags:
- install
---
# docker run
Our documentation provides two ways that you may start up a TVApp2 docker container:
<div class="grid cards" markdown>
- :material-circle: &nbsp; [docker run](docker-run.md)
---
Spin up the TVApp2 container using the `docker run` command.
This is useful for quick launches, but is not time efficient
if you plan to use this container long-term.
This requires a longer command that must be used each time
you wish to bring the container up.
- :material-circle: &nbsp; [docker compose](docker-compose.md)
---
Spin up the TVApp2 container by creating a `📄 docker-compose.yml`
file which will store all of your options such as env variables,
mounted volumes, and labels.
To bring the container up, `cd` into the folder with the
`📄 docker-compose.yml` file, and run the
command `docker compose up -d`.
</div>
<br />
The `🗔 docker run` command allows you to start up a docker container by providing a set of [options](#command-options) which define how the container should operate, including the environment variables, mounted volumes, assigned IP address, etc.
<br />
---
<br />
## Start TVApp2
Pulling the image if needed and starting the container. To spin up a TVApp2 container using this method; run a command similar to the below example. See the section [Options](#options) below for a list of what you can specify.
=== "Terminal"
```shell
docker run -d \
--restart=unless-stopped \ # (1)!
--name tvapp2 \ # (2)!
-p 4124:4124 \ # (3)!
-e "TZ=Etc/UTC" \ # (4)!
-v ${PWD}/app:/usr/bin/app \ # (5)!
ghcr.io/thebinaryninja/tvapp2:latest # (6)!
```
1. Specifies what happens if the container becomes unresponsive or goes down.
2. Name to assign the container; otherwise, a random id will be given.
3. Port that will be used for the container
4. Environment variable which specifies the timezone to use for the container.
5. Mount the container volume `/usr/bin/app` to your host machine in the subfolder `./app`
6. Specifies what docker image to spin up.
<br />
To confirm that the container has been brought up, run the command `docker ps | grep tvapp2`. If you have the app [Portainer](https://portainer.io/), you can sign into your admin interface and view your TVApp2 container details, instead of using a command-line.
```
e95236c42b43 binaryninja/tvapp2:1.4.0 "/init" 3 seconds ago Up 3 seconds 4124/tcp tvapp2
```
<br />
---
<br />
## Options
Review the list of docker run options below. These allow you to define how a docker container will start up.
???- note "Official Docker Documentation"
To view a full list of the available docker parameters, view the official docker documentation at:
- https://docs.docker.com/reference/cli/docker/container/run/
| Parameter / Flag | Description |
| --- | --- |
| `-d, --detach` | Run container in background and print container ID |
| `-e, --env` | Set environment variable |
| `--env-file` | Read in a file of environment variables |
| `--expose` | Expose a port or a range of ports |
| `--health-cmd` | Command to run to check health |
| `--health-interval` | Time between running the check<br/>`ms|s|m|h` (default 0s) |
| `--health-retries` | Consecutive failures needed to report unhealthy |
| `--health-start-interval` | Time between running the check during the start period<br/>`ms|s|m|h` (default 0s) |
| `--health-start-period` | Start period for the container to initialize before starting health-retries countdown<br/>`ms|s|m|h` (default 0s) |
| `--health-timeout` | Maximum time to allow one check to run<br/>`ms|s|m|h` (default 0s) |
| `-h, --hostname` | Container host name |
| `--ip` | IPv4 address (e.g., 172.30.100.104) |
| `--ip6` | IPv6 address (e.g., 2001:db8::33) |
| `-l, --label` | Set meta data on a container |
| `--mount` | Attach a filesystem mount to the container |
| `--name` | Assign a name to the container |
| `--network` | Connect a container to a network |
| `--privileged` | Give extended privileges to this container |
| `-p, --publish` | Publish a container's port(s) to the host |
| `--pull` | Pull image before running<br/>`always`, `missing`, `never` |
| `--restart` | Restart policy to apply when a container exits <br/> `on-failure[:max-retries]`, `always`, `unless-stopped` |
| `-u, --user` | Username or UID <br/>`<name|uid>[:<group|gid>]` |
| `-v, --volume` | Bind mount a volume |
| `-w, --workdir` | Working directory inside the container |
<br />
<br />
<br />

View File

@@ -0,0 +1,47 @@
---
title: Install
tags:
- install
---
# Installing TVApp2
To install TVApp2 using docker; you will need to use either the `🗔 docker run` command, or create a `📄 docker-compose.yml` file which contains information about how to pull the latest image and spin the container up. We have provided instructions for both.
<br />
{==
Select your desired option to bring up the TVApp2 container with:
==}
<div class="grid cards" markdown>
- :material-circle: &nbsp; [docker run](docker-run.md)
---
Spin up the TVApp2 container using the `docker run` command.
This is useful for quick launches, but is not time efficient
if you plan to use this container long-term.
This requires a longer command that must be used each time
you wish to bring the container up.
- :material-circle: &nbsp; [docker compose](docker-compose.md)
---
Spin up the TVApp2 container by creating a `📄 docker-compose.yml`
file which will store all of your options such as env variables,
mounted volumes, and labels.
To bring the container up, `cd` into the folder with the
`📄 docker-compose.yml` file, and run the
command `docker compose up -d`.
</div>
<br />
<br />

View File

@@ -179,6 +179,11 @@
} }
} }
[dir="ltr"] .md-typeset table th[role="columnheader"]::after
{
margin-left: 5px;
}
/* /*
Theme > Main Theme > Main
*/ */
@@ -191,6 +196,13 @@
--md-tooltip-width: 400px; --md-tooltip-width: 400px;
--glow-color: hsl(186 100% 69%); --glow-color: hsl(186 100% 69%);
--md-loglevel-error-color: #f06090;
--md-loglevel-warn-color: #e6695b;
--md-loglevel-notice-color: #e2d55e;
--md-loglevel-info-color: #6791e0;
--md-loglevel-debug-color: #c973d9;
--md-loglevel-trace-color: hsla(225deg,15%,90%,0.56);
--md-default-fg-color: hsla(var(--md-hue),15%,90%,0.82); --md-default-fg-color: hsla(var(--md-hue),15%,90%,0.82);
--md-default-fg-color--light: hsla(var(--md-hue),15%,90%,0.56); --md-default-fg-color--light: hsla(var(--md-hue),15%,90%,0.56);
--md-default-fg-color--lighter: hsla(var(--md-hue),15%,90%,0.32); --md-default-fg-color--lighter: hsla(var(--md-hue),15%,90%,0.32);
@@ -411,7 +423,28 @@
.md-typeset .tabbed-labels .md-typeset .tabbed-labels
{ {
background-color: #0d0d0d; background-color: #0b0b0b;
border-top-left-radius: 4px;
border-left: 1px solid #242735;
border-top: 1px solid #242735;
border-right: 1px solid #242735;
border-top-right-radius: 4px;
}
.tabbed-labels::before
{
background: hsla(343.1, 87.9%, 51.6%, 0.82);
bottom: 0;
content: "";
display: block;
height: 2px;
left: 0;
position: absolute;
transform: translateX(var(--md-indicator-x));
transition: width 225ms,background-color .25s,transform .25s;
transition-timing-function: ease, ease, ease;
transition-timing-function: cubic-bezier(.4,0,.2,1);
width: var(--md-indicator-width);
} }
/* /*
@@ -561,6 +594,25 @@ h4:has(.twemoji) .twemoji
--md-typeset-bg-color: #111; --md-typeset-bg-color: #111;
} }
@keyframes health
{
0%, 40%, 80%, 100% {
transform: scale(1);
color: #FFF;
fill: #FFF !important;
}
20%, 60% {
transform: scale(1.15);
color: #FF6593;
fill: #FF6593 !important;
}
}
.heart
{
animation: health 1000ms infinite;
}
.md-typeset .mdx-badge--heart { .md-typeset .mdx-badge--heart {
--md-badge-icon-color: #e92063; --md-badge-icon-color: #e92063;
--md-badge-icon-hover-color: #FFFFFF; --md-badge-icon-hover-color: #FFFFFF;

View File

@@ -0,0 +1,164 @@
---
title: Healthcheck
tags:
- usage
---
# Healthcheck
This docker container includes the ability to run health checks between the container api and outside services such as Portainer, or any other app that has the ability to query the endpoint `api/health`.
<br />
---
<br />
## API
<!-- md:version stable-1.4.0 -->
This container includes the ability to run health checks utilizing the built-in API. You can directly access the health check and view a json formatted result with the url `https://tvapp2.domain.lan/api/health`
=== "Example"
``` { .yaml .copy .select title="/api/health" linenums="1" }
{
"ip": "172.18.2.1",
"gateway": "172.18.0.1",
"uptime": 5903.549082501,
"message": "healthy",
"status": "healthy",
"ref": "/api/health",
"method": "GET",
"code": 200,
"timestamp": 1744386346242
}
```
<br />
Numerous aliases have been added; you can use any of the following URLs to access the same health check results as they go to the same endpoint:
- https://tvapp2.domain.lan/api/health
- https://tvapp2.domain.lan/api/status
<br />
---
<br />
## Portainer
<!-- md:version stable-1.3.0 -->
To run a health check between TVApp2 and Portainer, apply the following lines of code to your TVApp2 `docker-compose.yml`. Two examples have been provided, you can use either one; `wget` or `curl`:
=== "Using CURL"
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="14-18" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- TZ=Etc/UTC
health check:
test: "curl --fail --silent http://127.0.0.1:4124/api/health | grep -i healthy || exit 1"
interval: 15s
timeout: 10s
retries: 3
```
=== "Using WGET"
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="14-18" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- TZ=Etc/UTC
health check:
test: "wget -qO- http://127.0.0.1:4124/api/health | grep -i healthy || exit 1"
interval: 15s
timeout: 10s
retries: 3
```
<br />
---
<br />
## Web Interface
<!-- md:version stable-1.4.0 -->
The TVApp2 web interface is equip with its own health check which is ran every `10 minutes` by default. Users will notice a health indicator in the top right of the header navigation bar which is represented by the :octicons-heart-fill-24:{ .heart } icon.
When the health check is ran every 10 minutes; a toast notification will appear in the lower-right side of the page:
<figure markdown="span">
![Image settings](../../assets/images/health-toast.gif){ width="80%" }
<figcaption>Health check toast notification</figcaption>
</figure>
<br />
### Health Check Duration
<!-- md:control env -->
<!-- md:version stable-1.4.0 -->
<!-- md:default `600000` -->
By default, a health check between the TVApp2 container and the web interface is done every `10 minutes`, but you can change this duration to something less or more. Be aware that if you set the time too low, you will constantly be spammed with toast notifications.
To change the health check delay, add the environment variable `HEALTH_TIMER` to your TVApp2 `docker-compose.yml`. Duration is in milliseconds.
<br />
=== "Example"
``` { .yaml .copy .select title="docker-compose.yml" linenums="1" hl_lines="13" }
services:
tvapp2:
container_name: tvapp2
image: ghcr.io/thebinaryninja/tvapp2:latest
restart: unless-stopped
volumes:
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock
- ./config:/config
- ./app:/usr/bin/app
environment:
- HEALTH_TIMER=600000 # (1)
```
1. :information: Defines how often to perform health checks between the container and the web interface. Time is in milliseconds. <br /><br />Default value is `600000` (10 minutes)
=== "Time Chart"
| HEALTH_TIMER Value | Delay Between Checks |
| --- | --- |
| `HEALTH_TIMER=300000` | 5 minutes |
| `HEALTH_TIMER=600000` | 10 minutes |
| `HEALTH_TIMER=1200000` | 20 minutes |
| `HEALTH_TIMER=1800000` | 30 minutes |
| `HEALTH_TIMER=3600000` | 1 hour |
<br />
<br />

BIN
docs/img/screenshots/01.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2024 Fonticons, Inc. --><defs><style>.fa-secondary{opacity:.4}</style></defs><path class="fa-secondary" d="M0 96C0 60.7 28.7 32 64 32l384 0c35.3 0 64 28.7 64 64l0 224c0 5.5-.7 10.8-2 16c-3.6-13.8-11.6-25.8-22.5-34.4c-5.4-4.3-11.6-7.7-18.2-10c-3.3-1.2-6.8-2.1-10.3-2.7c-1.8-.3-3.6-.5-5.4-.7c-.9-.1-1.8-.1-2.8-.2s-1.8-.1-2.8-.1L64 288c-29.8 0-54.9 20.4-62 48c-1.3-5.1-2-10.5-2-16L0 96zM352 384a32 32 0 1 1 -64 0 32 32 0 1 1 64 0z"/><path class="fa-primary" d="M0 352c0-35.3 28.7-64 64-64l384 0c35.3 0 64 28.7 64 64l0 64c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64l0-64zm352 32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm64 32a32 32 0 1 0 0-64 32 32 0 1 0 0 64z"/></svg>

After

Width:  |  Height:  |  Size: 872 B

View File

@@ -164,6 +164,7 @@ def badgeControl( args: str, page: Page, files: Files ):
elif type == "button": return newControlButton( page, files ) elif type == "button": return newControlButton( page, files )
elif type == "slider": return newControlSlider( page, files ) elif type == "slider": return newControlSlider( page, files )
elif type == "env": return newControlEnvVar( page, files ) elif type == "env": return newControlEnvVar( page, files )
elif type == "volume": return newControlVolume( page, files )
elif type == "color": return newControlColor( args, page, files ) elif type == "color": return newControlColor( args, page, files )
else: return newControlDefault( page, files ) else: return newControlDefault( page, files )
@@ -935,7 +936,7 @@ def newControlColor( text: str, page: Page, files: Files ):
# Icon : Control : Env Variable # Icon : Control : Env Variable
# #
# use the following tag in your md file: # use the following tag in your md file:
# <!-- md:control slider --> # <!-- md:control env -->
# # # #
def newControlEnvVar( page: Page, files: Files ): def newControlEnvVar( page: Page, files: Files ):
@@ -948,3 +949,21 @@ def newControlEnvVar( page: Page, files: Files ):
icon = f"[:{icon}:]({href} 'Type: Environment Variable')", icon = f"[:{icon}:]({href} 'Type: Environment Variable')",
type = "env" type = "env"
) )
# #
# Icon : Control : Volume
#
# use the following tag in your md file:
# <!-- md:control volume -->
# #
def newControlVolume( page: Page, files: Files ):
icon = "aetherx-axd-volume"
href = _resolve_path( f"{PAGE_CONVENTIONS}#control", page, files )
print(clr.MAGENTA + 'VERBOSE - ' + clr.WHITE + ' Running ' + clr.YELLOW + inspect.stack()[0][3] + clr.WHITE + ' for page ' + clr.GREY + str(href) + clr.WHITE )
return badgeCreate(
icon = f"[:{icon}:]({href} 'Type: Mountable Volume')",
type = "volume"
)

View File

@@ -29,6 +29,7 @@ site_url: 'https://thebinaryninja.github.io/tvapp2/'
# # # #
markdown_extensions: markdown_extensions:
- pymdownx.critic
- markdown.extensions.extra - markdown.extensions.extra
- toc: - toc:
permalink: true permalink: true
@@ -238,9 +239,15 @@ nav:
- Wiki: - Wiki:
- Conventions: 'about/conventions.md' - Conventions: 'about/conventions.md'
- Tags: 'about/tags.md' - Tags: 'about/tags.md'
- Install:
- Getting Started: 'install/index.md'
- docker run: 'install/docker-run.md'
- docker compose: 'install/docker-compose.md'
- Config: - Config:
- Environment Variables: 'config/env.md' - Environment Variables: 'config/env.md'
- Volumes: 'config/volumes.md' - Volumes: 'config/volumes.md'
- Usage:
- Healthcheck: 'usage/healthcheck.md'
- Changelog: 'about/changelog.md' - Changelog: 'about/changelog.md'
- Discord: 'https://discord.gg/gTze6hRe' - Discord: 'https://discord.gg/gTze6hRe'

View File

@@ -9,10 +9,80 @@
# @repo.3 https://github.com/aetherinox/docker-base-alpine # @repo.3 https://github.com/aetherinox/docker-base-alpine
# # # #
# #
# define > colors
#
# Use the color table at:
# - https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
# #
declare -A c=(
[end]=$'\e[0m'
[white]=$'\e[97m'
[bold]=$'\e[1m'
[dim]=$'\e[2m'
[underline]=$'\e[4m'
[strike]=$'\e[9m'
[blink]=$'\e[5m'
[inverted]=$'\e[7m'
[hidden]=$'\e[8m'
[black]=$'\e[0;30m'
[redl]=$'\e[0;91m'
[redd]=$'\e[0;31m'
[magental]=$'\e[0;95m'
[magentad]=$'\e[0;35mm'
[bluel]=$'\e[0;94m'
[blued]=$'\e[0;34m'
[cyanl]=$'\e[0;96m'
[cyand]=$'\e[0;36m'
[greenl]=$'\e[0;92m'
[greend]=$'\e[0;32m'
[yellowl]=$'\e[0;93m'
[yellowd]=$'\e[0;33m'
[greyl]=$'\e[0;37m'
[greyd]=$'\e[0;90m'
[navy]=$'\e[38;5;62m'
[olive]=$'\e[38;5;144m'
[peach]=$'\e[38;5;210m'
)
# #
# unicode for emojis
# https://apps.timwhitlock.info/emoji/tables/unicode
# #
declare -A icon=(
["symbolic link"]=$'\xF0\x9F\x94\x97' # 🔗
["regular file"]=$'\xF0\x9F\x93\x84' # 📄
["directory"]=$'\xF0\x9F\x93\x81' # 📁
["regular empty file"]=$'\xe2\xad\x95' # ⭕
["log"]=$'\xF0\x9F\x93\x9C' # 📜
["1"]=$'\xF0\x9F\x93\x9C' # 📜
["2"]=$'\xF0\x9F\x93\x9C' # 📜
["3"]=$'\xF0\x9F\x93\x9C' # 📜
["4"]=$'\xF0\x9F\x93\x9C' # 📜
["5"]=$'\xF0\x9F\x93\x9C' # 📜
["pem"]=$'\xF0\x9F\x94\x92' # 🔑
["pub"]=$'\xF0\x9F\x94\x91' # 🔒
["pfx"]=$'\xF0\x9F\x94\x92' # 🔑
["p12"]=$'\xF0\x9F\x94\x92' # 🔑
["key"]=$'\xF0\x9F\x94\x91' # 🔒
["crt"]=$'\xF0\x9F\xAA\xAA ' # 🪪
["gz"]=$'\xF0\x9F\x93\xA6' # 📦
["zip"]=$'\xF0\x9F\x93\xA6' # 📦
["gzip"]=$'\xF0\x9F\x93\xA6' # 📦
["deb"]=$'\xF0\x9F\x93\xA6' # 📦
["sh"]=$'\xF0\x9F\x97\x94' # 🗔
)
# #
# define > general
# #
PLUGINS_PATH="/config/www/plugins" PLUGINS_PATH="/config/www/plugins"
# # # #
# Plugins > Start # Plugins > Start
# # # #
echo -e " Loader : Checking tvapp2-plugins" printf '%-29s %-65s\n' " ${c[bluel]}Loader${c[end]}" "${c[end]}Checking tvapp2-plugins${c[end]}"

View File

@@ -1 +1 @@
echo -e " Core : Completed loading container" echo -e " Completed loading container"

View File

@@ -1,35 +1,160 @@
#!/usr/bin/with-contenv bash #!/usr/bin/with-contenv bash
# shellcheck shell=bash # shellcheck shell=bash
# #
# defaults
# #
PUID=${PUID:-911} PUID=${PUID:-911}
PGID=${PGID:-911} PGID=${PGID:-911}
DIR_BUILD=${DIR_BUILD:-/usr/src/app}
DIR_RUN=${DIR_RUN:-/usr/bin/app}
bHasError=false
# #
# define > colors
#
# Use the color table at:
# - https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
# #
declare -A c=(
[end]=$'\e[0m'
[white]=$'\e[97m'
[bold]=$'\e[1m'
[dim]=$'\e[2m'
[underline]=$'\e[4m'
[strike]=$'\e[9m'
[blink]=$'\e[5m'
[inverted]=$'\e[7m'
[hidden]=$'\e[8m'
[black]=$'\e[0;30m'
[redl]=$'\e[0;91m'
[redd]=$'\e[0;31m'
[magental]=$'\e[0;95m'
[magentad]=$'\e[0;35mm'
[bluel]=$'\e[0;94m'
[blued]=$'\e[0;34m'
[cyanl]=$'\e[0;96m'
[cyand]=$'\e[0;36m'
[greenl]=$'\e[0;92m'
[greend]=$'\e[0;32m'
[yellowl]=$'\e[0;93m'
[yellowd]=$'\e[0;33m'
[greyl]=$'\e[0;37m'
[greyd]=$'\e[0;90m'
[navy]=$'\e[38;5;62m'
[olive]=$'\e[38;5;144m'
[peach]=$'\e[38;5;210m'
)
# #
# unicode for emojis
# https://apps.timwhitlock.info/emoji/tables/unicode
# #
declare -A icon=(
["symbolic link"]=$'\xF0\x9F\x94\x97' # 🔗
["regular file"]=$'\xF0\x9F\x93\x84' # 📄
["directory"]=$'\xF0\x9F\x93\x81' # 📁
["regular empty file"]=$'\xe2\xad\x95' # ⭕
["log"]=$'\xF0\x9F\x93\x9C' # 📜
["1"]=$'\xF0\x9F\x93\x9C' # 📜
["2"]=$'\xF0\x9F\x93\x9C' # 📜
["3"]=$'\xF0\x9F\x93\x9C' # 📜
["4"]=$'\xF0\x9F\x93\x9C' # 📜
["5"]=$'\xF0\x9F\x93\x9C' # 📜
["pem"]=$'\xF0\x9F\x94\x92' # 🔑
["pub"]=$'\xF0\x9F\x94\x91' # 🔒
["pfx"]=$'\xF0\x9F\x94\x92' # 🔑
["p12"]=$'\xF0\x9F\x94\x92' # 🔑
["key"]=$'\xF0\x9F\x94\x91' # 🔒
["crt"]=$'\xF0\x9F\xAA\xAA ' # 🪪
["gz"]=$'\xF0\x9F\x93\xA6' # 📦
["zip"]=$'\xF0\x9F\x93\xA6' # 📦
["gzip"]=$'\xF0\x9F\x93\xA6' # 📦
["deb"]=$'\xF0\x9F\x93\xA6' # 📦
["sh"]=$'\xF0\x9F\x97\x94' # 🗔
)
# #
# get container ips
# #
IP_GATEWAY=$(/sbin/ip route|awk '/default/ { print $3 }') IP_GATEWAY=$(/sbin/ip route|awk '/default/ { print $3 }')
IP_CONTAINER=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1') IP_CONTAINER=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')
# #
# usermod
# -o, --non-unique allow using duplicate (non-unique) UID
# -g, --gid GROUP force use GROUP as new primary group
# -G, --groups GROUPS new list of supplementary GROUPS
# -u, --uid UID new UID for the user account
# -U, --unlock unlock the user account
#
# groupmod
# -g, --gid GID change the group ID to GID
# -o, --non-unique allow to use a duplicate (non-unique) GID
# #
if [[ -z ${TVAPP_READ_ONLY_FS} ]] && [[ -z ${TVAPP_NON_ROOT_USER} ]]; then if [[ -z ${TVAPP_READ_ONLY_FS} ]] && [[ -z ${TVAPP_NON_ROOT_USER} ]]; then
groupmod -o -g "$PGID" dockerx groupmod -o -g "$PGID" dockerx
usermod -o -u "$PUID" dockerx usermod -o -u "$PUID" dockerx
fi fi
if { [[ -z ${TVAPP_READ_ONLY_FS} ]] && [[ -z ${TVAPP_NON_ROOT_USER} ]]; } || [[ ! ${TVAPP_FIRST_PARTY} = "true" ]]; then # #
cat /etc/s6-overlay/s6-rc.d/init-adduser/branding # s6 > branding
else # #
cat /run/branding
fi printf '%-1s\n' " ${c[greyd]}──────────────────────────────────────────────────────────────────────────────────────────${c[end]}"
printf '%-1s\n' " ${c[greyd]} TVApp2 Docker Image${c[end]}"
printf '%-1s\n' " ${c[greyd]}──────────────────────────────────────────────────────────────────────────────────────────${c[end]}"
printf '%-2s\n' " ${c[greyd]}The TvApp2 image allows you to fetch M3U playlist and EPG data for numerous IPTV ${c[end]}"
printf '%-2s\n' " ${c[greyd]}services online. ${c[end]}"
echo -e
printf '%-2s\n' " ${c[greyd]}Once the files are fetched by the image, you can visit the self-hosted webpage, copy ${c[end]}"
printf '%-2s\n' " ${c[greyd]}the links to the M3U and EPG files; and add them to your favorite IPTV app such as ${c[end]}"
printf '%-2s\n' " ${c[greyd]}Jellyfin, Plex, or Emby. ${c[end]}"
echo -e
printf '%-2s\n' " ${c[greyd]}For more information about this project; visit the links below. This app is served on ${c[end]}"
printf '%-2s\n' " ${c[greyd]}multiple repositories as backup. Use any of the repo links below: ${c[end]}"
echo -e
printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}TVApp2 Repo 1${c[end]}" "${c[end]}https://github.com/TheBinaryNinja/tvapp2 ${c[end]}"
printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}TVApp2 Repo 2${c[end]}" "${c[end]}https://git.binaryninja.net/BinaryNinja/tvapp2 ${c[end]}"
printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}Base Alpine Image${c[end]}" "${c[end]}https://github.com/Aetherinox/docker-base-alpine ${c[end]}"
echo -e
printf '%-2s\n' " ${c[greyd]}If you are making this container available on a public-facing domain, please consider ${c[end]}"
printf '%-2s\n' " ${c[greyd]}using Traefik and Authentik to protect this container from outside access. Your M3U ${c[end]}"
printf '%-2s\n' " ${c[greyd]}and EPG files will be available for the public to download and use. ${c[end]}"
# if { [[ -z ${TVAPP_READ_ONLY_FS} ]] && [[ -z ${TVAPP_NON_ROOT_USER} ]]; } || [[ ! ${TVAPP_FIRST_PARTY} = "true" ]]; then
# cat /etc/s6-overlay/s6-rc.d/init-adduser/branding
# else
# cat /run/branding
# fi
# #
# branding > non-root user
# #
if [[ -z ${TVAPP_NON_ROOT_USER} ]]; then if [[ -z ${TVAPP_NON_ROOT_USER} ]]; then
echo "" echo -e
echo " User:Group $(id -u dockerx):$(id -g dockerx)" printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}User:Group${c[end]}" "${c[end]}$(id -u dockerx):$(id -g dockerx)${c[end]}"
else else
echo " User:Group $(stat /run -c %u):$(stat /run -c %g)" printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}User:Group${c[end]}" "${c[end]}$(stat /run -c %u):$(stat /run -c %g)${c[end]}"
fi fi
echo " Port(s) $(echo $WEB_PORT)" printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}Port(s)${c[end]}" "${c[end]}$(echo $WEB_PORT)${c[end]}"
echo " Gateway $(echo $IP_GATEWAY)" printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}Gateway${c[end]}" "${c[end]}$(echo $IP_GATEWAY)${c[end]}"
echo " Web Server $(echo $IP_CONTAINER:$WEB_PORT)" printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}Web Server${c[end]}" "${c[end]}$(echo $IP_CONTAINER:$WEB_PORT)${c[end]}"
echo " App Folder $(echo $DIR_RUN)" printf '%-6s %-35s %-65s\n' "" " ${c[greenl]}App Folder${c[end]}" "${c[end]}$(echo $DIR_RUN)${c[end]}"
echo "" echo -e
echo '──────────────────────────────────────────────────────────────────────────────────────────' printf '%-1s\n' " ${c[greyd]}──────────────────────────────────────────────────────────────────────────────────────────${c[end]}"
# #
# set permissions
# #
if [[ -z ${TVAPP_READ_ONLY_FS} ]] && [[ -z ${TVAPP_NON_ROOT_USER} ]]; then if [[ -z ${TVAPP_READ_ONLY_FS} ]] && [[ -z ${TVAPP_NON_ROOT_USER} ]]; then
aetherxown dockerx:dockerx /app aetherxown dockerx:dockerx /app

View File

@@ -1,22 +1,88 @@
#!/usr/bin/with-contenv bash #!/usr/bin/with-contenv bash
# shellcheck shell=bash # shellcheck shell=bash
# #
# define > colors
#
# Use the color table at:
# - https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
# #
declare -A c=(
[end]=$'\e[0m'
[white]=$'\e[97m'
[bold]=$'\e[1m'
[dim]=$'\e[2m'
[underline]=$'\e[4m'
[strike]=$'\e[9m'
[blink]=$'\e[5m'
[inverted]=$'\e[7m'
[hidden]=$'\e[8m'
[black]=$'\e[0;30m'
[redl]=$'\e[0;91m'
[redd]=$'\e[0;31m'
[magental]=$'\e[0;95m'
[magentad]=$'\e[0;35mm'
[bluel]=$'\e[0;94m'
[blued]=$'\e[0;34m'
[cyanl]=$'\e[0;96m'
[cyand]=$'\e[0;36m'
[greenl]=$'\e[0;92m'
[greend]=$'\e[0;32m'
[yellowl]=$'\e[0;93m'
[yellowd]=$'\e[0;33m'
[greyl]=$'\e[0;37m'
[greyd]=$'\e[0;90m'
[navy]=$'\e[38;5;62m'
[olive]=$'\e[38;5;144m'
[peach]=$'\e[38;5;210m'
)
# #
# unicode for emojis
# https://apps.timwhitlock.info/emoji/tables/unicode
# #
declare -A icon=(
["symbolic link"]=$'\xF0\x9F\x94\x97' # 🔗
["regular file"]=$'\xF0\x9F\x93\x84' # 📄
["directory"]=$'\xF0\x9F\x93\x81' # 📁
["regular empty file"]=$'\xe2\xad\x95' # ⭕
["log"]=$'\xF0\x9F\x93\x9C' # 📜
["1"]=$'\xF0\x9F\x93\x9C' # 📜
["2"]=$'\xF0\x9F\x93\x9C' # 📜
["3"]=$'\xF0\x9F\x93\x9C' # 📜
["4"]=$'\xF0\x9F\x93\x9C' # 📜
["5"]=$'\xF0\x9F\x93\x9C' # 📜
["pem"]=$'\xF0\x9F\x94\x92' # 🔑
["pub"]=$'\xF0\x9F\x94\x91' # 🔒
["pfx"]=$'\xF0\x9F\x94\x92' # 🔑
["p12"]=$'\xF0\x9F\x94\x92' # 🔑
["key"]=$'\xF0\x9F\x94\x91' # 🔒
["crt"]=$'\xF0\x9F\xAA\xAA ' # 🪪
["gz"]=$'\xF0\x9F\x93\xA6' # 📦
["zip"]=$'\xF0\x9F\x93\xA6' # 📦
["gzip"]=$'\xF0\x9F\x93\xA6' # 📦
["deb"]=$'\xF0\x9F\x93\xA6' # 📦
["sh"]=$'\xF0\x9F\x97\x94' # 🗔
)
# Directories # Directories
SCRIPTS_DIR="/custom-cont-init.d" SCRIPTS_DIR="/custom-cont-init.d"
# Make sure custom init directory exists and has files in it # Make sure custom init directory exists and has files in it
if [[ -e "${SCRIPTS_DIR}" ]] && [[ -n "$(/bin/ls -A ${SCRIPTS_DIR} 2>/dev/null)" ]]; then if [[ -e "${SCRIPTS_DIR}" ]] && [[ -n "$(/bin/ls -A ${SCRIPTS_DIR} 2>/dev/null)" ]]; then
echo -e " Loader : Plugins found, loading them ..." printf '%-29s %-65s\n' " ${c[bluel]}Loader${c[end]}" "${c[end]}Loading any found plugins${c[end]}"
for SCRIPT in "${SCRIPTS_DIR}"/*; do for SCRIPT in "${SCRIPTS_DIR}"/*; do
NAME="$(basename "${SCRIPT}")" NAME="$(basename "${SCRIPT}")"
if [[ -f "${SCRIPT}" ]]; then if [[ -f "${SCRIPT}" ]]; then
echo -e " Loader : Executing ..." printf '%-29s %-65s\n' " ${c[bluel]}Loader${c[end]}" "${c[end]}Executing${c[end]}"
/bin/bash "${SCRIPT}" /bin/bash "${SCRIPT}"
echo -e " Loader : ${NAME}: Ran Successfully with code [$?]" printf '%-29s %-65s\n' " ${c[bluel]}Loader${c[end]}" "${c[end]}Successfully ran with code ${c[bluel]}[$?]${c[end]}"
elif [[ ! -f "${SCRIPT}" ]]; then elif [[ ! -f "${SCRIPT}" ]]; then
echo -e " Loader : ${NAME}: Not a valid file" printf '%-29s %-65s\n' " ${c[bluel]}Loader${c[end]}" "${c[end]}${c[bluel]}${NAME}${c[end]} is not a valid file${c[end]}"
fi fi
done done
else else
echo -e " Loader : No Plugins found, skipping..." printf '%-29s %-65s\n' " ${c[bluel]}Loader${c[end]}" "${c[end]}No plugins found; skipping${c[end]}"
fi fi

View File

@@ -1,14 +1,113 @@
#!/usr/bin/with-contenv sh #!/usr/bin/with-contenv bash
# shellcheck shell=bash
# # # #
# Store env variables in s6 # defaults
# #
PUID=${PUID:-911}
PGID=${PGID:-911}
DIR_BUILD=${DIR_BUILD:-/usr/src/app}
DIR_RUN=${DIR_RUN:-/usr/bin/app}
bHasError=false
# #
# define > colors
#
# Use the color table at:
# - https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
# #
declare -A c=(
[end]=$'\e[0m'
[white]=$'\e[97m'
[bold]=$'\e[1m'
[dim]=$'\e[2m'
[underline]=$'\e[4m'
[strike]=$'\e[9m'
[blink]=$'\e[5m'
[inverted]=$'\e[7m'
[hidden]=$'\e[8m'
[black]=$'\e[0;30m'
[redl]=$'\e[0;91m'
[redd]=$'\e[0;31m'
[magental]=$'\e[0;95m'
[magentad]=$'\e[0;35mm'
[bluel]=$'\e[0;94m'
[blued]=$'\e[0;34m'
[cyanl]=$'\e[0;96m'
[cyand]=$'\e[0;36m'
[greenl]=$'\e[0;92m'
[greend]=$'\e[0;32m'
[yellowl]=$'\e[0;93m'
[yellowd]=$'\e[0;33m'
[greyl]=$'\e[0;37m'
[greyd]=$'\e[0;90m'
[navy]=$'\e[38;5;62m'
[olive]=$'\e[38;5;144m'
[peach]=$'\e[38;5;210m'
)
# #
# unicode for emojis
# https://apps.timwhitlock.info/emoji/tables/unicode
# #
declare -A icon=(
["symbolic link"]=$'\xF0\x9F\x94\x97' # 🔗
["regular file"]=$'\xF0\x9F\x93\x84' # 📄
["directory"]=$'\xF0\x9F\x93\x81' # 📁
["regular empty file"]=$'\xe2\xad\x95' # ⭕
["log"]=$'\xF0\x9F\x93\x9C' # 📜
["1"]=$'\xF0\x9F\x93\x9C' # 📜
["2"]=$'\xF0\x9F\x93\x9C' # 📜
["3"]=$'\xF0\x9F\x93\x9C' # 📜
["4"]=$'\xF0\x9F\x93\x9C' # 📜
["5"]=$'\xF0\x9F\x93\x9C' # 📜
["pem"]=$'\xF0\x9F\x94\x92' # 🔑
["pub"]=$'\xF0\x9F\x94\x91' # 🔒
["pfx"]=$'\xF0\x9F\x94\x92' # 🔑
["p12"]=$'\xF0\x9F\x94\x92' # 🔑
["key"]=$'\xF0\x9F\x94\x91' # 🔒
["crt"]=$'\xF0\x9F\xAA\xAA ' # 🪪
["gz"]=$'\xF0\x9F\x93\xA6' # 📦
["zip"]=$'\xF0\x9F\x93\xA6' # 📦
["gzip"]=$'\xF0\x9F\x93\xA6' # 📦
["deb"]=$'\xF0\x9F\x93\xA6' # 📦
["sh"]=$'\xF0\x9F\x97\x94' # 🗔
)
# #
# define > system
# #
sys_os_ver="1.0.0"
sys_os_name="Unknown"
# #
# s6 > store env variables
# #
printf '%-29s %-65s\n' " ${c[bluel]}STATUS${c[end]}" "${c[end]}Fetching docker container and gateway addresses${c[end]}"
# #
# get container ips
# # # #
ip_gateway=$(/sbin/ip route|awk '/default/ { print $3 }') ip_gateway=$(/sbin/ip route|awk '/default/ { print $3 }')
ip_container=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1') ip_container=$(ifconfig | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')
if [ -d "/var/run/s6/container_environment/" ]; then
printf "$ip_gateway" > /var/run/s6/container_environment/IP_GATEWAY printf "$ip_gateway" > /var/run/s6/container_environment/IP_GATEWAY
printf "$ip_container" > /var/run/s6/container_environment/IP_CONTAINER printf "$ip_container" > /var/run/s6/container_environment/IP_CONTAINER
else
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot generate s6-overlay env files; folder ${c[redl]}/var/run/s6/container_environment/${c[end]} does not exist${c[end]}"
bHasError=true
fi
# #
# s6 > export env vars
# #
export IP_GATEWAY=$ip_gateway export IP_GATEWAY=$ip_gateway
export IP_GATEWAY=$ip_container export IP_GATEWAY=$ip_container
@@ -17,8 +116,75 @@ export IP_GATEWAY=$ip_container
# install and startup for tvapp2 # install and startup for tvapp2
# # # #
printf '%-29s %-65s\n' " ${c[bluel]}STATUS${c[end]}" "${c[end]}Copying ${c[bluel]}${DIR_BUILD}${c[end]} to ${c[bluel]}${DIR_RUN}${c[end]}"
if [ -z "${DIR_BUILD}" ]; then
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot copy; env var ${c[redl]}\${DIR_BUILD}${c[end]} missing${c[end]}"
bHasError=true
else
if [ -d "${DIR_BUILD}/" ]; then
cp -r ${DIR_BUILD}/* ${DIR_RUN} cp -r ${DIR_BUILD}/* ${DIR_RUN}
rm -rf ${DIR_BUILD}/* else
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot copy folder ${c[redl]}${DIR_BUILD}${c[end]} to ${c[redl]}${DIR_RUN}${c[end]}; build folder ${c[redl]}${DIR_BUILD}${c[end]} does not exist${c[end]}"
bHasError=true
fi
fi
# #
# remove build directory
# #
printf '%-29s %-65s\n' " ${c[bluel]}STATUS${c[end]}" "${c[end]}Remove ${c[bluel]}${DIR_BUILD}/${c[end]}"
if [ -z "${DIR_BUILD}" ]; then
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot remove; env var ${c[redl]}\${DIR_BUILD}${c[end]} missing${c[end]}"
else
if [ -d "${DIR_BUILD}" ]; then
rm -rf "${DIR_BUILD}/"
else
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot remove; build folder ${c[redl]}${DIR_BUILD}${c[end]} does not exist. Restart the container to re-initialize build folder.${c[end]}"
fi
fi
# #
# cd to BUILD_RUN directory
# #
printf '%-29s %-65s\n' " ${c[bluel]}STATUS${c[end]}" "${c[end]}Changing to run directory ${c[bluel]}${DIR_RUN}/${c[end]}"
if [ -z "${DIR_RUN}" ]; then
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot cd; env var ${c[redl]}\${DIR_RUN}${c[end]} missing${c[end]}"
bHasError=true
else
if [ -d "${DIR_RUN}" ]; then
cd ${DIR_RUN} cd ${DIR_RUN}
else
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot cd; run folder ${c[redl]}${DIR_RUN}${c[end]} does not exist${c[end]}"
bHasError=true
fi
fi
# #
# install tvapp2 via npm
# #
printf '%-29s %-65s\n' " ${c[bluel]}STATUS${c[end]}" "${c[end]}Running command ${c[bluel]}npm install --omit=dev${c[end]}"
if ! command -v npm; then
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Cannot install TVApp2 with npm because package ${c[redl]}npm${c[end]} not installed${c[end]}"
bHasError=true
else
npm install --omit=dev npm install --omit=dev
printf '%-29s %-65s\n' " ${c[bluel]}STATUS${c[end]}" "${c[end]}Running command ${c[bluel]}npm start${c[end]}"
npm start npm start
fi
# #
# finished run script
# #
printf '%-29s %-65s\n' " ${c[greenl]}OK${c[end]}" "${c[end]}Finished initializing script${c[end]}"
if [ "$bHasError" = true ] ; then
printf '%-29s %-65s\n' "" ""
printf '%-29s %-65s\n' " ${c[redl]}ERROR${c[end]}" "${c[end]}Fatal errors were detected${c[end]}"
printf '%-29s %-65s\n' " ${c[redl]}${c[end]}" "${c[end]}The run script detected that certain steps failed. This app may not${c[end]}"
printf '%-29s %-65s\n' " ${c[redl]}${c[end]}" "${c[end]}work properly. Try restarting the container.${c[end]}"
printf '%-29s %-65s\n' "" ""
fi

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,43 @@
{ {
"name": "tvapp2", "name": "tvapp2",
"version": "1.4.0", "version": "1.5.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"node_modules/@babel/runtime": { "node_modules/@aetherinox/noxenv": {
"version": "7.27.0", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", "resolved": "https://registry.npmjs.org/@aetherinox/noxenv/-/noxenv-1.1.1.tgz",
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", "integrity": "sha512-oSk7rEI4xzC4R1IykulJf+H2hZ6/OQIqX2ZA60IIU3yy91l5UgjkeHIDQguIdC2zVfjPeW5JqI14NMZyH4WMfw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"regenerator-runtime": "^0.14.0" "cross-spawn": "^7.0.6",
"uuid": "^11.1.0"
}, },
"bin": {
"noxenv": "src/bin/noxenv.js",
"noxenv-shell": "src/bin/noxenv-shell.js"
},
"engines": {
"node": ">=20.9.0",
"npm": ">=10",
"yarn": ">=1"
}
},
"node_modules/@babel/runtime": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
"dev": true,
"license": "MIT",
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@eslint-community/eslint-utils": { "node_modules/@eslint-community/eslint-utils": {
"version": "4.5.1", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
"integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -60,9 +77,9 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.19.2", "version": "0.20.0",
"resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz",
"integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -74,10 +91,20 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@eslint/config-helpers": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz",
"integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==",
"dev": true,
"license": "Apache-2.0",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@eslint/core": { "node_modules/@eslint/core": {
"version": "0.9.1", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz",
"integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -112,13 +139,16 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.17.0", "version": "9.28.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz",
"integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"url": "https://eslint.org/donate"
} }
}, },
"node_modules/@eslint/object-schema": { "node_modules/@eslint/object-schema": {
@@ -132,32 +162,19 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.2.8", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz",
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^0.13.0", "@eslint/core": "^0.14.0",
"levn": "^0.4.1" "levn": "^0.4.1"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@eslint/plugin-kit/node_modules/@eslint/core": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz",
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"@types/json-schema": "^7.0.15"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.1", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -211,9 +228,9 @@
} }
}, },
"node_modules/@humanwhocodes/retry": { "node_modules/@humanwhocodes/retry": {
"version": "0.4.2", "version": "0.4.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
"integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@@ -232,9 +249,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@stylistic/eslint-plugin-js": { "node_modules/@stylistic/eslint-plugin-js": {
"version": "3.1.0", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-3.1.0.tgz", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-4.4.0.tgz",
"integrity": "sha512-lQktsOiCr8S6StG29C5fzXYxLOD6ID1rp4j6TRS+E/qY1xd59Fm7dy5qm9UauJIEoSTlYx6yGsCHYh5UkgXPyg==", "integrity": "sha512-UeeQNRF73zJXnNGGbvwgUgzS+vzVGQoRuQKR6RhQCRHQmaBaVHxDDQVmN9RPLCnRxVjO/v8cqq/yMDqC7DikSQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -245,7 +262,7 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}, },
"peerDependencies": { "peerDependencies": {
"eslint": ">=8.40.0" "eslint": ">=9.0.0"
} }
}, },
"node_modules/@types/estree": { "node_modules/@types/estree": {
@@ -269,6 +286,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/luxon": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.6.2.tgz",
"integrity": "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==",
"license": "MIT"
},
"node_modules/@types/uuid": { "node_modules/@types/uuid": {
"version": "10.0.0", "version": "10.0.0",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
@@ -600,18 +623,6 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/body-parser/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -826,6 +837,19 @@
"node": ">=6.6.0" "node": ">=6.6.0"
} }
}, },
"node_modules/cron": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/cron/-/cron-4.3.1.tgz",
"integrity": "sha512-7x7DoEOxV11t3OPWWMjj1xrL1PGkTV5RV+/54IJTZD7gStiaMploY43EkeBSkDZTLRbUwk+OISbQ0TR133oXyA==",
"license": "MIT",
"dependencies": {
"@types/luxon": "~3.6.0",
"luxon": "~3.6.0"
},
"engines": {
"node": ">=18.x"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -896,9 +920,9 @@
} }
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.0", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ms": "^2.1.3" "ms": "^2.1.3"
@@ -1077,9 +1101,9 @@
} }
}, },
"node_modules/es-abstract": { "node_modules/es-abstract": {
"version": "1.23.9", "version": "1.23.10",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.10.tgz",
"integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "integrity": "sha512-MtUbM072wlJNyeYAe0mhzrD+M6DIJa96CZAOBBrhDbgKnB4MApIKefcyAB1eOdYn8cUNZgvwBvEzdoAYsxgEIw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -1087,18 +1111,18 @@
"arraybuffer.prototype.slice": "^1.0.4", "arraybuffer.prototype.slice": "^1.0.4",
"available-typed-arrays": "^1.0.7", "available-typed-arrays": "^1.0.7",
"call-bind": "^1.0.8", "call-bind": "^1.0.8",
"call-bound": "^1.0.3", "call-bound": "^1.0.4",
"data-view-buffer": "^1.0.2", "data-view-buffer": "^1.0.2",
"data-view-byte-length": "^1.0.2", "data-view-byte-length": "^1.0.2",
"data-view-byte-offset": "^1.0.1", "data-view-byte-offset": "^1.0.1",
"es-define-property": "^1.0.1", "es-define-property": "^1.0.1",
"es-errors": "^1.3.0", "es-errors": "^1.3.0",
"es-object-atoms": "^1.0.0", "es-object-atoms": "^1.1.1",
"es-set-tostringtag": "^2.1.0", "es-set-tostringtag": "^2.1.0",
"es-to-primitive": "^1.3.0", "es-to-primitive": "^1.3.0",
"function.prototype.name": "^1.1.8", "function.prototype.name": "^1.1.8",
"get-intrinsic": "^1.2.7", "get-intrinsic": "^1.3.0",
"get-proto": "^1.0.0", "get-proto": "^1.0.1",
"get-symbol-description": "^1.1.0", "get-symbol-description": "^1.1.0",
"globalthis": "^1.0.4", "globalthis": "^1.0.4",
"gopd": "^1.2.0", "gopd": "^1.2.0",
@@ -1114,13 +1138,13 @@
"is-shared-array-buffer": "^1.0.4", "is-shared-array-buffer": "^1.0.4",
"is-string": "^1.1.1", "is-string": "^1.1.1",
"is-typed-array": "^1.1.15", "is-typed-array": "^1.1.15",
"is-weakref": "^1.1.0", "is-weakref": "^1.1.1",
"math-intrinsics": "^1.1.0", "math-intrinsics": "^1.1.0",
"object-inspect": "^1.13.3", "object-inspect": "^1.13.4",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.7", "object.assign": "^4.1.7",
"own-keys": "^1.0.1", "own-keys": "^1.0.1",
"regexp.prototype.flags": "^1.5.3", "regexp.prototype.flags": "^1.5.4",
"safe-array-concat": "^1.1.3", "safe-array-concat": "^1.1.3",
"safe-push-apply": "^1.0.0", "safe-push-apply": "^1.0.0",
"safe-regex-test": "^1.1.0", "safe-regex-test": "^1.1.0",
@@ -1133,7 +1157,7 @@
"typed-array-byte-offset": "^1.0.4", "typed-array-byte-offset": "^1.0.4",
"typed-array-length": "^1.0.7", "typed-array-length": "^1.0.7",
"unbox-primitive": "^1.1.0", "unbox-primitive": "^1.1.0",
"which-typed-array": "^1.1.18" "which-typed-array": "^1.1.19"
}, },
"engines": { "engines": {
"node": ">= 0.4" "node": ">= 0.4"
@@ -1239,22 +1263,23 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.17.0", "version": "9.28.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz",
"integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
"@eslint/config-array": "^0.19.0", "@eslint/config-array": "^0.20.0",
"@eslint/core": "^0.9.0", "@eslint/config-helpers": "^0.2.1",
"@eslint/eslintrc": "^3.2.0", "@eslint/core": "^0.14.0",
"@eslint/js": "9.17.0", "@eslint/eslintrc": "^3.3.1",
"@eslint/plugin-kit": "^0.2.3", "@eslint/js": "9.28.0",
"@eslint/plugin-kit": "^0.3.1",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.1", "@humanwhocodes/retry": "^0.4.2",
"@types/estree": "^1.0.6", "@types/estree": "^1.0.6",
"@types/json-schema": "^7.0.15", "@types/json-schema": "^7.0.15",
"ajv": "^6.12.4", "ajv": "^6.12.4",
@@ -1262,7 +1287,7 @@
"cross-spawn": "^7.0.6", "cross-spawn": "^7.0.6",
"debug": "^4.3.2", "debug": "^4.3.2",
"escape-string-regexp": "^4.0.0", "escape-string-regexp": "^4.0.0",
"eslint-scope": "^8.2.0", "eslint-scope": "^8.3.0",
"eslint-visitor-keys": "^4.2.0", "eslint-visitor-keys": "^4.2.0",
"espree": "^10.3.0", "espree": "^10.3.0",
"esquery": "^1.5.0", "esquery": "^1.5.0",
@@ -1315,9 +1340,9 @@
} }
}, },
"node_modules/eslint-compat-utils/node_modules/semver": { "node_modules/eslint-compat-utils/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
@@ -1457,13 +1482,13 @@
} }
}, },
"node_modules/eslint-plugin-n": { "node_modules/eslint-plugin-n": {
"version": "17.15.0", "version": "17.18.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.15.0.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.18.0.tgz",
"integrity": "sha512-xF3zJkOfLlFOm5TvmqmsnA9/fO+/z2pYs0dkuKXKN/ymS6UB1yEcaoIkqxLKQ9Dw/WmLX/Tdh6/5ZS5azVixFQ==", "integrity": "sha512-hvZ/HusueqTJ7VDLoCpjN0hx4N4+jHIWTXD4TMLHy9F23XkDagR9v+xQWRWR57yY55GPF8NnD4ox9iGTxirY8A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.4.1", "@eslint-community/eslint-utils": "^4.5.0",
"enhanced-resolve": "^5.17.1", "enhanced-resolve": "^5.17.1",
"eslint-plugin-es-x": "^7.8.0", "eslint-plugin-es-x": "^7.8.0",
"get-tsconfig": "^4.8.1", "get-tsconfig": "^4.8.1",
@@ -1522,9 +1547,9 @@
} }
}, },
"node_modules/eslint-plugin-n/node_modules/semver": { "node_modules/eslint-plugin-n/node_modules/semver": {
"version": "7.7.1", "version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
@@ -1730,6 +1755,19 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/external-editor/node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
"dev": true,
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2015,9 +2053,9 @@
} }
}, },
"node_modules/get-tsconfig": { "node_modules/get-tsconfig": {
"version": "4.10.0", "version": "4.10.1",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
"integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -2197,13 +2235,12 @@
} }
}, },
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -2887,6 +2924,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/luxon": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.6.1.tgz",
"integrity": "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==",
"license": "MIT",
"engines": {
"node": ">=12"
}
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -3008,6 +3054,15 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/node-cron": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.1.0.tgz",
"integrity": "sha512-OS+3ORu+h03/haS6Di8Qr7CrVs4YaKZZOynZwQpyPZDnR3tqRbwJmuP2gVR16JfhLgyNlloAV1VTrrWlRogCFA==",
"license": "ISC",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/node-fetch": { "node_modules/node-fetch": {
"version": "2.7.0", "version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@@ -3335,10 +3390,10 @@
} }
}, },
"node_modules/playwright": { "node_modules/playwright": {
"version": "1.51.1", "version": "1.52.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.51.1" "playwright-core": "1.52.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@@ -3351,7 +3406,7 @@
} }
}, },
"node_modules/playwright-core": { "node_modules/playwright-core": {
"version": "1.51.1", "version": "1.52.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"playwright-core": "cli.js" "playwright-core": "cli.js"
@@ -3459,18 +3514,6 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/raw-body/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/reflect.getprototypeof": { "node_modules/reflect.getprototypeof": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -3494,13 +3537,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"dev": true,
"license": "MIT"
},
"node_modules/regexp.prototype.flags": { "node_modules/regexp.prototype.flags": {
"version": "1.5.4", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
@@ -4070,9 +4106,9 @@
} }
}, },
"node_modules/tapable": { "node_modules/tapable": {
"version": "2.2.1", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@@ -4292,7 +4328,9 @@
} }
}, },
"node_modules/user-agents": { "node_modules/user-agents": {
"version": "1.1.497", "version": "1.1.557",
"resolved": "https://registry.npmjs.org/user-agents/-/user-agents-1.1.557.tgz",
"integrity": "sha512-7WD00n8KYmGlQEVgV4qRvH7NkcRt9KgEd7Sc95nRPcHCK8iOa5Ke85psgQKI6R+iWmBuuQKNKl2e46m6EvUTfA==",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"dependencies": { "dependencies": {
"lodash.clonedeep": "^4.5.0" "lodash.clonedeep": "^4.5.0"

View File

@@ -3,43 +3,43 @@
"browsers": [ "browsers": [
{ {
"name": "chromium", "name": "chromium",
"revision": "1161", "revision": "1169",
"installByDefault": true, "installByDefault": true,
"browserVersion": "134.0.6998.35" "browserVersion": "136.0.7103.25"
}, },
{ {
"name": "chromium-headless-shell", "name": "chromium-headless-shell",
"revision": "1161", "revision": "1169",
"installByDefault": true, "installByDefault": true,
"browserVersion": "134.0.6998.35" "browserVersion": "136.0.7103.25"
}, },
{ {
"name": "chromium-tip-of-tree", "name": "chromium-tip-of-tree",
"revision": "1304", "revision": "1320",
"installByDefault": false, "installByDefault": false,
"browserVersion": "135.0.7021.0" "browserVersion": "137.0.7105.0"
}, },
{ {
"name": "chromium-tip-of-tree-headless-shell", "name": "chromium-tip-of-tree-headless-shell",
"revision": "1304", "revision": "1320",
"installByDefault": false, "installByDefault": false,
"browserVersion": "135.0.7021.0" "browserVersion": "137.0.7105.0"
}, },
{ {
"name": "firefox", "name": "firefox",
"revision": "1475", "revision": "1482",
"installByDefault": true, "installByDefault": true,
"browserVersion": "135.0" "browserVersion": "137.0"
}, },
{ {
"name": "firefox-beta", "name": "firefox-beta",
"revision": "1471", "revision": "1477",
"installByDefault": false, "installByDefault": false,
"browserVersion": "136.0b4" "browserVersion": "137.0b2"
}, },
{ {
"name": "webkit", "name": "webkit",
"revision": "2140", "revision": "2158",
"installByDefault": true, "installByDefault": true,
"revisionOverrides": { "revisionOverrides": {
"debian11-x64": "2105", "debian11-x64": "2105",
@@ -50,6 +50,8 @@
"mac11-arm64": "1816", "mac11-arm64": "1816",
"mac12": "2009", "mac12": "2009",
"mac12-arm64": "2009", "mac12-arm64": "2009",
"mac13": "2140",
"mac13-arm64": "2140",
"ubuntu20.04-x64": "2092", "ubuntu20.04-x64": "2092",
"ubuntu20.04-arm64": "2092" "ubuntu20.04-arm64": "2092"
}, },

View File

@@ -1,69 +1,63 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var androidServerImpl_exports = {};
__export(androidServerImpl_exports, {
AndroidServerLauncherImpl: () => AndroidServerLauncherImpl
}); });
exports.AndroidServerLauncherImpl = void 0; module.exports = __toCommonJS(androidServerImpl_exports);
var _playwrightServer = require("./remote/playwrightServer"); var import_playwrightServer = require("./remote/playwrightServer");
var _playwright = require("./server/playwright"); var import_playwright = require("./server/playwright");
var _crypto = require("./server/utils/crypto"); var import_crypto = require("./server/utils/crypto");
var _utilsBundle = require("./utilsBundle"); var import_utilsBundle = require("./utilsBundle");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class AndroidServerLauncherImpl { class AndroidServerLauncherImpl {
async launchServer(options = {}) { async launchServer(options = {}) {
const playwright = (0, _playwright.createPlaywright)({ const playwright = (0, import_playwright.createPlaywright)({ sdkLanguage: "javascript", isServer: true });
sdkLanguage: 'javascript',
isServer: true
});
// 1. Pre-connect to the device
let devices = await playwright.android.devices({ let devices = await playwright.android.devices({
host: options.adbHost, host: options.adbHost,
port: options.adbPort, port: options.adbPort,
omitDriverInstall: options.omitDriverInstall omitDriverInstall: options.omitDriverInstall
}); });
if (devices.length === 0) throw new Error('No devices found'); if (devices.length === 0)
throw new Error("No devices found");
if (options.deviceSerialNumber) { if (options.deviceSerialNumber) {
devices = devices.filter(d => d.serial === options.deviceSerialNumber); devices = devices.filter((d) => d.serial === options.deviceSerialNumber);
if (devices.length === 0) throw new Error(`No device with serial number '${options.deviceSerialNumber}' was found`); if (devices.length === 0)
throw new Error(`No device with serial number '${options.deviceSerialNumber}' was found`);
} }
if (devices.length > 1) throw new Error(`More than one device found. Please specify deviceSerialNumber`); if (devices.length > 1)
throw new Error(`More than one device found. Please specify deviceSerialNumber`);
const device = devices[0]; const device = devices[0];
const path = options.wsPath ? options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}` : `/${(0, _crypto.createGuid)()}`; const path = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${(0, import_crypto.createGuid)()}`;
const server = new import_playwrightServer.PlaywrightServer({ mode: "launchServer", path, maxConnections: 1, preLaunchedAndroidDevice: device });
// 2. Start the server
const server = new _playwrightServer.PlaywrightServer({
mode: 'launchServer',
path,
maxConnections: 1,
preLaunchedAndroidDevice: device
});
const wsEndpoint = await server.listen(options.port, options.host); const wsEndpoint = await server.listen(options.port, options.host);
const browserServer = new import_utilsBundle.ws.EventEmitter();
// 3. Return the BrowserServer interface
const browserServer = new _utilsBundle.ws.EventEmitter();
browserServer.wsEndpoint = () => wsEndpoint; browserServer.wsEndpoint = () => wsEndpoint;
browserServer.close = () => device.close(); browserServer.close = () => device.close();
browserServer.kill = () => device.close(); browserServer.kill = () => device.close();
device.on('close', () => { device.on("close", () => {
server.close(); server.close();
browserServer.emit('close'); browserServer.emit("close");
}); });
return browserServer; return browserServer;
} }
} }
exports.AndroidServerLauncherImpl = AndroidServerLauncherImpl; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AndroidServerLauncherImpl
});

View File

@@ -1,73 +1,57 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserServerImpl_exports = {};
__export(browserServerImpl_exports, {
BrowserServerLauncherImpl: () => BrowserServerLauncherImpl
}); });
exports.BrowserServerLauncherImpl = void 0; module.exports = __toCommonJS(browserServerImpl_exports);
var _socksProxy = require("./server/utils/socksProxy"); var import_socksProxy = require("./server/utils/socksProxy");
var _playwrightServer = require("./remote/playwrightServer"); var import_playwrightServer = require("./remote/playwrightServer");
var _helper = require("./server/helper"); var import_helper = require("./server/helper");
var _instrumentation = require("./server/instrumentation"); var import_instrumentation = require("./server/instrumentation");
var _playwright = require("./server/playwright"); var import_playwright = require("./server/playwright");
var _crypto = require("./server/utils/crypto"); var import_crypto = require("./server/utils/crypto");
var _stackTrace = require("./utils/isomorphic/stackTrace"); var import_stackTrace = require("./utils/isomorphic/stackTrace");
var _utilsBundle = require("./utilsBundle"); var import_utilsBundle = require("./utilsBundle");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class BrowserServerLauncherImpl { class BrowserServerLauncherImpl {
constructor(browserName) { constructor(browserName) {
this._browserName = void 0;
this._browserName = browserName; this._browserName = browserName;
} }
async launchServer(options = {}) { async launchServer(options = {}) {
const playwright = (0, _playwright.createPlaywright)({ const playwright = (0, import_playwright.createPlaywright)({ sdkLanguage: "javascript", isServer: true });
sdkLanguage: 'javascript', const socksProxy = false ? new SocksProxy() : void 0;
isServer: true playwright.options.socksProxyPort = await socksProxy?.listen(0);
}); const metadata = (0, import_instrumentation.serverSideCallMetadata)();
// TODO: enable socks proxy once ipv6 is supported.
const socksProxy = false ? new _socksProxy.SocksProxy() : undefined;
playwright.options.socksProxyPort = await (socksProxy === null || socksProxy === void 0 ? void 0 : socksProxy.listen(0));
// 1. Pre-launch the browser
const metadata = (0, _instrumentation.serverSideCallMetadata)();
const browser = await playwright[this._browserName].launch(metadata, { const browser = await playwright[this._browserName].launch(metadata, {
...options, ...options,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined, ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? envObjectToArray(options.env) : undefined env: options.env ? envObjectToArray(options.env) : void 0
}, toProtocolLogger(options.logger)).catch(e => { }, toProtocolLogger(options.logger)).catch((e) => {
const log = _helper.helper.formatBrowserLogs(metadata.log); const log = import_helper.helper.formatBrowserLogs(metadata.log);
(0, _stackTrace.rewriteErrorMessage)(e, `${e.message} Failed to launch browser.${log}`); (0, import_stackTrace.rewriteErrorMessage)(e, `${e.message} Failed to launch browser.${log}`);
throw e; throw e;
}); });
const path = options.wsPath ? options.wsPath.startsWith('/') ? options.wsPath : `/${options.wsPath}` : `/${(0, _crypto.createGuid)()}`; const path = options.wsPath ? options.wsPath.startsWith("/") ? options.wsPath : `/${options.wsPath}` : `/${(0, import_crypto.createGuid)()}`;
const server = new import_playwrightServer.PlaywrightServer({ mode: "launchServer", path, maxConnections: Infinity, preLaunchedBrowser: browser, preLaunchedSocksProxy: socksProxy });
// 2. Start the server
const server = new _playwrightServer.PlaywrightServer({
mode: 'launchServer',
path,
maxConnections: Infinity,
preLaunchedBrowser: browser,
preLaunchedSocksProxy: socksProxy
});
const wsEndpoint = await server.listen(options.port, options.host); const wsEndpoint = await server.listen(options.port, options.host);
const browserServer = new import_utilsBundle.ws.EventEmitter();
// 3. Return the BrowserServer interface
const browserServer = new _utilsBundle.ws.EventEmitter();
browserServer.process = () => browser.options.browserProcess.process; browserServer.process = () => browser.options.browserProcess.process;
browserServer.wsEndpoint = () => wsEndpoint; browserServer.wsEndpoint = () => wsEndpoint;
browserServer.close = () => browser.options.browserProcess.close(); browserServer.close = () => browser.options.browserProcess.close();
@@ -76,26 +60,29 @@ class BrowserServerLauncherImpl {
browserServer._disconnectForTest = () => server.close(); browserServer._disconnectForTest = () => server.close();
browserServer._userDataDirForTest = browser._userDataDirForTest; browserServer._userDataDirForTest = browser._userDataDirForTest;
browser.options.browserProcess.onclose = (exitCode, signal) => { browser.options.browserProcess.onclose = (exitCode, signal) => {
socksProxy === null || socksProxy === void 0 || socksProxy.close().catch(() => {}); socksProxy?.close().catch(() => {
});
server.close(); server.close();
browserServer.emit('close', exitCode, signal); browserServer.emit("close", exitCode, signal);
}; };
return browserServer; return browserServer;
} }
} }
exports.BrowserServerLauncherImpl = BrowserServerLauncherImpl;
function toProtocolLogger(logger) { function toProtocolLogger(logger) {
return logger ? (direction, message) => { return logger ? (direction, message) => {
if (logger.isEnabled('protocol', 'verbose')) logger.log('protocol', 'verbose', (direction === 'send' ? 'SEND ► ' : '◀ RECV ') + JSON.stringify(message), [], {}); if (logger.isEnabled("protocol", "verbose"))
} : undefined; logger.log("protocol", "verbose", (direction === "send" ? "SEND \u25BA " : "\u25C0 RECV ") + JSON.stringify(message), [], {});
} : void 0;
} }
function envObjectToArray(env) { function envObjectToArray(env) {
const result = []; const result = [];
for (const name in env) { for (const name in env) {
if (!Object.is(env[name], undefined)) result.push({ if (!Object.is(env[name], void 0))
name, result.push({ name, value: String(env[name]) });
value: String(env[name])
});
} }
return result; return result;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BrowserServerLauncherImpl
});

View File

@@ -1,95 +1,97 @@
"use strict"; "use strict";
var __create = Object.create;
Object.defineProperty(exports, "__esModule", { var __defProp = Object.defineProperty;
value: true var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var driver_exports = {};
__export(driver_exports, {
launchBrowserServer: () => launchBrowserServer,
printApiJson: () => printApiJson,
runDriver: () => runDriver,
runServer: () => runServer
}); });
exports.launchBrowserServer = launchBrowserServer; module.exports = __toCommonJS(driver_exports);
exports.printApiJson = printApiJson; var import_fs = __toESM(require("fs"));
exports.runDriver = runDriver; var playwright = __toESM(require("../.."));
exports.runServer = runServer; var import_pipeTransport = require("../server/utils/pipeTransport");
var _fs = _interopRequireDefault(require("fs")); var import_playwrightServer = require("../remote/playwrightServer");
var playwright = _interopRequireWildcard(require("../..")); var import_server = require("../server");
var _pipeTransport = require("../server/utils/pipeTransport"); var import_processLauncher = require("../server/utils/processLauncher");
var _playwrightServer = require("../remote/playwrightServer");
var _server = require("../server");
var _processLauncher = require("../server/utils/processLauncher");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-console */
function printApiJson() { function printApiJson() {
// Note: this file is generated by build-playwright-driver.sh console.log(JSON.stringify(require("../../api.json")));
console.log(JSON.stringify(require('../../api.json')));
} }
function runDriver() { function runDriver() {
const dispatcherConnection = new _server.DispatcherConnection(); const dispatcherConnection = new import_server.DispatcherConnection();
new _server.RootDispatcher(dispatcherConnection, async (rootScope, { new import_server.RootDispatcher(dispatcherConnection, async (rootScope, { sdkLanguage }) => {
sdkLanguage const playwright2 = (0, import_server.createPlaywright)({ sdkLanguage });
}) => { return new import_server.PlaywrightDispatcher(rootScope, playwright2);
const playwright = (0, _server.createPlaywright)({
sdkLanguage
}); });
return new _server.PlaywrightDispatcher(rootScope, playwright); const transport = new import_pipeTransport.PipeTransport(process.stdout, process.stdin);
}); transport.onmessage = (message) => dispatcherConnection.dispatch(JSON.parse(message));
const transport = new _pipeTransport.PipeTransport(process.stdout, process.stdin); const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === "javascript";
transport.onmessage = message => dispatcherConnection.dispatch(JSON.parse(message));
// Certain Language Binding JSON parsers (e.g. .NET) do not like strings with lone surrogates.
const isJavaScriptLanguageBinding = !process.env.PW_LANG_NAME || process.env.PW_LANG_NAME === 'javascript';
const replacer = !isJavaScriptLanguageBinding && String.prototype.toWellFormed ? (key, value) => { const replacer = !isJavaScriptLanguageBinding && String.prototype.toWellFormed ? (key, value) => {
if (typeof value === 'string') return value.toWellFormed(); if (typeof value === "string")
return value.toWellFormed();
return value; return value;
} : undefined; } : void 0;
dispatcherConnection.onmessage = message => transport.send(JSON.stringify(message, replacer)); dispatcherConnection.onmessage = (message) => transport.send(JSON.stringify(message, replacer));
transport.onclose = () => { transport.onclose = () => {
// Drop any messages during shutdown on the floor. dispatcherConnection.onmessage = () => {
dispatcherConnection.onmessage = () => {};
(0, _processLauncher.gracefullyProcessExitDoNotHang)(0);
}; };
// Ignore the SIGINT signal in the driver process so the parent can gracefully close the connection. (0, import_processLauncher.gracefullyProcessExitDoNotHang)(0);
// We still will destruct everything (close browsers and exit) when the transport pipe closes. };
process.on('SIGINT', () => { process.on("SIGINT", () => {
// Keep the process running.
}); });
} }
async function runServer(options) { async function runServer(options) {
const { const {
port, port,
host, host,
path = '/', path = "/",
maxConnections = Infinity, maxConnections = Infinity,
extension extension
} = options; } = options;
const server = new _playwrightServer.PlaywrightServer({ const server = new import_playwrightServer.PlaywrightServer({ mode: extension ? "extension" : "default", path, maxConnections });
mode: extension ? 'extension' : 'default',
path,
maxConnections
});
const wsEndpoint = await server.listen(port, host); const wsEndpoint = await server.listen(port, host);
process.on('exit', () => server.close().catch(console.error)); process.on("exit", () => server.close().catch(console.error));
console.log('Listening on ' + wsEndpoint); console.log("Listening on " + wsEndpoint);
process.stdin.on('close', () => (0, _processLauncher.gracefullyProcessExitDoNotHang)(0)); process.stdin.on("close", () => (0, import_processLauncher.gracefullyProcessExitDoNotHang)(0));
} }
async function launchBrowserServer(browserName, configFile) { async function launchBrowserServer(browserName, configFile) {
let options = {}; let options = {};
if (configFile) options = JSON.parse(_fs.default.readFileSync(configFile).toString()); if (configFile)
options = JSON.parse(import_fs.default.readFileSync(configFile).toString());
const browserType = playwright[browserName]; const browserType = playwright[browserName];
const server = await browserType.launchServer(options); const server = await browserType.launchServer(options);
console.log(server.wsEndpoint()); console.log(server.wsEndpoint());
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
launchBrowserServer,
printApiJson,
runDriver,
runServer
});

View File

@@ -1,134 +1,173 @@
"use strict"; "use strict";
var __create = Object.create;
Object.defineProperty(exports, "__esModule", { var __defProp = Object.defineProperty;
value: true var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
}); var __getOwnPropNames = Object.getOwnPropertyNames;
Object.defineProperty(exports, "program", { var __getProtoOf = Object.getPrototypeOf;
enumerable: true, var __hasOwnProp = Object.prototype.hasOwnProperty;
get: function () { var __export = (target, all) => {
return _utilsBundle.program; for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
} }
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var program_exports = {};
__export(program_exports, {
program: () => import_utilsBundle2.program
}); });
var _fs = _interopRequireDefault(require("fs")); module.exports = __toCommonJS(program_exports);
var _os = _interopRequireDefault(require("os")); var import_fs = __toESM(require("fs"));
var _path = _interopRequireDefault(require("path")); var import_os = __toESM(require("os"));
var playwright = _interopRequireWildcard(require("../..")); var import_path = __toESM(require("path"));
var _driver = require("./driver"); var playwright = __toESM(require("../.."));
var _server = require("../server"); var import_driver = require("./driver");
var _utils = require("../utils"); var import_server = require("../server");
var _traceViewer = require("../server/trace/viewer/traceViewer"); var import_utils = require("../utils");
var _ascii = require("../server/utils/ascii"); var import_traceViewer = require("../server/trace/viewer/traceViewer");
var _utilsBundle = require("../utilsBundle"); var import_utils2 = require("../utils");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } var import_ascii = require("../server/utils/ascii");
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } var import_utilsBundle = require("../utilsBundle");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } var import_utilsBundle2 = require("../utilsBundle");
/** const packageJSON = require("../../package.json");
* Copyright (c) Microsoft Corporation. import_utilsBundle.program.version("Version " + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME));
* import_utilsBundle.program.command("mark-docker-image [dockerImageNameTemplate]", { hidden: true }).description("mark docker image").allowUnknownOption(true).action(function(dockerImageNameTemplate) {
* Licensed under the Apache License, Version 2.0 (the "License"); (0, import_utils2.assert)(dockerImageNameTemplate, "dockerImageNameTemplate is required");
* you may not use this file except in compliance with the License. (0, import_server.writeDockerVersion)(dockerImageNameTemplate).catch(logErrorAndExit);
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-console */
const packageJSON = require('../../package.json');
_utilsBundle.program.version('Version ' + (process.env.PW_CLI_DISPLAY_VERSION || packageJSON.version)).name(buildBasePlaywrightCLICommand(process.env.PW_LANG_NAME));
_utilsBundle.program.command('mark-docker-image [dockerImageNameTemplate]', {
hidden: true
}).description('mark docker image').allowUnknownOption(true).action(function (dockerImageNameTemplate) {
(0, _utils.assert)(dockerImageNameTemplate, 'dockerImageNameTemplate is required');
(0, _server.writeDockerVersion)(dockerImageNameTemplate).catch(logErrorAndExit);
}); });
commandWithOpenOptions('open [url]', 'open page in browser specified via -b, --browser', []).action(function (url, options) { commandWithOpenOptions("open [url]", "open page in browser specified via -b, --browser", []).action(function(url, options) {
open(options, url, codegenId()).catch(logErrorAndExit); open(options, url, codegenId()).catch(logErrorAndExit);
}).addHelpText('afterAll', ` }).addHelpText("afterAll", `
Examples: Examples:
$ open $ open
$ open -b webkit https://example.com`); $ open -b webkit https://example.com`);
commandWithOpenOptions('codegen [url]', 'open page and generate code for user actions', [['-o, --output <file name>', 'saves the generated script to a file'], ['--target <language>', `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, java, java-junit`, codegenId()], ['--test-id-attribute <attributeName>', 'use the specified attribute to generate data test ID selectors']]).action(function (url, options) { commandWithOpenOptions(
"codegen [url]",
"open page and generate code for user actions",
[
["-o, --output <file name>", "saves the generated script to a file"],
["--target <language>", `language to generate, one of javascript, playwright-test, python, python-async, python-pytest, csharp, csharp-mstest, csharp-nunit, java, java-junit`, codegenId()],
["--test-id-attribute <attributeName>", "use the specified attribute to generate data test ID selectors"]
]
).action(function(url, options) {
codegen(options, url).catch(logErrorAndExit); codegen(options, url).catch(logErrorAndExit);
}).addHelpText('afterAll', ` }).addHelpText("afterAll", `
Examples: Examples:
$ codegen $ codegen
$ codegen --target=python $ codegen --target=python
$ codegen -b webkit https://example.com`); $ codegen -b webkit https://example.com`);
function suggestedBrowsersToInstall() { function suggestedBrowsersToInstall() {
return _server.registry.executables().filter(e => e.installType !== 'none' && e.type !== 'tool').map(e => e.name).join(', '); return import_server.registry.executables().filter((e) => e.installType !== "none" && e.type !== "tool").map((e) => e.name).join(", ");
} }
function defaultBrowsersToInstall(options) { function defaultBrowsersToInstall(options) {
let executables = _server.registry.defaultExecutables(); let executables = import_server.registry.defaultExecutables();
if (options.noShell) executables = executables.filter(e => e.name !== 'chromium-headless-shell'); if (options.noShell)
if (options.onlyShell) executables = executables.filter(e => e.name !== 'chromium'); executables = executables.filter((e) => e.name !== "chromium-headless-shell");
if (options.onlyShell)
executables = executables.filter((e) => e.name !== "chromium");
return executables; return executables;
} }
function checkBrowsersToInstall(args, options) { function checkBrowsersToInstall(args, options) {
if (options.noShell && options.onlyShell) throw new Error(`Only one of --no-shell and --only-shell can be specified`); if (options.noShell && options.onlyShell)
throw new Error(`Only one of --no-shell and --only-shell can be specified`);
const faultyArguments = []; const faultyArguments = [];
const executables = []; const executables = [];
const handleArgument = arg => { const handleArgument = (arg) => {
const executable = _server.registry.findExecutable(arg); const executable = import_server.registry.findExecutable(arg);
if (!executable || executable.installType === 'none') faultyArguments.push(arg);else executables.push(executable); if (!executable || executable.installType === "none")
if ((executable === null || executable === void 0 ? void 0 : executable.browserName) === 'chromium') executables.push(_server.registry.findExecutable('ffmpeg')); faultyArguments.push(arg);
else
executables.push(executable);
if (executable?.browserName === "chromium")
executables.push(import_server.registry.findExecutable("ffmpeg"));
}; };
for (const arg of args) { for (const arg of args) {
if (arg === 'chromium') { if (arg === "chromium") {
if (!options.onlyShell) handleArgument('chromium'); if (!options.onlyShell)
if (!options.noShell) handleArgument('chromium-headless-shell'); handleArgument("chromium");
if (!options.noShell)
handleArgument("chromium-headless-shell");
} else { } else {
handleArgument(arg); handleArgument(arg);
} }
} }
if (process.platform === 'win32') executables.push(_server.registry.findExecutable('winldd')); if (process.platform === "win32")
if (faultyArguments.length) throw new Error(`Invalid installation targets: ${faultyArguments.map(name => `'${name}'`).join(', ')}. Expecting one of: ${suggestedBrowsersToInstall()}`); executables.push(import_server.registry.findExecutable("winldd"));
if (faultyArguments.length)
throw new Error(`Invalid installation targets: ${faultyArguments.map((name) => `'${name}'`).join(", ")}. Expecting one of: ${suggestedBrowsersToInstall()}`);
return executables; return executables;
} }
_utilsBundle.program.command('install [browser...]').description('ensure browsers necessary for this version of Playwright are installed').option('--with-deps', 'install system dependencies for browsers').option('--dry-run', 'do not execute installation, only print information').option('--force', 'force reinstall of stable browser channels').option('--only-shell', 'only install headless shell when installing chromium').option('--no-shell', 'do not install chromium headless shell').action(async function (args, options) { import_utilsBundle.program.command("install [browser...]").description("ensure browsers necessary for this version of Playwright are installed").option("--with-deps", "install system dependencies for browsers").option("--dry-run", "do not execute installation, only print information").option("--force", "force reinstall of stable browser channels").option("--only-shell", "only install headless shell when installing chromium").option("--no-shell", "do not install chromium headless shell").action(async function(args, options) {
// For '--no-shell' option, commander sets `shell: false` instead. if (options.shell === false)
if (options.shell === false) options.noShell = true; options.noShell = true;
if ((0, _utils.isLikelyNpxGlobal)()) { if ((0, import_utils.isLikelyNpxGlobal)()) {
console.error((0, _ascii.wrapInASCIIBox)([`WARNING: It looks like you are running 'npx playwright install' without first`, `installing your project's dependencies.`, ``, `To avoid unexpected behavior, please install your dependencies first, and`, `then run Playwright's install command:`, ``, ` npm install`, ` npx playwright install`, ``, `If your project does not yet depend on Playwright, first install the`, `applicable npm package (most commonly @playwright/test), and`, `then run Playwright's install command to download the browsers:`, ``, ` npm install @playwright/test`, ` npx playwright install`, ``].join('\n'), 1)); console.error((0, import_ascii.wrapInASCIIBox)([
`WARNING: It looks like you are running 'npx playwright install' without first`,
`installing your project's dependencies.`,
``,
`To avoid unexpected behavior, please install your dependencies first, and`,
`then run Playwright's install command:`,
``,
` npm install`,
` npx playwright install`,
``,
`If your project does not yet depend on Playwright, first install the`,
`applicable npm package (most commonly @playwright/test), and`,
`then run Playwright's install command to download the browsers:`,
``,
` npm install @playwright/test`,
` npx playwright install`,
``
].join("\n"), 1));
} }
try { try {
const hasNoArguments = !args.length; const hasNoArguments = !args.length;
const executables = hasNoArguments ? defaultBrowsersToInstall(options) : checkBrowsersToInstall(args, options); const executables = hasNoArguments ? defaultBrowsersToInstall(options) : checkBrowsersToInstall(args, options);
if (options.withDeps) await _server.registry.installDeps(executables, !!options.dryRun); if (options.withDeps)
await import_server.registry.installDeps(executables, !!options.dryRun);
if (options.dryRun) { if (options.dryRun) {
for (const executable of executables) { for (const executable of executables) {
var _executable$directory, _executable$downloadU; const version = executable.browserVersion ? `version ` + executable.browserVersion : "";
const version = executable.browserVersion ? `version ` + executable.browserVersion : ''; console.log(`browser: ${executable.name}${version ? " " + version : ""}`);
console.log(`browser: ${executable.name}${version ? ' ' + version : ''}`); console.log(` Install location: ${executable.directory ?? "<system>"}`);
console.log(` Install location: ${(_executable$directory = executable.directory) !== null && _executable$directory !== void 0 ? _executable$directory : '<system>'}`); if (executable.downloadURLs?.length) {
if ((_executable$downloadU = executable.downloadURLs) !== null && _executable$downloadU !== void 0 && _executable$downloadU.length) {
const [url, ...fallbacks] = executable.downloadURLs; const [url, ...fallbacks] = executable.downloadURLs;
console.log(` Download url: ${url}`); console.log(` Download url: ${url}`);
for (let i = 0; i < fallbacks.length; ++i) console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`); for (let i = 0; i < fallbacks.length; ++i)
console.log(` Download fallback ${i + 1}: ${fallbacks[i]}`);
} }
console.log(``); console.log(``);
} }
} else { } else {
const forceReinstall = hasNoArguments ? false : !!options.force; const forceReinstall = hasNoArguments ? false : !!options.force;
await _server.registry.install(executables, forceReinstall); await import_server.registry.install(executables, forceReinstall);
await _server.registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || 'javascript').catch(e => { await import_server.registry.validateHostRequirementsForExecutablesIfNeeded(executables, process.env.PW_LANG_NAME || "javascript").catch((e) => {
e.name = 'Playwright Host validation warning'; e.name = "Playwright Host validation warning";
console.error(e); console.error(e);
}); });
} }
} catch (e) { } catch (e) {
console.log(`Failed to install browsers\n${e}`); console.log(`Failed to install browsers
(0, _utils.gracefullyProcessExitDoNotHang)(1); ${e}`);
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
} }
}).addHelpText('afterAll', ` }).addHelpText("afterAll", `
Examples: Examples:
- $ install - $ install
@@ -136,106 +175,110 @@ Examples:
- $ install chrome firefox - $ install chrome firefox
Install custom browsers, supports ${suggestedBrowsersToInstall()}.`); Install custom browsers, supports ${suggestedBrowsersToInstall()}.`);
_utilsBundle.program.command('uninstall').description('Removes browsers used by this installation of Playwright from the system (chromium, firefox, webkit, ffmpeg). This does not include branded channels.').option('--all', 'Removes all browsers used by any Playwright installation from the system.').action(async options => { import_utilsBundle.program.command("uninstall").description("Removes browsers used by this installation of Playwright from the system (chromium, firefox, webkit, ffmpeg). This does not include branded channels.").option("--all", "Removes all browsers used by any Playwright installation from the system.").action(async (options) => {
delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC; delete process.env.PLAYWRIGHT_SKIP_BROWSER_GC;
await _server.registry.uninstall(!!options.all).then(({ await import_server.registry.uninstall(!!options.all).then(({ numberOfBrowsersLeft }) => {
numberOfBrowsersLeft
}) => {
if (!options.all && numberOfBrowsersLeft > 0) { if (!options.all && numberOfBrowsersLeft > 0) {
console.log('Successfully uninstalled Playwright browsers for the current Playwright installation.'); console.log("Successfully uninstalled Playwright browsers for the current Playwright installation.");
console.log(`There are still ${numberOfBrowsersLeft} browsers left, used by other Playwright installations.\nTo uninstall Playwright browsers for all installations, re-run with --all flag.`); console.log(`There are still ${numberOfBrowsersLeft} browsers left, used by other Playwright installations.
To uninstall Playwright browsers for all installations, re-run with --all flag.`);
} }
}).catch(logErrorAndExit); }).catch(logErrorAndExit);
}); });
_utilsBundle.program.command('install-deps [browser...]').description('install dependencies necessary to run browsers (will ask for sudo permissions)').option('--dry-run', 'Do not execute installation commands, only print them').action(async function (args, options) { import_utilsBundle.program.command("install-deps [browser...]").description("install dependencies necessary to run browsers (will ask for sudo permissions)").option("--dry-run", "Do not execute installation commands, only print them").action(async function(args, options) {
try { try {
if (!args.length) await _server.registry.installDeps(defaultBrowsersToInstall({}), !!options.dryRun);else await _server.registry.installDeps(checkBrowsersToInstall(args, {}), !!options.dryRun); if (!args.length)
await import_server.registry.installDeps(defaultBrowsersToInstall({}), !!options.dryRun);
else
await import_server.registry.installDeps(checkBrowsersToInstall(args, {}), !!options.dryRun);
} catch (e) { } catch (e) {
console.log(`Failed to install browser dependencies\n${e}`); console.log(`Failed to install browser dependencies
(0, _utils.gracefullyProcessExitDoNotHang)(1); ${e}`);
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
} }
}).addHelpText('afterAll', ` }).addHelpText("afterAll", `
Examples: Examples:
- $ install-deps - $ install-deps
Install dependencies for default browsers. Install dependencies for default browsers.
- $ install-deps chrome firefox - $ install-deps chrome firefox
Install dependencies for specific browsers, supports ${suggestedBrowsersToInstall()}.`); Install dependencies for specific browsers, supports ${suggestedBrowsersToInstall()}.`);
const browsers = [{ const browsers = [
alias: 'cr', { alias: "cr", name: "Chromium", type: "chromium" },
name: 'Chromium', { alias: "ff", name: "Firefox", type: "firefox" },
type: 'chromium' { alias: "wk", name: "WebKit", type: "webkit" }
}, { ];
alias: 'ff', for (const { alias, name, type } of browsers) {
name: 'Firefox',
type: 'firefox'
}, {
alias: 'wk',
name: 'WebKit',
type: 'webkit'
}];
for (const {
alias,
name,
type
} of browsers) {
commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(function(url, options) { commandWithOpenOptions(`${alias} [url]`, `open page in ${name}`, []).action(function(url, options) {
open({ open({ ...options, browser: type }, url, options.target).catch(logErrorAndExit);
...options, }).addHelpText("afterAll", `
browser: type
}, url, options.target).catch(logErrorAndExit);
}).addHelpText('afterAll', `
Examples: Examples:
$ ${alias} https://example.com`); $ ${alias} https://example.com`);
} }
commandWithOpenOptions('screenshot <url> <filename>', 'capture a page screenshot', [['--wait-for-selector <selector>', 'wait for selector before taking a screenshot'], ['--wait-for-timeout <timeout>', 'wait for timeout in milliseconds before taking a screenshot'], ['--full-page', 'whether to take a full page screenshot (entire scrollable area)']]).action(function (url, filename, command) { commandWithOpenOptions(
"screenshot <url> <filename>",
"capture a page screenshot",
[
["--wait-for-selector <selector>", "wait for selector before taking a screenshot"],
["--wait-for-timeout <timeout>", "wait for timeout in milliseconds before taking a screenshot"],
["--full-page", "whether to take a full page screenshot (entire scrollable area)"]
]
).action(function(url, filename, command) {
screenshot(command, command, url, filename).catch(logErrorAndExit); screenshot(command, command, url, filename).catch(logErrorAndExit);
}).addHelpText('afterAll', ` }).addHelpText("afterAll", `
Examples: Examples:
$ screenshot -b webkit https://example.com example.png`); $ screenshot -b webkit https://example.com example.png`);
commandWithOpenOptions('pdf <url> <filename>', 'save page as pdf', [['--wait-for-selector <selector>', 'wait for given selector before saving as pdf'], ['--wait-for-timeout <timeout>', 'wait for given timeout in milliseconds before saving as pdf']]).action(function (url, filename, options) { commandWithOpenOptions(
"pdf <url> <filename>",
"save page as pdf",
[
["--paper-format <format>", "paper format: Letter, Legal, Tabloid, Ledger, A0, A1, A2, A3, A4, A5, A6"],
["--wait-for-selector <selector>", "wait for given selector before saving as pdf"],
["--wait-for-timeout <timeout>", "wait for given timeout in milliseconds before saving as pdf"]
]
).action(function(url, filename, options) {
pdf(options, options, url, filename).catch(logErrorAndExit); pdf(options, options, url, filename).catch(logErrorAndExit);
}).addHelpText('afterAll', ` }).addHelpText("afterAll", `
Examples: Examples:
$ pdf https://example.com example.pdf`); $ pdf https://example.com example.pdf`);
_utilsBundle.program.command('run-driver', { import_utilsBundle.program.command("run-driver", { hidden: true }).action(function(options) {
hidden: true (0, import_driver.runDriver)();
}).action(function (options) {
(0, _driver.runDriver)();
}); });
_utilsBundle.program.command('run-server').option('--port <port>', 'Server port').option('--host <host>', 'Server host').option('--path <path>', 'Endpoint Path', '/').option('--max-clients <maxClients>', 'Maximum clients').option('--mode <mode>', 'Server mode, either "default" or "extension"').action(function (options) { import_utilsBundle.program.command("run-server").option("--port <port>", "Server port").option("--host <host>", "Server host").option("--path <path>", "Endpoint Path", "/").option("--max-clients <maxClients>", "Maximum clients").option("--mode <mode>", 'Server mode, either "default" or "extension"').action(function(options) {
(0, _driver.runServer)({ (0, import_driver.runServer)({
port: options.port ? +options.port : undefined, port: options.port ? +options.port : void 0,
host: options.host, host: options.host,
path: options.path, path: options.path,
maxConnections: options.maxClients ? +options.maxClients : Infinity, maxConnections: options.maxClients ? +options.maxClients : Infinity,
extension: options.mode === 'extension' || !!process.env.PW_EXTENSION_MODE extension: options.mode === "extension" || !!process.env.PW_EXTENSION_MODE
}).catch(logErrorAndExit); }).catch(logErrorAndExit);
}); });
_utilsBundle.program.command('print-api-json', { import_utilsBundle.program.command("print-api-json", { hidden: true }).action(function(options) {
hidden: true (0, import_driver.printApiJson)();
}).action(function (options) {
(0, _driver.printApiJson)();
}); });
_utilsBundle.program.command('launch-server', { import_utilsBundle.program.command("launch-server", { hidden: true }).requiredOption("--browser <browserName>", 'Browser name, one of "chromium", "firefox" or "webkit"').option("--config <path-to-config-file>", "JSON file with launchServer options").action(function(options) {
hidden: true (0, import_driver.launchBrowserServer)(options.browser, options.config);
}).requiredOption('--browser <browserName>', 'Browser name, one of "chromium", "firefox" or "webkit"').option('--config <path-to-config-file>', 'JSON file with launchServer options').action(function (options) {
(0, _driver.launchBrowserServer)(options.browser, options.config);
}); });
_utilsBundle.program.command('show-trace [trace...]').option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium').option('-h, --host <host>', 'Host to serve trace on; specifying this option opens trace in a browser tab').option('-p, --port <port>', 'Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab').option('--stdin', 'Accept trace URLs over stdin to update the viewer').description('show trace viewer').action(function (traces, options) { import_utilsBundle.program.command("show-trace [trace...]").option("-b, --browser <browserType>", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("-h, --host <host>", "Host to serve trace on; specifying this option opens trace in a browser tab").option("-p, --port <port>", "Port to serve trace on, 0 for any free port; specifying this option opens trace in a browser tab").option("--stdin", "Accept trace URLs over stdin to update the viewer").description("show trace viewer").action(function(traces, options) {
if (options.browser === 'cr') options.browser = 'chromium'; if (options.browser === "cr")
if (options.browser === 'ff') options.browser = 'firefox'; options.browser = "chromium";
if (options.browser === 'wk') options.browser = 'webkit'; if (options.browser === "ff")
options.browser = "firefox";
if (options.browser === "wk")
options.browser = "webkit";
const openOptions = { const openOptions = {
host: options.host, host: options.host,
port: +options.port, port: +options.port,
isServer: !!options.stdin isServer: !!options.stdin
}; };
if (options.port !== undefined || options.host !== undefined) (0, _traceViewer.runTraceInBrowser)(traces, openOptions).catch(logErrorAndExit);else (0, _traceViewer.runTraceViewerApp)(traces, options.browser, openOptions, true).catch(logErrorAndExit); if (options.port !== void 0 || options.host !== void 0)
}).addHelpText('afterAll', ` (0, import_traceViewer.runTraceInBrowser)(traces, openOptions).catch(logErrorAndExit);
else
(0, import_traceViewer.runTraceViewerApp)(traces, options.browser, openOptions, true).catch(logErrorAndExit);
}).addHelpText("afterAll", `
Examples: Examples:
$ show-trace https://example.com/trace.zip`); $ show-trace https://example.com/trace.zip`);
@@ -243,79 +286,67 @@ async function launchContext(options, extraOptions) {
validateOptions(options); validateOptions(options);
const browserType = lookupBrowserType(options); const browserType = lookupBrowserType(options);
const launchOptions = extraOptions; const launchOptions = extraOptions;
if (options.channel) launchOptions.channel = options.channel; if (options.channel)
launchOptions.channel = options.channel;
launchOptions.handleSIGINT = false; launchOptions.handleSIGINT = false;
const contextOptions = const contextOptions = (
// Copy the device descriptor since we have to compare and modify the options. // Copy the device descriptor since we have to compare and modify the options.
options.device ? { options.device ? { ...playwright.devices[options.device] } : {}
...playwright.devices[options.device] );
} : {}; if (!extraOptions.headless)
contextOptions.deviceScaleFactor = import_os.default.platform() === "darwin" ? 2 : 1;
// In headful mode, use host device scale factor for things to look nice. if (browserType.name() === "webkit" && process.platform === "linux") {
// In headless, keep things the way it works in Playwright by default.
// Assume high-dpi on MacOS. TODO: this is not perfect.
if (!extraOptions.headless) contextOptions.deviceScaleFactor = _os.default.platform() === 'darwin' ? 2 : 1;
// Work around the WebKit GTK scrolling issue.
if (browserType.name() === 'webkit' && process.platform === 'linux') {
delete contextOptions.hasTouch; delete contextOptions.hasTouch;
delete contextOptions.isMobile; delete contextOptions.isMobile;
} }
if (contextOptions.isMobile && browserType.name() === 'firefox') contextOptions.isMobile = undefined; if (contextOptions.isMobile && browserType.name() === "firefox")
if (options.blockServiceWorkers) contextOptions.serviceWorkers = 'block'; contextOptions.isMobile = void 0;
if (options.blockServiceWorkers)
// Proxy contextOptions.serviceWorkers = "block";
if (options.proxyServer) { if (options.proxyServer) {
launchOptions.proxy = { launchOptions.proxy = {
server: options.proxyServer server: options.proxyServer
}; };
if (options.proxyBypass) launchOptions.proxy.bypass = options.proxyBypass; if (options.proxyBypass)
launchOptions.proxy.bypass = options.proxyBypass;
} }
const browser = await browserType.launch(launchOptions); const browser = await browserType.launch(launchOptions);
if (process.env.PWTEST_CLI_IS_UNDER_TEST) { if (process.env.PWTEST_CLI_IS_UNDER_TEST) {
process._didSetSourcesForTest = text => { process._didSetSourcesForTest = (text) => {
process.stdout.write('\n-------------8<-------------\n'); process.stdout.write("\n-------------8<-------------\n");
process.stdout.write(text); process.stdout.write(text);
process.stdout.write('\n-------------8<-------------\n'); process.stdout.write("\n-------------8<-------------\n");
const autoExitCondition = process.env.PWTEST_CLI_AUTO_EXIT_WHEN; const autoExitCondition = process.env.PWTEST_CLI_AUTO_EXIT_WHEN;
if (autoExitCondition && text.includes(autoExitCondition)) closeBrowser(); if (autoExitCondition && text.includes(autoExitCondition))
closeBrowser();
}; };
// Make sure we exit abnormally when browser crashes.
const logs = []; const logs = [];
require('playwright-core/lib/utilsBundle').debug.log = (...args) => { require("playwright-core/lib/utilsBundle").debug.log = (...args) => {
const line = require('util').format(...args) + '\n'; const line = require("util").format(...args) + "\n";
logs.push(line); logs.push(line);
process.stderr.write(line); process.stderr.write(line);
}; };
browser.on('disconnected', () => { browser.on("disconnected", () => {
const hasCrashLine = logs.some(line => line.includes('process did exit:') && !line.includes('process did exit: exitCode=0, signal=null')); const hasCrashLine = logs.some((line) => line.includes("process did exit:") && !line.includes("process did exit: exitCode=0, signal=null"));
if (hasCrashLine) { if (hasCrashLine) {
process.stderr.write('Detected browser crash.\n'); process.stderr.write("Detected browser crash.\n");
(0, _utils.gracefullyProcessExitDoNotHang)(1); (0, import_utils.gracefullyProcessExitDoNotHang)(1);
} }
}); });
} }
// Viewport size
if (options.viewportSize) { if (options.viewportSize) {
try { try {
const [width, height] = options.viewportSize.split(',').map(n => +n); const [width, height] = options.viewportSize.split(",").map((n) => +n);
if (isNaN(width) || isNaN(height)) throw new Error('bad values'); if (isNaN(width) || isNaN(height))
contextOptions.viewport = { throw new Error("bad values");
width, contextOptions.viewport = { width, height };
height
};
} catch (e) { } catch (e) {
throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"'); throw new Error('Invalid viewport size format: use "width,height", for example --viewport-size="800,600"');
} }
} }
// Geolocation
if (options.geolocation) { if (options.geolocation) {
try { try {
const [latitude, longitude] = options.geolocation.split(',').map(n => parseFloat(n.trim())); const [latitude, longitude] = options.geolocation.split(",").map((n) => parseFloat(n.trim()));
contextOptions.geolocation = { contextOptions.geolocation = {
latitude, latitude,
longitude longitude
@@ -323,94 +354,71 @@ async function launchContext(options, extraOptions) {
} catch (e) { } catch (e) {
throw new Error('Invalid geolocation format, should be "lat,long". For example --geolocation="37.819722,-122.478611"'); throw new Error('Invalid geolocation format, should be "lat,long". For example --geolocation="37.819722,-122.478611"');
} }
contextOptions.permissions = ['geolocation']; contextOptions.permissions = ["geolocation"];
} }
if (options.userAgent)
// User agent contextOptions.userAgent = options.userAgent;
if (options.lang)
if (options.userAgent) contextOptions.userAgent = options.userAgent; contextOptions.locale = options.lang;
if (options.colorScheme)
// Lang contextOptions.colorScheme = options.colorScheme;
if (options.timezone)
if (options.lang) contextOptions.locale = options.lang; contextOptions.timezoneId = options.timezone;
if (options.loadStorage)
// Color scheme contextOptions.storageState = options.loadStorage;
if (options.ignoreHttpsErrors)
if (options.colorScheme) contextOptions.colorScheme = options.colorScheme; contextOptions.ignoreHTTPSErrors = true;
// Timezone
if (options.timezone) contextOptions.timezoneId = options.timezone;
// Storage
if (options.loadStorage) contextOptions.storageState = options.loadStorage;
if (options.ignoreHttpsErrors) contextOptions.ignoreHTTPSErrors = true;
// HAR
if (options.saveHar) { if (options.saveHar) {
contextOptions.recordHar = { contextOptions.recordHar = { path: import_path.default.resolve(process.cwd(), options.saveHar), mode: "minimal" };
path: _path.default.resolve(process.cwd(), options.saveHar), if (options.saveHarGlob)
mode: 'minimal' contextOptions.recordHar.urlFilter = options.saveHarGlob;
}; contextOptions.serviceWorkers = "block";
if (options.saveHarGlob) contextOptions.recordHar.urlFilter = options.saveHarGlob;
contextOptions.serviceWorkers = 'block';
} }
// Close app when the last window closes.
const context = await browser.newContext(contextOptions); const context = await browser.newContext(contextOptions);
let closingBrowser = false; let closingBrowser = false;
async function closeBrowser() { async function closeBrowser() {
// We can come here multiple times. For example, saving storage creates if (closingBrowser)
// a temporary page and we call closeBrowser again when that page closes. return;
if (closingBrowser) return;
closingBrowser = true; closingBrowser = true;
if (options.saveStorage) await context.storageState({ if (options.saveStorage)
path: options.saveStorage await context.storageState({ path: options.saveStorage }).catch((e) => null);
}).catch(e => null); if (options.saveHar)
if (options.saveHar) await context.close(); await context.close();
await browser.close(); await browser.close();
} }
context.on('page', page => { context.on("page", (page) => {
page.on('dialog', () => {}); // Prevent dialogs from being automatically dismissed. page.on("dialog", () => {
page.on('close', () => { });
const hasPage = browser.contexts().some(context => context.pages().length > 0); page.on("close", () => {
if (hasPage) return; const hasPage = browser.contexts().some((context2) => context2.pages().length > 0);
// Avoid the error when the last page is closed because the browser has been closed. if (hasPage)
closeBrowser().catch(() => {}); return;
closeBrowser().catch(() => {
}); });
}); });
process.on('SIGINT', async () => { });
process.on("SIGINT", async () => {
await closeBrowser(); await closeBrowser();
(0, _utils.gracefullyProcessExitDoNotHang)(130); (0, import_utils.gracefullyProcessExitDoNotHang)(130);
}); });
const timeout = options.timeout ? parseInt(options.timeout, 10) : 0; const timeout = options.timeout ? parseInt(options.timeout, 10) : 0;
context.setDefaultTimeout(timeout); context.setDefaultTimeout(timeout);
context.setDefaultNavigationTimeout(timeout); context.setDefaultNavigationTimeout(timeout);
// Omit options that we add automatically for presentation purpose.
delete launchOptions.headless; delete launchOptions.headless;
delete launchOptions.executablePath; delete launchOptions.executablePath;
delete launchOptions.handleSIGINT; delete launchOptions.handleSIGINT;
delete contextOptions.deviceScaleFactor; delete contextOptions.deviceScaleFactor;
return { return { browser, browserName: browserType.name(), context, contextOptions, launchOptions };
browser,
browserName: browserType.name(),
context,
contextOptions,
launchOptions
};
} }
async function openPage(context, url) { async function openPage(context, url) {
const page = await context.newPage(); const page = await context.newPage();
if (url) { if (url) {
if (_fs.default.existsSync(url)) url = 'file://' + _path.default.resolve(url);else if (!url.startsWith('http') && !url.startsWith('file://') && !url.startsWith('about:') && !url.startsWith('data:')) url = 'http://' + url; if (import_fs.default.existsSync(url))
await page.goto(url).catch(error => { url = "file://" + import_path.default.resolve(url);
else if (!url.startsWith("http") && !url.startsWith("file://") && !url.startsWith("about:") && !url.startsWith("data:"))
url = "http://" + url;
await page.goto(url).catch((error) => {
if (process.env.PWTEST_CLI_AUTO_EXIT_WHEN) { if (process.env.PWTEST_CLI_AUTO_EXIT_WHEN) {
// Tests with PWTEST_CLI_AUTO_EXIT_WHEN might close page too fast, resulting
// in a stray navigation aborted error. We should ignore it.
} else { } else {
throw error; throw error;
} }
@@ -419,14 +427,7 @@ async function openPage(context, url) {
return page; return page;
} }
async function open(options, url, language) { async function open(options, url, language) {
const { const { context, launchOptions, contextOptions } = await launchContext(options, { headless: !!process.env.PWTEST_CLI_HEADLESS, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH });
context,
launchOptions,
contextOptions
} = await launchContext(options, {
headless: !!process.env.PWTEST_CLI_HEADLESS,
executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH
});
await context._enableRecorder({ await context._enableRecorder({
language, language,
launchOptions, launchOptions,
@@ -438,33 +439,23 @@ async function open(options, url, language) {
await openPage(context, url); await openPage(context, url);
} }
async function codegen(options, url) { async function codegen(options, url) {
const { const { target: language, output: outputFile, testIdAttribute: testIdAttributeName } = options;
target: language, const tracesDir = import_path.default.join(import_os.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`);
output: outputFile, const { context, launchOptions, contextOptions } = await launchContext(options, {
testIdAttribute: testIdAttributeName
} = options;
const tracesDir = _path.default.join(_os.default.tmpdir(), `playwright-recorder-trace-${Date.now()}`);
const {
context,
launchOptions,
contextOptions
} = await launchContext(options, {
headless: !!process.env.PWTEST_CLI_HEADLESS, headless: !!process.env.PWTEST_CLI_HEADLESS,
executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH, executablePath: process.env.PWTEST_CLI_EXECUTABLE_PATH,
tracesDir tracesDir
}); });
_utilsBundle.dotenv.config({ import_utilsBundle.dotenv.config({ path: "playwright.env" });
path: 'playwright.env'
});
await context._enableRecorder({ await context._enableRecorder({
language, language,
launchOptions, launchOptions,
contextOptions, contextOptions,
device: options.device, device: options.device,
saveStorage: options.saveStorage, saveStorage: options.saveStorage,
mode: 'recording', mode: "recording",
testIdAttributeName, testIdAttributeName,
outputFile: outputFile ? _path.default.resolve(outputFile) : undefined, outputFile: outputFile ? import_path.default.resolve(outputFile) : void 0,
handleSIGINT: false handleSIGINT: false
}); });
await openPage(context, url); await openPage(context, url);
@@ -479,41 +470,24 @@ async function waitForPage(page, captureOptions) {
await page.waitForTimeout(parseInt(captureOptions.waitForTimeout, 10)); await page.waitForTimeout(parseInt(captureOptions.waitForTimeout, 10));
} }
} }
async function screenshot(options, captureOptions, url, path) { async function screenshot(options, captureOptions, url, path2) {
const { const { context } = await launchContext(options, { headless: true });
context console.log("Navigating to " + url);
} = await launchContext(options, {
headless: true
});
console.log('Navigating to ' + url);
const page = await openPage(context, url); const page = await openPage(context, url);
await waitForPage(page, captureOptions); await waitForPage(page, captureOptions);
console.log('Capturing screenshot into ' + path); console.log("Capturing screenshot into " + path2);
await page.screenshot({ await page.screenshot({ path: path2, fullPage: !!captureOptions.fullPage });
path,
fullPage: !!captureOptions.fullPage
});
// launchContext takes care of closing the browser.
await page.close(); await page.close();
} }
async function pdf(options, captureOptions, url, path) { async function pdf(options, captureOptions, url, path2) {
if (options.browser !== 'chromium') throw new Error('PDF creation is only working with Chromium'); if (options.browser !== "chromium")
const { throw new Error("PDF creation is only working with Chromium");
context const { context } = await launchContext({ ...options, browser: "chromium" }, { headless: true });
} = await launchContext({ console.log("Navigating to " + url);
...options,
browser: 'chromium'
}, {
headless: true
});
console.log('Navigating to ' + url);
const page = await openPage(context, url); const page = await openPage(context, url);
await waitForPage(page, captureOptions); await waitForPage(page, captureOptions);
console.log('Saving as pdf into ' + path); console.log("Saving as pdf into " + path2);
await page.pdf({ await page.pdf({ path: path2, format: captureOptions.paperFormat });
path
});
// launchContext takes care of closing the browser.
await page.close(); await page.close();
} }
function lookupBrowserType(options) { function lookupBrowserType(options) {
@@ -524,60 +498,70 @@ function lookupBrowserType(options) {
} }
let browserType; let browserType;
switch (name) { switch (name) {
case 'chromium': case "chromium":
browserType = playwright.chromium; browserType = playwright.chromium;
break; break;
case 'webkit': case "webkit":
browserType = playwright.webkit; browserType = playwright.webkit;
break; break;
case 'firefox': case "firefox":
browserType = playwright.firefox; browserType = playwright.firefox;
break; break;
case 'cr': case "cr":
browserType = playwright.chromium; browserType = playwright.chromium;
break; break;
case 'wk': case "wk":
browserType = playwright.webkit; browserType = playwright.webkit;
break; break;
case 'ff': case "ff":
browserType = playwright.firefox; browserType = playwright.firefox;
break; break;
} }
if (browserType) return browserType; if (browserType)
_utilsBundle.program.help(); return browserType;
import_utilsBundle.program.help();
} }
function validateOptions(options) { function validateOptions(options) {
if (options.device && !(options.device in playwright.devices)) { if (options.device && !(options.device in playwright.devices)) {
const lines = [`Device descriptor not found: '${options.device}', available devices are:`]; const lines = [`Device descriptor not found: '${options.device}', available devices are:`];
for (const name in playwright.devices) lines.push(` "${name}"`); for (const name in playwright.devices)
throw new Error(lines.join('\n')); lines.push(` "${name}"`);
throw new Error(lines.join("\n"));
} }
if (options.colorScheme && !['light', 'dark'].includes(options.colorScheme)) throw new Error('Invalid color scheme, should be one of "light", "dark"'); if (options.colorScheme && !["light", "dark"].includes(options.colorScheme))
throw new Error('Invalid color scheme, should be one of "light", "dark"');
} }
function logErrorAndExit(e) { function logErrorAndExit(e) {
if (process.env.PWDEBUGIMPL) console.error(e);else console.error(e.name + ': ' + e.message); if (process.env.PWDEBUGIMPL)
(0, _utils.gracefullyProcessExitDoNotHang)(1); console.error(e);
else
console.error(e.name + ": " + e.message);
(0, import_utils.gracefullyProcessExitDoNotHang)(1);
} }
function codegenId() { function codegenId() {
return process.env.PW_LANG_NAME || 'playwright-test'; return process.env.PW_LANG_NAME || "playwright-test";
} }
function commandWithOpenOptions(command, description, options) { function commandWithOpenOptions(command, description, options) {
let result = _utilsBundle.program.command(command).description(description); let result = import_utilsBundle.program.command(command).description(description);
for (const option of options) result = result.option(option[0], ...option.slice(1)); for (const option of options)
return result.option('-b, --browser <browserType>', 'browser to use, one of cr, chromium, ff, firefox, wk, webkit', 'chromium').option('--block-service-workers', 'block service workers').option('--channel <channel>', 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc').option('--color-scheme <scheme>', 'emulate preferred color scheme, "light" or "dark"').option('--device <deviceName>', 'emulate device, for example "iPhone 11"').option('--geolocation <coordinates>', 'specify geolocation coordinates, for example "37.819722,-122.478611"').option('--ignore-https-errors', 'ignore https errors').option('--load-storage <filename>', 'load context storage state from the file, previously saved with --save-storage').option('--lang <language>', 'specify language / locale, for example "en-GB"').option('--proxy-server <proxy>', 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option('--proxy-bypass <bypass>', 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option('--save-har <filename>', 'save HAR file with all network activity at the end').option('--save-har-glob <glob pattern>', 'filter entries in the HAR by matching url against this glob pattern').option('--save-storage <filename>', 'save context storage state at the end, for later use with --load-storage').option('--timezone <time zone>', 'time zone to emulate, for example "Europe/Rome"').option('--timeout <timeout>', 'timeout for Playwright actions in milliseconds, no timeout by default').option('--user-agent <ua string>', 'specify user agent string').option('--viewport-size <size>', 'specify browser viewport size in pixels, for example "1280, 720"'); result = result.option(option[0], ...option.slice(1));
return result.option("-b, --browser <browserType>", "browser to use, one of cr, chromium, ff, firefox, wk, webkit", "chromium").option("--block-service-workers", "block service workers").option("--channel <channel>", 'Chromium distribution channel, "chrome", "chrome-beta", "msedge-dev", etc').option("--color-scheme <scheme>", 'emulate preferred color scheme, "light" or "dark"').option("--device <deviceName>", 'emulate device, for example "iPhone 11"').option("--geolocation <coordinates>", 'specify geolocation coordinates, for example "37.819722,-122.478611"').option("--ignore-https-errors", "ignore https errors").option("--load-storage <filename>", "load context storage state from the file, previously saved with --save-storage").option("--lang <language>", 'specify language / locale, for example "en-GB"').option("--proxy-server <proxy>", 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"').option("--proxy-bypass <bypass>", 'comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"').option("--save-har <filename>", "save HAR file with all network activity at the end").option("--save-har-glob <glob pattern>", "filter entries in the HAR by matching url against this glob pattern").option("--save-storage <filename>", "save context storage state at the end, for later use with --load-storage").option("--timezone <time zone>", 'time zone to emulate, for example "Europe/Rome"').option("--timeout <timeout>", "timeout for Playwright actions in milliseconds, no timeout by default").option("--user-agent <ua string>", "specify user agent string").option("--viewport-size <size>", 'specify browser viewport size in pixels, for example "1280, 720"');
} }
function buildBasePlaywrightCLICommand(cliTargetLang) { function buildBasePlaywrightCLICommand(cliTargetLang) {
switch (cliTargetLang) { switch (cliTargetLang) {
case 'python': case "python":
return `playwright`; return `playwright`;
case 'java': case "java":
return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="...options.."`; return `mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="...options.."`;
case 'csharp': case "csharp":
return `pwsh bin/Debug/netX/playwright.ps1`; return `pwsh bin/Debug/netX/playwright.ps1`;
default: default: {
{ const packageManagerCommand = (0, import_utils2.getPackageManagerExecCommand)();
const packageManagerCommand = (0, _utils.getPackageManagerExecCommand)();
return `${packageManagerCommand} playwright`; return `${packageManagerCommand} playwright`;
} }
} }
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
program
});

View File

@@ -1,68 +1,74 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
}); var __hasOwnProp = Object.prototype.hasOwnProperty;
Object.defineProperty(exports, "program", { var __export = (target, all) => {
enumerable: true, for (var name in all)
get: function () { __defProp(target, name, { get: all[name], enumerable: true });
return _program.program; };
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
} }
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var programWithTestStub_exports = {};
__export(programWithTestStub_exports, {
program: () => import_program2.program
}); });
var _processLauncher = require("../server/utils/processLauncher"); module.exports = __toCommonJS(programWithTestStub_exports);
var _utils = require("../utils"); var import_processLauncher = require("../server/utils/processLauncher");
var _program = require("./program"); var import_utils = require("../utils");
/** var import_program = require("./program");
* Copyright (c) Microsoft Corporation. var import_program2 = require("./program");
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable no-console */
function printPlaywrightTestError(command) { function printPlaywrightTestError(command) {
const packages = []; const packages = [];
for (const pkg of ['playwright', 'playwright-chromium', 'playwright-firefox', 'playwright-webkit']) { for (const pkg of ["playwright", "playwright-chromium", "playwright-firefox", "playwright-webkit"]) {
try { try {
require.resolve(pkg); require.resolve(pkg);
packages.push(pkg); packages.push(pkg);
} catch (e) {} } catch (e) {
} }
if (!packages.length) packages.push('playwright'); }
const packageManager = (0, _utils.getPackageManager)(); if (!packages.length)
if (packageManager === 'yarn') { packages.push("playwright");
const packageManager = (0, import_utils.getPackageManager)();
if (packageManager === "yarn") {
console.error(`Please install @playwright/test package before running "yarn playwright ${command}"`); console.error(`Please install @playwright/test package before running "yarn playwright ${command}"`);
console.error(` yarn remove ${packages.join(' ')}`); console.error(` yarn remove ${packages.join(" ")}`);
console.error(' yarn add -D @playwright/test'); console.error(" yarn add -D @playwright/test");
} else if (packageManager === 'pnpm') { } else if (packageManager === "pnpm") {
console.error(`Please install @playwright/test package before running "pnpm exec playwright ${command}"`); console.error(`Please install @playwright/test package before running "pnpm exec playwright ${command}"`);
console.error(` pnpm remove ${packages.join(' ')}`); console.error(` pnpm remove ${packages.join(" ")}`);
console.error(' pnpm add -D @playwright/test'); console.error(" pnpm add -D @playwright/test");
} else { } else {
console.error(`Please install @playwright/test package before running "npx playwright ${command}"`); console.error(`Please install @playwright/test package before running "npx playwright ${command}"`);
console.error(` npm uninstall ${packages.join(' ')}`); console.error(` npm uninstall ${packages.join(" ")}`);
console.error(' npm install -D @playwright/test'); console.error(" npm install -D @playwright/test");
} }
} }
const kExternalPlaywrightTestCommands = [['test', 'Run tests with Playwright Test.'], ['show-report', 'Show Playwright Test HTML report.'], ['merge-reports', 'Merge Playwright Test Blob reports']]; const kExternalPlaywrightTestCommands = [
["test", "Run tests with Playwright Test."],
["show-report", "Show Playwright Test HTML report."],
["merge-reports", "Merge Playwright Test Blob reports"]
];
function addExternalPlaywrightTestCommands() { function addExternalPlaywrightTestCommands() {
for (const [command, description] of kExternalPlaywrightTestCommands) { for (const [command, description] of kExternalPlaywrightTestCommands) {
const playwrightTest = _program.program.command(command).allowUnknownOption(true); const playwrightTest = import_program.program.command(command).allowUnknownOption(true);
playwrightTest.description(`${description} Available in @playwright/test package.`); playwrightTest.description(`${description} Available in @playwright/test package.`);
playwrightTest.action(async () => { playwrightTest.action(async () => {
printPlaywrightTestError(command); printPlaywrightTestError(command);
(0, _processLauncher.gracefullyProcessExitDoNotHang)(1); (0, import_processLauncher.gracefullyProcessExitDoNotHang)(1);
}); });
} }
} }
if (!process.env.PW_LANG_NAME) addExternalPlaywrightTestCommands(); if (!process.env.PW_LANG_NAME)
addExternalPlaywrightTestCommands();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
program
});

View File

@@ -1,33 +1,33 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var accessibility_exports = {};
__export(accessibility_exports, {
Accessibility: () => Accessibility
}); });
exports.Accessibility = void 0; module.exports = __toCommonJS(accessibility_exports);
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function axNodeFromProtocol(axNode) { function axNodeFromProtocol(axNode) {
const result = { const result = {
...axNode, ...axNode,
value: axNode.valueNumber !== undefined ? axNode.valueNumber : axNode.valueString, value: axNode.valueNumber !== void 0 ? axNode.valueNumber : axNode.valueString,
checked: axNode.checked === 'checked' ? true : axNode.checked === 'unchecked' ? false : axNode.checked, checked: axNode.checked === "checked" ? true : axNode.checked === "unchecked" ? false : axNode.checked,
pressed: axNode.pressed === 'pressed' ? true : axNode.pressed === 'released' ? false : axNode.pressed, pressed: axNode.pressed === "pressed" ? true : axNode.pressed === "released" ? false : axNode.pressed,
children: axNode.children ? axNode.children.map(axNodeFromProtocol) : undefined children: axNode.children ? axNode.children.map(axNodeFromProtocol) : void 0
}; };
delete result.valueNumber; delete result.valueNumber;
delete result.valueString; delete result.valueString;
@@ -35,16 +35,15 @@ function axNodeFromProtocol(axNode) {
} }
class Accessibility { class Accessibility {
constructor(channel) { constructor(channel) {
this._channel = void 0;
this._channel = channel; this._channel = channel;
} }
async snapshot(options = {}) { async snapshot(options = {}) {
const root = options.root ? options.root._elementChannel : undefined; const root = options.root ? options.root._elementChannel : void 0;
const result = await this._channel.accessibilitySnapshot({ const result = await this._channel.accessibilitySnapshot({ interestingOnly: options.interestingOnly, root });
interestingOnly: options.interestingOnly,
root
});
return result.rootAXNode ? axNodeFromProtocol(result.rootAXNode) : null; return result.rootAXNode ? axNodeFromProtocol(result.rootAXNode) : null;
} }
} }
exports.Accessibility = Accessibility; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Accessibility
});

View File

@@ -1,92 +1,84 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var android_exports = {};
__export(android_exports, {
Android: () => Android,
AndroidDevice: () => AndroidDevice,
AndroidInput: () => AndroidInput,
AndroidSocket: () => AndroidSocket,
AndroidWebView: () => AndroidWebView
}); });
exports.AndroidWebView = exports.AndroidSocket = exports.AndroidInput = exports.AndroidDevice = exports.Android = void 0; module.exports = __toCommonJS(android_exports);
var _eventEmitter = require("./eventEmitter"); var import_eventEmitter = require("./eventEmitter");
var _browserContext = require("./browserContext"); var import_browserContext = require("./browserContext");
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _errors = require("./errors"); var import_errors = require("./errors");
var _events = require("./events"); var import_events = require("./events");
var _waiter = require("./waiter"); var import_waiter = require("./waiter");
var _timeoutSettings = require("./timeoutSettings"); var import_timeoutSettings = require("./timeoutSettings");
var _rtti = require("../utils/isomorphic/rtti"); var import_rtti = require("../utils/isomorphic/rtti");
var _time = require("../utils/isomorphic/time"); var import_time = require("../utils/isomorphic/time");
var _timeoutRunner = require("../utils/isomorphic/timeoutRunner"); var import_timeoutRunner = require("../utils/isomorphic/timeoutRunner");
var _webSocket = require("./webSocket"); var import_webSocket = require("./webSocket");
/** class Android extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Android extends _channelOwner.ChannelOwner {
static from(android) { static from(android) {
return android._object; return android._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._timeoutSettings = void 0; this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
this._serverLauncher = void 0;
this._timeoutSettings = new _timeoutSettings.TimeoutSettings(this._platform);
} }
setDefaultTimeout(timeout) { setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout); this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => { this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ await this._channel.setDefaultTimeoutNoReply({ timeout });
timeout }, true).catch(() => {
}); });
}, true).catch(() => {});
} }
async devices(options = {}) { async devices(options = {}) {
const { const { devices } = await this._channel.devices(options);
devices return devices.map((d) => AndroidDevice.from(d));
} = await this._channel.devices(options);
return devices.map(d => AndroidDevice.from(d));
} }
async launchServer(options = {}) { async launchServer(options = {}) {
if (!this._serverLauncher) throw new Error('Launching server is not supported'); if (!this._serverLauncher)
throw new Error("Launching server is not supported");
return await this._serverLauncher.launchServer(options); return await this._serverLauncher.launchServer(options);
} }
async connect(wsEndpoint, options = {}) { async connect(wsEndpoint, options = {}) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const deadline = options.timeout ? (0, _time.monotonicTime)() + options.timeout : 0; const deadline = options.timeout ? (0, import_time.monotonicTime)() + options.timeout : 0;
const headers = { const headers = { "x-playwright-browser": "android", ...options.headers };
'x-playwright-browser': 'android', const connectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout };
...options.headers const connection = await (0, import_webSocket.connectOverWebSocket)(this._connection, connectParams);
};
const connectParams = {
wsEndpoint,
headers,
slowMo: options.slowMo,
timeout: options.timeout
};
const connection = await (0, _webSocket.connectOverWebSocket)(this._connection, connectParams);
let device; let device;
connection.on('close', () => { connection.on("close", () => {
var _device; device?._didClose();
(_device = device) === null || _device === void 0 || _device._didClose();
}); });
const result = await (0, _timeoutRunner.raceAgainstDeadline)(async () => { const result = await (0, import_timeoutRunner.raceAgainstDeadline)(async () => {
const playwright = await connection.initializePlaywright(); const playwright = await connection.initializePlaywright();
if (!playwright._initializer.preConnectedAndroidDevice) { if (!playwright._initializer.preConnectedAndroidDevice) {
connection.close(); connection.close();
throw new Error('Malformed endpoint. Did you use Android.launchServer method?'); throw new Error("Malformed endpoint. Did you use Android.launchServer method?");
} }
device = AndroidDevice.from(playwright._initializer.preConnectedAndroidDevice); device = AndroidDevice.from(playwright._initializer.preConnectedAndroidDevice);
device._shouldCloseConnectionOnClose = true; device._shouldCloseConnectionOnClose = true;
device.on(_events.Events.AndroidDevice.Close, () => connection.close()); device.on(import_events.Events.AndroidDevice.Close, () => connection.close());
return device; return device;
}, deadline); }, deadline);
if (!result.timedOut) { if (!result.timedOut) {
@@ -98,44 +90,37 @@ class Android extends _channelOwner.ChannelOwner {
}); });
} }
} }
exports.Android = Android; class AndroidDevice extends import_channelOwner.ChannelOwner {
class AndroidDevice extends _channelOwner.ChannelOwner {
static from(androidDevice) {
return androidDevice._object;
}
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._timeoutSettings = void 0; this._webViews = /* @__PURE__ */ new Map();
this._webViews = new Map();
this._shouldCloseConnectionOnClose = false; this._shouldCloseConnectionOnClose = false;
this.input = void 0;
this.input = new AndroidInput(this); this.input = new AndroidInput(this);
this._timeoutSettings = new _timeoutSettings.TimeoutSettings(this._platform, parent._timeoutSettings); this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform, parent._timeoutSettings);
this._channel.on('webViewAdded', ({ this._channel.on("webViewAdded", ({ webView }) => this._onWebViewAdded(webView));
webView this._channel.on("webViewRemoved", ({ socketName }) => this._onWebViewRemoved(socketName));
}) => this._onWebViewAdded(webView)); this._channel.on("close", () => this._didClose());
this._channel.on('webViewRemoved', ({ }
socketName static from(androidDevice) {
}) => this._onWebViewRemoved(socketName)); return androidDevice._object;
this._channel.on('close', () => this._didClose());
} }
_onWebViewAdded(webView) { _onWebViewAdded(webView) {
const view = new AndroidWebView(this, webView); const view = new AndroidWebView(this, webView);
this._webViews.set(webView.socketName, view); this._webViews.set(webView.socketName, view);
this.emit(_events.Events.AndroidDevice.WebView, view); this.emit(import_events.Events.AndroidDevice.WebView, view);
} }
_onWebViewRemoved(socketName) { _onWebViewRemoved(socketName) {
const view = this._webViews.get(socketName); const view = this._webViews.get(socketName);
this._webViews.delete(socketName); this._webViews.delete(socketName);
if (view) view.emit(_events.Events.AndroidWebView.Close); if (view)
view.emit(import_events.Events.AndroidWebView.Close);
} }
setDefaultTimeout(timeout) { setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout); this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => { this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ await this._channel.setDefaultTimeoutNoReply({ timeout });
timeout }, true).catch(() => {
}); });
}, true).catch(() => {});
} }
serial() { serial() {
return this._initializer.serial; return this._initializer.serial;
@@ -147,101 +132,59 @@ class AndroidDevice extends _channelOwner.ChannelOwner {
return [...this._webViews.values()]; return [...this._webViews.values()];
} }
async webView(selector, options) { async webView(selector, options) {
const predicate = v => { const predicate = (v) => {
if (selector.pkg) return v.pkg() === selector.pkg; if (selector.pkg)
if (selector.socketName) return v._socketName() === selector.socketName; return v.pkg() === selector.pkg;
if (selector.socketName)
return v._socketName() === selector.socketName;
return false; return false;
}; };
const webView = [...this._webViews.values()].find(predicate); const webView = [...this._webViews.values()].find(predicate);
if (webView) return webView; if (webView)
return await this.waitForEvent('webview', { return webView;
...options, return await this.waitForEvent("webview", { ...options, predicate });
predicate
});
} }
async wait(selector, options) { async wait(selector, options) {
await this._channel.wait({ await this._channel.wait({ selector: toSelectorChannel(selector), ...options });
selector: toSelectorChannel(selector),
...options
});
} }
async fill(selector, text, options) { async fill(selector, text, options) {
await this._channel.fill({ await this._channel.fill({ selector: toSelectorChannel(selector), text, ...options });
selector: toSelectorChannel(selector),
text,
...options
});
} }
async press(selector, key, options) { async press(selector, key, options) {
await this.tap(selector, options); await this.tap(selector, options);
await this.input.press(key); await this.input.press(key);
} }
async tap(selector, options) { async tap(selector, options) {
await this._channel.tap({ await this._channel.tap({ selector: toSelectorChannel(selector), ...options });
selector: toSelectorChannel(selector),
...options
});
} }
async drag(selector, dest, options) { async drag(selector, dest, options) {
await this._channel.drag({ await this._channel.drag({ selector: toSelectorChannel(selector), dest, ...options });
selector: toSelectorChannel(selector),
dest,
...options
});
} }
async fling(selector, direction, options) { async fling(selector, direction, options) {
await this._channel.fling({ await this._channel.fling({ selector: toSelectorChannel(selector), direction, ...options });
selector: toSelectorChannel(selector),
direction,
...options
});
} }
async longTap(selector, options) { async longTap(selector, options) {
await this._channel.longTap({ await this._channel.longTap({ selector: toSelectorChannel(selector), ...options });
selector: toSelectorChannel(selector),
...options
});
} }
async pinchClose(selector, percent, options) { async pinchClose(selector, percent, options) {
await this._channel.pinchClose({ await this._channel.pinchClose({ selector: toSelectorChannel(selector), percent, ...options });
selector: toSelectorChannel(selector),
percent,
...options
});
} }
async pinchOpen(selector, percent, options) { async pinchOpen(selector, percent, options) {
await this._channel.pinchOpen({ await this._channel.pinchOpen({ selector: toSelectorChannel(selector), percent, ...options });
selector: toSelectorChannel(selector),
percent,
...options
});
} }
async scroll(selector, direction, percent, options) { async scroll(selector, direction, percent, options) {
await this._channel.scroll({ await this._channel.scroll({ selector: toSelectorChannel(selector), direction, percent, ...options });
selector: toSelectorChannel(selector),
direction,
percent,
...options
});
} }
async swipe(selector, direction, percent, options) { async swipe(selector, direction, percent, options) {
await this._channel.swipe({ await this._channel.swipe({ selector: toSelectorChannel(selector), direction, percent, ...options });
selector: toSelectorChannel(selector),
direction,
percent,
...options
});
} }
async info(selector) { async info(selector) {
return (await this._channel.info({ return (await this._channel.info({ selector: toSelectorChannel(selector) })).info;
selector: toSelectorChannel(selector)
})).info;
} }
async screenshot(options = {}) { async screenshot(options = {}) {
const { const { binary } = await this._channel.screenshot();
binary if (options.path)
} = await this._channel.screenshot(); await this._platform.fs().promises.writeFile(options.path, binary);
if (options.path) await this._platform.fs().promises.writeFile(options.path, binary);
return binary; return binary;
} }
async [Symbol.asyncDispose]() { async [Symbol.asyncDispose]() {
@@ -249,77 +192,64 @@ class AndroidDevice extends _channelOwner.ChannelOwner {
} }
async close() { async close() {
try { try {
if (this._shouldCloseConnectionOnClose) this._connection.close();else await this._channel.close(); if (this._shouldCloseConnectionOnClose)
this._connection.close();
else
await this._channel.close();
} catch (e) { } catch (e) {
if ((0, _errors.isTargetClosedError)(e)) return; if ((0, import_errors.isTargetClosedError)(e))
return;
throw e; throw e;
} }
} }
_didClose() { _didClose() {
this.emit(_events.Events.AndroidDevice.Close, this); this.emit(import_events.Events.AndroidDevice.Close, this);
} }
async shell(command) { async shell(command) {
const { const { result } = await this._channel.shell({ command });
result
} = await this._channel.shell({
command
});
return result; return result;
} }
async open(command) { async open(command) {
return AndroidSocket.from((await this._channel.open({ return AndroidSocket.from((await this._channel.open({ command })).socket);
command
})).socket);
} }
async installApk(file, options) { async installApk(file, options) {
await this._channel.installApk({ await this._channel.installApk({ file: await loadFile(this._platform, file), args: options && options.args });
file: await loadFile(this._platform, file),
args: options && options.args
});
} }
async push(file, path, options) { async push(file, path, options) {
await this._channel.push({ await this._channel.push({ file: await loadFile(this._platform, file), path, mode: options ? options.mode : void 0 });
file: await loadFile(this._platform, file),
path,
mode: options ? options.mode : undefined
});
} }
async launchBrowser(options = {}) { async launchBrowser(options = {}) {
const contextOptions = await (0, _browserContext.prepareBrowserContextParams)(this._platform, options); const contextOptions = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
const result = await this._channel.launchBrowser(contextOptions); const result = await this._channel.launchBrowser(contextOptions);
const context = _browserContext.BrowserContext.from(result.context); const context = import_browserContext.BrowserContext.from(result.context);
context._setOptions(contextOptions, {}); context._setOptions(contextOptions, {});
return context; return context;
} }
async waitForEvent(event, optionsOrPredicate = {}) { async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = _waiter.Waiter.createForEvent(this, event); const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== _events.Events.AndroidDevice.Close) waiter.rejectOnEvent(this, _events.Events.AndroidDevice.Close, () => new _errors.TargetClosedError()); if (event !== import_events.Events.AndroidDevice.Close)
waiter.rejectOnEvent(this, import_events.Events.AndroidDevice.Close, () => new import_errors.TargetClosedError());
const result = await waiter.waitForEvent(this, event, predicate); const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose(); waiter.dispose();
return result; return result;
}); });
} }
} }
exports.AndroidDevice = AndroidDevice; class AndroidSocket extends import_channelOwner.ChannelOwner {
class AndroidSocket extends _channelOwner.ChannelOwner {
static from(androidDevice) { static from(androidDevice) {
return androidDevice._object; return androidDevice._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._channel.on('data', ({ this._channel.on("data", ({ data }) => this.emit(import_events.Events.AndroidSocket.Data, data));
data this._channel.on("close", () => this.emit(import_events.Events.AndroidSocket.Close));
}) => this.emit(_events.Events.AndroidSocket.Data, data));
this._channel.on('close', () => this.emit(_events.Events.AndroidSocket.Close));
} }
async write(data) { async write(data) {
await this._channel.write({ await this._channel.write({ data });
data
});
} }
async close() { async close() {
await this._channel.close(); await this._channel.close();
@@ -328,46 +258,31 @@ class AndroidSocket extends _channelOwner.ChannelOwner {
await this.close(); await this.close();
} }
} }
exports.AndroidSocket = AndroidSocket;
async function loadFile(platform, file) { async function loadFile(platform, file) {
if ((0, _rtti.isString)(file)) return await platform.fs().promises.readFile(file); if ((0, import_rtti.isString)(file))
return await platform.fs().promises.readFile(file);
return file; return file;
} }
class AndroidInput { class AndroidInput {
constructor(device) { constructor(device) {
this._device = void 0;
this._device = device; this._device = device;
} }
async type(text) { async type(text) {
await this._device._channel.inputType({ await this._device._channel.inputType({ text });
text
});
} }
async press(key) { async press(key) {
await this._device._channel.inputPress({ await this._device._channel.inputPress({ key });
key
});
} }
async tap(point) { async tap(point) {
await this._device._channel.inputTap({ await this._device._channel.inputTap({ point });
point
});
} }
async swipe(from, segments, steps) { async swipe(from, segments, steps) {
await this._device._channel.inputSwipe({ await this._device._channel.inputSwipe({ segments, steps });
segments,
steps
});
} }
async drag(from, to, steps) { async drag(from, to, steps) {
await this._device._channel.inputDrag({ await this._device._channel.inputDrag({ from, to, steps });
from,
to,
steps
});
} }
} }
exports.AndroidInput = AndroidInput;
function toSelectorChannel(selector) { function toSelectorChannel(selector) {
const { const {
checkable, checkable,
@@ -388,10 +303,12 @@ function toSelectorChannel(selector) {
selected, selected,
text text
} = selector; } = selector;
const toRegex = value => { const toRegex = (value) => {
if (value === undefined) return undefined; if (value === void 0)
if ((0, _rtti.isRegExp)(value)) return value.source; return void 0;
return '^' + value.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d') + '$'; if ((0, import_rtti.isRegExp)(value))
return value.source;
return "^" + value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d") + "$";
}; };
return { return {
checkable, checkable,
@@ -406,24 +323,16 @@ function toSelectorChannel(selector) {
enabled, enabled,
focusable, focusable,
focused, focused,
hasChild: hasChild ? { hasChild: hasChild ? { selector: toSelectorChannel(hasChild.selector) } : void 0,
selector: toSelectorChannel(hasChild.selector) hasDescendant: hasDescendant ? { selector: toSelectorChannel(hasDescendant.selector), maxDepth: hasDescendant.maxDepth } : void 0,
} : undefined,
hasDescendant: hasDescendant ? {
selector: toSelectorChannel(hasDescendant.selector),
maxDepth: hasDescendant.maxDepth
} : undefined,
longClickable, longClickable,
scrollable, scrollable,
selected selected
}; };
} }
class AndroidWebView extends _eventEmitter.EventEmitter { class AndroidWebView extends import_eventEmitter.EventEmitter {
constructor(device, data) { constructor(device, data) {
super(device._platform); super(device._platform);
this._device = void 0;
this._data = void 0;
this._pagePromise = void 0;
this._device = device; this._device = device;
this._data = data; this._data = data;
} }
@@ -437,16 +346,20 @@ class AndroidWebView extends _eventEmitter.EventEmitter {
return this._data.socketName; return this._data.socketName;
} }
async page() { async page() {
if (!this._pagePromise) this._pagePromise = this._fetchPage(); if (!this._pagePromise)
this._pagePromise = this._fetchPage();
return await this._pagePromise; return await this._pagePromise;
} }
async _fetchPage() { async _fetchPage() {
const { const { context } = await this._device._channel.connectToWebView({ socketName: this._data.socketName });
context return import_browserContext.BrowserContext.from(context).pages()[0];
} = await this._device._channel.connectToWebView({ }
socketName: this._data.socketName }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Android,
AndroidDevice,
AndroidInput,
AndroidSocket,
AndroidWebView
}); });
return _browserContext.BrowserContext.from(context).pages()[0];
}
}
exports.AndroidWebView = AndroidWebView;

View File

@@ -1,285 +1,137 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
}); var __hasOwnProp = Object.prototype.hasOwnProperty;
Object.defineProperty(exports, "APIRequest", { var __export = (target, all) => {
enumerable: true, for (var name in all)
get: function () { __defProp(target, name, { get: all[name], enumerable: true });
return _fetch.APIRequest; };
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
} }
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var api_exports = {};
__export(api_exports, {
APIRequest: () => import_fetch.APIRequest,
APIRequestContext: () => import_fetch.APIRequestContext,
APIResponse: () => import_fetch.APIResponse,
Accessibility: () => import_accessibility.Accessibility,
Android: () => import_android.Android,
AndroidDevice: () => import_android.AndroidDevice,
AndroidInput: () => import_android.AndroidInput,
AndroidSocket: () => import_android.AndroidSocket,
AndroidWebView: () => import_android.AndroidWebView,
Browser: () => import_browser.Browser,
BrowserContext: () => import_browserContext.BrowserContext,
BrowserType: () => import_browserType.BrowserType,
CDPSession: () => import_cdpSession.CDPSession,
Clock: () => import_clock.Clock,
ConsoleMessage: () => import_consoleMessage.ConsoleMessage,
Coverage: () => import_coverage.Coverage,
Dialog: () => import_dialog.Dialog,
Download: () => import_download.Download,
Electron: () => import_electron.Electron,
ElectronApplication: () => import_electron.ElectronApplication,
ElementHandle: () => import_elementHandle.ElementHandle,
FileChooser: () => import_fileChooser.FileChooser,
Frame: () => import_frame.Frame,
FrameLocator: () => import_locator.FrameLocator,
JSHandle: () => import_jsHandle.JSHandle,
Keyboard: () => import_input.Keyboard,
Locator: () => import_locator.Locator,
Mouse: () => import_input.Mouse,
Page: () => import_page.Page,
Playwright: () => import_playwright.Playwright,
Request: () => import_network.Request,
Response: () => import_network.Response,
Route: () => import_network.Route,
Selectors: () => import_selectors.Selectors,
TimeoutError: () => import_errors.TimeoutError,
Touchscreen: () => import_input.Touchscreen,
Tracing: () => import_tracing.Tracing,
Video: () => import_video.Video,
WebError: () => import_webError.WebError,
WebSocket: () => import_network.WebSocket,
WebSocketRoute: () => import_network.WebSocketRoute,
Worker: () => import_worker.Worker
}); });
Object.defineProperty(exports, "APIRequestContext", { module.exports = __toCommonJS(api_exports);
enumerable: true, var import_accessibility = require("./accessibility");
get: function () { var import_android = require("./android");
return _fetch.APIRequestContext; var import_browser = require("./browser");
} var import_browserContext = require("./browserContext");
var import_browserType = require("./browserType");
var import_clock = require("./clock");
var import_consoleMessage = require("./consoleMessage");
var import_coverage = require("./coverage");
var import_dialog = require("./dialog");
var import_download = require("./download");
var import_electron = require("./electron");
var import_locator = require("./locator");
var import_elementHandle = require("./elementHandle");
var import_fileChooser = require("./fileChooser");
var import_errors = require("./errors");
var import_frame = require("./frame");
var import_input = require("./input");
var import_jsHandle = require("./jsHandle");
var import_network = require("./network");
var import_fetch = require("./fetch");
var import_page = require("./page");
var import_selectors = require("./selectors");
var import_tracing = require("./tracing");
var import_video = require("./video");
var import_worker = require("./worker");
var import_cdpSession = require("./cdpSession");
var import_playwright = require("./playwright");
var import_webError = require("./webError");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
APIRequest,
APIRequestContext,
APIResponse,
Accessibility,
Android,
AndroidDevice,
AndroidInput,
AndroidSocket,
AndroidWebView,
Browser,
BrowserContext,
BrowserType,
CDPSession,
Clock,
ConsoleMessage,
Coverage,
Dialog,
Download,
Electron,
ElectronApplication,
ElementHandle,
FileChooser,
Frame,
FrameLocator,
JSHandle,
Keyboard,
Locator,
Mouse,
Page,
Playwright,
Request,
Response,
Route,
Selectors,
TimeoutError,
Touchscreen,
Tracing,
Video,
WebError,
WebSocket,
WebSocketRoute,
Worker
}); });
Object.defineProperty(exports, "APIResponse", {
enumerable: true,
get: function () {
return _fetch.APIResponse;
}
});
Object.defineProperty(exports, "Accessibility", {
enumerable: true,
get: function () {
return _accessibility.Accessibility;
}
});
Object.defineProperty(exports, "Android", {
enumerable: true,
get: function () {
return _android.Android;
}
});
Object.defineProperty(exports, "AndroidDevice", {
enumerable: true,
get: function () {
return _android.AndroidDevice;
}
});
Object.defineProperty(exports, "AndroidInput", {
enumerable: true,
get: function () {
return _android.AndroidInput;
}
});
Object.defineProperty(exports, "AndroidSocket", {
enumerable: true,
get: function () {
return _android.AndroidSocket;
}
});
Object.defineProperty(exports, "AndroidWebView", {
enumerable: true,
get: function () {
return _android.AndroidWebView;
}
});
Object.defineProperty(exports, "Browser", {
enumerable: true,
get: function () {
return _browser.Browser;
}
});
Object.defineProperty(exports, "BrowserContext", {
enumerable: true,
get: function () {
return _browserContext.BrowserContext;
}
});
Object.defineProperty(exports, "BrowserType", {
enumerable: true,
get: function () {
return _browserType.BrowserType;
}
});
Object.defineProperty(exports, "CDPSession", {
enumerable: true,
get: function () {
return _cdpSession.CDPSession;
}
});
Object.defineProperty(exports, "Clock", {
enumerable: true,
get: function () {
return _clock.Clock;
}
});
Object.defineProperty(exports, "ConsoleMessage", {
enumerable: true,
get: function () {
return _consoleMessage.ConsoleMessage;
}
});
Object.defineProperty(exports, "Coverage", {
enumerable: true,
get: function () {
return _coverage.Coverage;
}
});
Object.defineProperty(exports, "Dialog", {
enumerable: true,
get: function () {
return _dialog.Dialog;
}
});
Object.defineProperty(exports, "Download", {
enumerable: true,
get: function () {
return _download.Download;
}
});
Object.defineProperty(exports, "Electron", {
enumerable: true,
get: function () {
return _electron.Electron;
}
});
Object.defineProperty(exports, "ElectronApplication", {
enumerable: true,
get: function () {
return _electron.ElectronApplication;
}
});
Object.defineProperty(exports, "ElementHandle", {
enumerable: true,
get: function () {
return _elementHandle.ElementHandle;
}
});
Object.defineProperty(exports, "FileChooser", {
enumerable: true,
get: function () {
return _fileChooser.FileChooser;
}
});
Object.defineProperty(exports, "Frame", {
enumerable: true,
get: function () {
return _frame.Frame;
}
});
Object.defineProperty(exports, "FrameLocator", {
enumerable: true,
get: function () {
return _locator.FrameLocator;
}
});
Object.defineProperty(exports, "JSHandle", {
enumerable: true,
get: function () {
return _jsHandle.JSHandle;
}
});
Object.defineProperty(exports, "Keyboard", {
enumerable: true,
get: function () {
return _input.Keyboard;
}
});
Object.defineProperty(exports, "Locator", {
enumerable: true,
get: function () {
return _locator.Locator;
}
});
Object.defineProperty(exports, "Mouse", {
enumerable: true,
get: function () {
return _input.Mouse;
}
});
Object.defineProperty(exports, "Page", {
enumerable: true,
get: function () {
return _page.Page;
}
});
Object.defineProperty(exports, "Playwright", {
enumerable: true,
get: function () {
return _playwright.Playwright;
}
});
Object.defineProperty(exports, "Request", {
enumerable: true,
get: function () {
return _network.Request;
}
});
Object.defineProperty(exports, "Response", {
enumerable: true,
get: function () {
return _network.Response;
}
});
Object.defineProperty(exports, "Route", {
enumerable: true,
get: function () {
return _network.Route;
}
});
Object.defineProperty(exports, "Selectors", {
enumerable: true,
get: function () {
return _selectors.Selectors;
}
});
Object.defineProperty(exports, "TimeoutError", {
enumerable: true,
get: function () {
return _errors.TimeoutError;
}
});
Object.defineProperty(exports, "Touchscreen", {
enumerable: true,
get: function () {
return _input.Touchscreen;
}
});
Object.defineProperty(exports, "Tracing", {
enumerable: true,
get: function () {
return _tracing.Tracing;
}
});
Object.defineProperty(exports, "Video", {
enumerable: true,
get: function () {
return _video.Video;
}
});
Object.defineProperty(exports, "WebError", {
enumerable: true,
get: function () {
return _webError.WebError;
}
});
Object.defineProperty(exports, "WebSocket", {
enumerable: true,
get: function () {
return _network.WebSocket;
}
});
Object.defineProperty(exports, "WebSocketRoute", {
enumerable: true,
get: function () {
return _network.WebSocketRoute;
}
});
Object.defineProperty(exports, "Worker", {
enumerable: true,
get: function () {
return _worker.Worker;
}
});
var _accessibility = require("./accessibility");
var _android = require("./android");
var _browser = require("./browser");
var _browserContext = require("./browserContext");
var _browserType = require("./browserType");
var _clock = require("./clock");
var _consoleMessage = require("./consoleMessage");
var _coverage = require("./coverage");
var _dialog = require("./dialog");
var _download = require("./download");
var _electron = require("./electron");
var _locator = require("./locator");
var _elementHandle = require("./elementHandle");
var _fileChooser = require("./fileChooser");
var _errors = require("./errors");
var _frame = require("./frame");
var _input = require("./input");
var _jsHandle = require("./jsHandle");
var _network = require("./network");
var _fetch = require("./fetch");
var _page = require("./page");
var _selectors = require("./selectors");
var _tracing = require("./tracing");
var _video = require("./video");
var _worker = require("./worker");
var _cdpSession = require("./cdpSession");
var _playwright = require("./playwright");
var _webError = require("./webError");

View File

@@ -1,48 +1,48 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var artifact_exports = {};
__export(artifact_exports, {
Artifact: () => Artifact
}); });
exports.Artifact = void 0; module.exports = __toCommonJS(artifact_exports);
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _stream = require("./stream"); var import_stream = require("./stream");
var _fileUtils = require("./fileUtils"); var import_fileUtils = require("./fileUtils");
/** class Artifact extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Artifact extends _channelOwner.ChannelOwner {
static from(channel) { static from(channel) {
return channel._object; return channel._object;
} }
async pathAfterFinished() { async pathAfterFinished() {
if (this._connection.isRemote()) throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`); if (this._connection.isRemote())
throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
return (await this._channel.pathAfterFinished()).value; return (await this._channel.pathAfterFinished()).value;
} }
async saveAs(path) { async saveAs(path) {
if (!this._connection.isRemote()) { if (!this._connection.isRemote()) {
await this._channel.saveAs({ await this._channel.saveAs({ path });
path
});
return; return;
} }
const result = await this._channel.saveAsStream(); const result = await this._channel.saveAsStream();
const stream = _stream.Stream.from(result.stream); const stream = import_stream.Stream.from(result.stream);
await (0, _fileUtils.mkdirIfNeeded)(this._platform, path); await (0, import_fileUtils.mkdirIfNeeded)(this._platform, path);
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
stream.stream().pipe(this._platform.fs().createWriteStream(path)).on('finish', resolve).on('error', reject); stream.stream().pipe(this._platform.fs().createWriteStream(path)).on("finish", resolve).on("error", reject);
}); });
} }
async failure() { async failure() {
@@ -50,20 +50,20 @@ class Artifact extends _channelOwner.ChannelOwner {
} }
async createReadStream() { async createReadStream() {
const result = await this._channel.stream(); const result = await this._channel.stream();
const stream = _stream.Stream.from(result.stream); const stream = import_stream.Stream.from(result.stream);
return stream.stream(); return stream.stream();
} }
async readIntoBuffer() { async readIntoBuffer() {
const stream = await this.createReadStream(); const stream = await this.createReadStream();
return await new Promise((resolve, reject) => { return await new Promise((resolve, reject) => {
const chunks = []; const chunks = [];
stream.on('data', chunk => { stream.on("data", (chunk) => {
chunks.push(chunk); chunks.push(chunk);
}); });
stream.on('end', () => { stream.on("end", () => {
resolve(Buffer.concat(chunks)); resolve(Buffer.concat(chunks));
}); });
stream.on('error', reject); stream.on("error", reject);
}); });
} }
async cancel() { async cancel() {
@@ -73,4 +73,7 @@ class Artifact extends _channelOwner.ChannelOwner {
return await this._channel.delete(); return await this._channel.delete();
} }
} }
exports.Artifact = Artifact; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Artifact
});

View File

@@ -1,50 +1,46 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
}); var __hasOwnProp = Object.prototype.hasOwnProperty;
exports.Browser = void 0; var __export = (target, all) => {
var _artifact = require("./artifact"); for (var name in all)
var _browserContext = require("./browserContext"); __defProp(target, name, { get: all[name], enumerable: true });
var _cdpSession = require("./cdpSession"); };
var _channelOwner = require("./channelOwner"); var __copyProps = (to, from, except, desc) => {
var _errors = require("./errors"); if (from && typeof from === "object" || typeof from === "function") {
var _events = require("./events"); for (let key of __getOwnPropNames(from))
var _fileUtils = require("./fileUtils"); if (!__hasOwnProp.call(to, key) && key !== except)
/** __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Browser extends _channelOwner.ChannelOwner {
static from(browser) {
return browser._object;
} }
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browser_exports = {};
__export(browser_exports, {
Browser: () => Browser
});
module.exports = __toCommonJS(browser_exports);
var import_artifact = require("./artifact");
var import_browserContext = require("./browserContext");
var import_cdpSession = require("./cdpSession");
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
var import_events = require("./events");
var import_fileUtils = require("./fileUtils");
class Browser extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._contexts = new Set(); this._contexts = /* @__PURE__ */ new Set();
this._isConnected = true; this._isConnected = true;
this._closedPromise = void 0;
this._shouldCloseConnectionOnClose = false; this._shouldCloseConnectionOnClose = false;
this._browserType = void 0;
this._options = {}; this._options = {};
this._name = void 0;
this._path = void 0;
this._closeReason = void 0;
this._name = initializer.name; this._name = initializer.name;
this._channel.on('close', () => this._didClose()); this._channel.on("close", () => this._didClose());
this._closedPromise = new Promise(f => this.once(_events.Events.Browser.Disconnected, f)); this._closedPromise = new Promise((f) => this.once(import_events.Events.Browser.Disconnected, f));
}
static from(browser) {
return browser._object;
} }
browserType() { browserType() {
return this._browserType; return this._browserType;
@@ -56,7 +52,8 @@ class Browser extends _channelOwner.ChannelOwner {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
for (const context of this._contexts) { for (const context of this._contexts) {
await this._browserType._willCloseContext(context); await this._browserType._willCloseContext(context);
for (const page of context.pages()) page._onClose(); for (const page of context.pages())
page._onClose();
context._onClose(); context._onClose();
} }
return await this._innerNewContext(options, true); return await this._innerNewContext(options, true);
@@ -64,19 +61,14 @@ class Browser extends _channelOwner.ChannelOwner {
} }
async _stopPendingOperations(reason) { async _stopPendingOperations(reason) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
await this._channel.stopPendingOperations({ await this._channel.stopPendingOperations({ reason });
reason
});
}, true); }, true);
} }
async _innerNewContext(options = {}, forReuse) { async _innerNewContext(options = {}, forReuse) {
options = { options = { ...this._browserType._playwright._defaultContextOptions, ...options };
...this._browserType._playwright._defaultContextOptions, const contextOptions = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
...options
};
const contextOptions = await (0, _browserContext.prepareBrowserContextParams)(this._platform, options);
const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions); const response = forReuse ? await this._channel.newContextForReuse(contextOptions) : await this._channel.newContext(contextOptions);
const context = _browserContext.BrowserContext.from(response.context); const context = import_browserContext.BrowserContext.from(response.context);
await this._browserType._didCreateContext(context, contextOptions, this._options, options.logger || this._logger); await this._browserType._didCreateContext(context, contextOptions, this._options, options.logger || this._logger);
return context; return context;
} }
@@ -99,23 +91,20 @@ class Browser extends _channelOwner.ChannelOwner {
return this._isConnected; return this._isConnected;
} }
async newBrowserCDPSession() { async newBrowserCDPSession() {
return _cdpSession.CDPSession.from((await this._channel.newBrowserCDPSession()).session); return import_cdpSession.CDPSession.from((await this._channel.newBrowserCDPSession()).session);
} }
async startTracing(page, options = {}) { async startTracing(page, options = {}) {
this._path = options.path; this._path = options.path;
await this._channel.startTracing({ await this._channel.startTracing({ ...options, page: page ? page._channel : void 0 });
...options,
page: page ? page._channel : undefined
});
} }
async stopTracing() { async stopTracing() {
const artifact = _artifact.Artifact.from((await this._channel.stopTracing()).artifact); const artifact = import_artifact.Artifact.from((await this._channel.stopTracing()).artifact);
const buffer = await artifact.readIntoBuffer(); const buffer = await artifact.readIntoBuffer();
await artifact.delete(); await artifact.delete();
if (this._path) { if (this._path) {
await (0, _fileUtils.mkdirIfNeeded)(this._platform, this._path); await (0, import_fileUtils.mkdirIfNeeded)(this._platform, this._path);
await this._platform.fs().promises.writeFile(this._path, buffer); await this._platform.fs().promises.writeFile(this._path, buffer);
this._path = undefined; this._path = void 0;
} }
return buffer; return buffer;
} }
@@ -125,16 +114,23 @@ class Browser extends _channelOwner.ChannelOwner {
async close(options = {}) { async close(options = {}) {
this._closeReason = options.reason; this._closeReason = options.reason;
try { try {
if (this._shouldCloseConnectionOnClose) this._connection.close();else await this._channel.close(options); if (this._shouldCloseConnectionOnClose)
this._connection.close();
else
await this._channel.close(options);
await this._closedPromise; await this._closedPromise;
} catch (e) { } catch (e) {
if ((0, _errors.isTargetClosedError)(e)) return; if ((0, import_errors.isTargetClosedError)(e))
return;
throw e; throw e;
} }
} }
_didClose() { _didClose() {
this._isConnected = false; this._isConnected = false;
this.emit(_events.Events.Browser.Disconnected, this); this.emit(import_events.Events.Browser.Disconnected, this);
} }
} }
exports.Browser = Browser; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Browser
});

View File

@@ -1,250 +1,244 @@
"use strict"; "use strict";
var __create = Object.create;
Object.defineProperty(exports, "__esModule", { var __defProp = Object.defineProperty;
value: true var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserContext_exports = {};
__export(browserContext_exports, {
BrowserContext: () => BrowserContext,
prepareBrowserContextParams: () => prepareBrowserContextParams,
toClientCertificatesProtocol: () => toClientCertificatesProtocol
}); });
exports.BrowserContext = void 0; module.exports = __toCommonJS(browserContext_exports);
exports.prepareBrowserContextParams = prepareBrowserContextParams; var import_artifact = require("./artifact");
exports.toClientCertificatesProtocol = toClientCertificatesProtocol; var import_browser = require("./browser");
var _artifact = require("./artifact"); var import_cdpSession = require("./cdpSession");
var _browser = require("./browser"); var import_channelOwner = require("./channelOwner");
var _cdpSession = require("./cdpSession"); var import_clientHelper = require("./clientHelper");
var _channelOwner = require("./channelOwner"); var import_clock = require("./clock");
var _clientHelper = require("./clientHelper"); var import_consoleMessage = require("./consoleMessage");
var _clock = require("./clock"); var import_dialog = require("./dialog");
var _consoleMessage = require("./consoleMessage"); var import_errors = require("./errors");
var _dialog = require("./dialog"); var import_events = require("./events");
var _errors = require("./errors"); var import_fetch = require("./fetch");
var _events = require("./events"); var import_frame = require("./frame");
var _fetch = require("./fetch"); var import_harRouter = require("./harRouter");
var _frame = require("./frame"); var network = __toESM(require("./network"));
var _harRouter = require("./harRouter"); var import_page = require("./page");
var network = _interopRequireWildcard(require("./network")); var import_tracing = require("./tracing");
var _page = require("./page"); var import_waiter = require("./waiter");
var _tracing = require("./tracing"); var import_webError = require("./webError");
var _waiter = require("./waiter"); var import_worker = require("./worker");
var _webError = require("./webError"); var import_timeoutSettings = require("./timeoutSettings");
var _worker = require("./worker"); var import_fileUtils = require("./fileUtils");
var _timeoutSettings = require("./timeoutSettings"); var import_headers = require("../utils/isomorphic/headers");
var _fileUtils = require("./fileUtils"); var import_urlMatch = require("../utils/isomorphic/urlMatch");
var _headers = require("../utils/isomorphic/headers"); var import_rtti = require("../utils/isomorphic/rtti");
var _urlMatch = require("../utils/isomorphic/urlMatch"); var import_stackTrace = require("../utils/isomorphic/stackTrace");
var _rtti = require("../utils/isomorphic/rtti"); class BrowserContext extends import_channelOwner.ChannelOwner {
var _stackTrace = require("../utils/isomorphic/stackTrace"); constructor(parent, type, guid, initializer) {
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } super(parent, type, guid, initializer);
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } this._pages = /* @__PURE__ */ new Set();
/** this._routes = [];
* Copyright 2017 Google Inc. All rights reserved. this._webSocketRoutes = [];
* Modifications copyright (c) Microsoft Corporation. this._browser = null;
* this._bindings = /* @__PURE__ */ new Map();
* Licensed under the Apache License, Version 2.0 (the "License"); this._options = {};
* you may not use this file except in compliance with the License. this._backgroundPages = /* @__PURE__ */ new Set();
* You may obtain a copy of the License at this._serviceWorkers = /* @__PURE__ */ new Set();
* this._harRecorders = /* @__PURE__ */ new Map();
* http://www.apache.org/licenses/LICENSE-2.0 this._closeWasCalled = false;
* this._harRouters = [];
* Unless required by applicable law or agreed to in writing, software this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
* distributed under the License is distributed on an "AS IS" BASIS, if (parent instanceof import_browser.Browser)
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. this._browser = parent;
* See the License for the specific language governing permissions and this._browser?._contexts.add(this);
* limitations under the License. this._isChromium = this._browser?._name === "chromium";
*/ this.tracing = import_tracing.Tracing.from(initializer.tracing);
this.request = import_fetch.APIRequestContext.from(initializer.requestContext);
class BrowserContext extends _channelOwner.ChannelOwner { this.clock = new import_clock.Clock(this);
this._channel.on("bindingCall", ({ binding }) => this._onBinding(import_page.BindingCall.from(binding)));
this._channel.on("close", () => this._onClose());
this._channel.on("page", ({ page }) => this._onPage(import_page.Page.from(page)));
this._channel.on("route", ({ route }) => this._onRoute(network.Route.from(route)));
this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute)));
this._channel.on("backgroundPage", ({ page }) => {
const backgroundPage = import_page.Page.from(page);
this._backgroundPages.add(backgroundPage);
this.emit(import_events.Events.BrowserContext.BackgroundPage, backgroundPage);
});
this._channel.on("serviceWorker", ({ worker }) => {
const serviceWorker = import_worker.Worker.from(worker);
serviceWorker._context = this;
this._serviceWorkers.add(serviceWorker);
this.emit(import_events.Events.BrowserContext.ServiceWorker, serviceWorker);
});
this._channel.on("console", (event) => {
const consoleMessage = new import_consoleMessage.ConsoleMessage(this._platform, event);
this.emit(import_events.Events.BrowserContext.Console, consoleMessage);
const page = consoleMessage.page();
if (page)
page.emit(import_events.Events.Page.Console, consoleMessage);
});
this._channel.on("pageError", ({ error, page }) => {
const pageObject = import_page.Page.from(page);
const parsedError = (0, import_errors.parseError)(error);
this.emit(import_events.Events.BrowserContext.WebError, new import_webError.WebError(pageObject, parsedError));
if (pageObject)
pageObject.emit(import_events.Events.Page.PageError, parsedError);
});
this._channel.on("dialog", ({ dialog }) => {
const dialogObject = import_dialog.Dialog.from(dialog);
let hasListeners = this.emit(import_events.Events.BrowserContext.Dialog, dialogObject);
const page = dialogObject.page();
if (page)
hasListeners = page.emit(import_events.Events.Page.Dialog, dialogObject) || hasListeners;
if (!hasListeners) {
if (dialogObject.type() === "beforeunload")
dialog.accept({}).catch(() => {
});
else
dialog.dismiss().catch(() => {
});
}
});
this._channel.on("request", ({ request, page }) => this._onRequest(network.Request.from(request), import_page.Page.fromNullable(page)));
this._channel.on("requestFailed", ({ request, failureText, responseEndTiming, page }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, import_page.Page.fromNullable(page)));
this._channel.on("requestFinished", (params) => this._onRequestFinished(params));
this._channel.on("response", ({ response, page }) => this._onResponse(network.Response.from(response), import_page.Page.fromNullable(page)));
this._closedPromise = new Promise((f) => this.once(import_events.Events.BrowserContext.Close, f));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[import_events.Events.BrowserContext.Console, "console"],
[import_events.Events.BrowserContext.Dialog, "dialog"],
[import_events.Events.BrowserContext.Request, "request"],
[import_events.Events.BrowserContext.Response, "response"],
[import_events.Events.BrowserContext.RequestFinished, "requestFinished"],
[import_events.Events.BrowserContext.RequestFailed, "requestFailed"]
]));
}
static from(context) { static from(context) {
return context._object; return context._object;
} }
static fromNullable(context) { static fromNullable(context) {
return context ? BrowserContext.from(context) : null; return context ? BrowserContext.from(context) : null;
} }
constructor(parent, type, guid, initializer) {
var _this$_browser, _this$_browser2;
super(parent, type, guid, initializer);
this._pages = new Set();
this._routes = [];
this._webSocketRoutes = [];
this._browser = null;
this._browserType = void 0;
this._bindings = new Map();
this._timeoutSettings = void 0;
this._ownerPage = void 0;
this._closedPromise = void 0;
this._options = {};
this.request = void 0;
this.tracing = void 0;
this.clock = void 0;
this._backgroundPages = new Set();
this._serviceWorkers = new Set();
this._isChromium = void 0;
this._harRecorders = new Map();
this._closeWasCalled = false;
this._closeReason = void 0;
this._harRouters = [];
this._timeoutSettings = new _timeoutSettings.TimeoutSettings(this._platform);
if (parent instanceof _browser.Browser) this._browser = parent;
(_this$_browser = this._browser) === null || _this$_browser === void 0 || _this$_browser._contexts.add(this);
this._isChromium = ((_this$_browser2 = this._browser) === null || _this$_browser2 === void 0 ? void 0 : _this$_browser2._name) === 'chromium';
this.tracing = _tracing.Tracing.from(initializer.tracing);
this.request = _fetch.APIRequestContext.from(initializer.requestContext);
this.clock = new _clock.Clock(this);
this._channel.on('bindingCall', ({
binding
}) => this._onBinding(_page.BindingCall.from(binding)));
this._channel.on('close', () => this._onClose());
this._channel.on('page', ({
page
}) => this._onPage(_page.Page.from(page)));
this._channel.on('route', ({
route
}) => this._onRoute(network.Route.from(route)));
this._channel.on('webSocketRoute', ({
webSocketRoute
}) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute)));
this._channel.on('backgroundPage', ({
page
}) => {
const backgroundPage = _page.Page.from(page);
this._backgroundPages.add(backgroundPage);
this.emit(_events.Events.BrowserContext.BackgroundPage, backgroundPage);
});
this._channel.on('serviceWorker', ({
worker
}) => {
const serviceWorker = _worker.Worker.from(worker);
serviceWorker._context = this;
this._serviceWorkers.add(serviceWorker);
this.emit(_events.Events.BrowserContext.ServiceWorker, serviceWorker);
});
this._channel.on('console', event => {
const consoleMessage = new _consoleMessage.ConsoleMessage(this._platform, event);
this.emit(_events.Events.BrowserContext.Console, consoleMessage);
const page = consoleMessage.page();
if (page) page.emit(_events.Events.Page.Console, consoleMessage);
});
this._channel.on('pageError', ({
error,
page
}) => {
const pageObject = _page.Page.from(page);
const parsedError = (0, _errors.parseError)(error);
this.emit(_events.Events.BrowserContext.WebError, new _webError.WebError(pageObject, parsedError));
if (pageObject) pageObject.emit(_events.Events.Page.PageError, parsedError);
});
this._channel.on('dialog', ({
dialog
}) => {
const dialogObject = _dialog.Dialog.from(dialog);
let hasListeners = this.emit(_events.Events.BrowserContext.Dialog, dialogObject);
const page = dialogObject.page();
if (page) hasListeners = page.emit(_events.Events.Page.Dialog, dialogObject) || hasListeners;
if (!hasListeners) {
// Although we do similar handling on the server side, we still need this logic
// on the client side due to a possible race condition between two async calls:
// a) removing "dialog" listener subscription (client->server)
// b) actual "dialog" event (server->client)
if (dialogObject.type() === 'beforeunload') dialog.accept({}).catch(() => {});else dialog.dismiss().catch(() => {});
}
});
this._channel.on('request', ({
request,
page
}) => this._onRequest(network.Request.from(request), _page.Page.fromNullable(page)));
this._channel.on('requestFailed', ({
request,
failureText,
responseEndTiming,
page
}) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, _page.Page.fromNullable(page)));
this._channel.on('requestFinished', params => this._onRequestFinished(params));
this._channel.on('response', ({
response,
page
}) => this._onResponse(network.Response.from(response), _page.Page.fromNullable(page)));
this._closedPromise = new Promise(f => this.once(_events.Events.BrowserContext.Close, f));
this._setEventToSubscriptionMapping(new Map([[_events.Events.BrowserContext.Console, 'console'], [_events.Events.BrowserContext.Dialog, 'dialog'], [_events.Events.BrowserContext.Request, 'request'], [_events.Events.BrowserContext.Response, 'response'], [_events.Events.BrowserContext.RequestFinished, 'requestFinished'], [_events.Events.BrowserContext.RequestFailed, 'requestFailed']]));
}
_setOptions(contextOptions, browserOptions) { _setOptions(contextOptions, browserOptions) {
this._options = contextOptions; this._options = contextOptions;
if (this._options.recordHar) this._harRecorders.set('', { if (this._options.recordHar)
path: this._options.recordHar.path, this._harRecorders.set("", { path: this._options.recordHar.path, content: this._options.recordHar.content });
content: this._options.recordHar.content
});
this.tracing._tracesDir = browserOptions.tracesDir; this.tracing._tracesDir = browserOptions.tracesDir;
} }
_onPage(page) { _onPage(page) {
this._pages.add(page); this._pages.add(page);
this.emit(_events.Events.BrowserContext.Page, page); this.emit(import_events.Events.BrowserContext.Page, page);
if (page._opener && !page._opener.isClosed()) page._opener.emit(_events.Events.Page.Popup, page); if (page._opener && !page._opener.isClosed())
page._opener.emit(import_events.Events.Page.Popup, page);
} }
_onRequest(request, page) { _onRequest(request, page) {
this.emit(_events.Events.BrowserContext.Request, request); this.emit(import_events.Events.BrowserContext.Request, request);
if (page) page.emit(_events.Events.Page.Request, request); if (page)
page.emit(import_events.Events.Page.Request, request);
} }
_onResponse(response, page) { _onResponse(response, page) {
this.emit(_events.Events.BrowserContext.Response, response); this.emit(import_events.Events.BrowserContext.Response, response);
if (page) page.emit(_events.Events.Page.Response, response); if (page)
page.emit(import_events.Events.Page.Response, response);
} }
_onRequestFailed(request, responseEndTiming, failureText, page) { _onRequestFailed(request, responseEndTiming, failureText, page) {
request._failureText = failureText || null; request._failureText = failureText || null;
request._setResponseEndTiming(responseEndTiming); request._setResponseEndTiming(responseEndTiming);
this.emit(_events.Events.BrowserContext.RequestFailed, request); this.emit(import_events.Events.BrowserContext.RequestFailed, request);
if (page) page.emit(_events.Events.Page.RequestFailed, request); if (page)
page.emit(import_events.Events.Page.RequestFailed, request);
} }
_onRequestFinished(params) { _onRequestFinished(params) {
const { const { responseEndTiming } = params;
responseEndTiming
} = params;
const request = network.Request.from(params.request); const request = network.Request.from(params.request);
const response = network.Response.fromNullable(params.response); const response = network.Response.fromNullable(params.response);
const page = _page.Page.fromNullable(params.page); const page = import_page.Page.fromNullable(params.page);
request._setResponseEndTiming(responseEndTiming); request._setResponseEndTiming(responseEndTiming);
this.emit(_events.Events.BrowserContext.RequestFinished, request); this.emit(import_events.Events.BrowserContext.RequestFinished, request);
if (page) page.emit(_events.Events.Page.RequestFinished, request); if (page)
if (response) response._finishedPromise.resolve(null); page.emit(import_events.Events.Page.RequestFinished, request);
if (response)
response._finishedPromise.resolve(null);
} }
async _onRoute(route) { async _onRoute(route) {
route._context = this; route._context = this;
const page = route.request()._safePage(); const page = route.request()._safePage();
const routeHandlers = this._routes.slice(); const routeHandlers = this._routes.slice();
for (const routeHandler of routeHandlers) { for (const routeHandler of routeHandlers) {
// If the page or the context was closed we stall all requests right away. if (page?._closeWasCalled || this._closeWasCalled)
if (page !== null && page !== void 0 && page._closeWasCalled || this._closeWasCalled) return; return;
if (!routeHandler.matches(route.request().url())) continue; if (!routeHandler.matches(route.request().url()))
continue;
const index = this._routes.indexOf(routeHandler); const index = this._routes.indexOf(routeHandler);
if (index === -1) continue; if (index === -1)
if (routeHandler.willExpire()) this._routes.splice(index, 1); continue;
if (routeHandler.willExpire())
this._routes.splice(index, 1);
const handled = await routeHandler.handle(route); const handled = await routeHandler.handle(route);
if (!this._routes.length) this._wrapApiCall(() => this._updateInterceptionPatterns(), true).catch(() => {}); if (!this._routes.length)
if (handled) return; this._wrapApiCall(() => this._updateInterceptionPatterns(), true).catch(() => {
});
if (handled)
return;
} }
// If the page is closed or unrouteAll() was called without waiting and interception disabled, await route._innerContinue(
// the method will throw an error - silence it. true
await route._innerContinue(true /* isFallback */).catch(() => {}); /* isFallback */
).catch(() => {
});
} }
async _onWebSocketRoute(webSocketRoute) { async _onWebSocketRoute(webSocketRoute) {
const routeHandler = this._webSocketRoutes.find(route => route.matches(webSocketRoute.url())); const routeHandler = this._webSocketRoutes.find((route) => route.matches(webSocketRoute.url()));
if (routeHandler) await routeHandler.handle(webSocketRoute);else webSocketRoute.connectToServer(); if (routeHandler)
await routeHandler.handle(webSocketRoute);
else
webSocketRoute.connectToServer();
} }
async _onBinding(bindingCall) { async _onBinding(bindingCall) {
const func = this._bindings.get(bindingCall._initializer.name); const func = this._bindings.get(bindingCall._initializer.name);
if (!func) return; if (!func)
return;
await bindingCall.call(func); await bindingCall.call(func);
} }
setDefaultNavigationTimeout(timeout) { setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout); this._timeoutSettings.setDefaultNavigationTimeout(timeout);
this._wrapApiCall(async () => { this._wrapApiCall(async () => {
await this._channel.setDefaultNavigationTimeoutNoReply({ await this._channel.setDefaultNavigationTimeoutNoReply({ timeout });
timeout }, true).catch(() => {
}); });
}, true).catch(() => {});
} }
setDefaultTimeout(timeout) { setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout); this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => { this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ await this._channel.setDefaultTimeoutNoReply({ timeout });
timeout }, true).catch(() => {
}); });
}, true).catch(() => {});
} }
browser() { browser() {
return this._browser; return this._browser;
@@ -253,81 +247,62 @@ class BrowserContext extends _channelOwner.ChannelOwner {
return [...this._pages]; return [...this._pages];
} }
async newPage() { async newPage() {
if (this._ownerPage) throw new Error('Please use browser.newContext()'); if (this._ownerPage)
return _page.Page.from((await this._channel.newPage()).page); throw new Error("Please use browser.newContext()");
return import_page.Page.from((await this._channel.newPage()).page);
} }
async cookies(urls) { async cookies(urls) {
if (!urls) urls = []; if (!urls)
if (urls && typeof urls === 'string') urls = [urls]; urls = [];
return (await this._channel.cookies({ if (urls && typeof urls === "string")
urls: urls urls = [urls];
})).cookies; return (await this._channel.cookies({ urls })).cookies;
} }
async addCookies(cookies) { async addCookies(cookies) {
await this._channel.addCookies({ await this._channel.addCookies({ cookies });
cookies
});
} }
async clearCookies(options = {}) { async clearCookies(options = {}) {
await this._channel.clearCookies({ await this._channel.clearCookies({
name: (0, _rtti.isString)(options.name) ? options.name : undefined, name: (0, import_rtti.isString)(options.name) ? options.name : void 0,
nameRegexSource: (0, _rtti.isRegExp)(options.name) ? options.name.source : undefined, nameRegexSource: (0, import_rtti.isRegExp)(options.name) ? options.name.source : void 0,
nameRegexFlags: (0, _rtti.isRegExp)(options.name) ? options.name.flags : undefined, nameRegexFlags: (0, import_rtti.isRegExp)(options.name) ? options.name.flags : void 0,
domain: (0, _rtti.isString)(options.domain) ? options.domain : undefined, domain: (0, import_rtti.isString)(options.domain) ? options.domain : void 0,
domainRegexSource: (0, _rtti.isRegExp)(options.domain) ? options.domain.source : undefined, domainRegexSource: (0, import_rtti.isRegExp)(options.domain) ? options.domain.source : void 0,
domainRegexFlags: (0, _rtti.isRegExp)(options.domain) ? options.domain.flags : undefined, domainRegexFlags: (0, import_rtti.isRegExp)(options.domain) ? options.domain.flags : void 0,
path: (0, _rtti.isString)(options.path) ? options.path : undefined, path: (0, import_rtti.isString)(options.path) ? options.path : void 0,
pathRegexSource: (0, _rtti.isRegExp)(options.path) ? options.path.source : undefined, pathRegexSource: (0, import_rtti.isRegExp)(options.path) ? options.path.source : void 0,
pathRegexFlags: (0, _rtti.isRegExp)(options.path) ? options.path.flags : undefined pathRegexFlags: (0, import_rtti.isRegExp)(options.path) ? options.path.flags : void 0
}); });
} }
async grantPermissions(permissions, options) { async grantPermissions(permissions, options) {
await this._channel.grantPermissions({ await this._channel.grantPermissions({ permissions, ...options });
permissions,
...options
});
} }
async clearPermissions() { async clearPermissions() {
await this._channel.clearPermissions(); await this._channel.clearPermissions();
} }
async setGeolocation(geolocation) { async setGeolocation(geolocation) {
await this._channel.setGeolocation({ await this._channel.setGeolocation({ geolocation: geolocation || void 0 });
geolocation: geolocation || undefined
});
} }
async setExtraHTTPHeaders(headers) { async setExtraHTTPHeaders(headers) {
network.validateHeaders(headers); network.validateHeaders(headers);
await this._channel.setExtraHTTPHeaders({ await this._channel.setExtraHTTPHeaders({ headers: (0, import_headers.headersObjectToArray)(headers) });
headers: (0, _headers.headersObjectToArray)(headers)
});
} }
async setOffline(offline) { async setOffline(offline) {
await this._channel.setOffline({ await this._channel.setOffline({ offline });
offline
});
} }
async setHTTPCredentials(httpCredentials) { async setHTTPCredentials(httpCredentials) {
await this._channel.setHTTPCredentials({ await this._channel.setHTTPCredentials({ httpCredentials: httpCredentials || void 0 });
httpCredentials: httpCredentials || undefined
});
} }
async addInitScript(script, arg) { async addInitScript(script, arg) {
const source = await (0, _clientHelper.evaluationScript)(this._platform, script, arg); const source = await (0, import_clientHelper.evaluationScript)(this._platform, script, arg);
await this._channel.addInitScript({ await this._channel.addInitScript({ source });
source
});
} }
async exposeBinding(name, callback, options = {}) { async exposeBinding(name, callback, options = {}) {
await this._channel.exposeBinding({ await this._channel.exposeBinding({ name, needsHandle: options.handle });
name,
needsHandle: options.handle
});
this._bindings.set(name, callback); this._bindings.set(name, callback);
} }
async exposeFunction(name, callback) { async exposeFunction(name, callback) {
await this._channel.exposeBinding({ await this._channel.exposeBinding({ name });
name
});
const binding = (source, ...args) => callback(...args); const binding = (source, ...args) => callback(...args);
this._bindings.set(name, binding); this._bindings.set(name, binding);
} }
@@ -340,94 +315,85 @@ class BrowserContext extends _channelOwner.ChannelOwner {
await this._updateWebSocketInterceptionPatterns(); await this._updateWebSocketInterceptionPatterns();
} }
async _recordIntoHAR(har, page, options = {}) { async _recordIntoHAR(har, page, options = {}) {
var _options$updateConten, _options$updateMode, _options$updateConten2; const { harId } = await this._channel.harStart({
const { page: page?._channel,
harId
} = await this._channel.harStart({
page: page === null || page === void 0 ? void 0 : page._channel,
options: prepareRecordHarOptions({ options: prepareRecordHarOptions({
path: har, path: har,
content: (_options$updateConten = options.updateContent) !== null && _options$updateConten !== void 0 ? _options$updateConten : 'attach', content: options.updateContent ?? "attach",
mode: (_options$updateMode = options.updateMode) !== null && _options$updateMode !== void 0 ? _options$updateMode : 'minimal', mode: options.updateMode ?? "minimal",
urlFilter: options.url urlFilter: options.url
}) })
}); });
this._harRecorders.set(harId, { this._harRecorders.set(harId, { path: har, content: options.updateContent ?? "attach" });
path: har,
content: (_options$updateConten2 = options.updateContent) !== null && _options$updateConten2 !== void 0 ? _options$updateConten2 : 'attach'
});
} }
async routeFromHAR(har, options = {}) { async routeFromHAR(har, options = {}) {
const localUtils = this._connection.localUtils(); const localUtils = this._connection.localUtils();
if (!localUtils) throw new Error('Route from har is not supported in thin clients'); if (!localUtils)
throw new Error("Route from har is not supported in thin clients");
if (options.update) { if (options.update) {
await this._recordIntoHAR(har, null, options); await this._recordIntoHAR(har, null, options);
return; return;
} }
const harRouter = await _harRouter.HarRouter.create(localUtils, har, options.notFound || 'abort', { const harRouter = await import_harRouter.HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url });
urlMatch: options.url
});
this._harRouters.push(harRouter); this._harRouters.push(harRouter);
await harRouter.addContextRoute(this); await harRouter.addContextRoute(this);
} }
_disposeHarRouters() { _disposeHarRouters() {
this._harRouters.forEach(router => router.dispose()); this._harRouters.forEach((router) => router.dispose());
this._harRouters = []; this._harRouters = [];
} }
async unrouteAll(options) { async unrouteAll(options) {
await this._unrouteInternal(this._routes, [], options === null || options === void 0 ? void 0 : options.behavior); await this._unrouteInternal(this._routes, [], options?.behavior);
this._disposeHarRouters(); this._disposeHarRouters();
} }
async unroute(url, handler) { async unroute(url, handler) {
const removed = []; const removed = [];
const remaining = []; const remaining = [];
for (const route of this._routes) { for (const route of this._routes) {
if ((0, _urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler)) removed.push(route);else remaining.push(route); if ((0, import_urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler))
removed.push(route);
else
remaining.push(route);
} }
await this._unrouteInternal(removed, remaining, 'default'); await this._unrouteInternal(removed, remaining, "default");
} }
async _unrouteInternal(removed, remaining, behavior) { async _unrouteInternal(removed, remaining, behavior) {
this._routes = remaining; this._routes = remaining;
await this._updateInterceptionPatterns(); await this._updateInterceptionPatterns();
if (!behavior || behavior === 'default') return; if (!behavior || behavior === "default")
const promises = removed.map(routeHandler => routeHandler.stop(behavior)); return;
const promises = removed.map((routeHandler) => routeHandler.stop(behavior));
await Promise.all(promises); await Promise.all(promises);
} }
async _updateInterceptionPatterns() { async _updateInterceptionPatterns() {
const patterns = network.RouteHandler.prepareInterceptionPatterns(this._routes); const patterns = network.RouteHandler.prepareInterceptionPatterns(this._routes);
await this._channel.setNetworkInterceptionPatterns({ await this._channel.setNetworkInterceptionPatterns({ patterns });
patterns
});
} }
async _updateWebSocketInterceptionPatterns() { async _updateWebSocketInterceptionPatterns() {
const patterns = network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes); const patterns = network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
await this._channel.setWebSocketInterceptionPatterns({ await this._channel.setWebSocketInterceptionPatterns({ patterns });
patterns
});
} }
_effectiveCloseReason() { _effectiveCloseReason() {
var _this$_browser3; return this._closeReason || this._browser?._closeReason;
return this._closeReason || ((_this$_browser3 = this._browser) === null || _this$_browser3 === void 0 ? void 0 : _this$_browser3._closeReason);
} }
async waitForEvent(event, optionsOrPredicate = {}) { async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = _waiter.Waiter.createForEvent(this, event); const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== _events.Events.BrowserContext.Close) waiter.rejectOnEvent(this, _events.Events.BrowserContext.Close, () => new _errors.TargetClosedError(this._effectiveCloseReason())); if (event !== import_events.Events.BrowserContext.Close)
waiter.rejectOnEvent(this, import_events.Events.BrowserContext.Close, () => new import_errors.TargetClosedError(this._effectiveCloseReason()));
const result = await waiter.waitForEvent(this, event, predicate); const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose(); waiter.dispose();
return result; return result;
}); });
} }
async storageState(options = {}) { async storageState(options = {}) {
const state = await this._channel.storageState({ const state = await this._channel.storageState({ indexedDB: options.indexedDB });
indexedDB: options.indexedDB
});
if (options.path) { if (options.path) {
await (0, _fileUtils.mkdirIfNeeded)(this._platform, options.path); await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8'); await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8");
} }
return state; return state;
} }
@@ -438,52 +404,43 @@ class BrowserContext extends _channelOwner.ChannelOwner {
return [...this._serviceWorkers]; return [...this._serviceWorkers];
} }
async newCDPSession(page) { async newCDPSession(page) {
// channelOwner.ts's validation messages don't handle the pseudo-union type, so we're explicit here if (!(page instanceof import_page.Page) && !(page instanceof import_frame.Frame))
if (!(page instanceof _page.Page) && !(page instanceof _frame.Frame)) throw new Error('page: expected Page or Frame'); throw new Error("page: expected Page or Frame");
const result = await this._channel.newCDPSession(page instanceof _page.Page ? { const result = await this._channel.newCDPSession(page instanceof import_page.Page ? { page: page._channel } : { frame: page._channel });
page: page._channel return import_cdpSession.CDPSession.from(result.session);
} : {
frame: page._channel
});
return _cdpSession.CDPSession.from(result.session);
} }
_onClose() { _onClose() {
var _this$_browserType; if (this._browser)
if (this._browser) this._browser._contexts.delete(this); this._browser._contexts.delete(this);
(_this$_browserType = this._browserType) === null || _this$_browserType === void 0 || (_this$_browserType = _this$_browserType._contexts) === null || _this$_browserType === void 0 || _this$_browserType.delete(this); this._browserType?._contexts?.delete(this);
this._disposeHarRouters(); this._disposeHarRouters();
this.tracing._resetStackCounter(); this.tracing._resetStackCounter();
this.emit(_events.Events.BrowserContext.Close, this); this.emit(import_events.Events.BrowserContext.Close, this);
} }
async [Symbol.asyncDispose]() { async [Symbol.asyncDispose]() {
await this.close(); await this.close();
} }
async close(options = {}) { async close(options = {}) {
if (this._closeWasCalled) return; if (this._closeWasCalled)
return;
this._closeReason = options.reason; this._closeReason = options.reason;
this._closeWasCalled = true; this._closeWasCalled = true;
await this._wrapApiCall(async () => { await this._wrapApiCall(async () => {
await this.request.dispose(options); await this.request.dispose(options);
}, true); }, true);
await this._wrapApiCall(async () => { await this._wrapApiCall(async () => {
var _this$_browserType2; await this._browserType?._willCloseContext(this);
await ((_this$_browserType2 = this._browserType) === null || _this$_browserType2 === void 0 ? void 0 : _this$_browserType2._willCloseContext(this));
for (const [harId, harParams] of this._harRecorders) { for (const [harId, harParams] of this._harRecorders) {
const har = await this._channel.harExport({ const har = await this._channel.harExport({ harId });
harId const artifact = import_artifact.Artifact.from(har.artifact);
}); const isCompressed = harParams.content === "attach" || harParams.path.endsWith(".zip");
const artifact = _artifact.Artifact.from(har.artifact); const needCompressed = harParams.path.endsWith(".zip");
// Server side will compress artifact if content is attach or if file is .zip.
const isCompressed = harParams.content === 'attach' || harParams.path.endsWith('.zip');
const needCompressed = harParams.path.endsWith('.zip');
if (isCompressed && !needCompressed) { if (isCompressed && !needCompressed) {
const localUtils = this._connection.localUtils(); const localUtils = this._connection.localUtils();
if (!localUtils) throw new Error('Uncompressed har is not supported in thin clients'); if (!localUtils)
await artifact.saveAs(harParams.path + '.tmp'); throw new Error("Uncompressed har is not supported in thin clients");
await localUtils.harUnzip({ await artifact.saveAs(harParams.path + ".tmp");
zipFile: harParams.path + '.tmp', await localUtils.harUnzip({ zipFile: harParams.path + ".tmp", harFile: harParams.path });
harFile: harParams.path
});
} else { } else {
await artifact.saveAs(harParams.path); await artifact.saveAs(harParams.path);
} }
@@ -497,42 +454,46 @@ class BrowserContext extends _channelOwner.ChannelOwner {
await this._channel.enableRecorder(params); await this._channel.enableRecorder(params);
} }
} }
exports.BrowserContext = BrowserContext;
async function prepareStorageState(platform, options) { async function prepareStorageState(platform, options) {
if (typeof options.storageState !== 'string') return options.storageState; if (typeof options.storageState !== "string")
return options.storageState;
try { try {
return JSON.parse(await platform.fs().promises.readFile(options.storageState, 'utf8')); return JSON.parse(await platform.fs().promises.readFile(options.storageState, "utf8"));
} catch (e) { } catch (e) {
(0, _stackTrace.rewriteErrorMessage)(e, `Error reading storage state from ${options.storageState}:\n` + e.message); (0, import_stackTrace.rewriteErrorMessage)(e, `Error reading storage state from ${options.storageState}:
` + e.message);
throw e; throw e;
} }
} }
function prepareRecordHarOptions(options) { function prepareRecordHarOptions(options) {
if (!options) return; if (!options)
return;
return { return {
path: options.path, path: options.path,
content: options.content || (options.omitContent ? 'omit' : undefined), content: options.content || (options.omitContent ? "omit" : void 0),
urlGlob: (0, _rtti.isString)(options.urlFilter) ? options.urlFilter : undefined, urlGlob: (0, import_rtti.isString)(options.urlFilter) ? options.urlFilter : void 0,
urlRegexSource: (0, _rtti.isRegExp)(options.urlFilter) ? options.urlFilter.source : undefined, urlRegexSource: (0, import_rtti.isRegExp)(options.urlFilter) ? options.urlFilter.source : void 0,
urlRegexFlags: (0, _rtti.isRegExp)(options.urlFilter) ? options.urlFilter.flags : undefined, urlRegexFlags: (0, import_rtti.isRegExp)(options.urlFilter) ? options.urlFilter.flags : void 0,
mode: options.mode mode: options.mode
}; };
} }
async function prepareBrowserContextParams(platform, options) { async function prepareBrowserContextParams(platform, options) {
if (options.videoSize && !options.videosPath) throw new Error(`"videoSize" option requires "videosPath" to be specified`); if (options.videoSize && !options.videosPath)
if (options.extraHTTPHeaders) network.validateHeaders(options.extraHTTPHeaders); throw new Error(`"videoSize" option requires "videosPath" to be specified`);
if (options.extraHTTPHeaders)
network.validateHeaders(options.extraHTTPHeaders);
const contextParams = { const contextParams = {
...options, ...options,
viewport: options.viewport === null ? undefined : options.viewport, viewport: options.viewport === null ? void 0 : options.viewport,
noDefaultViewport: options.viewport === null, noDefaultViewport: options.viewport === null,
extraHTTPHeaders: options.extraHTTPHeaders ? (0, _headers.headersObjectToArray)(options.extraHTTPHeaders) : undefined, extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.headersObjectToArray)(options.extraHTTPHeaders) : void 0,
storageState: await prepareStorageState(platform, options), storageState: await prepareStorageState(platform, options),
serviceWorkers: options.serviceWorkers, serviceWorkers: options.serviceWorkers,
recordHar: prepareRecordHarOptions(options.recordHar), recordHar: prepareRecordHarOptions(options.recordHar),
colorScheme: options.colorScheme === null ? 'no-override' : options.colorScheme, colorScheme: options.colorScheme === null ? "no-override" : options.colorScheme,
reducedMotion: options.reducedMotion === null ? 'no-override' : options.reducedMotion, reducedMotion: options.reducedMotion === null ? "no-override" : options.reducedMotion,
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors, forcedColors: options.forcedColors === null ? "no-override" : options.forcedColors,
contrast: options.contrast === null ? 'no-override' : options.contrast, contrast: options.contrast === null ? "no-override" : options.contrast,
acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads), acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
clientCertificates: await toClientCertificatesProtocol(platform, options.clientCertificates) clientCertificates: await toClientCertificatesProtocol(platform, options.clientCertificates)
}; };
@@ -542,21 +503,27 @@ async function prepareBrowserContextParams(platform, options) {
size: options.videoSize size: options.videoSize
}; };
} }
if (contextParams.recordVideo && contextParams.recordVideo.dir) contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir); if (contextParams.recordVideo && contextParams.recordVideo.dir)
contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir);
return contextParams; return contextParams;
} }
function toAcceptDownloadsProtocol(acceptDownloads) { function toAcceptDownloadsProtocol(acceptDownloads) {
if (acceptDownloads === undefined) return undefined; if (acceptDownloads === void 0)
if (acceptDownloads) return 'accept'; return void 0;
return 'deny'; if (acceptDownloads)
return "accept";
return "deny";
} }
async function toClientCertificatesProtocol(platform, certs) { async function toClientCertificatesProtocol(platform, certs) {
if (!certs) return undefined; if (!certs)
return void 0;
const bufferizeContent = async (value, path) => { const bufferizeContent = async (value, path) => {
if (value) return value; if (value)
if (path) return await platform.fs().promises.readFile(path); return value;
if (path)
return await platform.fs().promises.readFile(path);
}; };
return await Promise.all(certs.map(async cert => ({ return await Promise.all(certs.map(async (cert) => ({
origin: cert.origin, origin: cert.origin,
cert: await bufferizeContent(cert.cert, cert.certPath), cert: await bufferizeContent(cert.cert, cert.certPath),
key: await bufferizeContent(cert.key, cert.keyPath), key: await bufferizeContent(cert.key, cert.keyPath),
@@ -564,3 +531,9 @@ async function toClientCertificatesProtocol(platform, certs) {
passphrase: cert.passphrase passphrase: cert.passphrase
}))); })));
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BrowserContext,
prepareBrowserContextParams,
toClientCertificatesProtocol
});

View File

@@ -1,160 +1,138 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var browserType_exports = {};
__export(browserType_exports, {
BrowserType: () => BrowserType
}); });
exports.BrowserType = void 0; module.exports = __toCommonJS(browserType_exports);
var _browser3 = require("./browser"); var import_browser = require("./browser");
var _browserContext = require("./browserContext"); var import_browserContext = require("./browserContext");
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _clientHelper = require("./clientHelper"); var import_clientHelper = require("./clientHelper");
var _events = require("./events"); var import_events = require("./events");
var _assert = require("../utils/isomorphic/assert"); var import_assert = require("../utils/isomorphic/assert");
var _headers = require("../utils/isomorphic/headers"); var import_headers = require("../utils/isomorphic/headers");
var _time = require("../utils/isomorphic/time"); var import_time = require("../utils/isomorphic/time");
var _timeoutRunner = require("../utils/isomorphic/timeoutRunner"); var import_timeoutRunner = require("../utils/isomorphic/timeoutRunner");
var _webSocket = require("./webSocket"); var import_webSocket = require("./webSocket");
/** class BrowserType extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation. constructor() {
* super(...arguments);
* Licensed under the Apache License, Version 2.0 (the "License"); this._contexts = /* @__PURE__ */ new Set();
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This is here just for api generation and checking.
class BrowserType extends _channelOwner.ChannelOwner {
constructor(...args) {
super(...args);
this._serverLauncher = void 0;
this._contexts = new Set();
this._playwright = void 0;
} }
static from(browserType) { static from(browserType) {
return browserType._object; return browserType._object;
} }
executablePath() { executablePath() {
if (!this._initializer.executablePath) throw new Error('Browser is not supported on current platform'); if (!this._initializer.executablePath)
throw new Error("Browser is not supported on current platform");
return this._initializer.executablePath; return this._initializer.executablePath;
} }
name() { name() {
return this._initializer.name; return this._initializer.name;
} }
async launch(options = {}) { async launch(options = {}) {
var _this$_playwright$_de; (0, import_assert.assert)(!options.userDataDir, "userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead");
(0, _assert.assert)(!options.userDataDir, 'userDataDir option is not supported in `browserType.launch`. Use `browserType.launchPersistentContext` instead'); (0, import_assert.assert)(!options.port, "Cannot specify a port without launching as a server.");
(0, _assert.assert)(!options.port, 'Cannot specify a port without launching as a server.'); const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
const logger = options.logger || ((_this$_playwright$_de = this._playwright._defaultLaunchOptions) === null || _this$_playwright$_de === void 0 ? void 0 : _this$_playwright$_de.logger); options = { ...this._playwright._defaultLaunchOptions, ...options };
options = {
...this._playwright._defaultLaunchOptions,
...options
};
const launchOptions = { const launchOptions = {
...options, ...options,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined, ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? (0, _clientHelper.envObjectToArray)(options.env) : undefined env: options.env ? (0, import_clientHelper.envObjectToArray)(options.env) : void 0
}; };
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const browser = _browser3.Browser.from((await this._channel.launch(launchOptions)).browser); const browser = import_browser.Browser.from((await this._channel.launch(launchOptions)).browser);
this._didLaunchBrowser(browser, options, logger); this._didLaunchBrowser(browser, options, logger);
return browser; return browser;
}); });
} }
async launchServer(options = {}) { async launchServer(options = {}) {
if (!this._serverLauncher) throw new Error('Launching server is not supported'); if (!this._serverLauncher)
options = { throw new Error("Launching server is not supported");
...this._playwright._defaultLaunchOptions, options = { ...this._playwright._defaultLaunchOptions, ...options };
...options
};
return await this._serverLauncher.launchServer(options); return await this._serverLauncher.launchServer(options);
} }
async launchPersistentContext(userDataDir, options = {}) { async launchPersistentContext(userDataDir, options = {}) {
var _this$_playwright$_de2; const logger = options.logger || this._playwright._defaultLaunchOptions?.logger;
const logger = options.logger || ((_this$_playwright$_de2 = this._playwright._defaultLaunchOptions) === null || _this$_playwright$_de2 === void 0 ? void 0 : _this$_playwright$_de2.logger); (0, import_assert.assert)(!options.port, "Cannot specify a port without launching as a server.");
(0, _assert.assert)(!options.port, 'Cannot specify a port without launching as a server.'); options = { ...this._playwright._defaultLaunchOptions, ...this._playwright._defaultContextOptions, ...options };
options = { const contextParams = await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options);
...this._playwright._defaultLaunchOptions,
...this._playwright._defaultContextOptions,
...options
};
const contextParams = await (0, _browserContext.prepareBrowserContextParams)(this._platform, options);
const persistentParams = { const persistentParams = {
...contextParams, ...contextParams,
ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : undefined, ignoreDefaultArgs: Array.isArray(options.ignoreDefaultArgs) ? options.ignoreDefaultArgs : void 0,
ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs), ignoreAllDefaultArgs: !!options.ignoreDefaultArgs && !Array.isArray(options.ignoreDefaultArgs),
env: options.env ? (0, _clientHelper.envObjectToArray)(options.env) : undefined, env: options.env ? (0, import_clientHelper.envObjectToArray)(options.env) : void 0,
channel: options.channel, channel: options.channel,
userDataDir: this._platform.path().isAbsolute(userDataDir) || !userDataDir ? userDataDir : this._platform.path().resolve(userDataDir) userDataDir: this._platform.path().isAbsolute(userDataDir) || !userDataDir ? userDataDir : this._platform.path().resolve(userDataDir)
}; };
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const result = await this._channel.launchPersistentContext(persistentParams); const result = await this._channel.launchPersistentContext(persistentParams);
const context = _browserContext.BrowserContext.from(result.context); const context = import_browserContext.BrowserContext.from(result.context);
await this._didCreateContext(context, contextParams, options, logger); await this._didCreateContext(context, contextParams, options, logger);
return context; return context;
}); });
} }
async connect(optionsOrWsEndpoint, options) { async connect(optionsOrWsEndpoint, options) {
if (typeof optionsOrWsEndpoint === 'string') return await this._connect({ if (typeof optionsOrWsEndpoint === "string")
...options, return await this._connect({ ...options, wsEndpoint: optionsOrWsEndpoint });
wsEndpoint: optionsOrWsEndpoint (0, import_assert.assert)(optionsOrWsEndpoint.wsEndpoint, "options.wsEndpoint is required");
});
(0, _assert.assert)(optionsOrWsEndpoint.wsEndpoint, 'options.wsEndpoint is required');
return await this._connect(optionsOrWsEndpoint); return await this._connect(optionsOrWsEndpoint);
} }
async _connect(params) { async _connect(params) {
const logger = params.logger; const logger = params.logger;
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
var _params$exposeNetwork; const deadline = params.timeout ? (0, import_time.monotonicTime)() + params.timeout : 0;
const deadline = params.timeout ? (0, _time.monotonicTime)() + params.timeout : 0; const headers = { "x-playwright-browser": this.name(), ...params.headers };
const headers = {
'x-playwright-browser': this.name(),
...params.headers
};
const connectParams = { const connectParams = {
wsEndpoint: params.wsEndpoint, wsEndpoint: params.wsEndpoint,
headers, headers,
exposeNetwork: (_params$exposeNetwork = params.exposeNetwork) !== null && _params$exposeNetwork !== void 0 ? _params$exposeNetwork : params._exposeNetwork, exposeNetwork: params.exposeNetwork ?? params._exposeNetwork,
slowMo: params.slowMo, slowMo: params.slowMo,
timeout: params.timeout timeout: params.timeout
}; };
if (params.__testHookRedirectPortForwarding) connectParams.socksProxyRedirectPortForTest = params.__testHookRedirectPortForwarding; if (params.__testHookRedirectPortForwarding)
const connection = await (0, _webSocket.connectOverWebSocket)(this._connection, connectParams); connectParams.socksProxyRedirectPortForTest = params.__testHookRedirectPortForwarding;
const connection = await (0, import_webSocket.connectOverWebSocket)(this._connection, connectParams);
let browser; let browser;
connection.on('close', () => { connection.on("close", () => {
// Emulate all pages, contexts and the browser closing upon disconnect. for (const context of browser?.contexts() || []) {
for (const context of ((_browser = browser) === null || _browser === void 0 ? void 0 : _browser.contexts()) || []) { for (const page of context.pages())
var _browser; page._onClose();
for (const page of context.pages()) page._onClose();
context._onClose(); context._onClose();
} }
setTimeout(() => { setTimeout(() => browser?._didClose(), 0);
var _browser2;
return (_browser2 = browser) === null || _browser2 === void 0 ? void 0 : _browser2._didClose();
}, 0);
}); });
const result = await (0, _timeoutRunner.raceAgainstDeadline)(async () => { const result = await (0, import_timeoutRunner.raceAgainstDeadline)(async () => {
// For tests. if (params.__testHookBeforeCreateBrowser)
if (params.__testHookBeforeCreateBrowser) await params.__testHookBeforeCreateBrowser(); await params.__testHookBeforeCreateBrowser();
const playwright = await connection.initializePlaywright(); const playwright = await connection.initializePlaywright();
if (!playwright._initializer.preLaunchedBrowser) { if (!playwright._initializer.preLaunchedBrowser) {
connection.close(); connection.close();
throw new Error('Malformed endpoint. Did you use BrowserType.launchServer method?'); throw new Error("Malformed endpoint. Did you use BrowserType.launchServer method?");
} }
playwright._setSelectors(this._playwright.selectors); playwright._setSelectors(this._playwright.selectors);
browser = _browser3.Browser.from(playwright._initializer.preLaunchedBrowser); browser = import_browser.Browser.from(playwright._initializer.preLaunchedBrowser);
this._didLaunchBrowser(browser, {}, logger); this._didLaunchBrowser(browser, {}, logger);
browser._shouldCloseConnectionOnClose = true; browser._shouldCloseConnectionOnClose = true;
browser.on(_events.Events.Browser.Disconnected, () => connection.close()); browser.on(import_events.Events.Browser.Disconnected, () => connection.close());
return browser; return browser;
}, deadline); }, deadline);
if (!result.timedOut) { if (!result.timedOut) {
@@ -166,23 +144,26 @@ class BrowserType extends _channelOwner.ChannelOwner {
}); });
} }
async connectOverCDP(endpointURLOrOptions, options) { async connectOverCDP(endpointURLOrOptions, options) {
if (typeof endpointURLOrOptions === 'string') return await this._connectOverCDP(endpointURLOrOptions, options); if (typeof endpointURLOrOptions === "string")
const endpointURL = 'endpointURL' in endpointURLOrOptions ? endpointURLOrOptions.endpointURL : endpointURLOrOptions.wsEndpoint; return await this._connectOverCDP(endpointURLOrOptions, options);
(0, _assert.assert)(endpointURL, 'Cannot connect over CDP without wsEndpoint.'); const endpointURL = "endpointURL" in endpointURLOrOptions ? endpointURLOrOptions.endpointURL : endpointURLOrOptions.wsEndpoint;
(0, import_assert.assert)(endpointURL, "Cannot connect over CDP without wsEndpoint.");
return await this.connectOverCDP(endpointURL, endpointURLOrOptions); return await this.connectOverCDP(endpointURL, endpointURLOrOptions);
} }
async _connectOverCDP(endpointURL, params = {}) { async _connectOverCDP(endpointURL, params = {}) {
if (this.name() !== 'chromium') throw new Error('Connecting over CDP is only supported in Chromium.'); if (this.name() !== "chromium")
const headers = params.headers ? (0, _headers.headersObjectToArray)(params.headers) : undefined; throw new Error("Connecting over CDP is only supported in Chromium.");
const headers = params.headers ? (0, import_headers.headersObjectToArray)(params.headers) : void 0;
const result = await this._channel.connectOverCDP({ const result = await this._channel.connectOverCDP({
endpointURL, endpointURL,
headers, headers,
slowMo: params.slowMo, slowMo: params.slowMo,
timeout: params.timeout timeout: params.timeout
}); });
const browser = _browser3.Browser.from(result.browser); const browser = import_browser.Browser.from(result.browser);
this._didLaunchBrowser(browser, {}, params.logger); this._didLaunchBrowser(browser, {}, params.logger);
if (result.defaultContext) await this._didCreateContext(_browserContext.BrowserContext.from(result.defaultContext), {}, {}, params.logger); if (result.defaultContext)
await this._didCreateContext(import_browserContext.BrowserContext.from(result.defaultContext), {}, {}, params.logger);
return browser; return browser;
} }
_didLaunchBrowser(browser, browserOptions, logger) { _didLaunchBrowser(browser, browserOptions, logger) {
@@ -195,8 +176,10 @@ class BrowserType extends _channelOwner.ChannelOwner {
context._browserType = this; context._browserType = this;
this._contexts.add(context); this._contexts.add(context);
context._setOptions(contextOptions, browserOptions); context._setOptions(contextOptions, browserOptions);
if (this._playwright._defaultContextTimeout !== undefined) context.setDefaultTimeout(this._playwright._defaultContextTimeout); if (this._playwright._defaultContextTimeout !== void 0)
if (this._playwright._defaultContextNavigationTimeout !== undefined) context.setDefaultNavigationTimeout(this._playwright._defaultContextNavigationTimeout); context.setDefaultTimeout(this._playwright._defaultContextTimeout);
if (this._playwright._defaultContextNavigationTimeout !== void 0)
context.setDefaultNavigationTimeout(this._playwright._defaultContextNavigationTimeout);
await this._instrumentation.runAfterCreateBrowserContext(context); await this._instrumentation.runAfterCreateBrowserContext(context);
} }
async _willCloseContext(context) { async _willCloseContext(context) {
@@ -204,4 +187,7 @@ class BrowserType extends _channelOwner.ChannelOwner {
await this._instrumentation.runBeforeCloseBrowserContext(context); await this._instrumentation.runBeforeCloseBrowserContext(context);
} }
} }
exports.BrowserType = BrowserType; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BrowserType
});

View File

@@ -1,36 +1,34 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var cdpSession_exports = {};
__export(cdpSession_exports, {
CDPSession: () => CDPSession
}); });
exports.CDPSession = void 0; module.exports = __toCommonJS(cdpSession_exports);
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
/** class CDPSession extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class CDPSession extends _channelOwner.ChannelOwner {
static from(cdpSession) { static from(cdpSession) {
return cdpSession._object; return cdpSession._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._channel.on('event', ({ this._channel.on("event", ({ method, params }) => {
method,
params
}) => {
this.emit(method, params); this.emit(method, params);
}); });
this.on = super.on; this.on = super.on;
@@ -40,14 +38,14 @@ class CDPSession extends _channelOwner.ChannelOwner {
this.once = super.once; this.once = super.once;
} }
async send(method, params) { async send(method, params) {
const result = await this._channel.send({ const result = await this._channel.send({ method, params });
method,
params
});
return result.result; return result.result;
} }
async detach() { async detach() {
return await this._channel.detach(); return await this._channel.detach();
} }
} }
exports.CDPSession = CDPSession; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CDPSession
});

View File

@@ -1,57 +1,50 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var channelOwner_exports = {};
__export(channelOwner_exports, {
ChannelOwner: () => ChannelOwner
}); });
exports.ChannelOwner = void 0; module.exports = __toCommonJS(channelOwner_exports);
var _eventEmitter = require("./eventEmitter"); var import_eventEmitter = require("./eventEmitter");
var _validator = require("../protocol/validator"); var import_validator = require("../protocol/validator");
var _clientStackTrace = require("./clientStackTrace"); var import_clientStackTrace = require("./clientStackTrace");
var _stackTrace = require("../utils/isomorphic/stackTrace"); var import_stackTrace = require("../utils/isomorphic/stackTrace");
/** class ChannelOwner extends import_eventEmitter.EventEmitter {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ChannelOwner extends _eventEmitter.EventEmitter {
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
const connection = parent instanceof ChannelOwner ? parent._connection : parent; const connection = parent instanceof ChannelOwner ? parent._connection : parent;
super(connection._platform); super(connection._platform);
this._connection = void 0; this._objects = /* @__PURE__ */ new Map();
this._parent = void 0; this._eventToSubscriptionMapping = /* @__PURE__ */ new Map();
this._objects = new Map();
this._type = void 0;
this._guid = void 0;
this._channel = void 0;
this._initializer = void 0;
this._logger = void 0;
this._instrumentation = void 0;
this._eventToSubscriptionMapping = new Map();
this._isInternalType = false; this._isInternalType = false;
this._wasCollected = false; this._wasCollected = false;
this.setMaxListeners(0); this.setMaxListeners(0);
this._connection = connection; this._connection = connection;
this._type = type; this._type = type;
this._guid = guid; this._guid = guid;
this._parent = parent instanceof ChannelOwner ? parent : undefined; this._parent = parent instanceof ChannelOwner ? parent : void 0;
this._instrumentation = this._connection._instrumentation; this._instrumentation = this._connection._instrumentation;
this._connection._objects.set(guid, this); this._connection._objects.set(guid, this);
if (this._parent) { if (this._parent) {
this._parent._objects.set(guid, this); this._parent._objects.set(guid, this);
this._logger = this._parent._logger; this._logger = this._parent._logger;
} }
this._channel = this._createChannel(new _eventEmitter.EventEmitter(connection._platform)); this._channel = this._createChannel(new import_eventEmitter.EventEmitter(connection._platform));
this._initializer = initializer; this._initializer = initializer;
} }
markAsInternalType() { markAsInternalType() {
@@ -64,36 +57,39 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
const protocolEvent = this._eventToSubscriptionMapping.get(String(event)); const protocolEvent = this._eventToSubscriptionMapping.get(String(event));
if (protocolEvent) { if (protocolEvent) {
this._wrapApiCall(async () => { this._wrapApiCall(async () => {
await this._channel.updateSubscription({ await this._channel.updateSubscription({ event: protocolEvent, enabled });
event: protocolEvent, }, true).catch(() => {
enabled
}); });
}, true).catch(() => {});
} }
} }
on(event, listener) { on(event, listener) {
if (!this.listenerCount(event)) this._updateSubscription(event, true); if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.on(event, listener); super.on(event, listener);
return this; return this;
} }
addListener(event, listener) { addListener(event, listener) {
if (!this.listenerCount(event)) this._updateSubscription(event, true); if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.addListener(event, listener); super.addListener(event, listener);
return this; return this;
} }
prependListener(event, listener) { prependListener(event, listener) {
if (!this.listenerCount(event)) this._updateSubscription(event, true); if (!this.listenerCount(event))
this._updateSubscription(event, true);
super.prependListener(event, listener); super.prependListener(event, listener);
return this; return this;
} }
off(event, listener) { off(event, listener) {
super.off(event, listener); super.off(event, listener);
if (!this.listenerCount(event)) this._updateSubscription(event, false); if (!this.listenerCount(event))
this._updateSubscription(event, false);
return this; return this;
} }
removeListener(event, listener) { removeListener(event, listener) {
super.removeListener(event, listener); super.removeListener(event, listener);
if (!this.listenerCount(event)) this._updateSubscription(event, false); if (!this.listenerCount(event))
this._updateSubscription(event, false);
return this; return this;
} }
_adopt(child) { _adopt(child) {
@@ -102,48 +98,44 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
child._parent = this; child._parent = this;
} }
_dispose(reason) { _dispose(reason) {
// Clean up from parent and connection. if (this._parent)
if (this._parent) this._parent._objects.delete(this._guid); this._parent._objects.delete(this._guid);
this._connection._objects.delete(this._guid); this._connection._objects.delete(this._guid);
this._wasCollected = reason === 'gc'; this._wasCollected = reason === "gc";
for (const object of [...this._objects.values()])
// Dispose all children. object._dispose(reason);
for (const object of [...this._objects.values()]) object._dispose(reason);
this._objects.clear(); this._objects.clear();
} }
_debugScopeState() { _debugScopeState() {
return { return {
_guid: this._guid, _guid: this._guid,
objects: Array.from(this._objects.values()).map(o => o._debugScopeState()) objects: Array.from(this._objects.values()).map((o) => o._debugScopeState())
}; };
} }
_validatorToWireContext() { _validatorToWireContext() {
return { return {
tChannelImpl: tChannelImplToWire, tChannelImpl: tChannelImplToWire,
binary: this._connection.rawBuffers() ? 'buffer' : 'toBase64', binary: this._connection.rawBuffers() ? "buffer" : "toBase64",
isUnderTest: () => this._platform.isUnderTest() isUnderTest: () => this._platform.isUnderTest()
}; };
} }
_createChannel(base) { _createChannel(base) {
const channel = new Proxy(base, { const channel = new Proxy(base, {
get: (obj, prop) => { get: (obj, prop) => {
if (typeof prop === 'string') { if (typeof prop === "string") {
const validator = (0, _validator.maybeFindValidator)(this._type, prop, 'Params'); const validator = (0, import_validator.maybeFindValidator)(this._type, prop, "Params");
if (validator) { if (validator) {
return async params => { return async (params) => {
return await this._wrapApiCall(async apiZone => { return await this._wrapApiCall(async (apiZone) => {
const validatedParams = validator(params, '', this._validatorToWireContext()); const validatedParams = validator(params, "", this._validatorToWireContext());
if (!apiZone.isInternal && !apiZone.reported) { if (!apiZone.isInternal && !apiZone.reported) {
// Reporting/tracing/logging this api call for the first time.
apiZone.params = params; apiZone.params = params;
apiZone.reported = true; apiZone.reported = true;
this._instrumentation.onApiCallBegin(apiZone); this._instrumentation.onApiCallBegin(apiZone);
logApiCall(this._platform, this._logger, `=> ${apiZone.apiName} started`); logApiCall(this._platform, this._logger, `=> ${apiZone.apiName} started`);
return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone.apiName, apiZone.frames, apiZone.stepId); return await this._connection.sendMessageToServer(this, prop, validatedParams, apiZone.apiName, apiZone.frames, apiZone.stepId);
} }
// Since this api call is either internal, or has already been reported/traced once, return await this._connection.sendMessageToServer(this, prop, validatedParams, void 0, [], void 0);
// passing undefined apiName will avoid an extra unneeded tracing entry.
return await this._connection.sendMessageToServer(this, prop, validatedParams, undefined, [], undefined);
}); });
}; };
} }
@@ -157,17 +149,12 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
async _wrapApiCall(func, isInternal) { async _wrapApiCall(func, isInternal) {
const logger = this._logger; const logger = this._logger;
const existingApiZone = this._platform.zones.current().data(); const existingApiZone = this._platform.zones.current().data();
if (existingApiZone) return await func(existingApiZone); if (existingApiZone)
if (isInternal === undefined) isInternal = this._isInternalType; return await func(existingApiZone);
const stackTrace = (0, _clientStackTrace.captureLibraryStackTrace)(this._platform); if (isInternal === void 0)
const apiZone = { isInternal = this._isInternalType;
apiName: stackTrace.apiName, const stackTrace = (0, import_clientStackTrace.captureLibraryStackTrace)(this._platform);
frames: stackTrace.frames, const apiZone = { apiName: stackTrace.apiName, frames: stackTrace.frames, isInternal, reported: false, userData: void 0, stepId: void 0 };
isInternal,
reported: false,
userData: undefined,
stepId: undefined
};
try { try {
const result = await this._platform.zones.current().push(apiZone).run(async () => await func(apiZone)); const result = await this._platform.zones.current().push(apiZone).run(async () => await func(apiZone));
if (!isInternal) { if (!isInternal) {
@@ -176,10 +163,14 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
} }
return result; return result;
} catch (e) { } catch (e) {
const innerError = (this._platform.showInternalStackFrames() || this._platform.isUnderTest()) && e.stack ? '\n<inner error>\n' + e.stack : ''; const innerError = (this._platform.showInternalStackFrames() || this._platform.isUnderTest()) && e.stack ? "\n<inner error>\n" + e.stack : "";
if (apiZone.apiName && !apiZone.apiName.includes('<anonymous>')) e.message = apiZone.apiName + ': ' + e.message; if (apiZone.apiName && !apiZone.apiName.includes("<anonymous>"))
const stackFrames = '\n' + (0, _stackTrace.stringifyStackFrames)(stackTrace.frames).join('\n') + innerError; e.message = apiZone.apiName + ": " + e.message;
if (stackFrames.trim()) e.stack = e.message + stackFrames;else e.stack = ''; const stackFrames = "\n" + (0, import_stackTrace.stringifyStackFrames)(stackTrace.frames).join("\n") + innerError;
if (stackFrames.trim())
e.stack = e.message + stackFrames;
else
e.stack = "";
if (!isInternal) { if (!isInternal) {
apiZone.error = e; apiZone.error = e;
logApiCall(this._platform, logger, `<= ${apiZone.apiName} failed`); logApiCall(this._platform, logger, `<= ${apiZone.apiName} failed`);
@@ -189,30 +180,26 @@ class ChannelOwner extends _eventEmitter.EventEmitter {
} }
} }
_toImpl() { _toImpl() {
var _this$_connection$toI, _this$_connection; return this._connection.toImpl?.(this);
return (_this$_connection$toI = (_this$_connection = this._connection).toImpl) === null || _this$_connection$toI === void 0 ? void 0 : _this$_connection$toI.call(_this$_connection, this);
} }
toJSON() { toJSON() {
// Jest's expect library tries to print objects sometimes.
// RPC objects can contain links to lots of other objects,
// which can cause jest to crash. Let's help it out
// by just returning the important values.
return { return {
_type: this._type, _type: this._type,
_guid: this._guid _guid: this._guid
}; };
} }
} }
exports.ChannelOwner = ChannelOwner;
function logApiCall(platform, logger, message) { function logApiCall(platform, logger, message) {
if (logger && logger.isEnabled('api', 'info')) logger.log('api', 'info', message, [], { if (logger && logger.isEnabled("api", "info"))
color: 'cyan' logger.log("api", "info", message, [], { color: "cyan" });
}); platform.log("api", message);
platform.log('api', message);
} }
function tChannelImplToWire(names, arg, path, context) { function tChannelImplToWire(names, arg, path, context) {
if (arg._object instanceof ChannelOwner && (names === '*' || names.includes(arg._object._type))) return { if (arg._object instanceof ChannelOwner && (names === "*" || names.includes(arg._object._type)))
guid: arg._object._guid return { guid: arg._object._guid };
}; throw new import_validator.ValidationError(`${path}: expected channel ${names.toString()}`);
throw new _validator.ValidationError(`${path}: expected channel ${names.toString()}`);
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ChannelOwner
});

View File

@@ -1,55 +1,64 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var clientHelper_exports = {};
__export(clientHelper_exports, {
addSourceUrlToScript: () => addSourceUrlToScript,
envObjectToArray: () => envObjectToArray,
evaluationScript: () => evaluationScript
}); });
exports.addSourceUrlToScript = addSourceUrlToScript; module.exports = __toCommonJS(clientHelper_exports);
exports.envObjectToArray = envObjectToArray; var import_rtti = require("../utils/isomorphic/rtti");
exports.evaluationScript = evaluationScript;
var _rtti = require("../utils/isomorphic/rtti");
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function envObjectToArray(env) { function envObjectToArray(env) {
const result = []; const result = [];
for (const name in env) { for (const name in env) {
if (!Object.is(env[name], undefined)) result.push({ if (!Object.is(env[name], void 0))
name, result.push({ name, value: String(env[name]) });
value: String(env[name])
});
} }
return result; return result;
} }
async function evaluationScript(platform, fun, arg, addSourceUrl = true) { async function evaluationScript(platform, fun, arg, addSourceUrl = true) {
if (typeof fun === 'function') { if (typeof fun === "function") {
const source = fun.toString(); const source = fun.toString();
const argString = Object.is(arg, undefined) ? 'undefined' : JSON.stringify(arg); const argString = Object.is(arg, void 0) ? "undefined" : JSON.stringify(arg);
return `(${source})(${argString})`; return `(${source})(${argString})`;
} }
if (arg !== undefined) throw new Error('Cannot evaluate a string with arguments'); if (arg !== void 0)
if ((0, _rtti.isString)(fun)) return fun; throw new Error("Cannot evaluate a string with arguments");
if (fun.content !== undefined) return fun.content; if ((0, import_rtti.isString)(fun))
if (fun.path !== undefined) { return fun;
let source = await platform.fs().promises.readFile(fun.path, 'utf8'); if (fun.content !== void 0)
if (addSourceUrl) source = addSourceUrlToScript(source, fun.path); return fun.content;
if (fun.path !== void 0) {
let source = await platform.fs().promises.readFile(fun.path, "utf8");
if (addSourceUrl)
source = addSourceUrlToScript(source, fun.path);
return source; return source;
} }
throw new Error('Either path or content property must be present'); throw new Error("Either path or content property must be present");
} }
function addSourceUrlToScript(source, path) { function addSourceUrlToScript(source, path) {
return `${source}\n//# sourceURL=${path.replace(/\n/g, '')}`; return `${source}
//# sourceURL=${path.replace(/\n/g, "")}`;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
addSourceUrlToScript,
envObjectToArray,
evaluationScript
});

View File

@@ -1,52 +1,55 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var clientInstrumentation_exports = {};
__export(clientInstrumentation_exports, {
createInstrumentation: () => createInstrumentation
}); });
exports.createInstrumentation = createInstrumentation; module.exports = __toCommonJS(clientInstrumentation_exports);
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Instrumentation can mutate the data, for example change apiName or stepId.
function createInstrumentation() { function createInstrumentation() {
const listeners = []; const listeners = [];
return new Proxy({}, { return new Proxy({}, {
get: (obj, prop) => { get: (obj, prop) => {
if (typeof prop !== 'string') return obj[prop]; if (typeof prop !== "string")
if (prop === 'addListener') return listener => listeners.push(listener); return obj[prop];
if (prop === 'removeListener') return listener => listeners.splice(listeners.indexOf(listener), 1); if (prop === "addListener")
if (prop === 'removeAllListeners') return () => listeners.splice(0, listeners.length); return (listener) => listeners.push(listener);
if (prop.startsWith('run')) { if (prop === "removeListener")
return (listener) => listeners.splice(listeners.indexOf(listener), 1);
if (prop === "removeAllListeners")
return () => listeners.splice(0, listeners.length);
if (prop.startsWith("run")) {
return async (...params) => { return async (...params) => {
for (const listener of listeners) { for (const listener of listeners)
var _prop, _ref; await listener[prop]?.(...params);
await ((_prop = (_ref = listener)[prop]) === null || _prop === void 0 ? void 0 : _prop.call(_ref, ...params));
}
}; };
} }
if (prop.startsWith('on')) { if (prop.startsWith("on")) {
return (...params) => { return (...params) => {
for (const listener of listeners) { for (const listener of listeners)
var _prop2, _ref2; listener[prop]?.(...params);
(_prop2 = (_ref2 = listener)[prop]) === null || _prop2 === void 0 || _prop2.call(_ref2, ...params);
}
}; };
} }
return obj[prop]; return obj[prop];
} }
}); });
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createInstrumentation
});

View File

@@ -1,32 +1,32 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var clock_exports = {};
__export(clock_exports, {
Clock: () => Clock
}); });
exports.Clock = void 0; module.exports = __toCommonJS(clock_exports);
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Clock { class Clock {
constructor(browserContext) { constructor(browserContext) {
this._browserContext = void 0;
this._browserContext = browserContext; this._browserContext = browserContext;
} }
async install(options = {}) { async install(options = {}) {
await this._browserContext._channel.clockInstall(options.time !== undefined ? parseTime(options.time) : {}); await this._browserContext._channel.clockInstall(options.time !== void 0 ? parseTime(options.time) : {});
} }
async fastForward(ticks) { async fastForward(ticks) {
await this._browserContext._channel.clockFastForward(parseTicks(ticks)); await this._browserContext._channel.clockFastForward(parseTicks(ticks));
@@ -47,22 +47,22 @@ class Clock {
await this._browserContext._channel.clockSetSystemTime(parseTime(time)); await this._browserContext._channel.clockSetSystemTime(parseTime(time));
} }
} }
exports.Clock = Clock;
function parseTime(time) { function parseTime(time) {
if (typeof time === 'number') return { if (typeof time === "number")
timeNumber: time return { timeNumber: time };
}; if (typeof time === "string")
if (typeof time === 'string') return { return { timeString: time };
timeString: time if (!isFinite(time.getTime()))
}; throw new Error(`Invalid date: ${time}`);
if (!isFinite(time.getTime())) throw new Error(`Invalid date: ${time}`); return { timeNumber: time.getTime() };
return {
timeNumber: time.getTime()
};
} }
function parseTicks(ticks) { function parseTicks(ticks) {
return { return {
ticksNumber: typeof ticks === 'number' ? ticks : undefined, ticksNumber: typeof ticks === "number" ? ticks : void 0,
ticksString: typeof ticks === 'string' ? ticks : undefined ticksString: typeof ticks === "string" ? ticks : void 0
}; };
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Clock
});

View File

@@ -1,83 +1,78 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var connection_exports = {};
__export(connection_exports, {
Connection: () => Connection
}); });
exports.Connection = void 0; module.exports = __toCommonJS(connection_exports);
var _eventEmitter = require("./eventEmitter"); var import_eventEmitter = require("./eventEmitter");
var _android = require("./android"); var import_android = require("./android");
var _artifact = require("./artifact"); var import_artifact = require("./artifact");
var _browser = require("./browser"); var import_browser = require("./browser");
var _browserContext = require("./browserContext"); var import_browserContext = require("./browserContext");
var _browserType = require("./browserType"); var import_browserType = require("./browserType");
var _cdpSession = require("./cdpSession"); var import_cdpSession = require("./cdpSession");
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _clientInstrumentation = require("./clientInstrumentation"); var import_clientInstrumentation = require("./clientInstrumentation");
var _dialog = require("./dialog"); var import_dialog = require("./dialog");
var _electron = require("./electron"); var import_electron = require("./electron");
var _elementHandle = require("./elementHandle"); var import_elementHandle = require("./elementHandle");
var _errors = require("./errors"); var import_errors = require("./errors");
var _fetch = require("./fetch"); var import_fetch = require("./fetch");
var _frame = require("./frame"); var import_frame = require("./frame");
var _jsHandle = require("./jsHandle"); var import_jsHandle = require("./jsHandle");
var _jsonPipe = require("./jsonPipe"); var import_jsonPipe = require("./jsonPipe");
var _localUtils = require("./localUtils"); var import_localUtils = require("./localUtils");
var _network = require("./network"); var import_network = require("./network");
var _page = require("./page"); var import_page = require("./page");
var _playwright = require("./playwright"); var import_playwright = require("./playwright");
var _selectors = require("./selectors"); var import_selectors = require("./selectors");
var _stream = require("./stream"); var import_stream = require("./stream");
var _tracing = require("./tracing"); var import_tracing = require("./tracing");
var _worker = require("./worker"); var import_worker = require("./worker");
var _writableStream = require("./writableStream"); var import_writableStream = require("./writableStream");
var _validator = require("../protocol/validator"); var import_validator = require("../protocol/validator");
var _stackTrace = require("../utils/isomorphic/stackTrace"); var import_stackTrace = require("../utils/isomorphic/stackTrace");
/** class Root extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Root extends _channelOwner.ChannelOwner {
constructor(connection) { constructor(connection) {
super(connection, 'Root', '', {}); super(connection, "Root", "", {});
} }
async initialize() { async initialize() {
return _playwright.Playwright.from((await this._channel.initialize({ return import_playwright.Playwright.from((await this._channel.initialize({
sdkLanguage: 'javascript' sdkLanguage: "javascript"
})).playwright); })).playwright);
} }
} }
class DummyChannelOwner extends _channelOwner.ChannelOwner {} class DummyChannelOwner extends import_channelOwner.ChannelOwner {
class Connection extends _eventEmitter.EventEmitter { }
class Connection extends import_eventEmitter.EventEmitter {
constructor(platform, localUtils, instrumentation, headers = []) { constructor(platform, localUtils, instrumentation, headers = []) {
super(platform); super(platform);
this._objects = new Map(); this._objects = /* @__PURE__ */ new Map();
this.onmessage = message => {}; this.onmessage = (message) => {
};
this._lastId = 0; this._lastId = 0;
this._callbacks = new Map(); this._callbacks = /* @__PURE__ */ new Map();
this._rootObject = void 0;
this._closedError = void 0;
this._isRemote = false; this._isRemote = false;
this._localUtils = void 0;
this._rawBuffers = false; this._rawBuffers = false;
// Some connections allow resolving in-process dispatchers.
this.toImpl = void 0;
this._tracingCount = 0; this._tracingCount = 0;
this._instrumentation = void 0; this._instrumentation = instrumentation || (0, import_clientInstrumentation.createInstrumentation)();
// Used from @playwright/test fixtures -> TODO remove?
this.headers = void 0;
this._instrumentation = instrumentation || (0, _clientInstrumentation.createInstrumentation)();
this._localUtils = localUtils; this._localUtils = localUtils;
this._rootObject = new Root(this); this._rootObject = new Root(this);
this.headers = headers; this.headers = headers;
@@ -104,237 +99,220 @@ class Connection extends _eventEmitter.EventEmitter {
return this._objects.get(guid); return this._objects.get(guid);
} }
setIsTracing(isTracing) { setIsTracing(isTracing) {
if (isTracing) this._tracingCount++;else this._tracingCount--; if (isTracing)
this._tracingCount++;
else
this._tracingCount--;
} }
async sendMessageToServer(object, method, params, apiName, frames, stepId) { async sendMessageToServer(object, method, params, apiName, frames, stepId) {
var _this$_localUtils; if (this._closedError)
if (this._closedError) throw this._closedError; throw this._closedError;
if (object._wasCollected) throw new Error('The object has been collected to prevent unbounded heap growth.'); if (object._wasCollected)
throw new Error("The object has been collected to prevent unbounded heap growth.");
const guid = object._guid; const guid = object._guid;
const type = object._type; const type = object._type;
const id = ++this._lastId; const id = ++this._lastId;
const message = { const message = { id, guid, method, params };
id, if (this._platform.isLogEnabled("channel")) {
guid, this._platform.log("channel", "SEND> " + JSON.stringify(message));
method,
params
};
if (this._platform.isLogEnabled('channel')) {
// Do not include metadata in debug logs to avoid noise.
this._platform.log('channel', 'SEND> ' + JSON.stringify(message));
} }
const location = frames[0] ? { const location = frames[0] ? { file: frames[0].file, line: frames[0].line, column: frames[0].column } : void 0;
file: frames[0].file, const metadata = { apiName, location, internal: !apiName, stepId };
line: frames[0].line, if (this._tracingCount && frames && type !== "LocalUtils")
column: frames[0].column this._localUtils?.addStackToTracingNoReply({ callData: { stack: frames, id } }).catch(() => {
} : undefined; });
const metadata = { this._platform.zones.empty.run(() => this.onmessage({ ...message, metadata }));
apiName, return await new Promise((resolve, reject) => this._callbacks.set(id, { resolve, reject, apiName, type, method }));
location,
internal: !apiName,
stepId
};
if (this._tracingCount && frames && type !== 'LocalUtils') (_this$_localUtils = this._localUtils) === null || _this$_localUtils === void 0 || _this$_localUtils.addStackToTracingNoReply({
callData: {
stack: frames,
id
}
}).catch(() => {});
// We need to exit zones before calling into the server, otherwise
// when we receive events from the server, we would be in an API zone.
this._platform.zones.empty.run(() => this.onmessage({
...message,
metadata
}));
return await new Promise((resolve, reject) => this._callbacks.set(id, {
resolve,
reject,
apiName,
type,
method
}));
} }
_validatorFromWireContext() { _validatorFromWireContext() {
return { return {
tChannelImpl: this._tChannelImplFromWire.bind(this), tChannelImpl: this._tChannelImplFromWire.bind(this),
binary: this._rawBuffers ? 'buffer' : 'fromBase64', binary: this._rawBuffers ? "buffer" : "fromBase64",
isUnderTest: () => this._platform.isUnderTest() isUnderTest: () => this._platform.isUnderTest()
}; };
} }
dispatch(message) { dispatch(message) {
if (this._closedError) return; if (this._closedError)
const { return;
id, const { id, guid, method, params, result, error, log } = message;
guid,
method,
params,
result,
error,
log
} = message;
if (id) { if (id) {
if (this._platform.isLogEnabled('channel')) this._platform.log('channel', '<RECV ' + JSON.stringify(message)); if (this._platform.isLogEnabled("channel"))
this._platform.log("channel", "<RECV " + JSON.stringify(message));
const callback = this._callbacks.get(id); const callback = this._callbacks.get(id);
if (!callback) throw new Error(`Cannot find command to respond: ${id}`); if (!callback)
throw new Error(`Cannot find command to respond: ${id}`);
this._callbacks.delete(id); this._callbacks.delete(id);
if (error && !result) { if (error && !result) {
const parsedError = (0, _errors.parseError)(error); const parsedError = (0, import_errors.parseError)(error);
(0, _stackTrace.rewriteErrorMessage)(parsedError, parsedError.message + formatCallLog(this._platform, log)); (0, import_stackTrace.rewriteErrorMessage)(parsedError, parsedError.message + formatCallLog(this._platform, log));
callback.reject(parsedError); callback.reject(parsedError);
} else { } else {
const validator = (0, _validator.findValidator)(callback.type, callback.method, 'Result'); const validator2 = (0, import_validator.findValidator)(callback.type, callback.method, "Result");
callback.resolve(validator(result, '', this._validatorFromWireContext())); callback.resolve(validator2(result, "", this._validatorFromWireContext()));
} }
return; return;
} }
if (this._platform.isLogEnabled('channel')) this._platform.log('channel', '<EVENT ' + JSON.stringify(message)); if (this._platform.isLogEnabled("channel"))
if (method === '__create__') { this._platform.log("channel", "<EVENT " + JSON.stringify(message));
if (method === "__create__") {
this._createRemoteObject(guid, params.type, params.guid, params.initializer); this._createRemoteObject(guid, params.type, params.guid, params.initializer);
return; return;
} }
const object = this._objects.get(guid); const object = this._objects.get(guid);
if (!object) throw new Error(`Cannot find object to "${method}": ${guid}`); if (!object)
if (method === '__adopt__') { throw new Error(`Cannot find object to "${method}": ${guid}`);
if (method === "__adopt__") {
const child = this._objects.get(params.guid); const child = this._objects.get(params.guid);
if (!child) throw new Error(`Unknown new child: ${params.guid}`); if (!child)
throw new Error(`Unknown new child: ${params.guid}`);
object._adopt(child); object._adopt(child);
return; return;
} }
if (method === '__dispose__') { if (method === "__dispose__") {
object._dispose(params.reason); object._dispose(params.reason);
return; return;
} }
const validator = (0, _validator.findValidator)(object._type, method, 'Event'); const validator = (0, import_validator.findValidator)(object._type, method, "Event");
object._channel.emit(method, validator(params, '', this._validatorFromWireContext())); object._channel.emit(method, validator(params, "", this._validatorFromWireContext()));
} }
close(cause) { close(cause) {
if (this._closedError) return; if (this._closedError)
this._closedError = new _errors.TargetClosedError(cause); return;
for (const callback of this._callbacks.values()) callback.reject(this._closedError); this._closedError = new import_errors.TargetClosedError(cause);
for (const callback of this._callbacks.values())
callback.reject(this._closedError);
this._callbacks.clear(); this._callbacks.clear();
this.emit('close'); this.emit("close");
} }
_tChannelImplFromWire(names, arg, path, context) { _tChannelImplFromWire(names, arg, path, context) {
if (arg && typeof arg === 'object' && typeof arg.guid === 'string') { if (arg && typeof arg === "object" && typeof arg.guid === "string") {
const object = this._objects.get(arg.guid); const object = this._objects.get(arg.guid);
if (!object) throw new Error(`Object with guid ${arg.guid} was not bound in the connection`); if (!object)
if (names !== '*' && !names.includes(object._type)) throw new _validator.ValidationError(`${path}: expected channel ${names.toString()}`); throw new Error(`Object with guid ${arg.guid} was not bound in the connection`);
if (names !== "*" && !names.includes(object._type))
throw new import_validator.ValidationError(`${path}: expected channel ${names.toString()}`);
return object._channel; return object._channel;
} }
throw new _validator.ValidationError(`${path}: expected channel ${names.toString()}`); throw new import_validator.ValidationError(`${path}: expected channel ${names.toString()}`);
} }
_createRemoteObject(parentGuid, type, guid, initializer) { _createRemoteObject(parentGuid, type, guid, initializer) {
const parent = this._objects.get(parentGuid); const parent = this._objects.get(parentGuid);
if (!parent) throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`); if (!parent)
throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`);
let result; let result;
const validator = (0, _validator.findValidator)(type, '', 'Initializer'); const validator = (0, import_validator.findValidator)(type, "", "Initializer");
initializer = validator(initializer, '', this._validatorFromWireContext()); initializer = validator(initializer, "", this._validatorFromWireContext());
switch (type) { switch (type) {
case 'Android': case "Android":
result = new _android.Android(parent, type, guid, initializer); result = new import_android.Android(parent, type, guid, initializer);
break; break;
case 'AndroidSocket': case "AndroidSocket":
result = new _android.AndroidSocket(parent, type, guid, initializer); result = new import_android.AndroidSocket(parent, type, guid, initializer);
break; break;
case 'AndroidDevice': case "AndroidDevice":
result = new _android.AndroidDevice(parent, type, guid, initializer); result = new import_android.AndroidDevice(parent, type, guid, initializer);
break; break;
case 'APIRequestContext': case "APIRequestContext":
result = new _fetch.APIRequestContext(parent, type, guid, initializer); result = new import_fetch.APIRequestContext(parent, type, guid, initializer);
break; break;
case 'Artifact': case "Artifact":
result = new _artifact.Artifact(parent, type, guid, initializer); result = new import_artifact.Artifact(parent, type, guid, initializer);
break; break;
case 'BindingCall': case "BindingCall":
result = new _page.BindingCall(parent, type, guid, initializer); result = new import_page.BindingCall(parent, type, guid, initializer);
break; break;
case 'Browser': case "Browser":
result = new _browser.Browser(parent, type, guid, initializer); result = new import_browser.Browser(parent, type, guid, initializer);
break; break;
case 'BrowserContext': case "BrowserContext":
result = new _browserContext.BrowserContext(parent, type, guid, initializer); result = new import_browserContext.BrowserContext(parent, type, guid, initializer);
break; break;
case 'BrowserType': case "BrowserType":
result = new _browserType.BrowserType(parent, type, guid, initializer); result = new import_browserType.BrowserType(parent, type, guid, initializer);
break; break;
case 'CDPSession': case "CDPSession":
result = new _cdpSession.CDPSession(parent, type, guid, initializer); result = new import_cdpSession.CDPSession(parent, type, guid, initializer);
break; break;
case 'Dialog': case "Dialog":
result = new _dialog.Dialog(parent, type, guid, initializer); result = new import_dialog.Dialog(parent, type, guid, initializer);
break; break;
case 'Electron': case "Electron":
result = new _electron.Electron(parent, type, guid, initializer); result = new import_electron.Electron(parent, type, guid, initializer);
break; break;
case 'ElectronApplication': case "ElectronApplication":
result = new _electron.ElectronApplication(parent, type, guid, initializer); result = new import_electron.ElectronApplication(parent, type, guid, initializer);
break; break;
case 'ElementHandle': case "ElementHandle":
result = new _elementHandle.ElementHandle(parent, type, guid, initializer); result = new import_elementHandle.ElementHandle(parent, type, guid, initializer);
break; break;
case 'Frame': case "Frame":
result = new _frame.Frame(parent, type, guid, initializer); result = new import_frame.Frame(parent, type, guid, initializer);
break; break;
case 'JSHandle': case "JSHandle":
result = new _jsHandle.JSHandle(parent, type, guid, initializer); result = new import_jsHandle.JSHandle(parent, type, guid, initializer);
break; break;
case 'JsonPipe': case "JsonPipe":
result = new _jsonPipe.JsonPipe(parent, type, guid, initializer); result = new import_jsonPipe.JsonPipe(parent, type, guid, initializer);
break; break;
case 'LocalUtils': case "LocalUtils":
result = new _localUtils.LocalUtils(parent, type, guid, initializer); result = new import_localUtils.LocalUtils(parent, type, guid, initializer);
if (!this._localUtils) this._localUtils = result; if (!this._localUtils)
this._localUtils = result;
break; break;
case 'Page': case "Page":
result = new _page.Page(parent, type, guid, initializer); result = new import_page.Page(parent, type, guid, initializer);
break; break;
case 'Playwright': case "Playwright":
result = new _playwright.Playwright(parent, type, guid, initializer); result = new import_playwright.Playwright(parent, type, guid, initializer);
break; break;
case 'Request': case "Request":
result = new _network.Request(parent, type, guid, initializer); result = new import_network.Request(parent, type, guid, initializer);
break; break;
case 'Response': case "Response":
result = new _network.Response(parent, type, guid, initializer); result = new import_network.Response(parent, type, guid, initializer);
break; break;
case 'Route': case "Route":
result = new _network.Route(parent, type, guid, initializer); result = new import_network.Route(parent, type, guid, initializer);
break; break;
case 'Stream': case "Stream":
result = new _stream.Stream(parent, type, guid, initializer); result = new import_stream.Stream(parent, type, guid, initializer);
break; break;
case 'Selectors': case "Selectors":
result = new _selectors.SelectorsOwner(parent, type, guid, initializer); result = new import_selectors.SelectorsOwner(parent, type, guid, initializer);
break; break;
case 'SocksSupport': case "SocksSupport":
result = new DummyChannelOwner(parent, type, guid, initializer); result = new DummyChannelOwner(parent, type, guid, initializer);
break; break;
case 'Tracing': case "Tracing":
result = new _tracing.Tracing(parent, type, guid, initializer); result = new import_tracing.Tracing(parent, type, guid, initializer);
break; break;
case 'WebSocket': case "WebSocket":
result = new _network.WebSocket(parent, type, guid, initializer); result = new import_network.WebSocket(parent, type, guid, initializer);
break; break;
case 'WebSocketRoute': case "WebSocketRoute":
result = new _network.WebSocketRoute(parent, type, guid, initializer); result = new import_network.WebSocketRoute(parent, type, guid, initializer);
break; break;
case 'Worker': case "Worker":
result = new _worker.Worker(parent, type, guid, initializer); result = new import_worker.Worker(parent, type, guid, initializer);
break; break;
case 'WritableStream': case "WritableStream":
result = new _writableStream.WritableStream(parent, type, guid, initializer); result = new import_writableStream.WritableStream(parent, type, guid, initializer);
break; break;
default: default:
throw new Error('Missing type ' + type); throw new Error("Missing type " + type);
} }
return result; return result;
} }
} }
exports.Connection = Connection;
function formatCallLog(platform, log) { function formatCallLog(platform, log) {
if (!log || !log.some(l => !!l)) return ''; if (!log || !log.some((l) => !!l))
return "";
return ` return `
Call log: Call log:
${platform.colors.dim(log.join('\n'))} ${platform.colors.dim(log.join("\n"))}
`; `;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Connection
});

View File

@@ -1,34 +1,34 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var consoleMessage_exports = {};
__export(consoleMessage_exports, {
ConsoleMessage: () => ConsoleMessage
}); });
exports.ConsoleMessage = void 0; module.exports = __toCommonJS(consoleMessage_exports);
var _jsHandle = require("./jsHandle"); var import_jsHandle = require("./jsHandle");
var _page = require("./page"); var import_page = require("./page");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ConsoleMessage { class ConsoleMessage {
constructor(platform, event) { constructor(platform, event) {
this._page = void 0; this._page = "page" in event && event.page ? import_page.Page.from(event.page) : null;
this._event = void 0;
this._page = 'page' in event && event.page ? _page.Page.from(event.page) : null;
this._event = event; this._event = event;
if (platform.inspectCustom) this[platform.inspectCustom] = () => this._inspect(); if (platform.inspectCustom)
this[platform.inspectCustom] = () => this._inspect();
} }
page() { page() {
return this._page; return this._page;
@@ -40,7 +40,7 @@ class ConsoleMessage {
return this._event.text; return this._event.text;
} }
args() { args() {
return this._event.args.map(_jsHandle.JSHandle.from); return this._event.args.map(import_jsHandle.JSHandle.from);
} }
location() { location() {
return this._event.location; return this._event.location;
@@ -49,4 +49,7 @@ class ConsoleMessage {
return this.text(); return this.text();
} }
} }
exports.ConsoleMessage = ConsoleMessage; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ConsoleMessage
});

View File

@@ -1,28 +1,28 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var coverage_exports = {};
__export(coverage_exports, {
Coverage: () => Coverage
}); });
exports.Coverage = void 0; module.exports = __toCommonJS(coverage_exports);
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Coverage { class Coverage {
constructor(channel) { constructor(channel) {
this._channel = void 0;
this._channel = channel; this._channel = channel;
} }
async startJSCoverage(options = {}) { async startJSCoverage(options = {}) {
@@ -38,4 +38,7 @@ class Coverage {
return (await this._channel.stopCSSCoverage()).entries; return (await this._channel.stopCSSCoverage()).entries;
} }
} }
exports.Coverage = Coverage; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Coverage
});

View File

@@ -1,37 +1,35 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var dialog_exports = {};
__export(dialog_exports, {
Dialog: () => Dialog
}); });
exports.Dialog = void 0; module.exports = __toCommonJS(dialog_exports);
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _page = require("./page"); var import_page = require("./page");
/** class Dialog extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Dialog extends _channelOwner.ChannelOwner {
static from(dialog) { static from(dialog) {
return dialog._object; return dialog._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
// Note: dialogs that open early during page initialization block it. this._page = import_page.Page.fromNullable(initializer.page);
// Therefore, we must report the dialog without a page to be able to handle it.
this._page = void 0;
this._page = _page.Page.fromNullable(initializer.page);
} }
page() { page() {
return this._page; return this._page;
@@ -46,12 +44,13 @@ class Dialog extends _channelOwner.ChannelOwner {
return this._initializer.defaultValue; return this._initializer.defaultValue;
} }
async accept(promptText) { async accept(promptText) {
await this._channel.accept({ await this._channel.accept({ promptText });
promptText
});
} }
async dismiss() { async dismiss() {
await this._channel.dismiss(); await this._channel.dismiss();
} }
} }
exports.Dialog = Dialog; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Dialog
});

View File

@@ -1,31 +1,28 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var download_exports = {};
__export(download_exports, {
Download: () => Download
}); });
exports.Download = void 0; module.exports = __toCommonJS(download_exports);
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Download { class Download {
constructor(page, url, suggestedFilename, artifact) { constructor(page, url, suggestedFilename, artifact) {
this._page = void 0;
this._url = void 0;
this._suggestedFilename = void 0;
this._artifact = void 0;
this._page = page; this._page = page;
this._url = url; this._url = url;
this._suggestedFilename = suggestedFilename; this._suggestedFilename = suggestedFilename;
@@ -59,4 +56,7 @@ class Download {
return await this._artifact.delete(); return await this._artifact.delete();
} }
} }
exports.Download = Download; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Download
});

View File

@@ -1,35 +1,37 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var electron_exports = {};
__export(electron_exports, {
Electron: () => Electron,
ElectronApplication: () => ElectronApplication
}); });
exports.ElectronApplication = exports.Electron = void 0; module.exports = __toCommonJS(electron_exports);
var _browserContext = require("./browserContext"); var import_browserContext = require("./browserContext");
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _clientHelper = require("./clientHelper"); var import_clientHelper = require("./clientHelper");
var _consoleMessage = require("./consoleMessage"); var import_consoleMessage = require("./consoleMessage");
var _errors = require("./errors"); var import_errors = require("./errors");
var _events = require("./events"); var import_events = require("./events");
var _jsHandle = require("./jsHandle"); var import_jsHandle = require("./jsHandle");
var _waiter = require("./waiter"); var import_waiter = require("./waiter");
var _timeoutSettings = require("./timeoutSettings"); var import_timeoutSettings = require("./timeoutSettings");
/** class Electron extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Electron extends _channelOwner.ChannelOwner {
static from(electron) { static from(electron) {
return electron._object; return electron._object;
} }
@@ -38,8 +40,8 @@ class Electron extends _channelOwner.ChannelOwner {
} }
async launch(options = {}) { async launch(options = {}) {
const params = { const params = {
...(await (0, _browserContext.prepareBrowserContextParams)(this._platform, options)), ...await (0, import_browserContext.prepareBrowserContextParams)(this._platform, options),
env: (0, _clientHelper.envObjectToArray)(options.env ? options.env : this._platform.env), env: (0, import_clientHelper.envObjectToArray)(options.env ? options.env : this._platform.env),
tracesDir: options.tracesDir tracesDir: options.tracesDir
}; };
const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication); const app = ElectronApplication.from((await this._channel.launch(params)).electronApplication);
@@ -47,41 +49,41 @@ class Electron extends _channelOwner.ChannelOwner {
return app; return app;
} }
} }
exports.Electron = Electron; class ElectronApplication extends import_channelOwner.ChannelOwner {
class ElectronApplication extends _channelOwner.ChannelOwner {
static from(electronApplication) {
return electronApplication._object;
}
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._context = void 0; this._windows = /* @__PURE__ */ new Set();
this._windows = new Set(); this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
this._timeoutSettings = void 0; this._context = import_browserContext.BrowserContext.from(initializer.context);
this._timeoutSettings = new _timeoutSettings.TimeoutSettings(this._platform); for (const page of this._context._pages)
this._context = _browserContext.BrowserContext.from(initializer.context); this._onPage(page);
for (const page of this._context._pages) this._onPage(page); this._context.on(import_events.Events.BrowserContext.Page, (page) => this._onPage(page));
this._context.on(_events.Events.BrowserContext.Page, page => this._onPage(page)); this._channel.on("close", () => {
this._channel.on('close', () => { this.emit(import_events.Events.ElectronApplication.Close);
this.emit(_events.Events.ElectronApplication.Close);
}); });
this._channel.on('console', event => this.emit(_events.Events.ElectronApplication.Console, new _consoleMessage.ConsoleMessage(this._platform, event))); this._channel.on("console", (event) => this.emit(import_events.Events.ElectronApplication.Console, new import_consoleMessage.ConsoleMessage(this._platform, event)));
this._setEventToSubscriptionMapping(new Map([[_events.Events.ElectronApplication.Console, 'console']])); this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[import_events.Events.ElectronApplication.Console, "console"]
]));
}
static from(electronApplication) {
return electronApplication._object;
} }
process() { process() {
return this._toImpl().process(); return this._toImpl().process();
} }
_onPage(page) { _onPage(page) {
this._windows.add(page); this._windows.add(page);
this.emit(_events.Events.ElectronApplication.Window, page); this.emit(import_events.Events.ElectronApplication.Window, page);
page.once(_events.Events.Page.Close, () => this._windows.delete(page)); page.once(import_events.Events.Page.Close, () => this._windows.delete(page));
} }
windows() { windows() {
// TODO: add ElectronPage class inheriting from Page.
return [...this._windows]; return [...this._windows];
} }
async firstWindow(options) { async firstWindow(options) {
if (this._windows.size) return this._windows.values().next().value; if (this._windows.size)
return await this.waitForEvent('window', options); return this._windows.values().next().value;
return await this.waitForEvent("window", options);
} }
context() { context() {
return this._context; return this._context;
@@ -93,43 +95,39 @@ class ElectronApplication extends _channelOwner.ChannelOwner {
try { try {
await this._context.close(); await this._context.close();
} catch (e) { } catch (e) {
if ((0, _errors.isTargetClosedError)(e)) return; if ((0, import_errors.isTargetClosedError)(e))
return;
throw e; throw e;
} }
} }
async waitForEvent(event, optionsOrPredicate = {}) { async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = _waiter.Waiter.createForEvent(this, event); const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== _events.Events.ElectronApplication.Close) waiter.rejectOnEvent(this, _events.Events.ElectronApplication.Close, () => new _errors.TargetClosedError()); if (event !== import_events.Events.ElectronApplication.Close)
waiter.rejectOnEvent(this, import_events.Events.ElectronApplication.Close, () => new import_errors.TargetClosedError());
const result = await waiter.waitForEvent(this, event, predicate); const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose(); waiter.dispose();
return result; return result;
}); });
} }
async browserWindow(page) { async browserWindow(page) {
const result = await this._channel.browserWindow({ const result = await this._channel.browserWindow({ page: page._channel });
page: page._channel return import_jsHandle.JSHandle.from(result.handle);
});
return _jsHandle.JSHandle.from(result.handle);
} }
async evaluate(pageFunction, arg) { async evaluate(pageFunction, arg) {
const result = await this._channel.evaluateExpression({ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
expression: String(pageFunction), return (0, import_jsHandle.parseResult)(result.value);
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async evaluateHandle(pageFunction, arg) { async evaluateHandle(pageFunction, arg) {
const result = await this._channel.evaluateExpressionHandle({ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
expression: String(pageFunction), return import_jsHandle.JSHandle.from(result.handle);
isFunction: typeof pageFunction === 'function', }
arg: (0, _jsHandle.serializeArgument)(arg) }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Electron,
ElectronApplication
}); });
return _jsHandle.JSHandle.from(result.handle);
}
}
exports.ElectronApplication = ElectronApplication;

View File

@@ -1,36 +1,37 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var elementHandle_exports = {};
__export(elementHandle_exports, {
ElementHandle: () => ElementHandle,
convertInputFiles: () => convertInputFiles,
convertSelectOptionValues: () => convertSelectOptionValues,
determineScreenshotType: () => determineScreenshotType
}); });
exports.ElementHandle = void 0; module.exports = __toCommonJS(elementHandle_exports);
exports.convertInputFiles = convertInputFiles; var import_frame = require("./frame");
exports.convertSelectOptionValues = convertSelectOptionValues; var import_jsHandle = require("./jsHandle");
exports.determineScreenshotType = determineScreenshotType; var import_assert = require("../utils/isomorphic/assert");
var _frame = require("./frame"); var import_fileUtils = require("./fileUtils");
var _jsHandle = require("./jsHandle"); var import_rtti = require("../utils/isomorphic/rtti");
var _assert = require("../utils/isomorphic/assert"); var import_writableStream = require("./writableStream");
var _fileUtils = require("./fileUtils"); var import_mimeType = require("../utils/isomorphic/mimeType");
var _rtti = require("../utils/isomorphic/rtti"); class ElementHandle extends import_jsHandle.JSHandle {
var _writableStream = require("./writableStream");
var _mimeType = require("../utils/isomorphic/mimeType");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ElementHandle extends _jsHandle.JSHandle {
static from(handle) { static from(handle) {
return handle._object; return handle._object;
} }
@@ -39,34 +40,31 @@ class ElementHandle extends _jsHandle.JSHandle {
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._elementChannel = void 0;
this._elementChannel = this._channel; this._elementChannel = this._channel;
} }
asElement() { asElement() {
return this; return this;
} }
async ownerFrame() { async ownerFrame() {
return _frame.Frame.fromNullable((await this._elementChannel.ownerFrame()).frame); return import_frame.Frame.fromNullable((await this._elementChannel.ownerFrame()).frame);
} }
async contentFrame() { async contentFrame() {
return _frame.Frame.fromNullable((await this._elementChannel.contentFrame()).frame); return import_frame.Frame.fromNullable((await this._elementChannel.contentFrame()).frame);
} }
async _generateLocatorString() { async _generateLocatorString() {
const value = (await this._elementChannel.generateLocatorString()).value; const value = (await this._elementChannel.generateLocatorString()).value;
return value === undefined ? null : value; return value === void 0 ? null : value;
} }
async getAttribute(name) { async getAttribute(name) {
const value = (await this._elementChannel.getAttribute({ const value = (await this._elementChannel.getAttribute({ name })).value;
name return value === void 0 ? null : value;
})).value;
return value === undefined ? null : value;
} }
async inputValue() { async inputValue() {
return (await this._elementChannel.inputValue()).value; return (await this._elementChannel.inputValue()).value;
} }
async textContent() { async textContent() {
const value = (await this._elementChannel.textContent()).value; const value = (await this._elementChannel.textContent()).value;
return value === undefined ? null : value; return value === void 0 ? null : value;
} }
async innerText() { async innerText() {
return (await this._elementChannel.innerText()).value; return (await this._elementChannel.innerText()).value;
@@ -93,10 +91,7 @@ class ElementHandle extends _jsHandle.JSHandle {
return (await this._elementChannel.isVisible()).value; return (await this._elementChannel.isVisible()).value;
} }
async dispatchEvent(type, eventInit = {}) { async dispatchEvent(type, eventInit = {}) {
await this._elementChannel.dispatchEvent({ await this._elementChannel.dispatchEvent({ type, eventInit: (0, import_jsHandle.serializeArgument)(eventInit) });
type,
eventInit: (0, _jsHandle.serializeArgument)(eventInit)
});
} }
async scrollIntoViewIfNeeded(options = {}) { async scrollIntoViewIfNeeded(options = {}) {
await this._elementChannel.scrollIntoViewIfNeeded(options); await this._elementChannel.scrollIntoViewIfNeeded(options);
@@ -114,44 +109,30 @@ class ElementHandle extends _jsHandle.JSHandle {
return await this._elementChannel.tap(options); return await this._elementChannel.tap(options);
} }
async selectOption(values, options = {}) { async selectOption(values, options = {}) {
const result = await this._elementChannel.selectOption({ const result = await this._elementChannel.selectOption({ ...convertSelectOptionValues(values), ...options });
...convertSelectOptionValues(values),
...options
});
return result.values; return result.values;
} }
async fill(value, options = {}) { async fill(value, options = {}) {
return await this._elementChannel.fill({ return await this._elementChannel.fill({ value, ...options });
value,
...options
});
} }
async selectText(options = {}) { async selectText(options = {}) {
await this._elementChannel.selectText(options); await this._elementChannel.selectText(options);
} }
async setInputFiles(files, options = {}) { async setInputFiles(files, options = {}) {
const frame = await this.ownerFrame(); const frame = await this.ownerFrame();
if (!frame) throw new Error('Cannot set input files to detached element'); if (!frame)
throw new Error("Cannot set input files to detached element");
const converted = await convertInputFiles(this._platform, files, frame.page().context()); const converted = await convertInputFiles(this._platform, files, frame.page().context());
await this._elementChannel.setInputFiles({ await this._elementChannel.setInputFiles({ ...converted, ...options });
...converted,
...options
});
} }
async focus() { async focus() {
await this._elementChannel.focus(); await this._elementChannel.focus();
} }
async type(text, options = {}) { async type(text, options = {}) {
await this._elementChannel.type({ await this._elementChannel.type({ text, ...options });
text,
...options
});
} }
async press(key, options = {}) { async press(key, options = {}) {
await this._elementChannel.press({ await this._elementChannel.press({ key, ...options });
key,
...options
});
} }
async check(options = {}) { async check(options = {}) {
return await this._elementChannel.check(options); return await this._elementChannel.check(options);
@@ -160,129 +141,103 @@ class ElementHandle extends _jsHandle.JSHandle {
return await this._elementChannel.uncheck(options); return await this._elementChannel.uncheck(options);
} }
async setChecked(checked, options) { async setChecked(checked, options) {
if (checked) await this.check(options);else await this.uncheck(options); if (checked)
await this.check(options);
else
await this.uncheck(options);
} }
async boundingBox() { async boundingBox() {
const value = (await this._elementChannel.boundingBox()).value; const value = (await this._elementChannel.boundingBox()).value;
return value === undefined ? null : value; return value === void 0 ? null : value;
} }
async screenshot(options = {}) { async screenshot(options = {}) {
const mask = options.mask; const mask = options.mask;
const copy = { const copy = { ...options, mask: void 0 };
...options, if (!copy.type)
mask: undefined copy.type = determineScreenshotType(options);
};
if (!copy.type) copy.type = determineScreenshotType(options);
if (mask) { if (mask) {
copy.mask = mask.map(locator => ({ copy.mask = mask.map((locator) => ({
frame: locator._frame._channel, frame: locator._frame._channel,
selector: locator._selector selector: locator._selector
})); }));
} }
const result = await this._elementChannel.screenshot(copy); const result = await this._elementChannel.screenshot(copy);
if (options.path) { if (options.path) {
await (0, _fileUtils.mkdirIfNeeded)(this._platform, options.path); await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, result.binary); await this._platform.fs().promises.writeFile(options.path, result.binary);
} }
return result.binary; return result.binary;
} }
async $(selector) { async $(selector) {
return ElementHandle.fromNullable((await this._elementChannel.querySelector({ return ElementHandle.fromNullable((await this._elementChannel.querySelector({ selector })).element);
selector
})).element);
} }
async $$(selector) { async $$(selector) {
const result = await this._elementChannel.querySelectorAll({ const result = await this._elementChannel.querySelectorAll({ selector });
selector return result.elements.map((h) => ElementHandle.from(h));
});
return result.elements.map(h => ElementHandle.from(h));
} }
async $eval(selector, pageFunction, arg) { async $eval(selector, pageFunction, arg) {
const result = await this._elementChannel.evalOnSelector({ const result = await this._elementChannel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
selector, return (0, import_jsHandle.parseResult)(result.value);
expression: String(pageFunction),
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async $$eval(selector, pageFunction, arg) { async $$eval(selector, pageFunction, arg) {
const result = await this._elementChannel.evalOnSelectorAll({ const result = await this._elementChannel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
selector, return (0, import_jsHandle.parseResult)(result.value);
expression: String(pageFunction),
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async waitForElementState(state, options = {}) { async waitForElementState(state, options = {}) {
return await this._elementChannel.waitForElementState({ return await this._elementChannel.waitForElementState({ state, ...options });
state,
...options
});
} }
async waitForSelector(selector, options = {}) { async waitForSelector(selector, options = {}) {
const result = await this._elementChannel.waitForSelector({ const result = await this._elementChannel.waitForSelector({ selector, ...options });
selector,
...options
});
return ElementHandle.fromNullable(result.element); return ElementHandle.fromNullable(result.element);
} }
} }
exports.ElementHandle = ElementHandle;
function convertSelectOptionValues(values) { function convertSelectOptionValues(values) {
if (values === null) return {}; if (values === null)
if (!Array.isArray(values)) values = [values]; return {};
if (!values.length) return {}; if (!Array.isArray(values))
for (let i = 0; i < values.length; i++) (0, _assert.assert)(values[i] !== null, `options[${i}]: expected object, got null`); values = [values];
if (values[0] instanceof ElementHandle) return { if (!values.length)
elements: values.map(v => v._elementChannel) return {};
}; for (let i = 0; i < values.length; i++)
if ((0, _rtti.isString)(values[0])) return { (0, import_assert.assert)(values[i] !== null, `options[${i}]: expected object, got null`);
options: values.map(valueOrLabel => ({ if (values[0] instanceof ElementHandle)
valueOrLabel return { elements: values.map((v) => v._elementChannel) };
})) if ((0, import_rtti.isString)(values[0]))
}; return { options: values.map((valueOrLabel) => ({ valueOrLabel })) };
return { return { options: values };
options: values
};
} }
function filePayloadExceedsSizeLimit(payloads) { function filePayloadExceedsSizeLimit(payloads) {
return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= _fileUtils.fileUploadSizeLimit; return payloads.reduce((size, item) => size + (item.buffer ? item.buffer.byteLength : 0), 0) >= import_fileUtils.fileUploadSizeLimit;
} }
async function resolvePathsAndDirectoryForInputFiles(platform, items) { async function resolvePathsAndDirectoryForInputFiles(platform, items) {
var _localPaths;
let localPaths; let localPaths;
let localDirectory; let localDirectory;
for (const item of items) { for (const item of items) {
const stat = await platform.fs().promises.stat(item); const stat = await platform.fs().promises.stat(item);
if (stat.isDirectory()) { if (stat.isDirectory()) {
if (localDirectory) throw new Error('Multiple directories are not supported'); if (localDirectory)
throw new Error("Multiple directories are not supported");
localDirectory = platform.path().resolve(item); localDirectory = platform.path().resolve(item);
} else { } else {
localPaths !== null && localPaths !== void 0 ? localPaths : localPaths = []; localPaths ??= [];
localPaths.push(platform.path().resolve(item)); localPaths.push(platform.path().resolve(item));
} }
} }
if ((_localPaths = localPaths) !== null && _localPaths !== void 0 && _localPaths.length && localDirectory) throw new Error('File paths must be all files or a single directory'); if (localPaths?.length && localDirectory)
throw new Error("File paths must be all files or a single directory");
return [localPaths, localDirectory]; return [localPaths, localDirectory];
} }
async function convertInputFiles(platform, files, context) { async function convertInputFiles(platform, files, context) {
const items = Array.isArray(files) ? files.slice() : [files]; const items = Array.isArray(files) ? files.slice() : [files];
if (items.some(item => typeof item === 'string')) { if (items.some((item) => typeof item === "string")) {
if (!items.every(item => typeof item === 'string')) throw new Error('File paths cannot be mixed with buffers'); if (!items.every((item) => typeof item === "string"))
throw new Error("File paths cannot be mixed with buffers");
const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(platform, items); const [localPaths, localDirectory] = await resolvePathsAndDirectoryForInputFiles(platform, items);
if (context._connection.isRemote()) { if (context._connection.isRemote()) {
const files = localDirectory ? (await platform.fs().promises.readdir(localDirectory, { const files2 = localDirectory ? (await platform.fs().promises.readdir(localDirectory, { withFileTypes: true, recursive: true })).filter((f) => f.isFile()).map((f) => platform.path().join(f.path, f.name)) : localPaths;
withFileTypes: true, const { writableStreams, rootDir } = await context._wrapApiCall(async () => context._channel.createTempFiles({
recursive: true rootDirName: localDirectory ? platform.path().basename(localDirectory) : void 0,
})).filter(f => f.isFile()).map(f => platform.path().join(f.path, f.name)) : localPaths; items: await Promise.all(files2.map(async (file) => {
const {
writableStreams,
rootDir
} = await context._wrapApiCall(async () => context._channel.createTempFiles({
rootDirName: localDirectory ? platform.path().basename(localDirectory) : undefined,
items: await Promise.all(files.map(async file => {
const lastModifiedMs = (await platform.fs().promises.stat(file)).mtimeMs; const lastModifiedMs = (await platform.fs().promises.stat(file)).mtimeMs;
return { return {
name: localDirectory ? platform.path().relative(localDirectory, file) : platform.path().basename(file), name: localDirectory ? platform.path().relative(localDirectory, file) : platform.path().basename(file),
@@ -290,13 +245,13 @@ async function convertInputFiles(platform, files, context) {
}; };
})) }))
}), true); }), true);
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files2.length; i++) {
const writable = _writableStream.WritableStream.from(writableStreams[i]); const writable = import_writableStream.WritableStream.from(writableStreams[i]);
await platform.streamFile(files[i], writable.stream()); await platform.streamFile(files2[i], writable.stream());
} }
return { return {
directoryStream: rootDir, directoryStream: rootDir,
streams: localDirectory ? undefined : writableStreams streams: localDirectory ? void 0 : writableStreams
}; };
} }
return { return {
@@ -305,16 +260,25 @@ async function convertInputFiles(platform, files, context) {
}; };
} }
const payloads = items; const payloads = items;
if (filePayloadExceedsSizeLimit(payloads)) throw new Error('Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.'); if (filePayloadExceedsSizeLimit(payloads))
return { throw new Error("Cannot set buffer larger than 50Mb, please write it to a file and pass its path instead.");
payloads return { payloads };
};
} }
function determineScreenshotType(options) { function determineScreenshotType(options) {
if (options.path) { if (options.path) {
const mimeType = (0, _mimeType.getMimeTypeForPath)(options.path); const mimeType = (0, import_mimeType.getMimeTypeForPath)(options.path);
if (mimeType === 'image/png') return 'png';else if (mimeType === 'image/jpeg') return 'jpeg'; if (mimeType === "image/png")
return "png";
else if (mimeType === "image/jpeg")
return "jpeg";
throw new Error(`path: unsupported mime type "${mimeType}"`); throw new Error(`path: unsupported mime type "${mimeType}"`);
} }
return options.type; return options.type;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ElementHandle,
convertInputFiles,
convertSelectOptionValues,
determineScreenshotType
});

View File

@@ -1,77 +1,77 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var errors_exports = {};
__export(errors_exports, {
TargetClosedError: () => TargetClosedError,
TimeoutError: () => TimeoutError,
isTargetClosedError: () => isTargetClosedError,
parseError: () => parseError,
serializeError: () => serializeError
}); });
exports.TimeoutError = exports.TargetClosedError = void 0; module.exports = __toCommonJS(errors_exports);
exports.isTargetClosedError = isTargetClosedError; var import_serializers = require("../protocol/serializers");
exports.parseError = parseError; var import_rtti = require("../utils/isomorphic/rtti");
exports.serializeError = serializeError;
var _serializers = require("../protocol/serializers");
var _rtti = require("../utils/isomorphic/rtti");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class TimeoutError extends Error { class TimeoutError extends Error {
constructor(message) { constructor(message) {
super(message); super(message);
this.name = 'TimeoutError'; this.name = "TimeoutError";
} }
} }
exports.TimeoutError = TimeoutError;
class TargetClosedError extends Error { class TargetClosedError extends Error {
constructor(cause) { constructor(cause) {
super(cause || 'Target page, context or browser has been closed'); super(cause || "Target page, context or browser has been closed");
} }
} }
exports.TargetClosedError = TargetClosedError;
function isTargetClosedError(error) { function isTargetClosedError(error) {
return error instanceof TargetClosedError; return error instanceof TargetClosedError;
} }
function serializeError(e) { function serializeError(e) {
if ((0, _rtti.isError)(e)) return { if ((0, import_rtti.isError)(e))
error: { return { error: { message: e.message, stack: e.stack, name: e.name } };
message: e.message, return { value: (0, import_serializers.serializeValue)(e, (value) => ({ fallThrough: value })) };
stack: e.stack,
name: e.name
}
};
return {
value: (0, _serializers.serializeValue)(e, value => ({
fallThrough: value
}))
};
} }
function parseError(error) { function parseError(error) {
if (!error.error) { if (!error.error) {
if (error.value === undefined) throw new Error('Serialized error must have either an error or a value'); if (error.value === void 0)
return (0, _serializers.parseSerializedValue)(error.value, undefined); throw new Error("Serialized error must have either an error or a value");
return (0, import_serializers.parseSerializedValue)(error.value, void 0);
} }
if (error.error.name === 'TimeoutError') { if (error.error.name === "TimeoutError") {
const e = new TimeoutError(error.error.message); const e2 = new TimeoutError(error.error.message);
e.stack = error.error.stack || ''; e2.stack = error.error.stack || "";
return e; return e2;
} }
if (error.error.name === 'TargetClosedError') { if (error.error.name === "TargetClosedError") {
const e = new TargetClosedError(error.error.message); const e2 = new TargetClosedError(error.error.message);
e.stack = error.error.stack || ''; e2.stack = error.error.stack || "";
return e; return e2;
} }
const e = new Error(error.error.message); const e = new Error(error.error.message);
e.stack = error.error.stack || ''; e.stack = error.error.stack || "";
e.name = error.error.name; e.name = error.error.name;
return e; return e;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
TargetClosedError,
TimeoutError,
isTargetClosedError,
parseError,
serializeError
});

View File

@@ -1,83 +1,82 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var eventEmitter_exports = {};
__export(eventEmitter_exports, {
EventEmitter: () => EventEmitter
}); });
exports.EventEmitter = void 0; module.exports = __toCommonJS(eventEmitter_exports);
/**
* Copyright Joyent, Inc. and other Node contributors.
* Modifications copyright (c) Microsoft Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
class EventEmitter { class EventEmitter {
constructor(platform) { constructor(platform) {
this._events = undefined; this._events = void 0;
this._eventsCount = 0; this._eventsCount = 0;
this._maxListeners = undefined; this._maxListeners = void 0;
this._pendingHandlers = new Map(); this._pendingHandlers = /* @__PURE__ */ new Map();
this._rejectionHandler = void 0;
this._platform = void 0;
this._platform = platform; this._platform = platform;
if (this._events === undefined || this._events === Object.getPrototypeOf(this)._events) { if (this._events === void 0 || this._events === Object.getPrototypeOf(this)._events) {
this._events = Object.create(null); this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} }
this._maxListeners = this._maxListeners || undefined; this._maxListeners = this._maxListeners || void 0;
this.on = this.addListener; this.on = this.addListener;
this.off = this.removeListener; this.off = this.removeListener;
} }
setMaxListeners(n) { setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || Number.isNaN(n)) throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.'); if (typeof n !== "number" || n < 0 || Number.isNaN(n))
throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + ".");
this._maxListeners = n; this._maxListeners = n;
return this; return this;
} }
getMaxListeners() { getMaxListeners() {
return this._maxListeners === undefined ? this._platform.defaultMaxListeners() : this._maxListeners; return this._maxListeners === void 0 ? this._platform.defaultMaxListeners() : this._maxListeners;
} }
emit(type, ...args) { emit(type, ...args) {
const events = this._events; const events = this._events;
if (events === undefined) return false; if (events === void 0)
const handler = events === null || events === void 0 ? void 0 : events[type]; return false;
if (handler === undefined) return false; const handler = events?.[type];
if (typeof handler === 'function') { if (handler === void 0)
return false;
if (typeof handler === "function") {
this._callHandler(type, handler, args); this._callHandler(type, handler, args);
} else { } else {
const len = handler.length; const len = handler.length;
const listeners = handler.slice(); const listeners = handler.slice();
for (let i = 0; i < len; ++i) this._callHandler(type, listeners[i], args); for (let i = 0; i < len; ++i)
this._callHandler(type, listeners[i], args);
} }
return true; return true;
} }
_callHandler(type, handler, args) { _callHandler(type, handler, args) {
const promise = Reflect.apply(handler, this, args); const promise = Reflect.apply(handler, this, args);
if (!(promise instanceof Promise)) return; if (!(promise instanceof Promise))
return;
let set = this._pendingHandlers.get(type); let set = this._pendingHandlers.get(type);
if (!set) { if (!set) {
set = new Set(); set = /* @__PURE__ */ new Set();
this._pendingHandlers.set(type, set); this._pendingHandlers.set(type, set);
} }
set.add(promise); set.add(promise);
promise.catch(e => { promise.catch((e) => {
if (this._rejectionHandler) this._rejectionHandler(e);else throw e; if (this._rejectionHandler)
this._rejectionHandler(e);
else
throw e;
}).finally(() => set.delete(promise)); }).finally(() => set.delete(promise));
} }
addListener(type, listener) { addListener(type, listener) {
@@ -90,48 +89,36 @@ class EventEmitter {
checkListener(listener); checkListener(listener);
let events = this._events; let events = this._events;
let existing; let existing;
if (events === undefined) { if (events === void 0) {
events = this._events = Object.create(null); events = this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} else { } else {
// To avoid recursion in the case that type === "newListener"! Before if (events.newListener !== void 0) {
// adding it to the listeners, first emit "newListener". this.emit("newListener", type, unwrapListener(listener));
if (events.newListener !== undefined) {
this.emit('newListener', type, unwrapListener(listener));
// Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = this._events; events = this._events;
} }
existing = events[type]; existing = events[type];
} }
if (existing === undefined) { if (existing === void 0) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener; existing = events[type] = listener;
++this._eventsCount; ++this._eventsCount;
} else { } else {
if (typeof existing === 'function') { if (typeof existing === "function") {
// Adding the second element, need to change to array.
existing = events[type] = prepend ? [listener, existing] : [existing, listener]; existing = events[type] = prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) { } else if (prepend) {
existing.unshift(listener); existing.unshift(listener);
} else { } else {
existing.push(listener); existing.push(listener);
} }
// Check for listener leak
const m = this.getMaxListeners(); const m = this.getMaxListeners();
if (m > 0 && existing.length > m && !existing.warned) { if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true; existing.warned = true;
// No error code for this since it is a Warning const w = new Error("Possible EventEmitter memory leak detected. " + existing.length + " " + String(type) + " listeners added. Use emitter.setMaxListeners() to increase limit");
const w = new Error('Possible EventEmitter memory leak detected. ' + existing.length + ' ' + String(type) + ' listeners ' + 'added. Use emitter.setMaxListeners() to ' + 'increase limit'); w.name = "MaxListenersExceededWarning";
w.name = 'MaxListenersExceededWarning';
w.emitter = this; w.emitter = this;
w.type = type; w.type = type;
w.count = existing.length; w.count = existing.length;
if (!this._platform.isUnderTest()) { if (!this._platform.isUnderTest()) {
// eslint-disable-next-line no-console
console.warn(w); console.warn(w);
} }
} }
@@ -154,18 +141,20 @@ class EventEmitter {
removeListener(type, listener) { removeListener(type, listener) {
checkListener(listener); checkListener(listener);
const events = this._events; const events = this._events;
if (events === undefined) return this; if (events === void 0)
return this;
const list = events[type]; const list = events[type];
if (list === undefined) return this; if (list === void 0)
return this;
if (list === listener || list.listener === listener) { if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) { if (--this._eventsCount === 0) {
this._events = Object.create(null); this._events = /* @__PURE__ */ Object.create(null);
} else { } else {
var _listener;
delete events[type]; delete events[type];
if (events.removeListener) this.emit('removeListener', type, (_listener = list.listener) !== null && _listener !== void 0 ? _listener : listener); if (events.removeListener)
this.emit("removeListener", type, list.listener ?? listener);
} }
} else if (typeof list !== 'function') { } else if (typeof list !== "function") {
let position = -1; let position = -1;
let originalListener; let originalListener;
for (let i = list.length - 1; i >= 0; i--) { for (let i = list.length - 1; i >= 0; i--) {
@@ -175,10 +164,16 @@ class EventEmitter {
break; break;
} }
} }
if (position < 0) return this; if (position < 0)
if (position === 0) list.shift();else list.splice(position, 1); return this;
if (list.length === 1) events[type] = list[0]; if (position === 0)
if (events.removeListener !== undefined) this.emit('removeListener', type, originalListener || listener); list.shift();
else
list.splice(position, 1);
if (list.length === 1)
events[type] = list[0];
if (events.removeListener !== void 0)
this.emit("removeListener", type, originalListener || listener);
} }
return this; return this;
} }
@@ -187,52 +182,57 @@ class EventEmitter {
} }
removeAllListeners(type, options) { removeAllListeners(type, options) {
this._removeAllListeners(type); this._removeAllListeners(type);
if (!options) return this; if (!options)
if (options.behavior === 'wait') { return this;
if (options.behavior === "wait") {
const errors = []; const errors = [];
this._rejectionHandler = error => errors.push(error); this._rejectionHandler = (error) => errors.push(error);
return this._waitFor(type).then(() => { return this._waitFor(type).then(() => {
if (errors.length) throw errors[0]; if (errors.length)
throw errors[0];
}); });
} }
if (options.behavior === 'ignoreErrors') this._rejectionHandler = () => {}; if (options.behavior === "ignoreErrors")
this._rejectionHandler = () => {
};
return Promise.resolve(); return Promise.resolve();
} }
_removeAllListeners(type) { _removeAllListeners(type) {
const events = this._events; const events = this._events;
if (!events) return; if (!events)
return;
// not listening for removeListener, no need to emit
if (!events.removeListener) { if (!events.removeListener) {
if (type === undefined) { if (type === void 0) {
this._events = Object.create(null); this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
} else if (events[type] !== undefined) { } else if (events[type] !== void 0) {
if (--this._eventsCount === 0) this._events = Object.create(null);else delete events[type]; if (--this._eventsCount === 0)
this._events = /* @__PURE__ */ Object.create(null);
else
delete events[type];
} }
return; return;
} }
if (type === void 0) {
// emit removeListener for all listeners on all events
if (type === undefined) {
const keys = Object.keys(events); const keys = Object.keys(events);
let key; let key;
for (let i = 0; i < keys.length; ++i) { for (let i = 0; i < keys.length; ++i) {
key = keys[i]; key = keys[i];
if (key === 'removeListener') continue; if (key === "removeListener")
continue;
this._removeAllListeners(key); this._removeAllListeners(key);
} }
this._removeAllListeners('removeListener'); this._removeAllListeners("removeListener");
this._events = Object.create(null); this._events = /* @__PURE__ */ Object.create(null);
this._eventsCount = 0; this._eventsCount = 0;
return; return;
} }
const listeners = events[type]; const listeners = events[type];
if (typeof listeners === 'function') { if (typeof listeners === "function") {
this.removeListener(type, listeners); this.removeListener(type, listeners);
} else if (listeners !== undefined) { } else if (listeners !== void 0) {
// LIFO order for (let i = listeners.length - 1; i >= 0; i--)
for (let i = listeners.length - 1; i >= 0; i--) this.removeListener(type, listeners[i]); this.removeListener(type, listeners[i]);
} }
} }
listeners(type) { listeners(type) {
@@ -243,10 +243,12 @@ class EventEmitter {
} }
listenerCount(type) { listenerCount(type) {
const events = this._events; const events = this._events;
if (events !== undefined) { if (events !== void 0) {
const listener = events[type]; const listener = events[type];
if (typeof listener === 'function') return 1; if (typeof listener === "function")
if (listener !== undefined) return listener.length; return 1;
if (listener !== void 0)
return listener.length;
} }
return 0; return 0;
} }
@@ -256,33 +258,33 @@ class EventEmitter {
async _waitFor(type) { async _waitFor(type) {
let promises = []; let promises = [];
if (type) { if (type) {
promises = [...(this._pendingHandlers.get(type) || [])]; promises = [...this._pendingHandlers.get(type) || []];
} else { } else {
promises = []; promises = [];
for (const [, pending] of this._pendingHandlers) promises.push(...pending); for (const [, pending] of this._pendingHandlers)
promises.push(...pending);
} }
await Promise.all(promises); await Promise.all(promises);
} }
_listeners(target, type, unwrap) { _listeners(target, type, unwrap) {
const events = target._events; const events = target._events;
if (events === undefined) return []; if (events === void 0)
return [];
const listener = events[type]; const listener = events[type];
if (listener === undefined) return []; if (listener === void 0)
if (typeof listener === 'function') return unwrap ? [unwrapListener(listener)] : [listener]; return [];
if (typeof listener === "function")
return unwrap ? [unwrapListener(listener)] : [listener];
return unwrap ? unwrapListeners(listener) : listener.slice(); return unwrap ? unwrapListeners(listener) : listener.slice();
} }
} }
exports.EventEmitter = EventEmitter;
function checkListener(listener) { function checkListener(listener) {
if (typeof listener !== 'function') throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener); if (typeof listener !== "function")
throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
} }
class OnceWrapper { class OnceWrapper {
constructor(eventEmitter, eventType, listener) { constructor(eventEmitter, eventType, listener) {
this._fired = false; this._fired = false;
this.wrapperFunction = void 0;
this._listener = void 0;
this._eventEmitter = void 0;
this._eventType = void 0;
this._eventEmitter = eventEmitter; this._eventEmitter = eventEmitter;
this._eventType = eventType; this._eventType = eventType;
this._listener = listener; this._listener = listener;
@@ -290,22 +292,23 @@ class OnceWrapper {
this.wrapperFunction.listener = listener; this.wrapperFunction.listener = listener;
} }
_handle(...args) { _handle(...args) {
if (this._fired) return; if (this._fired)
return;
this._fired = true; this._fired = true;
this._eventEmitter.removeListener(this._eventType, this.wrapperFunction); this._eventEmitter.removeListener(this._eventType, this.wrapperFunction);
return this._listener.apply(this._eventEmitter, args); return this._listener.apply(this._eventEmitter, args);
} }
} }
function unwrapListener(l) { function unwrapListener(l) {
var _wrappedListener; return wrappedListener(l) ?? l;
return (_wrappedListener = wrappedListener(l)) !== null && _wrappedListener !== void 0 ? _wrappedListener : l;
} }
function unwrapListeners(arr) { function unwrapListeners(arr) {
return arr.map(l => { return arr.map((l) => wrappedListener(l) ?? l);
var _wrappedListener2;
return (_wrappedListener2 = wrappedListener(l)) !== null && _wrappedListener2 !== void 0 ? _wrappedListener2 : l;
});
} }
function wrappedListener(l) { function wrappedListener(l) {
return l.listener; return l.listener;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
EventEmitter
});

View File

@@ -1,94 +1,98 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var events_exports = {};
__export(events_exports, {
Events: () => Events
}); });
exports.Events = void 0; module.exports = __toCommonJS(events_exports);
/** const Events = {
* Copyright 2019 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const Events = exports.Events = {
AndroidDevice: { AndroidDevice: {
WebView: 'webview', WebView: "webview",
Close: 'close' Close: "close"
}, },
AndroidSocket: { AndroidSocket: {
Data: 'data', Data: "data",
Close: 'close' Close: "close"
}, },
AndroidWebView: { AndroidWebView: {
Close: 'close' Close: "close"
}, },
Browser: { Browser: {
Disconnected: 'disconnected' Disconnected: "disconnected"
}, },
BrowserContext: { BrowserContext: {
Console: 'console', Console: "console",
Close: 'close', Close: "close",
Dialog: 'dialog', Dialog: "dialog",
Page: 'page', Page: "page",
// Can't use just 'error' due to node.js special treatment of error events. // Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events // @see https://nodejs.org/api/events.html#events_error_events
WebError: 'weberror', WebError: "weberror",
BackgroundPage: 'backgroundpage', BackgroundPage: "backgroundpage",
ServiceWorker: 'serviceworker', ServiceWorker: "serviceworker",
Request: 'request', Request: "request",
Response: 'response', Response: "response",
RequestFailed: 'requestfailed', RequestFailed: "requestfailed",
RequestFinished: 'requestfinished' RequestFinished: "requestfinished"
}, },
BrowserServer: { BrowserServer: {
Close: 'close' Close: "close"
}, },
Page: { Page: {
Close: 'close', Close: "close",
Crash: 'crash', Crash: "crash",
Console: 'console', Console: "console",
Dialog: 'dialog', Dialog: "dialog",
Download: 'download', Download: "download",
FileChooser: 'filechooser', FileChooser: "filechooser",
DOMContentLoaded: 'domcontentloaded', DOMContentLoaded: "domcontentloaded",
// Can't use just 'error' due to node.js special treatment of error events. // Can't use just 'error' due to node.js special treatment of error events.
// @see https://nodejs.org/api/events.html#events_error_events // @see https://nodejs.org/api/events.html#events_error_events
PageError: 'pageerror', PageError: "pageerror",
Request: 'request', Request: "request",
Response: 'response', Response: "response",
RequestFailed: 'requestfailed', RequestFailed: "requestfailed",
RequestFinished: 'requestfinished', RequestFinished: "requestfinished",
FrameAttached: 'frameattached', FrameAttached: "frameattached",
FrameDetached: 'framedetached', FrameDetached: "framedetached",
FrameNavigated: 'framenavigated', FrameNavigated: "framenavigated",
Load: 'load', Load: "load",
Popup: 'popup', Popup: "popup",
WebSocket: 'websocket', WebSocket: "websocket",
Worker: 'worker' Worker: "worker"
}, },
WebSocket: { WebSocket: {
Close: 'close', Close: "close",
Error: 'socketerror', Error: "socketerror",
FrameReceived: 'framereceived', FrameReceived: "framereceived",
FrameSent: 'framesent' FrameSent: "framesent"
}, },
Worker: { Worker: {
Close: 'close' Close: "close"
}, },
ElectronApplication: { ElectronApplication: {
Close: 'close', Close: "close",
Console: 'console', Console: "console",
Window: 'window' Window: "window"
} }
}; };
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Events
});

View File

@@ -1,161 +1,159 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var fetch_exports = {};
__export(fetch_exports, {
APIRequest: () => APIRequest,
APIRequestContext: () => APIRequestContext,
APIResponse: () => APIResponse
}); });
exports.APIResponse = exports.APIRequestContext = exports.APIRequest = void 0; module.exports = __toCommonJS(fetch_exports);
var _browserContext = require("./browserContext"); var import_browserContext = require("./browserContext");
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _errors = require("./errors"); var import_errors = require("./errors");
var _network = require("./network"); var import_network = require("./network");
var _tracing = require("./tracing"); var import_tracing = require("./tracing");
var _assert = require("../utils/isomorphic/assert"); var import_assert = require("../utils/isomorphic/assert");
var _fileUtils = require("./fileUtils"); var import_fileUtils = require("./fileUtils");
var _headers = require("../utils/isomorphic/headers"); var import_headers = require("../utils/isomorphic/headers");
var _rtti = require("../utils/isomorphic/rtti"); var import_rtti = require("../utils/isomorphic/rtti");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class APIRequest { class APIRequest {
constructor(playwright) { constructor(playwright) {
this._playwright = void 0; this._contexts = /* @__PURE__ */ new Set();
this._contexts = new Set();
this._playwright = playwright; this._playwright = playwright;
} }
async newContext(options = {}) { async newContext(options = {}) {
var _this$_playwright$_de, _this$_playwright$_de2;
options = { options = {
...this._playwright._defaultContextOptions, ...this._playwright._defaultContextOptions,
timeout: this._playwright._defaultContextTimeout, timeout: this._playwright._defaultContextTimeout,
...options ...options
}; };
const storageState = typeof options.storageState === 'string' ? JSON.parse(await this._playwright._platform.fs().promises.readFile(options.storageState, 'utf8')) : options.storageState; const storageState = typeof options.storageState === "string" ? JSON.parse(await this._playwright._platform.fs().promises.readFile(options.storageState, "utf8")) : options.storageState;
const context = APIRequestContext.from((await this._playwright._channel.newRequest({ const context = APIRequestContext.from((await this._playwright._channel.newRequest({
...options, ...options,
extraHTTPHeaders: options.extraHTTPHeaders ? (0, _headers.headersObjectToArray)(options.extraHTTPHeaders) : undefined, extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.headersObjectToArray)(options.extraHTTPHeaders) : void 0,
storageState, storageState,
tracesDir: (_this$_playwright$_de = this._playwright._defaultLaunchOptions) === null || _this$_playwright$_de === void 0 ? void 0 : _this$_playwright$_de.tracesDir, tracesDir: this._playwright._defaultLaunchOptions?.tracesDir,
// We do not expose tracesDir in the API, so do not allow options to accidentally override it. // We do not expose tracesDir in the API, so do not allow options to accidentally override it.
clientCertificates: await (0, _browserContext.toClientCertificatesProtocol)(this._playwright._platform, options.clientCertificates) clientCertificates: await (0, import_browserContext.toClientCertificatesProtocol)(this._playwright._platform, options.clientCertificates)
})).request); })).request);
this._contexts.add(context); this._contexts.add(context);
context._request = this; context._request = this;
context._tracing._tracesDir = (_this$_playwright$_de2 = this._playwright._defaultLaunchOptions) === null || _this$_playwright$_de2 === void 0 ? void 0 : _this$_playwright$_de2.tracesDir; context._tracing._tracesDir = this._playwright._defaultLaunchOptions?.tracesDir;
await context._instrumentation.runAfterCreateRequestContext(context); await context._instrumentation.runAfterCreateRequestContext(context);
return context; return context;
} }
} }
exports.APIRequest = APIRequest; class APIRequestContext extends import_channelOwner.ChannelOwner {
class APIRequestContext extends _channelOwner.ChannelOwner {
static from(channel) { static from(channel) {
return channel._object; return channel._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._request = void 0; this._tracing = import_tracing.Tracing.from(initializer.tracing);
this._tracing = void 0;
this._closeReason = void 0;
this._tracing = _tracing.Tracing.from(initializer.tracing);
} }
async [Symbol.asyncDispose]() { async [Symbol.asyncDispose]() {
await this.dispose(); await this.dispose();
} }
async dispose(options = {}) { async dispose(options = {}) {
var _this$_request;
this._closeReason = options.reason; this._closeReason = options.reason;
await this._instrumentation.runBeforeCloseRequestContext(this); await this._instrumentation.runBeforeCloseRequestContext(this);
try { try {
await this._channel.dispose(options); await this._channel.dispose(options);
} catch (e) { } catch (e) {
if ((0, _errors.isTargetClosedError)(e)) return; if ((0, import_errors.isTargetClosedError)(e))
return;
throw e; throw e;
} }
this._tracing._resetStackCounter(); this._tracing._resetStackCounter();
(_this$_request = this._request) === null || _this$_request === void 0 || _this$_request._contexts.delete(this); this._request?._contexts.delete(this);
} }
async delete(url, options) { async delete(url, options) {
return await this.fetch(url, { return await this.fetch(url, {
...options, ...options,
method: 'DELETE' method: "DELETE"
}); });
} }
async head(url, options) { async head(url, options) {
return await this.fetch(url, { return await this.fetch(url, {
...options, ...options,
method: 'HEAD' method: "HEAD"
}); });
} }
async get(url, options) { async get(url, options) {
return await this.fetch(url, { return await this.fetch(url, {
...options, ...options,
method: 'GET' method: "GET"
}); });
} }
async patch(url, options) { async patch(url, options) {
return await this.fetch(url, { return await this.fetch(url, {
...options, ...options,
method: 'PATCH' method: "PATCH"
}); });
} }
async post(url, options) { async post(url, options) {
return await this.fetch(url, { return await this.fetch(url, {
...options, ...options,
method: 'POST' method: "POST"
}); });
} }
async put(url, options) { async put(url, options) {
return await this.fetch(url, { return await this.fetch(url, {
...options, ...options,
method: 'PUT' method: "PUT"
}); });
} }
async fetch(urlOrRequest, options = {}) { async fetch(urlOrRequest, options = {}) {
const url = (0, _rtti.isString)(urlOrRequest) ? urlOrRequest : undefined; const url = (0, import_rtti.isString)(urlOrRequest) ? urlOrRequest : void 0;
const request = (0, _rtti.isString)(urlOrRequest) ? undefined : urlOrRequest; const request = (0, import_rtti.isString)(urlOrRequest) ? void 0 : urlOrRequest;
return await this._innerFetch({ return await this._innerFetch({ url, request, ...options });
url,
request,
...options
});
} }
async _innerFetch(options = {}) { async _innerFetch(options = {}) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
var _options$request, _options$request2, _options$request3; if (this._closeReason)
if (this._closeReason) throw new _errors.TargetClosedError(this._closeReason); throw new import_errors.TargetClosedError(this._closeReason);
(0, _assert.assert)(options.request || typeof options.url === 'string', 'First argument must be either URL string or Request'); (0, import_assert.assert)(options.request || typeof options.url === "string", "First argument must be either URL string or Request");
(0, _assert.assert)((options.data === undefined ? 0 : 1) + (options.form === undefined ? 0 : 1) + (options.multipart === undefined ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`); (0, import_assert.assert)((options.data === void 0 ? 0 : 1) + (options.form === void 0 ? 0 : 1) + (options.multipart === void 0 ? 0 : 1) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
(0, _assert.assert)(options.maxRedirects === undefined || options.maxRedirects >= 0, `'maxRedirects' must be greater than or equal to '0'`); (0, import_assert.assert)(options.maxRedirects === void 0 || options.maxRedirects >= 0, `'maxRedirects' must be greater than or equal to '0'`);
(0, _assert.assert)(options.maxRetries === undefined || options.maxRetries >= 0, `'maxRetries' must be greater than or equal to '0'`); (0, import_assert.assert)(options.maxRetries === void 0 || options.maxRetries >= 0, `'maxRetries' must be greater than or equal to '0'`);
const url = options.url !== undefined ? options.url : options.request.url(); const url = options.url !== void 0 ? options.url : options.request.url();
const method = options.method || ((_options$request = options.request) === null || _options$request === void 0 ? void 0 : _options$request.method()); const method = options.method || options.request?.method();
let encodedParams = undefined; let encodedParams = void 0;
if (typeof options.params === 'string') encodedParams = options.params;else if (options.params instanceof URLSearchParams) encodedParams = options.params.toString(); if (typeof options.params === "string")
// Cannot call allHeaders() here as the request may be paused inside route handler. encodedParams = options.params;
const headersObj = options.headers || ((_options$request2 = options.request) === null || _options$request2 === void 0 ? void 0 : _options$request2.headers()); else if (options.params instanceof URLSearchParams)
const headers = headersObj ? (0, _headers.headersObjectToArray)(headersObj) : undefined; encodedParams = options.params.toString();
const headersObj = options.headers || options.request?.headers();
const headers = headersObj ? (0, import_headers.headersObjectToArray)(headersObj) : void 0;
let jsonData; let jsonData;
let formData; let formData;
let multipartData; let multipartData;
let postDataBuffer; let postDataBuffer;
if (options.data !== undefined) { if (options.data !== void 0) {
if ((0, _rtti.isString)(options.data)) { if ((0, import_rtti.isString)(options.data)) {
if (isJsonContentType(headers)) jsonData = isJsonParsable(options.data) ? options.data : JSON.stringify(options.data);else postDataBuffer = Buffer.from(options.data, 'utf8'); if (isJsonContentType(headers))
jsonData = isJsonParsable(options.data) ? options.data : JSON.stringify(options.data);
else
postDataBuffer = Buffer.from(options.data, "utf8");
} else if (Buffer.isBuffer(options.data)) { } else if (Buffer.isBuffer(options.data)) {
postDataBuffer = options.data; postDataBuffer = options.data;
} else if (typeof options.data === 'object' || typeof options.data === 'number' || typeof options.data === 'boolean') { } else if (typeof options.data === "object" || typeof options.data === "number" || typeof options.data === "boolean") {
jsonData = JSON.stringify(options.data); jsonData = JSON.stringify(options.data);
} else { } else {
throw new Error(`Unexpected 'data' type`); throw new Error(`Unexpected 'data' type`);
@@ -164,11 +162,9 @@ class APIRequestContext extends _channelOwner.ChannelOwner {
if (globalThis.FormData && options.form instanceof FormData) { if (globalThis.FormData && options.form instanceof FormData) {
formData = []; formData = [];
for (const [name, value] of options.form.entries()) { for (const [name, value] of options.form.entries()) {
if (typeof value !== 'string') throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`); if (typeof value !== "string")
formData.push({ throw new Error(`Expected string for options.form["${name}"], found File. Please use options.multipart instead.`);
name, formData.push({ name, value });
value
});
} }
} else { } else {
formData = objectToArray(options.form); formData = objectToArray(options.form);
@@ -178,35 +174,30 @@ class APIRequestContext extends _channelOwner.ChannelOwner {
if (globalThis.FormData && options.multipart instanceof FormData) { if (globalThis.FormData && options.multipart instanceof FormData) {
const form = options.multipart; const form = options.multipart;
for (const [name, value] of form.entries()) { for (const [name, value] of form.entries()) {
if ((0, _rtti.isString)(value)) { if ((0, import_rtti.isString)(value)) {
multipartData.push({ multipartData.push({ name, value });
name,
value
});
} else { } else {
const file = { const file = {
name: value.name, name: value.name,
mimeType: value.type, mimeType: value.type,
buffer: Buffer.from(await value.arrayBuffer()) buffer: Buffer.from(await value.arrayBuffer())
}; };
multipartData.push({ multipartData.push({ name, file });
name,
file
});
} }
} }
} else { } else {
// Convert file-like values to ServerFilePayload structs. for (const [name, value] of Object.entries(options.multipart))
for (const [name, value] of Object.entries(options.multipart)) multipartData.push(await toFormField(this._platform, name, value)); multipartData.push(await toFormField(this._platform, name, value));
} }
} }
if (postDataBuffer === undefined && jsonData === undefined && formData === undefined && multipartData === undefined) postDataBuffer = ((_options$request3 = options.request) === null || _options$request3 === void 0 ? void 0 : _options$request3.postDataBuffer()) || undefined; if (postDataBuffer === void 0 && jsonData === void 0 && formData === void 0 && multipartData === void 0)
postDataBuffer = options.request?.postDataBuffer() || void 0;
const fixtures = { const fixtures = {
__testHookLookup: options.__testHookLookup __testHookLookup: options.__testHookLookup
}; };
const result = await this._channel.fetch({ const result = await this._channel.fetch({
url, url,
params: typeof options.params === 'object' ? objectToArray(options.params) : undefined, params: typeof options.params === "object" ? objectToArray(options.params) : void 0,
encodedParams, encodedParams,
method, method,
headers, headers,
@@ -225,56 +216,47 @@ class APIRequestContext extends _channelOwner.ChannelOwner {
}); });
} }
async storageState(options = {}) { async storageState(options = {}) {
const state = await this._channel.storageState({ const state = await this._channel.storageState({ indexedDB: options.indexedDB });
indexedDB: options.indexedDB
});
if (options.path) { if (options.path) {
await (0, _fileUtils.mkdirIfNeeded)(this._platform, options.path); await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8'); await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8");
} }
return state; return state;
} }
} }
exports.APIRequestContext = APIRequestContext;
async function toFormField(platform, name, value) { async function toFormField(platform, name, value) {
const typeOfValue = typeof value; const typeOfValue = typeof value;
if (isFilePayload(value)) { if (isFilePayload(value)) {
const payload = value; const payload = value;
if (!Buffer.isBuffer(payload.buffer)) throw new Error(`Unexpected buffer type of 'data.${name}'`); if (!Buffer.isBuffer(payload.buffer))
return { throw new Error(`Unexpected buffer type of 'data.${name}'`);
name, return { name, file: filePayloadToJson(payload) };
file: filePayloadToJson(payload) } else if (typeOfValue === "string" || typeOfValue === "number" || typeOfValue === "boolean") {
}; return { name, value: String(value) };
} else if (typeOfValue === 'string' || typeOfValue === 'number' || typeOfValue === 'boolean') {
return {
name,
value: String(value)
};
} else { } else {
return { return { name, file: await readStreamToJson(platform, value) };
name,
file: await readStreamToJson(platform, value)
};
} }
} }
function isJsonParsable(value) { function isJsonParsable(value) {
if (typeof value !== 'string') return false; if (typeof value !== "string")
return false;
try { try {
JSON.parse(value); JSON.parse(value);
return true; return true;
} catch (e) { } catch (e) {
if (e instanceof SyntaxError) return false;else throw e; if (e instanceof SyntaxError)
return false;
else
throw e;
} }
} }
class APIResponse { class APIResponse {
constructor(context, initializer) { constructor(context, initializer) {
this._initializer = void 0;
this._headers = void 0;
this._request = void 0;
this._request = context; this._request = context;
this._initializer = initializer; this._initializer = initializer;
this._headers = new _network.RawHeaders(this._initializer.headers); this._headers = new import_network.RawHeaders(this._initializer.headers);
if (context._platform.inspectCustom) this[context._platform.inspectCustom] = () => this._inspect(); if (context._platform.inspectCustom)
this[context._platform.inspectCustom] = () => this._inspect();
} }
ok() { ok() {
return this._initializer.status >= 200 && this._initializer.status <= 299; return this._initializer.status >= 200 && this._initializer.status <= 299;
@@ -297,20 +279,20 @@ class APIResponse {
async body() { async body() {
return await this._request._wrapApiCall(async () => { return await this._request._wrapApiCall(async () => {
try { try {
const result = await this._request._channel.fetchResponseBody({ const result = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() });
fetchUid: this._fetchUid() if (result.binary === void 0)
}); throw new Error("Response has been disposed");
if (result.binary === undefined) throw new Error('Response has been disposed');
return result.binary; return result.binary;
} catch (e) { } catch (e) {
if ((0, _errors.isTargetClosedError)(e)) throw new Error('Response has been disposed'); if ((0, import_errors.isTargetClosedError)(e))
throw new Error("Response has been disposed");
throw e; throw e;
} }
}, true); }, true);
} }
async text() { async text() {
const content = await this.body(); const content = await this.body();
return content.toString('utf8'); return content.toString("utf8");
} }
async json() { async json() {
const content = await this.text(); const content = await this.text();
@@ -320,30 +302,21 @@ class APIResponse {
await this.dispose(); await this.dispose();
} }
async dispose() { async dispose() {
await this._request._channel.disposeAPIResponse({ await this._request._channel.disposeAPIResponse({ fetchUid: this._fetchUid() });
fetchUid: this._fetchUid()
});
} }
_inspect() { _inspect() {
const headers = this.headersArray().map(({ const headers = this.headersArray().map(({ name, value }) => ` ${name}: ${value}`);
name, return `APIResponse: ${this.status()} ${this.statusText()}
value ${headers.join("\n")}`;
}) => ` ${name}: ${value}`);
return `APIResponse: ${this.status()} ${this.statusText()}\n${headers.join('\n')}`;
} }
_fetchUid() { _fetchUid() {
return this._initializer.fetchUid; return this._initializer.fetchUid;
} }
async _fetchLog() { async _fetchLog() {
const { const { log } = await this._request._channel.fetchLog({ fetchUid: this._fetchUid() });
log
} = await this._request._channel.fetchLog({
fetchUid: this._fetchUid()
});
return log; return log;
} }
} }
exports.APIResponse = APIResponse;
function filePayloadToJson(payload) { function filePayloadToJson(payload) {
return { return {
name: payload.name, name: payload.name,
@@ -354,37 +327,41 @@ function filePayloadToJson(payload) {
async function readStreamToJson(platform, stream) { async function readStreamToJson(platform, stream) {
const buffer = await new Promise((resolve, reject) => { const buffer = await new Promise((resolve, reject) => {
const chunks = []; const chunks = [];
stream.on('data', chunk => chunks.push(chunk)); stream.on("data", (chunk) => chunks.push(chunk));
stream.on('end', () => resolve(Buffer.concat(chunks))); stream.on("end", () => resolve(Buffer.concat(chunks)));
stream.on('error', err => reject(err)); stream.on("error", (err) => reject(err));
}); });
const streamPath = Buffer.isBuffer(stream.path) ? stream.path.toString('utf8') : stream.path; const streamPath = Buffer.isBuffer(stream.path) ? stream.path.toString("utf8") : stream.path;
return { return {
name: platform.path().basename(streamPath), name: platform.path().basename(streamPath),
buffer buffer
}; };
} }
function isJsonContentType(headers) { function isJsonContentType(headers) {
if (!headers) return false; if (!headers)
for (const { return false;
name, for (const { name, value } of headers) {
value if (name.toLocaleLowerCase() === "content-type")
} of headers) { return value === "application/json";
if (name.toLocaleLowerCase() === 'content-type') return value === 'application/json';
} }
return false; return false;
} }
function objectToArray(map) { function objectToArray(map) {
if (!map) return undefined; if (!map)
return void 0;
const result = []; const result = [];
for (const [name, value] of Object.entries(map)) { for (const [name, value] of Object.entries(map)) {
if (value !== undefined) result.push({ if (value !== void 0)
name, result.push({ name, value: String(value) });
value: String(value)
});
} }
return result; return result;
} }
function isFilePayload(value) { function isFilePayload(value) {
return typeof value === 'object' && value['name'] && value['mimeType'] && value['buffer']; return typeof value === "object" && value["name"] && value["mimeType"] && value["buffer"];
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
APIRequest,
APIRequestContext,
APIResponse
});

View File

@@ -1,30 +1,28 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var fileChooser_exports = {};
__export(fileChooser_exports, {
FileChooser: () => FileChooser
}); });
exports.FileChooser = void 0; module.exports = __toCommonJS(fileChooser_exports);
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class FileChooser { class FileChooser {
constructor(page, elementHandle, isMultiple) { constructor(page, elementHandle, isMultiple) {
this._page = void 0;
this._elementHandle = void 0;
this._isMultiple = void 0;
this._page = page; this._page = page;
this._elementHandle = elementHandle; this._elementHandle = elementHandle;
this._isMultiple = isMultiple; this._isMultiple = isMultiple;
@@ -42,4 +40,7 @@ class FileChooser {
return await this._elementHandle.setInputFiles(files, options); return await this._elementHandle.setInputFiles(files, options);
} }
} }
exports.FileChooser = FileChooser; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FileChooser
});

View File

@@ -1,123 +1,129 @@
"use strict"; "use strict";
var __create = Object.create;
Object.defineProperty(exports, "__esModule", { var __defProp = Object.defineProperty;
value: true var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var frame_exports = {};
__export(frame_exports, {
Frame: () => Frame,
verifyLoadState: () => verifyLoadState
}); });
exports.Frame = void 0; module.exports = __toCommonJS(frame_exports);
exports.verifyLoadState = verifyLoadState; var import_eventEmitter = require("./eventEmitter");
var _eventEmitter = require("./eventEmitter"); var import_channelOwner = require("./channelOwner");
var _channelOwner = require("./channelOwner"); var import_clientHelper = require("./clientHelper");
var _clientHelper = require("./clientHelper"); var import_elementHandle = require("./elementHandle");
var _elementHandle = require("./elementHandle"); var import_events = require("./events");
var _events = require("./events"); var import_jsHandle = require("./jsHandle");
var _jsHandle = require("./jsHandle"); var import_locator = require("./locator");
var _locator = require("./locator"); var network = __toESM(require("./network"));
var network = _interopRequireWildcard(require("./network")); var import_types = require("./types");
var _types = require("./types"); var import_waiter = require("./waiter");
var _waiter = require("./waiter"); var import_assert = require("../utils/isomorphic/assert");
var _assert = require("../utils/isomorphic/assert"); var import_locatorUtils = require("../utils/isomorphic/locatorUtils");
var _locatorUtils = require("../utils/isomorphic/locatorUtils"); var import_urlMatch = require("../utils/isomorphic/urlMatch");
var _urlMatch = require("../utils/isomorphic/urlMatch"); class Frame extends import_channelOwner.ChannelOwner {
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } constructor(parent, type, guid, initializer) {
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } super(parent, type, guid, initializer);
/** this._parentFrame = null;
* Copyright 2017 Google Inc. All rights reserved. this._url = "";
* Modifications copyright (c) Microsoft Corporation. this._name = "";
* this._detached = false;
* Licensed under the Apache License, Version 2.0 (the "License"); this._childFrames = /* @__PURE__ */ new Set();
* you may not use this file except in compliance with the License. this._eventEmitter = new import_eventEmitter.EventEmitter(parent._platform);
* You may obtain a copy of the License at this._eventEmitter.setMaxListeners(0);
* this._parentFrame = Frame.fromNullable(initializer.parentFrame);
* http://www.apache.org/licenses/LICENSE-2.0 if (this._parentFrame)
* this._parentFrame._childFrames.add(this);
* Unless required by applicable law or agreed to in writing, software this._name = initializer.name;
* distributed under the License is distributed on an "AS IS" BASIS, this._url = initializer.url;
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. this._loadStates = new Set(initializer.loadStates);
* See the License for the specific language governing permissions and this._channel.on("loadstate", (event) => {
* limitations under the License. if (event.add) {
*/ this._loadStates.add(event.add);
this._eventEmitter.emit("loadstate", event.add);
class Frame extends _channelOwner.ChannelOwner { }
if (event.remove)
this._loadStates.delete(event.remove);
if (!this._parentFrame && event.add === "load" && this._page)
this._page.emit(import_events.Events.Page.Load, this._page);
if (!this._parentFrame && event.add === "domcontentloaded" && this._page)
this._page.emit(import_events.Events.Page.DOMContentLoaded, this._page);
});
this._channel.on("navigated", (event) => {
this._url = event.url;
this._name = event.name;
this._eventEmitter.emit("navigated", event);
if (!event.error && this._page)
this._page.emit(import_events.Events.Page.FrameNavigated, this);
});
}
static from(frame) { static from(frame) {
return frame._object; return frame._object;
} }
static fromNullable(frame) { static fromNullable(frame) {
return frame ? Frame.from(frame) : null; return frame ? Frame.from(frame) : null;
} }
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._eventEmitter = void 0;
this._loadStates = void 0;
this._parentFrame = null;
this._url = '';
this._name = '';
this._detached = false;
this._childFrames = new Set();
this._page = void 0;
this._eventEmitter = new _eventEmitter.EventEmitter(parent._platform);
this._eventEmitter.setMaxListeners(0);
this._parentFrame = Frame.fromNullable(initializer.parentFrame);
if (this._parentFrame) this._parentFrame._childFrames.add(this);
this._name = initializer.name;
this._url = initializer.url;
this._loadStates = new Set(initializer.loadStates);
this._channel.on('loadstate', event => {
if (event.add) {
this._loadStates.add(event.add);
this._eventEmitter.emit('loadstate', event.add);
}
if (event.remove) this._loadStates.delete(event.remove);
if (!this._parentFrame && event.add === 'load' && this._page) this._page.emit(_events.Events.Page.Load, this._page);
if (!this._parentFrame && event.add === 'domcontentloaded' && this._page) this._page.emit(_events.Events.Page.DOMContentLoaded, this._page);
});
this._channel.on('navigated', event => {
this._url = event.url;
this._name = event.name;
this._eventEmitter.emit('navigated', event);
if (!event.error && this._page) this._page.emit(_events.Events.Page.FrameNavigated, this);
});
}
page() { page() {
return this._page; return this._page;
} }
async goto(url, options = {}) { async goto(url, options = {}) {
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return network.Response.fromNullable((await this._channel.goto({ return network.Response.fromNullable((await this._channel.goto({ url, ...options, waitUntil })).response);
url,
...options,
waitUntil
})).response);
} }
_setupNavigationWaiter(options) { _setupNavigationWaiter(options) {
const waiter = new _waiter.Waiter(this._page, ''); const waiter = new import_waiter.Waiter(this._page, "");
if (this._page.isClosed()) waiter.rejectImmediately(this._page._closeErrorWithReason()); if (this._page.isClosed())
waiter.rejectOnEvent(this._page, _events.Events.Page.Close, () => this._page._closeErrorWithReason()); waiter.rejectImmediately(this._page._closeErrorWithReason());
waiter.rejectOnEvent(this._page, _events.Events.Page.Crash, new Error('Navigation failed because page crashed!')); waiter.rejectOnEvent(this._page, import_events.Events.Page.Close, () => this._page._closeErrorWithReason());
waiter.rejectOnEvent(this._page, _events.Events.Page.FrameDetached, new Error('Navigating frame was detached!'), frame => frame === this); waiter.rejectOnEvent(this._page, import_events.Events.Page.Crash, new Error("Navigation failed because page crashed!"));
waiter.rejectOnEvent(this._page, import_events.Events.Page.FrameDetached, new Error("Navigating frame was detached!"), (frame) => frame === this);
const timeout = this._page._timeoutSettings.navigationTimeout(options); const timeout = this._page._timeoutSettings.navigationTimeout(options);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`); waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded.`);
return waiter; return waiter;
} }
async waitForNavigation(options = {}) { async waitForNavigation(options = {}) {
return await this._page._wrapApiCall(async () => { return await this._page._wrapApiCall(async () => {
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
const waiter = this._setupNavigationWaiter(options); const waiter = this._setupNavigationWaiter(options);
const toUrl = typeof options.url === 'string' ? ` to "${options.url}"` : ''; const toUrl = typeof options.url === "string" ? ` to "${options.url}"` : "";
waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`); waiter.log(`waiting for navigation${toUrl} until "${waitUntil}"`);
const navigatedEvent = await waiter.waitForEvent(this._eventEmitter, 'navigated', event => { const navigatedEvent = await waiter.waitForEvent(this._eventEmitter, "navigated", (event) => {
var _this$_page; if (event.error)
// Any failed navigation results in a rejection. return true;
if (event.error) return true;
waiter.log(` navigated to "${event.url}"`); waiter.log(` navigated to "${event.url}"`);
return (0, _urlMatch.urlMatches)((_this$_page = this._page) === null || _this$_page === void 0 ? void 0 : _this$_page.context()._options.baseURL, event.url, options.url); return (0, import_urlMatch.urlMatches)(this._page?.context()._options.baseURL, event.url, options.url);
}); });
if (navigatedEvent.error) { if (navigatedEvent.error) {
const e = new Error(navigatedEvent.error); const e = new Error(navigatedEvent.error);
e.stack = ''; e.stack = "";
await waiter.waitForPromise(Promise.reject(e)); await waiter.waitForPromise(Promise.reject(e));
} }
if (!this._loadStates.has(waitUntil)) { if (!this._loadStates.has(waitUntil)) {
await waiter.waitForEvent(this._eventEmitter, 'loadstate', s => { await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => {
waiter.log(` "${s}" event fired`); waiter.log(` "${s}" event fired`);
return s === waitUntil; return s === waitUntil;
}); });
@@ -128,14 +134,14 @@ class Frame extends _channelOwner.ChannelOwner {
return response; return response;
}); });
} }
async waitForLoadState(state = 'load', options = {}) { async waitForLoadState(state = "load", options = {}) {
state = verifyLoadState('state', state); state = verifyLoadState("state", state);
return await this._page._wrapApiCall(async () => { return await this._page._wrapApiCall(async () => {
const waiter = this._setupNavigationWaiter(options); const waiter = this._setupNavigationWaiter(options);
if (this._loadStates.has(state)) { if (this._loadStates.has(state)) {
waiter.log(` not waiting, "${state}" event already fired`); waiter.log(` not waiting, "${state}" event already fired`);
} else { } else {
await waiter.waitForEvent(this._eventEmitter, 'loadstate', s => { await waiter.waitForEvent(this._eventEmitter, "loadstate", (s) => {
waiter.log(` "${s}" event fired`); waiter.log(` "${s}" event fired`);
return s === state; return s === state;
}); });
@@ -144,111 +150,69 @@ class Frame extends _channelOwner.ChannelOwner {
}); });
} }
async waitForURL(url, options = {}) { async waitForURL(url, options = {}) {
var _this$_page2; if ((0, import_urlMatch.urlMatches)(this._page?.context()._options.baseURL, this.url(), url))
if ((0, _urlMatch.urlMatches)((_this$_page2 = this._page) === null || _this$_page2 === void 0 ? void 0 : _this$_page2.context()._options.baseURL, this.url(), url)) return await this.waitForLoadState(options.waitUntil, options); return await this.waitForLoadState(options.waitUntil, options);
await this.waitForNavigation({ await this.waitForNavigation({ url, ...options });
url,
...options
});
} }
async frameElement() { async frameElement() {
return _elementHandle.ElementHandle.from((await this._channel.frameElement()).element); return import_elementHandle.ElementHandle.from((await this._channel.frameElement()).element);
} }
async evaluateHandle(pageFunction, arg) { async evaluateHandle(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2); (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpressionHandle({ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
expression: String(pageFunction), return import_jsHandle.JSHandle.from(result.handle);
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return _jsHandle.JSHandle.from(result.handle);
} }
async evaluate(pageFunction, arg) { async evaluate(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2); (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpression({ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
expression: String(pageFunction), return (0, import_jsHandle.parseResult)(result.value);
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async _evaluateExposeUtilityScript(pageFunction, arg) { async _evaluateExposeUtilityScript(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2); (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpression({ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
expression: String(pageFunction), return (0, import_jsHandle.parseResult)(result.value);
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async $(selector, options) { async $(selector, options) {
const result = await this._channel.querySelector({ const result = await this._channel.querySelector({ selector, ...options });
selector, return import_elementHandle.ElementHandle.fromNullable(result.element);
...options
});
return _elementHandle.ElementHandle.fromNullable(result.element);
} }
async waitForSelector(selector, options = {}) { async waitForSelector(selector, options = {}) {
if (options.visibility) throw new Error('options.visibility is not supported, did you mean options.state?'); if (options.visibility)
if (options.waitFor && options.waitFor !== 'visible') throw new Error('options.waitFor is not supported, did you mean options.state?'); throw new Error("options.visibility is not supported, did you mean options.state?");
const result = await this._channel.waitForSelector({ if (options.waitFor && options.waitFor !== "visible")
selector, throw new Error("options.waitFor is not supported, did you mean options.state?");
...options const result = await this._channel.waitForSelector({ selector, ...options });
}); return import_elementHandle.ElementHandle.fromNullable(result.element);
return _elementHandle.ElementHandle.fromNullable(result.element);
} }
async dispatchEvent(selector, type, eventInit, options = {}) { async dispatchEvent(selector, type, eventInit, options = {}) {
await this._channel.dispatchEvent({ await this._channel.dispatchEvent({ selector, type, eventInit: (0, import_jsHandle.serializeArgument)(eventInit), ...options });
selector,
type,
eventInit: (0, _jsHandle.serializeArgument)(eventInit),
...options
});
} }
async $eval(selector, pageFunction, arg) { async $eval(selector, pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 3); (0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
const result = await this._channel.evalOnSelector({ const result = await this._channel.evalOnSelector({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
selector, return (0, import_jsHandle.parseResult)(result.value);
expression: String(pageFunction),
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async $$eval(selector, pageFunction, arg) { async $$eval(selector, pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 3); (0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
const result = await this._channel.evalOnSelectorAll({ const result = await this._channel.evalOnSelectorAll({ selector, expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
selector, return (0, import_jsHandle.parseResult)(result.value);
expression: String(pageFunction),
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async $$(selector) { async $$(selector) {
const result = await this._channel.querySelectorAll({ const result = await this._channel.querySelectorAll({ selector });
selector return result.elements.map((e) => import_elementHandle.ElementHandle.from(e));
});
return result.elements.map(e => _elementHandle.ElementHandle.from(e));
} }
async _queryCount(selector) { async _queryCount(selector) {
return (await this._channel.queryCount({ return (await this._channel.queryCount({ selector })).value;
selector
})).value;
} }
async content() { async content() {
return (await this._channel.content()).value; return (await this._channel.content()).value;
} }
async setContent(html, options = {}) { async setContent(html, options = {}) {
const waitUntil = verifyLoadState('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = verifyLoadState("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
await this._channel.setContent({ await this._channel.setContent({ html, ...options, waitUntil });
html,
...options,
waitUntil
});
} }
name() { name() {
return this._name || ''; return this._name || "";
} }
url() { url() {
return this._url; return this._url;
@@ -263,241 +227,160 @@ class Frame extends _channelOwner.ChannelOwner {
return this._detached; return this._detached;
} }
async addScriptTag(options = {}) { async addScriptTag(options = {}) {
const copy = { const copy = { ...options };
...options
};
if (copy.path) { if (copy.path) {
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString(); copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
copy.content = (0, _clientHelper.addSourceUrlToScript)(copy.content, copy.path); copy.content = (0, import_clientHelper.addSourceUrlToScript)(copy.content, copy.path);
} }
return _elementHandle.ElementHandle.from((await this._channel.addScriptTag({ return import_elementHandle.ElementHandle.from((await this._channel.addScriptTag({ ...copy })).element);
...copy
})).element);
} }
async addStyleTag(options = {}) { async addStyleTag(options = {}) {
const copy = { const copy = { ...options };
...options
};
if (copy.path) { if (copy.path) {
copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString(); copy.content = (await this._platform.fs().promises.readFile(copy.path)).toString();
copy.content += '/*# sourceURL=' + copy.path.replace(/\n/g, '') + '*/'; copy.content += "/*# sourceURL=" + copy.path.replace(/\n/g, "") + "*/";
} }
return _elementHandle.ElementHandle.from((await this._channel.addStyleTag({ return import_elementHandle.ElementHandle.from((await this._channel.addStyleTag({ ...copy })).element);
...copy
})).element);
} }
async click(selector, options = {}) { async click(selector, options = {}) {
return await this._channel.click({ return await this._channel.click({ selector, ...options });
selector,
...options
});
} }
async dblclick(selector, options = {}) { async dblclick(selector, options = {}) {
return await this._channel.dblclick({ return await this._channel.dblclick({ selector, ...options });
selector,
...options
});
} }
async dragAndDrop(source, target, options = {}) { async dragAndDrop(source, target, options = {}) {
return await this._channel.dragAndDrop({ return await this._channel.dragAndDrop({ source, target, ...options });
source,
target,
...options
});
} }
async tap(selector, options = {}) { async tap(selector, options = {}) {
return await this._channel.tap({ return await this._channel.tap({ selector, ...options });
selector,
...options
});
} }
async fill(selector, value, options = {}) { async fill(selector, value, options = {}) {
return await this._channel.fill({ return await this._channel.fill({ selector, value, ...options });
selector,
value,
...options
});
} }
async _highlight(selector) { async _highlight(selector) {
return await this._channel.highlight({ return await this._channel.highlight({ selector });
selector
});
} }
locator(selector, options) { locator(selector, options) {
return new _locator.Locator(this, selector, options); return new import_locator.Locator(this, selector, options);
} }
getByTestId(testId) { getByTestId(testId) {
return this.locator((0, _locatorUtils.getByTestIdSelector)((0, _locator.testIdAttributeName)(), testId)); return this.locator((0, import_locatorUtils.getByTestIdSelector)((0, import_locator.testIdAttributeName)(), testId));
} }
getByAltText(text, options) { getByAltText(text, options) {
return this.locator((0, _locatorUtils.getByAltTextSelector)(text, options)); return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
} }
getByLabel(text, options) { getByLabel(text, options) {
return this.locator((0, _locatorUtils.getByLabelSelector)(text, options)); return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
} }
getByPlaceholder(text, options) { getByPlaceholder(text, options) {
return this.locator((0, _locatorUtils.getByPlaceholderSelector)(text, options)); return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
} }
getByText(text, options) { getByText(text, options) {
return this.locator((0, _locatorUtils.getByTextSelector)(text, options)); return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
} }
getByTitle(text, options) { getByTitle(text, options) {
return this.locator((0, _locatorUtils.getByTitleSelector)(text, options)); return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
} }
getByRole(role, options = {}) { getByRole(role, options = {}) {
return this.locator((0, _locatorUtils.getByRoleSelector)(role, options)); return this.locator((0, import_locatorUtils.getByRoleSelector)(role, options));
} }
frameLocator(selector) { frameLocator(selector) {
return new _locator.FrameLocator(this, selector); return new import_locator.FrameLocator(this, selector);
} }
async focus(selector, options = {}) { async focus(selector, options = {}) {
await this._channel.focus({ await this._channel.focus({ selector, ...options });
selector,
...options
});
} }
async textContent(selector, options = {}) { async textContent(selector, options = {}) {
const value = (await this._channel.textContent({ const value = (await this._channel.textContent({ selector, ...options })).value;
selector, return value === void 0 ? null : value;
...options
})).value;
return value === undefined ? null : value;
} }
async innerText(selector, options = {}) { async innerText(selector, options = {}) {
return (await this._channel.innerText({ return (await this._channel.innerText({ selector, ...options })).value;
selector,
...options
})).value;
} }
async innerHTML(selector, options = {}) { async innerHTML(selector, options = {}) {
return (await this._channel.innerHTML({ return (await this._channel.innerHTML({ selector, ...options })).value;
selector,
...options
})).value;
} }
async getAttribute(selector, name, options = {}) { async getAttribute(selector, name, options = {}) {
const value = (await this._channel.getAttribute({ const value = (await this._channel.getAttribute({ selector, name, ...options })).value;
selector, return value === void 0 ? null : value;
name,
...options
})).value;
return value === undefined ? null : value;
} }
async inputValue(selector, options = {}) { async inputValue(selector, options = {}) {
return (await this._channel.inputValue({ return (await this._channel.inputValue({ selector, ...options })).value;
selector,
...options
})).value;
} }
async isChecked(selector, options = {}) { async isChecked(selector, options = {}) {
return (await this._channel.isChecked({ return (await this._channel.isChecked({ selector, ...options })).value;
selector,
...options
})).value;
} }
async isDisabled(selector, options = {}) { async isDisabled(selector, options = {}) {
return (await this._channel.isDisabled({ return (await this._channel.isDisabled({ selector, ...options })).value;
selector,
...options
})).value;
} }
async isEditable(selector, options = {}) { async isEditable(selector, options = {}) {
return (await this._channel.isEditable({ return (await this._channel.isEditable({ selector, ...options })).value;
selector,
...options
})).value;
} }
async isEnabled(selector, options = {}) { async isEnabled(selector, options = {}) {
return (await this._channel.isEnabled({ return (await this._channel.isEnabled({ selector, ...options })).value;
selector,
...options
})).value;
} }
async isHidden(selector, options = {}) { async isHidden(selector, options = {}) {
return (await this._channel.isHidden({ return (await this._channel.isHidden({ selector, ...options })).value;
selector,
...options
})).value;
} }
async isVisible(selector, options = {}) { async isVisible(selector, options = {}) {
return (await this._channel.isVisible({ return (await this._channel.isVisible({ selector, ...options })).value;
selector,
...options
})).value;
} }
async hover(selector, options = {}) { async hover(selector, options = {}) {
await this._channel.hover({ await this._channel.hover({ selector, ...options });
selector,
...options
});
} }
async selectOption(selector, values, options = {}) { async selectOption(selector, values, options = {}) {
return (await this._channel.selectOption({ return (await this._channel.selectOption({ selector, ...(0, import_elementHandle.convertSelectOptionValues)(values), ...options })).values;
selector,
...(0, _elementHandle.convertSelectOptionValues)(values),
...options
})).values;
} }
async setInputFiles(selector, files, options = {}) { async setInputFiles(selector, files, options = {}) {
const converted = await (0, _elementHandle.convertInputFiles)(this._platform, files, this.page().context()); const converted = await (0, import_elementHandle.convertInputFiles)(this._platform, files, this.page().context());
await this._channel.setInputFiles({ await this._channel.setInputFiles({ selector, ...converted, ...options });
selector,
...converted,
...options
});
} }
async type(selector, text, options = {}) { async type(selector, text, options = {}) {
await this._channel.type({ await this._channel.type({ selector, text, ...options });
selector,
text,
...options
});
} }
async press(selector, key, options = {}) { async press(selector, key, options = {}) {
await this._channel.press({ await this._channel.press({ selector, key, ...options });
selector,
key,
...options
});
} }
async check(selector, options = {}) { async check(selector, options = {}) {
await this._channel.check({ await this._channel.check({ selector, ...options });
selector,
...options
});
} }
async uncheck(selector, options = {}) { async uncheck(selector, options = {}) {
await this._channel.uncheck({ await this._channel.uncheck({ selector, ...options });
selector,
...options
});
} }
async setChecked(selector, checked, options) { async setChecked(selector, checked, options) {
if (checked) await this.check(selector, options);else await this.uncheck(selector, options); if (checked)
await this.check(selector, options);
else
await this.uncheck(selector, options);
} }
async waitForTimeout(timeout) { async waitForTimeout(timeout) {
await this._channel.waitForTimeout({ await this._channel.waitForTimeout({ timeout });
timeout
});
} }
async waitForFunction(pageFunction, arg, options = {}) { async waitForFunction(pageFunction, arg, options = {}) {
if (typeof options.polling === 'string') (0, _assert.assert)(options.polling === 'raf', 'Unknown polling option: ' + options.polling); if (typeof options.polling === "string")
(0, import_assert.assert)(options.polling === "raf", "Unknown polling option: " + options.polling);
const result = await this._channel.waitForFunction({ const result = await this._channel.waitForFunction({
...options, ...options,
pollingInterval: options.polling === 'raf' ? undefined : options.polling, pollingInterval: options.polling === "raf" ? void 0 : options.polling,
expression: String(pageFunction), expression: String(pageFunction),
isFunction: typeof pageFunction === 'function', isFunction: typeof pageFunction === "function",
arg: (0, _jsHandle.serializeArgument)(arg) arg: (0, import_jsHandle.serializeArgument)(arg)
}); });
return _jsHandle.JSHandle.from(result.handle); return import_jsHandle.JSHandle.from(result.handle);
} }
async title() { async title() {
return (await this._channel.title()).value; return (await this._channel.title()).value;
} }
} }
exports.Frame = Frame;
function verifyLoadState(name, waitUntil) { function verifyLoadState(name, waitUntil) {
if (waitUntil === 'networkidle0') waitUntil = 'networkidle'; if (waitUntil === "networkidle0")
if (!_types.kLifecycleEvents.has(waitUntil)) throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`); waitUntil = "networkidle";
if (!import_types.kLifecycleEvents.has(waitUntil))
throw new Error(`${name}: expected one of (load|domcontentloaded|networkidle|commit)`);
return waitUntil; return waitUntil;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Frame,
verifyLoadState
});

View File

@@ -1,41 +1,34 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var harRouter_exports = {};
__export(harRouter_exports, {
HarRouter: () => HarRouter
}); });
exports.HarRouter = void 0; module.exports = __toCommonJS(harRouter_exports);
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class HarRouter { class HarRouter {
static async create(localUtils, file, notFoundAction, options) { static async create(localUtils, file, notFoundAction, options) {
const { const { harId, error } = await localUtils.harOpen({ file });
harId, if (error)
error throw new Error(error);
} = await localUtils.harOpen({
file
});
if (error) throw new Error(error);
return new HarRouter(localUtils, harId, notFoundAction, options); return new HarRouter(localUtils, harId, notFoundAction, options);
} }
constructor(localUtils, harId, notFoundAction, options) { constructor(localUtils, harId, notFoundAction, options) {
this._localUtils = void 0;
this._harId = void 0;
this._notFoundAction = void 0;
this._options = void 0;
this._localUtils = localUtils; this._localUtils = localUtils;
this._harId = harId; this._harId = harId;
this._options = options; this._options = options;
@@ -48,50 +41,47 @@ class HarRouter {
url: request.url(), url: request.url(),
method: request.method(), method: request.method(),
headers: await request.headersArray(), headers: await request.headersArray(),
postData: request.postDataBuffer() || undefined, postData: request.postDataBuffer() || void 0,
isNavigationRequest: request.isNavigationRequest() isNavigationRequest: request.isNavigationRequest()
}); });
if (response.action === 'redirect') { if (response.action === "redirect") {
route._platform.log('api', `HAR: ${route.request().url()} redirected to ${response.redirectURL}`); route._platform.log("api", `HAR: ${route.request().url()} redirected to ${response.redirectURL}`);
await route._redirectNavigationRequest(response.redirectURL); await route._redirectNavigationRequest(response.redirectURL);
return; return;
} }
if (response.action === 'fulfill') { if (response.action === "fulfill") {
// If the response status is -1, the request was canceled or stalled, so we just stall it here. if (response.status === -1)
// See https://github.com/microsoft/playwright/issues/29311. return;
// TODO: it'd be better to abort such requests, but then we likely need to respect the timing,
// because the request might have been stalled for a long time until the very end of the
// test when HAR was recorded but we'd abort it immediately.
if (response.status === -1) return;
await route.fulfill({ await route.fulfill({
status: response.status, status: response.status,
headers: Object.fromEntries(response.headers.map(h => [h.name, h.value])), headers: Object.fromEntries(response.headers.map((h) => [h.name, h.value])),
body: response.body body: response.body
}); });
return; return;
} }
if (response.action === 'error') route._platform.log('api', 'HAR: ' + response.message); if (response.action === "error")
// Report the error, but fall through to the default handler. route._platform.log("api", "HAR: " + response.message);
if (this._notFoundAction === "abort") {
if (this._notFoundAction === 'abort') {
await route.abort(); await route.abort();
return; return;
} }
await route.fallback(); await route.fallback();
} }
async addContextRoute(context) { async addContextRoute(context) {
await context.route(this._options.urlMatch || '**/*', route => this._handle(route)); await context.route(this._options.urlMatch || "**/*", (route) => this._handle(route));
} }
async addPageRoute(page) { async addPageRoute(page) {
await page.route(this._options.urlMatch || '**/*', route => this._handle(route)); await page.route(this._options.urlMatch || "**/*", (route) => this._handle(route));
} }
async [Symbol.asyncDispose]() { async [Symbol.asyncDispose]() {
await this.dispose(); await this.dispose();
} }
dispose() { dispose() {
this._localUtils.harClose({ this._localUtils.harClose({ harId: this._harId }).catch(() => {
harId: this._harId });
}).catch(() => {});
} }
} }
exports.HarRouter = HarRouter; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
HarRouter
});

View File

@@ -1,111 +1,82 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var input_exports = {};
__export(input_exports, {
Keyboard: () => Keyboard,
Mouse: () => Mouse,
Touchscreen: () => Touchscreen
}); });
exports.Touchscreen = exports.Mouse = exports.Keyboard = void 0; module.exports = __toCommonJS(input_exports);
/**
* Copyright 2017 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Keyboard { class Keyboard {
constructor(page) { constructor(page) {
this._page = void 0;
this._page = page; this._page = page;
} }
async down(key) { async down(key) {
await this._page._channel.keyboardDown({ await this._page._channel.keyboardDown({ key });
key
});
} }
async up(key) { async up(key) {
await this._page._channel.keyboardUp({ await this._page._channel.keyboardUp({ key });
key
});
} }
async insertText(text) { async insertText(text) {
await this._page._channel.keyboardInsertText({ await this._page._channel.keyboardInsertText({ text });
text
});
} }
async type(text, options = {}) { async type(text, options = {}) {
await this._page._channel.keyboardType({ await this._page._channel.keyboardType({ text, ...options });
text,
...options
});
} }
async press(key, options = {}) { async press(key, options = {}) {
await this._page._channel.keyboardPress({ await this._page._channel.keyboardPress({ key, ...options });
key,
...options
});
} }
} }
exports.Keyboard = Keyboard;
class Mouse { class Mouse {
constructor(page) { constructor(page) {
this._page = void 0;
this._page = page; this._page = page;
} }
async move(x, y, options = {}) { async move(x, y, options = {}) {
await this._page._channel.mouseMove({ await this._page._channel.mouseMove({ x, y, ...options });
x,
y,
...options
});
} }
async down(options = {}) { async down(options = {}) {
await this._page._channel.mouseDown({ await this._page._channel.mouseDown({ ...options });
...options
});
} }
async up(options = {}) { async up(options = {}) {
await this._page._channel.mouseUp(options); await this._page._channel.mouseUp(options);
} }
async click(x, y, options = {}) { async click(x, y, options = {}) {
await this._page._channel.mouseClick({ await this._page._channel.mouseClick({ x, y, ...options });
x,
y,
...options
});
} }
async dblclick(x, y, options = {}) { async dblclick(x, y, options = {}) {
await this.click(x, y, { await this.click(x, y, { ...options, clickCount: 2 });
...options,
clickCount: 2
});
} }
async wheel(deltaX, deltaY) { async wheel(deltaX, deltaY) {
await this._page._channel.mouseWheel({ await this._page._channel.mouseWheel({ deltaX, deltaY });
deltaX,
deltaY
});
} }
} }
exports.Mouse = Mouse;
class Touchscreen { class Touchscreen {
constructor(page) { constructor(page) {
this._page = void 0;
this._page = page; this._page = page;
} }
async tap(x, y) { async tap(x, y) {
await this._page._channel.touchscreenTap({ await this._page._channel.touchscreenTap({ x, y });
x, }
y }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Keyboard,
Mouse,
Touchscreen
}); });
}
}
exports.Touchscreen = Touchscreen;

View File

@@ -1,71 +1,57 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var jsHandle_exports = {};
__export(jsHandle_exports, {
JSHandle: () => JSHandle,
assertMaxArguments: () => assertMaxArguments,
parseResult: () => parseResult,
serializeArgument: () => serializeArgument
}); });
exports.JSHandle = void 0; module.exports = __toCommonJS(jsHandle_exports);
exports.assertMaxArguments = assertMaxArguments; var import_channelOwner = require("./channelOwner");
exports.parseResult = parseResult; var import_errors = require("./errors");
exports.serializeArgument = serializeArgument; var import_serializers = require("../protocol/serializers");
var _channelOwner = require("./channelOwner"); class JSHandle extends import_channelOwner.ChannelOwner {
var _errors = require("./errors");
var _serializers = require("../protocol/serializers");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class JSHandle extends _channelOwner.ChannelOwner {
static from(handle) { static from(handle) {
return handle._object; return handle._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._preview = void 0;
this._preview = this._initializer.preview; this._preview = this._initializer.preview;
this._channel.on('previewUpdated', ({ this._channel.on("previewUpdated", ({ preview }) => this._preview = preview);
preview
}) => this._preview = preview);
} }
async evaluate(pageFunction, arg) { async evaluate(pageFunction, arg) {
const result = await this._channel.evaluateExpression({ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
expression: String(pageFunction),
isFunction: typeof pageFunction === 'function',
arg: serializeArgument(arg)
});
return parseResult(result.value); return parseResult(result.value);
} }
async evaluateHandle(pageFunction, arg) { async evaluateHandle(pageFunction, arg) {
const result = await this._channel.evaluateExpressionHandle({ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: serializeArgument(arg) });
expression: String(pageFunction),
isFunction: typeof pageFunction === 'function',
arg: serializeArgument(arg)
});
return JSHandle.from(result.handle); return JSHandle.from(result.handle);
} }
async getProperty(propertyName) { async getProperty(propertyName) {
const result = await this._channel.getProperty({ const result = await this._channel.getProperty({ name: propertyName });
name: propertyName
});
return JSHandle.from(result.handle); return JSHandle.from(result.handle);
} }
async getProperties() { async getProperties() {
const map = new Map(); const map = /* @__PURE__ */ new Map();
for (const { for (const { name, value } of (await this._channel.getPropertyList()).properties)
name, map.set(name, JSHandle.from(value));
value
} of (await this._channel.getPropertyList()).properties) map.set(name, JSHandle.from(value));
return map; return map;
} }
async jsonValue() { async jsonValue() {
@@ -81,7 +67,8 @@ class JSHandle extends _channelOwner.ChannelOwner {
try { try {
await this._channel.dispose(); await this._channel.dispose();
} catch (e) { } catch (e) {
if ((0, _errors.isTargetClosedError)(e)) return; if ((0, import_errors.isTargetClosedError)(e))
return;
throw e; throw e;
} }
} }
@@ -89,32 +76,30 @@ class JSHandle extends _channelOwner.ChannelOwner {
return this._preview; return this._preview;
} }
} }
// This function takes care of converting all JSHandles to their channels,
// so that generic channel serializer converts them to guids.
exports.JSHandle = JSHandle;
function serializeArgument(arg) { function serializeArgument(arg) {
const handles = []; const handles = [];
const pushHandle = channel => { const pushHandle = (channel) => {
handles.push(channel); handles.push(channel);
return handles.length - 1; return handles.length - 1;
}; };
const value = (0, _serializers.serializeValue)(arg, value => { const value = (0, import_serializers.serializeValue)(arg, (value2) => {
if (value instanceof JSHandle) return { if (value2 instanceof JSHandle)
h: pushHandle(value._channel) return { h: pushHandle(value2._channel) };
}; return { fallThrough: value2 };
return {
fallThrough: value
};
}); });
return { return { value, handles };
value,
handles
};
} }
function parseResult(value) { function parseResult(value) {
return (0, _serializers.parseSerializedValue)(value, undefined); return (0, import_serializers.parseSerializedValue)(value, void 0);
} }
function assertMaxArguments(count, max) { function assertMaxArguments(count, max) {
if (count > max) throw new Error('Too many arguments. If you need to pass more than 1 argument to the function wrap them in an object.'); if (count > max)
throw new Error("Too many arguments. If you need to pass more than 1 argument to the function wrap them in an object.");
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
JSHandle,
assertMaxArguments,
parseResult,
serializeArgument
});

View File

@@ -1,27 +1,28 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var jsonPipe_exports = {};
__export(jsonPipe_exports, {
JsonPipe: () => JsonPipe
}); });
exports.JsonPipe = void 0; module.exports = __toCommonJS(jsonPipe_exports);
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
/** class JsonPipe extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class JsonPipe extends _channelOwner.ChannelOwner {
static from(jsonPipe) { static from(jsonPipe) {
return jsonPipe._object; return jsonPipe._object;
} }
@@ -32,4 +33,7 @@ class JsonPipe extends _channelOwner.ChannelOwner {
return this._channel; return this._channel;
} }
} }
exports.JsonPipe = JsonPipe; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
JsonPipe
});

View File

@@ -1,36 +1,34 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var localUtils_exports = {};
__export(localUtils_exports, {
LocalUtils: () => LocalUtils
}); });
exports.LocalUtils = void 0; module.exports = __toCommonJS(localUtils_exports);
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
/** class LocalUtils extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class LocalUtils extends _channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this.devices = void 0;
this.markAsInternalType(); this.markAsInternalType();
this.devices = {}; this.devices = {};
for (const { for (const { name, descriptor } of initializer.deviceDescriptors)
name, this.devices[name] = descriptor;
descriptor
} of initializer.deviceDescriptors) this.devices[name] = descriptor;
} }
async zip(params) { async zip(params) {
return await this._channel.zip(params); return await this._channel.zip(params);
@@ -57,4 +55,7 @@ class LocalUtils extends _channelOwner.ChannelOwner {
return await this._channel.addStackToTracingNoReply(params); return await this._channel.addStackToTracingNoReply(params);
} }
} }
exports.LocalUtils = LocalUtils; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
LocalUtils
});

View File

@@ -1,71 +1,71 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var locator_exports = {};
__export(locator_exports, {
FrameLocator: () => FrameLocator,
Locator: () => Locator,
setTestIdAttribute: () => setTestIdAttribute,
testIdAttributeName: () => testIdAttributeName
}); });
exports.Locator = exports.FrameLocator = void 0; module.exports = __toCommonJS(locator_exports);
exports.setTestIdAttribute = setTestIdAttribute; var import_elementHandle = require("./elementHandle");
exports.testIdAttributeName = testIdAttributeName; var import_jsHandle = require("./jsHandle");
var _elementHandle = require("./elementHandle"); var import_locatorGenerators = require("../utils/isomorphic/locatorGenerators");
var _jsHandle = require("./jsHandle"); var import_locatorUtils = require("../utils/isomorphic/locatorUtils");
var _locatorGenerators = require("../utils/isomorphic/locatorGenerators"); var import_stringUtils = require("../utils/isomorphic/stringUtils");
var _locatorUtils = require("../utils/isomorphic/locatorUtils"); var import_rtti = require("../utils/isomorphic/rtti");
var _stringUtils = require("../utils/isomorphic/stringUtils"); var import_time = require("../utils/isomorphic/time");
var _rtti = require("../utils/isomorphic/rtti");
var _time = require("../utils/isomorphic/time");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Locator { class Locator {
constructor(frame, selector, options) { constructor(frame, selector, options) {
this._frame = void 0;
this._selector = void 0;
this._frame = frame; this._frame = frame;
this._selector = selector; this._selector = selector;
if (options !== null && options !== void 0 && options.hasText) this._selector += ` >> internal:has-text=${(0, _stringUtils.escapeForTextSelector)(options.hasText, false)}`; if (options?.hasText)
if (options !== null && options !== void 0 && options.hasNotText) this._selector += ` >> internal:has-not-text=${(0, _stringUtils.escapeForTextSelector)(options.hasNotText, false)}`; this._selector += ` >> internal:has-text=${(0, import_stringUtils.escapeForTextSelector)(options.hasText, false)}`;
if (options !== null && options !== void 0 && options.has) { if (options?.hasNotText)
this._selector += ` >> internal:has-not-text=${(0, import_stringUtils.escapeForTextSelector)(options.hasNotText, false)}`;
if (options?.has) {
const locator = options.has; const locator = options.has;
if (locator._frame !== frame) throw new Error(`Inner "has" locator must belong to the same frame.`); if (locator._frame !== frame)
throw new Error(`Inner "has" locator must belong to the same frame.`);
this._selector += ` >> internal:has=` + JSON.stringify(locator._selector); this._selector += ` >> internal:has=` + JSON.stringify(locator._selector);
} }
if (options !== null && options !== void 0 && options.hasNot) { if (options?.hasNot) {
const locator = options.hasNot; const locator = options.hasNot;
if (locator._frame !== frame) throw new Error(`Inner "hasNot" locator must belong to the same frame.`); if (locator._frame !== frame)
throw new Error(`Inner "hasNot" locator must belong to the same frame.`);
this._selector += ` >> internal:has-not=` + JSON.stringify(locator._selector); this._selector += ` >> internal:has-not=` + JSON.stringify(locator._selector);
} }
if ((options === null || options === void 0 ? void 0 : options.visible) !== undefined) this._selector += ` >> visible=${options.visible ? 'true' : 'false'}`; if (options?.visible !== void 0)
if (this._frame._platform.inspectCustom) this[this._frame._platform.inspectCustom] = () => this._inspect(); this._selector += ` >> visible=${options.visible ? "true" : "false"}`;
if (this._frame._platform.inspectCustom)
this[this._frame._platform.inspectCustom] = () => this._inspect();
} }
async _withElement(task, timeout) { async _withElement(task, timeout) {
timeout = this._frame.page()._timeoutSettings.timeout({ timeout = this._frame.page()._timeoutSettings.timeout({ timeout });
timeout const deadline = timeout ? (0, import_time.monotonicTime)() + timeout : 0;
});
const deadline = timeout ? (0, _time.monotonicTime)() + timeout : 0;
return await this._frame._wrapApiCall(async () => { return await this._frame._wrapApiCall(async () => {
const result = await this._frame._channel.waitForSelector({ const result = await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, state: "attached", timeout });
selector: this._selector, const handle = import_elementHandle.ElementHandle.fromNullable(result.element);
strict: true, if (!handle)
state: 'attached', throw new Error(`Could not resolve ${this._selector} to DOM Element`);
timeout
});
const handle = _elementHandle.ElementHandle.fromNullable(result.element);
if (!handle) throw new Error(`Could not resolve ${this._selector} to DOM Element`);
try { try {
return await task(handle, deadline ? deadline - (0, _time.monotonicTime)() : 0); return await task(handle, deadline ? deadline - (0, import_time.monotonicTime)() : 0);
} finally { } finally {
await handle.dispose(); await handle.dispose();
} }
@@ -78,31 +78,19 @@ class Locator {
return this._frame.page(); return this._frame.page();
} }
async boundingBox(options) { async boundingBox(options) {
return await this._withElement(h => h.boundingBox(), options === null || options === void 0 ? void 0 : options.timeout); return await this._withElement((h) => h.boundingBox(), options?.timeout);
} }
async check(options = {}) { async check(options = {}) {
return await this._frame.check(this._selector, { return await this._frame.check(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async click(options = {}) { async click(options = {}) {
return await this._frame.click(this._selector, { return await this._frame.click(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async dblclick(options = {}) { async dblclick(options = {}) {
return await this._frame.dblclick(this._selector, { return await this._frame.dblclick(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async dispatchEvent(type, eventInit = {}, options) { async dispatchEvent(type, eventInit = {}, options) {
return await this._frame.dispatchEvent(this._selector, type, eventInit, { return await this._frame.dispatchEvent(this._selector, type, eventInit, { strict: true, ...options });
strict: true,
...options
});
} }
async dragTo(target, options = {}) { async dragTo(target, options = {}) {
return await this._frame.dragAndDrop(this._selector, target._selector, { return await this._frame.dragAndDrop(this._selector, target._selector, {
@@ -111,68 +99,62 @@ class Locator {
}); });
} }
async evaluate(pageFunction, arg, options) { async evaluate(pageFunction, arg, options) {
return await this._withElement(h => h.evaluate(pageFunction, arg), options === null || options === void 0 ? void 0 : options.timeout); return await this._withElement((h) => h.evaluate(pageFunction, arg), options?.timeout);
} }
async evaluateAll(pageFunction, arg) { async evaluateAll(pageFunction, arg) {
return await this._frame.$$eval(this._selector, pageFunction, arg); return await this._frame.$$eval(this._selector, pageFunction, arg);
} }
async evaluateHandle(pageFunction, arg, options) { async evaluateHandle(pageFunction, arg, options) {
return await this._withElement(h => h.evaluateHandle(pageFunction, arg), options === null || options === void 0 ? void 0 : options.timeout); return await this._withElement((h) => h.evaluateHandle(pageFunction, arg), options?.timeout);
} }
async fill(value, options = {}) { async fill(value, options = {}) {
return await this._frame.fill(this._selector, value, { return await this._frame.fill(this._selector, value, { strict: true, ...options });
strict: true,
...options
});
} }
async clear(options = {}) { async clear(options = {}) {
return await this.fill('', options); return await this.fill("", options);
} }
async _highlight() { async _highlight() {
// VS Code extension uses this one, keep it for now.
return await this._frame._highlight(this._selector); return await this._frame._highlight(this._selector);
} }
async highlight() { async highlight() {
return await this._frame._highlight(this._selector); return await this._frame._highlight(this._selector);
} }
locator(selectorOrLocator, options) { locator(selectorOrLocator, options) {
if ((0, _rtti.isString)(selectorOrLocator)) return new Locator(this._frame, this._selector + ' >> ' + selectorOrLocator, options); if ((0, import_rtti.isString)(selectorOrLocator))
if (selectorOrLocator._frame !== this._frame) throw new Error(`Locators must belong to the same frame.`); return new Locator(this._frame, this._selector + " >> " + selectorOrLocator, options);
return new Locator(this._frame, this._selector + ' >> internal:chain=' + JSON.stringify(selectorOrLocator._selector), options); if (selectorOrLocator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._selector + " >> internal:chain=" + JSON.stringify(selectorOrLocator._selector), options);
} }
getByTestId(testId) { getByTestId(testId) {
return this.locator((0, _locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId)); return this.locator((0, import_locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId));
} }
getByAltText(text, options) { getByAltText(text, options) {
return this.locator((0, _locatorUtils.getByAltTextSelector)(text, options)); return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
} }
getByLabel(text, options) { getByLabel(text, options) {
return this.locator((0, _locatorUtils.getByLabelSelector)(text, options)); return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
} }
getByPlaceholder(text, options) { getByPlaceholder(text, options) {
return this.locator((0, _locatorUtils.getByPlaceholderSelector)(text, options)); return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
} }
getByText(text, options) { getByText(text, options) {
return this.locator((0, _locatorUtils.getByTextSelector)(text, options)); return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
} }
getByTitle(text, options) { getByTitle(text, options) {
return this.locator((0, _locatorUtils.getByTitleSelector)(text, options)); return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
} }
getByRole(role, options = {}) { getByRole(role, options = {}) {
return this.locator((0, _locatorUtils.getByRoleSelector)(role, options)); return this.locator((0, import_locatorUtils.getByRoleSelector)(role, options));
} }
frameLocator(selector) { frameLocator(selector) {
return new FrameLocator(this._frame, this._selector + ' >> ' + selector); return new FrameLocator(this._frame, this._selector + " >> " + selector);
} }
filter(options) { filter(options) {
return new Locator(this._frame, this._selector, options); return new Locator(this._frame, this._selector, options);
} }
async elementHandle(options) { async elementHandle(options) {
return await this._frame.waitForSelector(this._selector, { return await this._frame.waitForSelector(this._selector, { strict: true, state: "attached", ...options });
strict: true,
state: 'attached',
...options
});
} }
async elementHandles() { async elementHandles() {
return await this._frame.$$(this._selector); return await this._frame.$$(this._selector);
@@ -181,7 +163,7 @@ class Locator {
return new FrameLocator(this._frame, this._selector); return new FrameLocator(this._frame, this._selector);
} }
first() { first() {
return new Locator(this._frame, this._selector + ' >> nth=0'); return new Locator(this._frame, this._selector + " >> nth=0");
} }
last() { last() {
return new Locator(this._frame, this._selector + ` >> nth=-1`); return new Locator(this._frame, this._selector + ` >> nth=-1`);
@@ -190,253 +172,172 @@ class Locator {
return new Locator(this._frame, this._selector + ` >> nth=${index}`); return new Locator(this._frame, this._selector + ` >> nth=${index}`);
} }
and(locator) { and(locator) {
if (locator._frame !== this._frame) throw new Error(`Locators must belong to the same frame.`); if (locator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._selector + ` >> internal:and=` + JSON.stringify(locator._selector)); return new Locator(this._frame, this._selector + ` >> internal:and=` + JSON.stringify(locator._selector));
} }
or(locator) { or(locator) {
if (locator._frame !== this._frame) throw new Error(`Locators must belong to the same frame.`); if (locator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._selector + ` >> internal:or=` + JSON.stringify(locator._selector)); return new Locator(this._frame, this._selector + ` >> internal:or=` + JSON.stringify(locator._selector));
} }
async focus(options) { async focus(options) {
return await this._frame.focus(this._selector, { return await this._frame.focus(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async blur(options) { async blur(options) {
await this._frame._channel.blur({ await this._frame._channel.blur({ selector: this._selector, strict: true, ...options });
selector: this._selector,
strict: true,
...options
});
} }
async count() { async count() {
return await this._frame._queryCount(this._selector); return await this._frame._queryCount(this._selector);
} }
async _generateLocatorString() { async _generateLocatorString() {
return await this._withElement(h => h._generateLocatorString()); return await this._withElement((h) => h._generateLocatorString());
} }
async getAttribute(name, options) { async getAttribute(name, options) {
return await this._frame.getAttribute(this._selector, name, { return await this._frame.getAttribute(this._selector, name, { strict: true, ...options });
strict: true,
...options
});
} }
async hover(options = {}) { async hover(options = {}) {
return await this._frame.hover(this._selector, { return await this._frame.hover(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async innerHTML(options) { async innerHTML(options) {
return await this._frame.innerHTML(this._selector, { return await this._frame.innerHTML(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async innerText(options) { async innerText(options) {
return await this._frame.innerText(this._selector, { return await this._frame.innerText(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async inputValue(options) { async inputValue(options) {
return await this._frame.inputValue(this._selector, { return await this._frame.inputValue(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async isChecked(options) { async isChecked(options) {
return await this._frame.isChecked(this._selector, { return await this._frame.isChecked(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async isDisabled(options) { async isDisabled(options) {
return await this._frame.isDisabled(this._selector, { return await this._frame.isDisabled(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async isEditable(options) { async isEditable(options) {
return await this._frame.isEditable(this._selector, { return await this._frame.isEditable(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async isEnabled(options) { async isEnabled(options) {
return await this._frame.isEnabled(this._selector, { return await this._frame.isEnabled(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async isHidden(options) { async isHidden(options) {
return await this._frame.isHidden(this._selector, { return await this._frame.isHidden(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async isVisible(options) { async isVisible(options) {
return await this._frame.isVisible(this._selector, { return await this._frame.isVisible(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async press(key, options = {}) { async press(key, options = {}) {
return await this._frame.press(this._selector, key, { return await this._frame.press(this._selector, key, { strict: true, ...options });
strict: true,
...options
});
} }
async screenshot(options = {}) { async screenshot(options = {}) {
const mask = options.mask; const mask = options.mask;
return await this._withElement((h, timeout) => h.screenshot({ return await this._withElement((h, timeout) => h.screenshot({ ...options, mask, timeout }), options.timeout);
...options,
mask,
timeout
}), options.timeout);
} }
async ariaSnapshot(options) { async ariaSnapshot(options) {
const result = await this._frame._channel.ariaSnapshot({ const result = await this._frame._channel.ariaSnapshot({ ...options, mode: options?._mode, selector: this._selector });
...options,
id: options === null || options === void 0 ? void 0 : options._id,
mode: options === null || options === void 0 ? void 0 : options._mode,
selector: this._selector
});
return result.snapshot; return result.snapshot;
} }
async scrollIntoViewIfNeeded(options = {}) { async scrollIntoViewIfNeeded(options = {}) {
return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ return await this._withElement((h, timeout) => h.scrollIntoViewIfNeeded({ ...options, timeout }), options.timeout);
...options,
timeout
}), options.timeout);
} }
async selectOption(values, options = {}) { async selectOption(values, options = {}) {
return await this._frame.selectOption(this._selector, values, { return await this._frame.selectOption(this._selector, values, { strict: true, ...options });
strict: true,
...options
});
} }
async selectText(options = {}) { async selectText(options = {}) {
return await this._withElement((h, timeout) => h.selectText({ return await this._withElement((h, timeout) => h.selectText({ ...options, timeout }), options.timeout);
...options,
timeout
}), options.timeout);
} }
async setChecked(checked, options) { async setChecked(checked, options) {
if (checked) await this.check(options);else await this.uncheck(options); if (checked)
await this.check(options);
else
await this.uncheck(options);
} }
async setInputFiles(files, options = {}) { async setInputFiles(files, options = {}) {
return await this._frame.setInputFiles(this._selector, files, { return await this._frame.setInputFiles(this._selector, files, { strict: true, ...options });
strict: true,
...options
});
} }
async tap(options = {}) { async tap(options = {}) {
return await this._frame.tap(this._selector, { return await this._frame.tap(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async textContent(options) { async textContent(options) {
return await this._frame.textContent(this._selector, { return await this._frame.textContent(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async type(text, options = {}) { async type(text, options = {}) {
return await this._frame.type(this._selector, text, { return await this._frame.type(this._selector, text, { strict: true, ...options });
strict: true,
...options
});
} }
async pressSequentially(text, options = {}) { async pressSequentially(text, options = {}) {
return await this.type(text, options); return await this.type(text, options);
} }
async uncheck(options = {}) { async uncheck(options = {}) {
return await this._frame.uncheck(this._selector, { return await this._frame.uncheck(this._selector, { strict: true, ...options });
strict: true,
...options
});
} }
async all() { async all() {
return new Array(await this.count()).fill(0).map((e, i) => this.nth(i)); return new Array(await this.count()).fill(0).map((e, i) => this.nth(i));
} }
async allInnerTexts() { async allInnerTexts() {
return await this._frame.$$eval(this._selector, ee => ee.map(e => e.innerText)); return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.innerText));
} }
async allTextContents() { async allTextContents() {
return await this._frame.$$eval(this._selector, ee => ee.map(e => e.textContent || '')); return await this._frame.$$eval(this._selector, (ee) => ee.map((e) => e.textContent || ""));
} }
async waitFor(options) { async waitFor(options) {
await this._frame._channel.waitForSelector({ await this._frame._channel.waitForSelector({ selector: this._selector, strict: true, omitReturnValue: true, ...options });
selector: this._selector,
strict: true,
omitReturnValue: true,
...options
});
} }
async _expect(expression, options) { async _expect(expression, options) {
const params = { const params = { selector: this._selector, expression, ...options, isNot: !!options.isNot };
selector: this._selector, params.expectedValue = (0, import_jsHandle.serializeArgument)(options.expectedValue);
expression,
...options,
isNot: !!options.isNot
};
params.expectedValue = (0, _jsHandle.serializeArgument)(options.expectedValue);
const result = await this._frame._channel.expect(params); const result = await this._frame._channel.expect(params);
if (result.received !== undefined) result.received = (0, _jsHandle.parseResult)(result.received); if (result.received !== void 0)
result.received = (0, import_jsHandle.parseResult)(result.received);
return result; return result;
} }
_inspect() { _inspect() {
return this.toString(); return this.toString();
} }
toString() { toString() {
return (0, _locatorGenerators.asLocator)('javascript', this._selector); return (0, import_locatorGenerators.asLocator)("javascript", this._selector);
} }
} }
exports.Locator = Locator;
class FrameLocator { class FrameLocator {
constructor(frame, selector) { constructor(frame, selector) {
this._frame = void 0;
this._frameSelector = void 0;
this._frame = frame; this._frame = frame;
this._frameSelector = selector; this._frameSelector = selector;
} }
locator(selectorOrLocator, options) { locator(selectorOrLocator, options) {
if ((0, _rtti.isString)(selectorOrLocator)) return new Locator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selectorOrLocator, options); if ((0, import_rtti.isString)(selectorOrLocator))
if (selectorOrLocator._frame !== this._frame) throw new Error(`Locators must belong to the same frame.`); return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator, options);
return new Locator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selectorOrLocator._selector, options); if (selectorOrLocator._frame !== this._frame)
throw new Error(`Locators must belong to the same frame.`);
return new Locator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selectorOrLocator._selector, options);
} }
getByTestId(testId) { getByTestId(testId) {
return this.locator((0, _locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId)); return this.locator((0, import_locatorUtils.getByTestIdSelector)(testIdAttributeName(), testId));
} }
getByAltText(text, options) { getByAltText(text, options) {
return this.locator((0, _locatorUtils.getByAltTextSelector)(text, options)); return this.locator((0, import_locatorUtils.getByAltTextSelector)(text, options));
} }
getByLabel(text, options) { getByLabel(text, options) {
return this.locator((0, _locatorUtils.getByLabelSelector)(text, options)); return this.locator((0, import_locatorUtils.getByLabelSelector)(text, options));
} }
getByPlaceholder(text, options) { getByPlaceholder(text, options) {
return this.locator((0, _locatorUtils.getByPlaceholderSelector)(text, options)); return this.locator((0, import_locatorUtils.getByPlaceholderSelector)(text, options));
} }
getByText(text, options) { getByText(text, options) {
return this.locator((0, _locatorUtils.getByTextSelector)(text, options)); return this.locator((0, import_locatorUtils.getByTextSelector)(text, options));
} }
getByTitle(text, options) { getByTitle(text, options) {
return this.locator((0, _locatorUtils.getByTitleSelector)(text, options)); return this.locator((0, import_locatorUtils.getByTitleSelector)(text, options));
} }
getByRole(role, options = {}) { getByRole(role, options = {}) {
return this.locator((0, _locatorUtils.getByRoleSelector)(role, options)); return this.locator((0, import_locatorUtils.getByRoleSelector)(role, options));
} }
owner() { owner() {
return new Locator(this._frame, this._frameSelector); return new Locator(this._frame, this._frameSelector);
} }
frameLocator(selector) { frameLocator(selector) {
return new FrameLocator(this._frame, this._frameSelector + ' >> internal:control=enter-frame >> ' + selector); return new FrameLocator(this._frame, this._frameSelector + " >> internal:control=enter-frame >> " + selector);
} }
first() { first() {
return new FrameLocator(this._frame, this._frameSelector + ' >> nth=0'); return new FrameLocator(this._frame, this._frameSelector + " >> nth=0");
} }
last() { last() {
return new FrameLocator(this._frame, this._frameSelector + ` >> nth=-1`); return new FrameLocator(this._frame, this._frameSelector + ` >> nth=-1`);
@@ -445,11 +346,17 @@ class FrameLocator {
return new FrameLocator(this._frame, this._frameSelector + ` >> nth=${index}`); return new FrameLocator(this._frame, this._frameSelector + ` >> nth=${index}`);
} }
} }
exports.FrameLocator = FrameLocator; let _testIdAttributeName = "data-testid";
let _testIdAttributeName = 'data-testid';
function testIdAttributeName() { function testIdAttributeName() {
return _testIdAttributeName; return _testIdAttributeName;
} }
function setTestIdAttribute(attributeName) { function setTestIdAttribute(attributeName) {
_testIdAttributeName = attributeName; _testIdAttributeName = attributeName;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
FrameLocator,
Locator,
setTestIdAttribute,
testIdAttributeName
});

View File

@@ -1,60 +1,60 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var network_exports = {};
__export(network_exports, {
RawHeaders: () => RawHeaders,
Request: () => Request,
Response: () => Response,
Route: () => Route,
RouteHandler: () => RouteHandler,
WebSocket: () => WebSocket,
WebSocketRoute: () => WebSocketRoute,
WebSocketRouteHandler: () => WebSocketRouteHandler,
validateHeaders: () => validateHeaders
}); });
exports.WebSocketRouteHandler = exports.WebSocketRoute = exports.WebSocket = exports.RouteHandler = exports.Route = exports.Response = exports.Request = exports.RawHeaders = void 0; module.exports = __toCommonJS(network_exports);
exports.validateHeaders = validateHeaders; var import_channelOwner = require("./channelOwner");
var _channelOwner = require("./channelOwner"); var import_errors = require("./errors");
var _errors = require("./errors"); var import_events = require("./events");
var _events = require("./events"); var import_fetch = require("./fetch");
var _fetch = require("./fetch"); var import_frame = require("./frame");
var _frame = require("./frame"); var import_waiter = require("./waiter");
var _waiter = require("./waiter"); var import_worker = require("./worker");
var _worker = require("./worker"); var import_assert = require("../utils/isomorphic/assert");
var _assert = require("../utils/isomorphic/assert"); var import_headers = require("../utils/isomorphic/headers");
var _headers = require("../utils/isomorphic/headers"); var import_urlMatch = require("../utils/isomorphic/urlMatch");
var _urlMatch = require("../utils/isomorphic/urlMatch"); var import_manualPromise = require("../utils/isomorphic/manualPromise");
var _manualPromise = require("../utils/isomorphic/manualPromise"); var import_multimap = require("../utils/isomorphic/multimap");
var _multimap = require("../utils/isomorphic/multimap"); var import_rtti = require("../utils/isomorphic/rtti");
var _rtti = require("../utils/isomorphic/rtti"); var import_stackTrace = require("../utils/isomorphic/stackTrace");
var _stackTrace = require("../utils/isomorphic/stackTrace"); var import_mimeType = require("../utils/isomorphic/mimeType");
var _mimeType = require("../utils/isomorphic/mimeType"); class Request extends import_channelOwner.ChannelOwner {
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Request extends _channelOwner.ChannelOwner {
static from(request) {
return request._object;
}
static fromNullable(request) {
return request ? Request.from(request) : null;
}
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._redirectedFrom = null; this._redirectedFrom = null;
this._redirectedTo = null; this._redirectedTo = null;
this._failureText = null; this._failureText = null;
this._provisionalHeaders = void 0;
this._actualHeadersPromise = void 0;
this._timing = void 0;
this._fallbackOverrides = {}; this._fallbackOverrides = {};
this.markAsInternalType(); this.markAsInternalType();
this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom); this._redirectedFrom = Request.fromNullable(initializer.redirectedFrom);
if (this._redirectedFrom) this._redirectedFrom._redirectedTo = this; if (this._redirectedFrom)
this._redirectedFrom._redirectedTo = this;
this._provisionalHeaders = new RawHeaders(initializer.headers); this._provisionalHeaders = new RawHeaders(initializer.headers);
this._timing = { this._timing = {
startTime: 0, startTime: 0,
@@ -68,6 +68,12 @@ class Request extends _channelOwner.ChannelOwner {
responseEnd: -1 responseEnd: -1
}; };
} }
static from(request) {
return request._object;
}
static fromNullable(request) {
return request ? Request.from(request) : null;
}
url() { url() {
return this._fallbackOverrides.url || this._initializer.url; return this._fallbackOverrides.url || this._initializer.url;
} }
@@ -78,38 +84,40 @@ class Request extends _channelOwner.ChannelOwner {
return this._fallbackOverrides.method || this._initializer.method; return this._fallbackOverrides.method || this._initializer.method;
} }
postData() { postData() {
var _ref; return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString("utf-8") || null;
return ((_ref = this._fallbackOverrides.postDataBuffer || this._initializer.postData) === null || _ref === void 0 ? void 0 : _ref.toString('utf-8')) || null;
} }
postDataBuffer() { postDataBuffer() {
return this._fallbackOverrides.postDataBuffer || this._initializer.postData || null; return this._fallbackOverrides.postDataBuffer || this._initializer.postData || null;
} }
postDataJSON() { postDataJSON() {
const postData = this.postData(); const postData = this.postData();
if (!postData) return null; if (!postData)
const contentType = this.headers()['content-type']; return null;
if (contentType !== null && contentType !== void 0 && contentType.includes('application/x-www-form-urlencoded')) { const contentType = this.headers()["content-type"];
if (contentType?.includes("application/x-www-form-urlencoded")) {
const entries = {}; const entries = {};
const parsed = new URLSearchParams(postData); const parsed = new URLSearchParams(postData);
for (const [k, v] of parsed.entries()) entries[k] = v; for (const [k, v] of parsed.entries())
entries[k] = v;
return entries; return entries;
} }
try { try {
return JSON.parse(postData); return JSON.parse(postData);
} catch (e) { } catch (e) {
throw new Error('POST data is not a valid JSON object: ' + postData); throw new Error("POST data is not a valid JSON object: " + postData);
} }
} }
/** /**
* @deprecated * @deprecated
*/ */
headers() { headers() {
if (this._fallbackOverrides.headers) return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers).headers(); if (this._fallbackOverrides.headers)
return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers).headers();
return this._provisionalHeaders.headers(); return this._provisionalHeaders.headers();
} }
async _actualHeaders() { async _actualHeaders() {
if (this._fallbackOverrides.headers) return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers); if (this._fallbackOverrides.headers)
return RawHeaders._fromHeadersObjectLossy(this._fallbackOverrides.headers);
if (!this._actualHeadersPromise) { if (!this._actualHeadersPromise) {
this._actualHeadersPromise = this._wrapApiCall(async () => { this._actualHeadersPromise = this._wrapApiCall(async () => {
return new RawHeaders((await this._channel.rawRequestHeaders()).headers); return new RawHeaders((await this._channel.rawRequestHeaders()).headers);
@@ -136,21 +144,24 @@ class Request extends _channelOwner.ChannelOwner {
} }
frame() { frame() {
if (!this._initializer.frame) { if (!this._initializer.frame) {
(0, _assert.assert)(this.serviceWorker()); (0, import_assert.assert)(this.serviceWorker());
throw new Error('Service Worker requests do not have an associated frame.'); throw new Error("Service Worker requests do not have an associated frame.");
} }
const frame = _frame.Frame.from(this._initializer.frame); const frame = import_frame.Frame.from(this._initializer.frame);
if (!frame._page) { if (!frame._page) {
throw new Error(['Frame for this navigation request is not available, because the request', 'was issued before the frame is created. You can check whether the request', 'is a navigation request by calling isNavigationRequest() method.'].join('\n')); throw new Error([
"Frame for this navigation request is not available, because the request",
"was issued before the frame is created. You can check whether the request",
"is a navigation request by calling isNavigationRequest() method."
].join("\n"));
} }
return frame; return frame;
} }
_safePage() { _safePage() {
var _Frame$fromNullable; return import_frame.Frame.fromNullable(this._initializer.frame)?._page || null;
return ((_Frame$fromNullable = _frame.Frame.fromNullable(this._initializer.frame)) === null || _Frame$fromNullable === void 0 ? void 0 : _Frame$fromNullable._page) || null;
} }
serviceWorker() { serviceWorker() {
return this._initializer.serviceWorker ? _worker.Worker.from(this._initializer.serviceWorker) : null; return this._initializer.serviceWorker ? import_worker.Worker.from(this._initializer.serviceWorker) : null;
} }
isNavigationRequest() { isNavigationRequest() {
return this._initializer.isNavigationRequest; return this._initializer.isNavigationRequest;
@@ -162,7 +173,8 @@ class Request extends _channelOwner.ChannelOwner {
return this._redirectedTo; return this._redirectedTo;
} }
failure() { failure() {
if (this._failureText === null) return null; if (this._failureText === null)
return null;
return { return {
errorText: this._failureText errorText: this._failureText
}; };
@@ -172,53 +184,57 @@ class Request extends _channelOwner.ChannelOwner {
} }
async sizes() { async sizes() {
const response = await this.response(); const response = await this.response();
if (!response) throw new Error('Unable to fetch sizes for failed request'); if (!response)
throw new Error("Unable to fetch sizes for failed request");
return (await response._channel.sizes()).sizes; return (await response._channel.sizes()).sizes;
} }
_setResponseEndTiming(responseEndTiming) { _setResponseEndTiming(responseEndTiming) {
this._timing.responseEnd = responseEndTiming; this._timing.responseEnd = responseEndTiming;
if (this._timing.responseStart === -1) this._timing.responseStart = responseEndTiming; if (this._timing.responseStart === -1)
this._timing.responseStart = responseEndTiming;
} }
_finalRequest() { _finalRequest() {
return this._redirectedTo ? this._redirectedTo._finalRequest() : this; return this._redirectedTo ? this._redirectedTo._finalRequest() : this;
} }
_applyFallbackOverrides(overrides) { _applyFallbackOverrides(overrides) {
if (overrides.url) this._fallbackOverrides.url = overrides.url; if (overrides.url)
if (overrides.method) this._fallbackOverrides.method = overrides.method; this._fallbackOverrides.url = overrides.url;
if (overrides.headers) this._fallbackOverrides.headers = overrides.headers; if (overrides.method)
if ((0, _rtti.isString)(overrides.postData)) this._fallbackOverrides.postDataBuffer = Buffer.from(overrides.postData, 'utf-8');else if (overrides.postData instanceof Buffer) this._fallbackOverrides.postDataBuffer = overrides.postData;else if (overrides.postData) this._fallbackOverrides.postDataBuffer = Buffer.from(JSON.stringify(overrides.postData), 'utf-8'); this._fallbackOverrides.method = overrides.method;
if (overrides.headers)
this._fallbackOverrides.headers = overrides.headers;
if ((0, import_rtti.isString)(overrides.postData))
this._fallbackOverrides.postDataBuffer = Buffer.from(overrides.postData, "utf-8");
else if (overrides.postData instanceof Buffer)
this._fallbackOverrides.postDataBuffer = overrides.postData;
else if (overrides.postData)
this._fallbackOverrides.postDataBuffer = Buffer.from(JSON.stringify(overrides.postData), "utf-8");
} }
_fallbackOverridesForContinue() { _fallbackOverridesForContinue() {
return this._fallbackOverrides; return this._fallbackOverrides;
} }
_targetClosedScope() { _targetClosedScope() {
var _this$serviceWorker, _this$_safePage; return this.serviceWorker()?._closedScope || this._safePage()?._closedOrCrashedScope || new import_manualPromise.LongStandingScope();
return ((_this$serviceWorker = this.serviceWorker()) === null || _this$serviceWorker === void 0 ? void 0 : _this$serviceWorker._closedScope) || ((_this$_safePage = this._safePage()) === null || _this$_safePage === void 0 ? void 0 : _this$_safePage._closedOrCrashedScope) || new _manualPromise.LongStandingScope();
} }
} }
exports.Request = Request; class Route extends import_channelOwner.ChannelOwner {
class Route extends _channelOwner.ChannelOwner {
static from(route) {
return route._object;
}
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._handlingPromise = null; this._handlingPromise = null;
this._context = void 0;
this._didThrow = false; this._didThrow = false;
this.markAsInternalType(); this.markAsInternalType();
} }
static from(route) {
return route._object;
}
request() { request() {
return Request.from(this._initializer.request); return Request.from(this._initializer.request);
} }
async _raceWithTargetClose(promise) { async _raceWithTargetClose(promise) {
// When page closes or crashes, we catch any potential rejects from this Route.
// Note that page could be missing when routing popup's initial request that
// does not have a Page initialized just yet.
return await this.request()._targetClosedScope().safeRace(promise); return await this.request()._targetClosedScope().safeRace(promise);
} }
async _startHandling() { async _startHandling() {
this._handlingPromise = new _manualPromise.ManualPromise(); this._handlingPromise = new import_manualPromise.ManualPromise();
return await this._handlingPromise; return await this._handlingPromise;
} }
async fallback(options = {}) { async fallback(options = {}) {
@@ -228,25 +244,17 @@ class Route extends _channelOwner.ChannelOwner {
} }
async abort(errorCode) { async abort(errorCode) {
await this._handleRoute(async () => { await this._handleRoute(async () => {
await this._raceWithTargetClose(this._channel.abort({ await this._raceWithTargetClose(this._channel.abort({ errorCode }));
errorCode
}));
}); });
} }
async _redirectNavigationRequest(url) { async _redirectNavigationRequest(url) {
await this._handleRoute(async () => { await this._handleRoute(async () => {
await this._raceWithTargetClose(this._channel.redirectNavigationRequest({ await this._raceWithTargetClose(this._channel.redirectNavigationRequest({ url }));
url
}));
}); });
} }
async fetch(options = {}) { async fetch(options = {}) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
return await this._context.request._innerFetch({ return await this._context.request._innerFetch({ request: this.request(), data: options.postData, ...options });
request: this.request(),
data: options.postData,
...options
});
}); });
} }
async fulfill(options = {}) { async fulfill(options = {}) {
@@ -268,44 +276,50 @@ class Route extends _channelOwner.ChannelOwner {
} }
async _innerFulfill(options = {}) { async _innerFulfill(options = {}) {
let fetchResponseUid; let fetchResponseUid;
let { let { status: statusOption, headers: headersOption, body } = options;
status: statusOption, if (options.json !== void 0) {
headers: headersOption, (0, import_assert.assert)(options.body === void 0, "Can specify either body or json parameters");
body
} = options;
if (options.json !== undefined) {
(0, _assert.assert)(options.body === undefined, 'Can specify either body or json parameters');
body = JSON.stringify(options.json); body = JSON.stringify(options.json);
} }
if (options.response instanceof _fetch.APIResponse) { if (options.response instanceof import_fetch.APIResponse) {
statusOption !== null && statusOption !== void 0 ? statusOption : statusOption = options.response.status(); statusOption ??= options.response.status();
headersOption !== null && headersOption !== void 0 ? headersOption : headersOption = options.response.headers(); headersOption ??= options.response.headers();
if (body === undefined && options.path === undefined) { if (body === void 0 && options.path === void 0) {
if (options.response._request._connection === this._connection) fetchResponseUid = options.response._fetchUid();else body = await options.response.body(); if (options.response._request._connection === this._connection)
fetchResponseUid = options.response._fetchUid();
else
body = await options.response.body();
} }
} }
let isBase64 = false; let isBase64 = false;
let length = 0; let length = 0;
if (options.path) { if (options.path) {
const buffer = await this._platform.fs().promises.readFile(options.path); const buffer = await this._platform.fs().promises.readFile(options.path);
body = buffer.toString('base64'); body = buffer.toString("base64");
isBase64 = true; isBase64 = true;
length = buffer.length; length = buffer.length;
} else if ((0, _rtti.isString)(body)) { } else if ((0, import_rtti.isString)(body)) {
isBase64 = false; isBase64 = false;
length = Buffer.byteLength(body); length = Buffer.byteLength(body);
} else if (body) { } else if (body) {
length = body.length; length = body.length;
body = body.toString('base64'); body = body.toString("base64");
isBase64 = true; isBase64 = true;
} }
const headers = {}; const headers = {};
for (const header of Object.keys(headersOption || {})) headers[header.toLowerCase()] = String(headersOption[header]); for (const header of Object.keys(headersOption || {}))
if (options.contentType) headers['content-type'] = String(options.contentType);else if (options.json) headers['content-type'] = 'application/json';else if (options.path) headers['content-type'] = (0, _mimeType.getMimeTypeForPath)(options.path) || 'application/octet-stream'; headers[header.toLowerCase()] = String(headersOption[header]);
if (length && !('content-length' in headers)) headers['content-length'] = String(length); if (options.contentType)
headers["content-type"] = String(options.contentType);
else if (options.json)
headers["content-type"] = "application/json";
else if (options.path)
headers["content-type"] = (0, import_mimeType.getMimeTypeForPath)(options.path) || "application/octet-stream";
if (length && !("content-length" in headers))
headers["content-length"] = String(length);
await this._raceWithTargetClose(this._channel.fulfill({ await this._raceWithTargetClose(this._channel.fulfill({
status: statusOption || 200, status: statusOption || 200,
headers: (0, _headers.headersObjectToArray)(headers), headers: (0, import_headers.headersObjectToArray)(headers),
body, body,
isBase64, isBase64,
fetchResponseUid fetchResponseUid
@@ -314,11 +328,15 @@ class Route extends _channelOwner.ChannelOwner {
async continue(options = {}) { async continue(options = {}) {
await this._handleRoute(async () => { await this._handleRoute(async () => {
this.request()._applyFallbackOverrides(options); this.request()._applyFallbackOverrides(options);
await this._innerContinue(false /* isFallback */); await this._innerContinue(
false
/* isFallback */
);
}); });
} }
_checkNotHandled() { _checkNotHandled() {
if (!this._handlingPromise) throw new Error('Route is already handled!'); if (!this._handlingPromise)
throw new Error("Route is already handled!");
} }
_reportHandled(done) { _reportHandled(done) {
const chain = this._handlingPromise; const chain = this._handlingPromise;
@@ -330,31 +348,22 @@ class Route extends _channelOwner.ChannelOwner {
return await this._raceWithTargetClose(this._channel.continue({ return await this._raceWithTargetClose(this._channel.continue({
url: options.url, url: options.url,
method: options.method, method: options.method,
headers: options.headers ? (0, _headers.headersObjectToArray)(options.headers) : undefined, headers: options.headers ? (0, import_headers.headersObjectToArray)(options.headers) : void 0,
postData: options.postDataBuffer, postData: options.postDataBuffer,
isFallback isFallback
})); }));
} }
} }
exports.Route = Route; class WebSocketRoute extends import_channelOwner.ChannelOwner {
class WebSocketRoute extends _channelOwner.ChannelOwner {
static from(route) {
return route._object;
}
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._onPageMessage = void 0;
this._onPageClose = void 0;
this._onServerMessage = void 0;
this._onServerClose = void 0;
this._server = void 0;
this._connected = false; this._connected = false;
this.markAsInternalType(); this.markAsInternalType();
this._server = { this._server = {
onMessage: handler => { onMessage: (handler) => {
this._onServerMessage = handler; this._onServerMessage = handler;
}, },
onClose: handler => { onClose: (handler) => {
this._onServerClose = handler; this._onServerClose = handler;
}, },
connectToServer: () => { connectToServer: () => {
@@ -364,88 +373,75 @@ class WebSocketRoute extends _channelOwner.ChannelOwner {
return this._initializer.url; return this._initializer.url;
}, },
close: async (options = {}) => { close: async (options = {}) => {
await this._channel.closeServer({ await this._channel.closeServer({ ...options, wasClean: true }).catch(() => {
...options, });
wasClean: true
}).catch(() => {});
}, },
send: message => { send: (message) => {
if ((0, _rtti.isString)(message)) this._channel.sendToServer({ if ((0, import_rtti.isString)(message))
message, this._channel.sendToServer({ message, isBase64: false }).catch(() => {
isBase64: false });
}).catch(() => {});else this._channel.sendToServer({ else
message: message.toString('base64'), this._channel.sendToServer({ message: message.toString("base64"), isBase64: true }).catch(() => {
isBase64: true });
}).catch(() => {});
}, },
async [Symbol.asyncDispose]() { async [Symbol.asyncDispose]() {
await this.close(); await this.close();
} }
}; };
this._channel.on('messageFromPage', ({ this._channel.on("messageFromPage", ({ message, isBase64 }) => {
message, if (this._onPageMessage)
isBase64 this._onPageMessage(isBase64 ? Buffer.from(message, "base64") : message);
}) => { else if (this._connected)
if (this._onPageMessage) this._onPageMessage(isBase64 ? Buffer.from(message, 'base64') : message);else if (this._connected) this._channel.sendToServer({ this._channel.sendToServer({ message, isBase64 }).catch(() => {
message,
isBase64
}).catch(() => {});
}); });
this._channel.on('messageFromServer', ({
message,
isBase64
}) => {
if (this._onServerMessage) this._onServerMessage(isBase64 ? Buffer.from(message, 'base64') : message);else this._channel.sendToPage({
message,
isBase64
}).catch(() => {});
}); });
this._channel.on('closePage', ({ this._channel.on("messageFromServer", ({ message, isBase64 }) => {
code, if (this._onServerMessage)
reason, this._onServerMessage(isBase64 ? Buffer.from(message, "base64") : message);
wasClean else
}) => { this._channel.sendToPage({ message, isBase64 }).catch(() => {
if (this._onPageClose) this._onPageClose(code, reason);else this._channel.closeServer({
code,
reason,
wasClean
}).catch(() => {});
}); });
this._channel.on('closeServer', ({
code,
reason,
wasClean
}) => {
if (this._onServerClose) this._onServerClose(code, reason);else this._channel.closePage({
code,
reason,
wasClean
}).catch(() => {});
}); });
this._channel.on("closePage", ({ code, reason, wasClean }) => {
if (this._onPageClose)
this._onPageClose(code, reason);
else
this._channel.closeServer({ code, reason, wasClean }).catch(() => {
});
});
this._channel.on("closeServer", ({ code, reason, wasClean }) => {
if (this._onServerClose)
this._onServerClose(code, reason);
else
this._channel.closePage({ code, reason, wasClean }).catch(() => {
});
});
}
static from(route) {
return route._object;
} }
url() { url() {
return this._initializer.url; return this._initializer.url;
} }
async close(options = {}) { async close(options = {}) {
await this._channel.closePage({ await this._channel.closePage({ ...options, wasClean: true }).catch(() => {
...options, });
wasClean: true
}).catch(() => {});
} }
connectToServer() { connectToServer() {
if (this._connected) throw new Error('Already connected to the server'); if (this._connected)
throw new Error("Already connected to the server");
this._connected = true; this._connected = true;
this._channel.connect().catch(() => {}); this._channel.connect().catch(() => {
});
return this._server; return this._server;
} }
send(message) { send(message) {
if ((0, _rtti.isString)(message)) this._channel.sendToPage({ if ((0, import_rtti.isString)(message))
message, this._channel.sendToPage({ message, isBase64: false }).catch(() => {
isBase64: false });
}).catch(() => {});else this._channel.sendToPage({ else
message: message.toString('base64'), this._channel.sendToPage({ message: message.toString("base64"), isBase64: true }).catch(() => {
isBase64: true });
}).catch(() => {});
} }
onMessage(handler) { onMessage(handler) {
this._onPageMessage = handler; this._onPageMessage = handler;
@@ -457,17 +453,13 @@ class WebSocketRoute extends _channelOwner.ChannelOwner {
await this.close(); await this.close();
} }
async _afterHandle() { async _afterHandle() {
if (this._connected) return; if (this._connected)
// Ensure that websocket is "open" and can send messages without an actual server connection. return;
await this._channel.ensureOpened(); await this._channel.ensureOpened();
} }
} }
exports.WebSocketRoute = WebSocketRoute;
class WebSocketRouteHandler { class WebSocketRouteHandler {
constructor(baseURL, url, handler) { constructor(baseURL, url, handler) {
this._baseURL = void 0;
this.url = void 0;
this.handler = void 0;
this._baseURL = baseURL; this._baseURL = baseURL;
this.url = url; this.url = url;
this.handler = handler; this.handler = handler;
@@ -476,20 +468,19 @@ class WebSocketRouteHandler {
const patterns = []; const patterns = [];
let all = false; let all = false;
for (const handler of handlers) { for (const handler of handlers) {
if ((0, _rtti.isString)(handler.url)) patterns.push({ if ((0, import_rtti.isString)(handler.url))
glob: handler.url patterns.push({ glob: handler.url });
});else if ((0, _rtti.isRegExp)(handler.url)) patterns.push({ else if ((0, import_rtti.isRegExp)(handler.url))
regexSource: handler.url.source, patterns.push({ regexSource: handler.url.source, regexFlags: handler.url.flags });
regexFlags: handler.url.flags else
});else all = true; all = true;
} }
if (all) return [{ if (all)
glob: '**/*' return [{ glob: "**/*" }];
}];
return patterns; return patterns;
} }
matches(wsURL) { matches(wsURL) {
return (0, _urlMatch.urlMatches)(this._baseURL, wsURL, this.url); return (0, import_urlMatch.urlMatches)(this._baseURL, wsURL, this.url, true);
} }
async handle(webSocketRoute) { async handle(webSocketRoute) {
const handler = this.handler; const handler = this.handler;
@@ -497,30 +488,25 @@ class WebSocketRouteHandler {
await webSocketRoute._afterHandle(); await webSocketRoute._afterHandle();
} }
} }
exports.WebSocketRouteHandler = WebSocketRouteHandler; class Response extends import_channelOwner.ChannelOwner {
class Response extends _channelOwner.ChannelOwner { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._finishedPromise = new import_manualPromise.ManualPromise();
this.markAsInternalType();
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._request = Request.from(this._initializer.request);
Object.assign(this._request._timing, this._initializer.timing);
}
static from(response) { static from(response) {
return response._object; return response._object;
} }
static fromNullable(response) { static fromNullable(response) {
return response ? Response.from(response) : null; return response ? Response.from(response) : null;
} }
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._provisionalHeaders = void 0;
this._actualHeadersPromise = void 0;
this._request = void 0;
this._finishedPromise = new _manualPromise.ManualPromise();
this.markAsInternalType();
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._request = Request.from(this._initializer.request);
Object.assign(this._request._timing, this._initializer.timing);
}
url() { url() {
return this._initializer.url; return this._initializer.url;
} }
ok() { ok() {
// Status 0 is for file:// URLs
return this._initializer.status === 0 || this._initializer.status >= 200 && this._initializer.status <= 299; return this._initializer.status === 0 || this._initializer.status >= 200 && this._initializer.status <= 299;
} }
status() { status() {
@@ -532,7 +518,6 @@ class Response extends _channelOwner.ChannelOwner {
fromServiceWorker() { fromServiceWorker() {
return this._initializer.fromServiceWorker; return this._initializer.fromServiceWorker;
} }
/** /**
* @deprecated * @deprecated
*/ */
@@ -567,7 +552,7 @@ class Response extends _channelOwner.ChannelOwner {
} }
async text() { async text() {
const content = await this.body(); const content = await this.body();
return content.toString('utf8'); return content.toString("utf8");
} }
async json() { async json() {
const content = await this.text(); const content = await this.text();
@@ -586,37 +571,30 @@ class Response extends _channelOwner.ChannelOwner {
return (await this._channel.securityDetails()).value || null; return (await this._channel.securityDetails()).value || null;
} }
} }
exports.Response = Response; class WebSocket extends import_channelOwner.ChannelOwner {
class WebSocket extends _channelOwner.ChannelOwner {
static from(webSocket) { static from(webSocket) {
return webSocket._object; return webSocket._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._page = void 0;
this._isClosed = void 0;
this._isClosed = false; this._isClosed = false;
this._page = parent; this._page = parent;
this._channel.on('frameSent', event => { this._channel.on("frameSent", (event) => {
if (event.opcode === 1) this.emit(_events.Events.WebSocket.FrameSent, { if (event.opcode === 1)
payload: event.data this.emit(import_events.Events.WebSocket.FrameSent, { payload: event.data });
});else if (event.opcode === 2) this.emit(_events.Events.WebSocket.FrameSent, { else if (event.opcode === 2)
payload: Buffer.from(event.data, 'base64') this.emit(import_events.Events.WebSocket.FrameSent, { payload: Buffer.from(event.data, "base64") });
}); });
this._channel.on("frameReceived", (event) => {
if (event.opcode === 1)
this.emit(import_events.Events.WebSocket.FrameReceived, { payload: event.data });
else if (event.opcode === 2)
this.emit(import_events.Events.WebSocket.FrameReceived, { payload: Buffer.from(event.data, "base64") });
}); });
this._channel.on('frameReceived', event => { this._channel.on("socketError", ({ error }) => this.emit(import_events.Events.WebSocket.Error, error));
if (event.opcode === 1) this.emit(_events.Events.WebSocket.FrameReceived, { this._channel.on("close", () => {
payload: event.data
});else if (event.opcode === 2) this.emit(_events.Events.WebSocket.FrameReceived, {
payload: Buffer.from(event.data, 'base64')
});
});
this._channel.on('socketError', ({
error
}) => this.emit(_events.Events.WebSocket.Error, error));
this._channel.on('close', () => {
this._isClosed = true; this._isClosed = true;
this.emit(_events.Events.WebSocket.Close, this); this.emit(import_events.Events.WebSocket.Close, this);
}); });
} }
url() { url() {
@@ -627,36 +605,33 @@ class WebSocket extends _channelOwner.ChannelOwner {
} }
async waitForEvent(event, optionsOrPredicate = {}) { async waitForEvent(event, optionsOrPredicate = {}) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._page._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = _waiter.Waiter.createForEvent(this, event); const waiter = import_waiter.Waiter.createForEvent(this, event);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== _events.Events.WebSocket.Error) waiter.rejectOnEvent(this, _events.Events.WebSocket.Error, new Error('Socket error')); if (event !== import_events.Events.WebSocket.Error)
if (event !== _events.Events.WebSocket.Close) waiter.rejectOnEvent(this, _events.Events.WebSocket.Close, new Error('Socket closed')); waiter.rejectOnEvent(this, import_events.Events.WebSocket.Error, new Error("Socket error"));
waiter.rejectOnEvent(this._page, _events.Events.Page.Close, () => this._page._closeErrorWithReason()); if (event !== import_events.Events.WebSocket.Close)
waiter.rejectOnEvent(this, import_events.Events.WebSocket.Close, new Error("Socket closed"));
waiter.rejectOnEvent(this._page, import_events.Events.Page.Close, () => this._page._closeErrorWithReason());
const result = await waiter.waitForEvent(this, event, predicate); const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose(); waiter.dispose();
return result; return result;
}); });
} }
} }
exports.WebSocket = WebSocket;
function validateHeaders(headers) { function validateHeaders(headers) {
for (const key of Object.keys(headers)) { for (const key of Object.keys(headers)) {
const value = headers[key]; const value = headers[key];
if (!Object.is(value, undefined) && !(0, _rtti.isString)(value)) throw new Error(`Expected value of header "${key}" to be String, but "${typeof value}" is found.`); if (!Object.is(value, void 0) && !(0, import_rtti.isString)(value))
throw new Error(`Expected value of header "${key}" to be String, but "${typeof value}" is found.`);
} }
} }
class RouteHandler { class RouteHandler {
constructor(platform, baseURL, url, handler, times = Number.MAX_SAFE_INTEGER) { constructor(platform, baseURL, url, handler, times = Number.MAX_SAFE_INTEGER) {
this.handledCount = 0; this.handledCount = 0;
this._baseURL = void 0;
this._times = void 0;
this.url = void 0;
this.handler = void 0;
this._ignoreException = false; this._ignoreException = false;
this._activeInvocations = new Set(); this._activeInvocations = /* @__PURE__ */ new Set();
this._savedZone = void 0;
this._baseURL = baseURL; this._baseURL = baseURL;
this._times = times; this._times = times;
this.url = url; this.url = url;
@@ -667,39 +642,35 @@ class RouteHandler {
const patterns = []; const patterns = [];
let all = false; let all = false;
for (const handler of handlers) { for (const handler of handlers) {
if ((0, _rtti.isString)(handler.url)) patterns.push({ if ((0, import_rtti.isString)(handler.url))
glob: handler.url patterns.push({ glob: handler.url });
});else if ((0, _rtti.isRegExp)(handler.url)) patterns.push({ else if ((0, import_rtti.isRegExp)(handler.url))
regexSource: handler.url.source, patterns.push({ regexSource: handler.url.source, regexFlags: handler.url.flags });
regexFlags: handler.url.flags else
});else all = true; all = true;
} }
if (all) return [{ if (all)
glob: '**/*' return [{ glob: "**/*" }];
}];
return patterns; return patterns;
} }
matches(requestURL) { matches(requestURL) {
return (0, _urlMatch.urlMatches)(this._baseURL, requestURL, this.url); return (0, import_urlMatch.urlMatches)(this._baseURL, requestURL, this.url);
} }
async handle(route) { async handle(route) {
return await this._savedZone.run(async () => this._handleImpl(route)); return await this._savedZone.run(async () => this._handleImpl(route));
} }
async _handleImpl(route) { async _handleImpl(route) {
const handlerInvocation = { const handlerInvocation = { complete: new import_manualPromise.ManualPromise(), route };
complete: new _manualPromise.ManualPromise(),
route
};
this._activeInvocations.add(handlerInvocation); this._activeInvocations.add(handlerInvocation);
try { try {
return await this._handleInternal(route); return await this._handleInternal(route);
} catch (e) { } catch (e) {
// If the handler was stopped (without waiting for completion), we ignore all exceptions. if (this._ignoreException)
if (this._ignoreException) return false; return false;
if ((0, _errors.isTargetClosedError)(e)) { if ((0, import_errors.isTargetClosedError)(e)) {
// We are failing in the handler because the target close closed. (0, import_stackTrace.rewriteErrorMessage)(e, `"${e.message}" while running route callback.
// Give user a hint! Consider awaiting \`await page.unrouteAll({ behavior: 'ignoreErrors' })\`
(0, _stackTrace.rewriteErrorMessage)(e, `"${e.message}" while running route callback.\nConsider awaiting \`await page.unrouteAll({ behavior: 'ignoreErrors' })\`\nbefore the end of the test to ignore remaining routes in flight.`); before the end of the test to ignore remaining routes in flight.`);
} }
throw e; throw e;
} finally { } finally {
@@ -708,16 +679,13 @@ class RouteHandler {
} }
} }
async stop(behavior) { async stop(behavior) {
// When a handler is manually unrouted or its page/context is closed we either if (behavior === "ignoreErrors") {
// - wait for the current handler invocations to finish
// - or do not wait, if the user opted out of it, but swallow all exceptions
// that happen after the unroute/close.
if (behavior === 'ignoreErrors') {
this._ignoreException = true; this._ignoreException = true;
} else { } else {
const promises = []; const promises = [];
for (const activation of this._activeInvocations) { for (const activation of this._activeInvocations) {
if (!activation.route._didThrow) promises.push(activation.complete); if (!activation.route._didThrow)
promises.push(activation.complete);
} }
await Promise.all(promises); await Promise.all(promises);
} }
@@ -725,45 +693,59 @@ class RouteHandler {
async _handleInternal(route) { async _handleInternal(route) {
++this.handledCount; ++this.handledCount;
const handledPromise = route._startHandling(); const handledPromise = route._startHandling();
// Extract handler into a variable to avoid [RouteHandler.handler] in the stack.
const handler = this.handler; const handler = this.handler;
const [handled] = await Promise.all([handledPromise, handler(route, route.request())]); const [handled] = await Promise.all([
handledPromise,
handler(route, route.request())
]);
return handled; return handled;
} }
willExpire() { willExpire() {
return this.handledCount + 1 >= this._times; return this.handledCount + 1 >= this._times;
} }
} }
exports.RouteHandler = RouteHandler;
class RawHeaders { class RawHeaders {
constructor(headers) {
this._headersMap = new import_multimap.MultiMap();
this._headersArray = headers;
for (const header of headers)
this._headersMap.set(header.name.toLowerCase(), header.value);
}
static _fromHeadersObjectLossy(headers) { static _fromHeadersObjectLossy(headers) {
const headersArray = Object.entries(headers).map(([name, value]) => ({ const headersArray = Object.entries(headers).map(([name, value]) => ({
name, name,
value value
})).filter(header => header.value !== undefined); })).filter((header) => header.value !== void 0);
return new RawHeaders(headersArray); return new RawHeaders(headersArray);
} }
constructor(headers) {
this._headersArray = void 0;
this._headersMap = new _multimap.MultiMap();
this._headersArray = headers;
for (const header of headers) this._headersMap.set(header.name.toLowerCase(), header.value);
}
get(name) { get(name) {
const values = this.getAll(name); const values = this.getAll(name);
if (!values || !values.length) return null; if (!values || !values.length)
return values.join(name.toLowerCase() === 'set-cookie' ? '\n' : ', '); return null;
return values.join(name.toLowerCase() === "set-cookie" ? "\n" : ", ");
} }
getAll(name) { getAll(name) {
return [...this._headersMap.get(name.toLowerCase())]; return [...this._headersMap.get(name.toLowerCase())];
} }
headers() { headers() {
const result = {}; const result = {};
for (const name of this._headersMap.keys()) result[name] = this.get(name); for (const name of this._headersMap.keys())
result[name] = this.get(name);
return result; return result;
} }
headersArray() { headersArray() {
return this._headersArray; return this._headersArray;
} }
} }
exports.RawHeaders = RawHeaders; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
RawHeaders,
Request,
Response,
Route,
RouteHandler,
WebSocket,
WebSocketRoute,
WebSocketRouteHandler,
validateHeaders
});

View File

@@ -1,180 +1,161 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var page_exports = {};
__export(page_exports, {
BindingCall: () => BindingCall,
Page: () => Page
}); });
exports.Page = exports.BindingCall = void 0; module.exports = __toCommonJS(page_exports);
var _accessibility = require("./accessibility"); var import_accessibility = require("./accessibility");
var _artifact = require("./artifact"); var import_artifact = require("./artifact");
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
var _clientHelper = require("./clientHelper"); var import_clientHelper = require("./clientHelper");
var _coverage = require("./coverage"); var import_coverage = require("./coverage");
var _download = require("./download"); var import_download = require("./download");
var _elementHandle = require("./elementHandle"); var import_elementHandle = require("./elementHandle");
var _errors = require("./errors"); var import_errors = require("./errors");
var _events = require("./events"); var import_events = require("./events");
var _fileChooser = require("./fileChooser"); var import_fileChooser = require("./fileChooser");
var _frame = require("./frame"); var import_frame = require("./frame");
var _harRouter = require("./harRouter"); var import_harRouter = require("./harRouter");
var _input = require("./input"); var import_input = require("./input");
var _jsHandle = require("./jsHandle"); var import_jsHandle = require("./jsHandle");
var _network = require("./network"); var import_network = require("./network");
var _video = require("./video"); var import_video = require("./video");
var _waiter = require("./waiter"); var import_waiter = require("./waiter");
var _worker = require("./worker"); var import_worker = require("./worker");
var _timeoutSettings = require("./timeoutSettings"); var import_timeoutSettings = require("./timeoutSettings");
var _assert = require("../utils/isomorphic/assert"); var import_assert = require("../utils/isomorphic/assert");
var _fileUtils = require("./fileUtils"); var import_fileUtils = require("./fileUtils");
var _headers = require("../utils/isomorphic/headers"); var import_headers = require("../utils/isomorphic/headers");
var _stringUtils = require("../utils/isomorphic/stringUtils"); var import_stringUtils = require("../utils/isomorphic/stringUtils");
var _urlMatch = require("../utils/isomorphic/urlMatch"); var import_urlMatch = require("../utils/isomorphic/urlMatch");
var _manualPromise = require("../utils/isomorphic/manualPromise"); var import_manualPromise = require("../utils/isomorphic/manualPromise");
var _rtti = require("../utils/isomorphic/rtti"); var import_rtti = require("../utils/isomorphic/rtti");
/** class Page extends import_channelOwner.ChannelOwner {
* Copyright 2017 Google Inc. All rights reserved. constructor(parent, type, guid, initializer) {
* Modifications copyright (c) Microsoft Corporation. super(parent, type, guid, initializer);
* this._frames = /* @__PURE__ */ new Set();
* Licensed under the Apache License, Version 2.0 (the "License"); this._workers = /* @__PURE__ */ new Set();
* you may not use this file except in compliance with the License. this._closed = false;
* You may obtain a copy of the License at this._closedOrCrashedScope = new import_manualPromise.LongStandingScope();
* this._routes = [];
* http://www.apache.org/licenses/LICENSE-2.0 this._webSocketRoutes = [];
* this._bindings = /* @__PURE__ */ new Map();
* Unless required by applicable law or agreed to in writing, software this._video = null;
* distributed under the License is distributed on an "AS IS" BASIS, this._closeWasCalled = false;
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. this._harRouters = [];
* See the License for the specific language governing permissions and this._locatorHandlers = /* @__PURE__ */ new Map();
* limitations under the License. this._browserContext = parent;
*/ this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform, this._browserContext._timeoutSettings);
this.accessibility = new import_accessibility.Accessibility(this._channel);
class Page extends _channelOwner.ChannelOwner { this.keyboard = new import_input.Keyboard(this);
this.mouse = new import_input.Mouse(this);
this.request = this._browserContext.request;
this.touchscreen = new import_input.Touchscreen(this);
this.clock = this._browserContext.clock;
this._mainFrame = import_frame.Frame.from(initializer.mainFrame);
this._mainFrame._page = this;
this._frames.add(this._mainFrame);
this._viewportSize = initializer.viewportSize || null;
this._closed = initializer.isClosed;
this._opener = Page.fromNullable(initializer.opener);
this._channel.on("bindingCall", ({ binding }) => this._onBinding(BindingCall.from(binding)));
this._channel.on("close", () => this._onClose());
this._channel.on("crash", () => this._onCrash());
this._channel.on("download", ({ url, suggestedFilename, artifact }) => {
const artifactObject = import_artifact.Artifact.from(artifact);
this.emit(import_events.Events.Page.Download, new import_download.Download(this, url, suggestedFilename, artifactObject));
});
this._channel.on("fileChooser", ({ element, isMultiple }) => this.emit(import_events.Events.Page.FileChooser, new import_fileChooser.FileChooser(this, import_elementHandle.ElementHandle.from(element), isMultiple)));
this._channel.on("frameAttached", ({ frame }) => this._onFrameAttached(import_frame.Frame.from(frame)));
this._channel.on("frameDetached", ({ frame }) => this._onFrameDetached(import_frame.Frame.from(frame)));
this._channel.on("locatorHandlerTriggered", ({ uid }) => this._onLocatorHandlerTriggered(uid));
this._channel.on("route", ({ route }) => this._onRoute(import_network.Route.from(route)));
this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(import_network.WebSocketRoute.from(webSocketRoute)));
this._channel.on("video", ({ artifact }) => {
const artifactObject = import_artifact.Artifact.from(artifact);
this._forceVideo()._artifactReady(artifactObject);
});
this._channel.on("webSocket", ({ webSocket }) => this.emit(import_events.Events.Page.WebSocket, import_network.WebSocket.from(webSocket)));
this._channel.on("worker", ({ worker }) => this._onWorker(import_worker.Worker.from(worker)));
this.coverage = new import_coverage.Coverage(this._channel);
this.once(import_events.Events.Page.Close, () => this._closedOrCrashedScope.close(this._closeErrorWithReason()));
this.once(import_events.Events.Page.Crash, () => this._closedOrCrashedScope.close(new import_errors.TargetClosedError()));
this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
[import_events.Events.Page.Console, "console"],
[import_events.Events.Page.Dialog, "dialog"],
[import_events.Events.Page.Request, "request"],
[import_events.Events.Page.Response, "response"],
[import_events.Events.Page.RequestFinished, "requestFinished"],
[import_events.Events.Page.RequestFailed, "requestFailed"],
[import_events.Events.Page.FileChooser, "fileChooser"]
]));
}
static from(page) { static from(page) {
return page._object; return page._object;
} }
static fromNullable(page) { static fromNullable(page) {
return page ? Page.from(page) : null; return page ? Page.from(page) : null;
} }
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this._browserContext = void 0;
this._ownedContext = void 0;
this._mainFrame = void 0;
this._frames = new Set();
this._workers = new Set();
this._closed = false;
this._closedOrCrashedScope = new _manualPromise.LongStandingScope();
this._viewportSize = void 0;
this._routes = [];
this._webSocketRoutes = [];
this.accessibility = void 0;
this.coverage = void 0;
this.keyboard = void 0;
this.mouse = void 0;
this.request = void 0;
this.touchscreen = void 0;
this.clock = void 0;
this._bindings = new Map();
this._timeoutSettings = void 0;
this._video = null;
this._opener = void 0;
this._closeReason = void 0;
this._closeWasCalled = false;
this._harRouters = [];
this._locatorHandlers = new Map();
this._browserContext = parent;
this._timeoutSettings = new _timeoutSettings.TimeoutSettings(this._platform, this._browserContext._timeoutSettings);
this.accessibility = new _accessibility.Accessibility(this._channel);
this.keyboard = new _input.Keyboard(this);
this.mouse = new _input.Mouse(this);
this.request = this._browserContext.request;
this.touchscreen = new _input.Touchscreen(this);
this.clock = this._browserContext.clock;
this._mainFrame = _frame.Frame.from(initializer.mainFrame);
this._mainFrame._page = this;
this._frames.add(this._mainFrame);
this._viewportSize = initializer.viewportSize || null;
this._closed = initializer.isClosed;
this._opener = Page.fromNullable(initializer.opener);
this._channel.on('bindingCall', ({
binding
}) => this._onBinding(BindingCall.from(binding)));
this._channel.on('close', () => this._onClose());
this._channel.on('crash', () => this._onCrash());
this._channel.on('download', ({
url,
suggestedFilename,
artifact
}) => {
const artifactObject = _artifact.Artifact.from(artifact);
this.emit(_events.Events.Page.Download, new _download.Download(this, url, suggestedFilename, artifactObject));
});
this._channel.on('fileChooser', ({
element,
isMultiple
}) => this.emit(_events.Events.Page.FileChooser, new _fileChooser.FileChooser(this, _elementHandle.ElementHandle.from(element), isMultiple)));
this._channel.on('frameAttached', ({
frame
}) => this._onFrameAttached(_frame.Frame.from(frame)));
this._channel.on('frameDetached', ({
frame
}) => this._onFrameDetached(_frame.Frame.from(frame)));
this._channel.on('locatorHandlerTriggered', ({
uid
}) => this._onLocatorHandlerTriggered(uid));
this._channel.on('route', ({
route
}) => this._onRoute(_network.Route.from(route)));
this._channel.on('webSocketRoute', ({
webSocketRoute
}) => this._onWebSocketRoute(_network.WebSocketRoute.from(webSocketRoute)));
this._channel.on('video', ({
artifact
}) => {
const artifactObject = _artifact.Artifact.from(artifact);
this._forceVideo()._artifactReady(artifactObject);
});
this._channel.on('webSocket', ({
webSocket
}) => this.emit(_events.Events.Page.WebSocket, _network.WebSocket.from(webSocket)));
this._channel.on('worker', ({
worker
}) => this._onWorker(_worker.Worker.from(worker)));
this.coverage = new _coverage.Coverage(this._channel);
this.once(_events.Events.Page.Close, () => this._closedOrCrashedScope.close(this._closeErrorWithReason()));
this.once(_events.Events.Page.Crash, () => this._closedOrCrashedScope.close(new _errors.TargetClosedError()));
this._setEventToSubscriptionMapping(new Map([[_events.Events.Page.Console, 'console'], [_events.Events.Page.Dialog, 'dialog'], [_events.Events.Page.Request, 'request'], [_events.Events.Page.Response, 'response'], [_events.Events.Page.RequestFinished, 'requestFinished'], [_events.Events.Page.RequestFailed, 'requestFailed'], [_events.Events.Page.FileChooser, 'fileChooser']]));
}
_onFrameAttached(frame) { _onFrameAttached(frame) {
frame._page = this; frame._page = this;
this._frames.add(frame); this._frames.add(frame);
if (frame._parentFrame) frame._parentFrame._childFrames.add(frame); if (frame._parentFrame)
this.emit(_events.Events.Page.FrameAttached, frame); frame._parentFrame._childFrames.add(frame);
this.emit(import_events.Events.Page.FrameAttached, frame);
} }
_onFrameDetached(frame) { _onFrameDetached(frame) {
this._frames.delete(frame); this._frames.delete(frame);
frame._detached = true; frame._detached = true;
if (frame._parentFrame) frame._parentFrame._childFrames.delete(frame); if (frame._parentFrame)
this.emit(_events.Events.Page.FrameDetached, frame); frame._parentFrame._childFrames.delete(frame);
this.emit(import_events.Events.Page.FrameDetached, frame);
} }
async _onRoute(route) { async _onRoute(route) {
route._context = this.context(); route._context = this.context();
const routeHandlers = this._routes.slice(); const routeHandlers = this._routes.slice();
for (const routeHandler of routeHandlers) { for (const routeHandler of routeHandlers) {
// If the page was closed we stall all requests right away. if (this._closeWasCalled || this._browserContext._closeWasCalled)
if (this._closeWasCalled || this._browserContext._closeWasCalled) return; return;
if (!routeHandler.matches(route.request().url())) continue; if (!routeHandler.matches(route.request().url()))
continue;
const index = this._routes.indexOf(routeHandler); const index = this._routes.indexOf(routeHandler);
if (index === -1) continue; if (index === -1)
if (routeHandler.willExpire()) this._routes.splice(index, 1); continue;
if (routeHandler.willExpire())
this._routes.splice(index, 1);
const handled = await routeHandler.handle(route); const handled = await routeHandler.handle(route);
if (!this._routes.length) this._wrapApiCall(() => this._updateInterceptionPatterns(), true).catch(() => {}); if (!this._routes.length)
if (handled) return; this._wrapApiCall(() => this._updateInterceptionPatterns(), true).catch(() => {
});
if (handled)
return;
} }
await this._browserContext._onRoute(route); await this._browserContext._onRoute(route);
} }
async _onWebSocketRoute(webSocketRoute) { async _onWebSocketRoute(webSocketRoute) {
const routeHandler = this._webSocketRoutes.find(route => route.matches(webSocketRoute.url())); const routeHandler = this._webSocketRoutes.find((route) => route.matches(webSocketRoute.url()));
if (routeHandler) await routeHandler.handle(webSocketRoute);else await this._browserContext._onWebSocketRoute(webSocketRoute); if (routeHandler)
await routeHandler.handle(webSocketRoute);
else
await this._browserContext._onWebSocketRoute(webSocketRoute);
} }
async _onBinding(bindingCall) { async _onBinding(bindingCall) {
const func = this._bindings.get(bindingCall._initializer.name); const func = this._bindings.get(bindingCall._initializer.name);
@@ -187,35 +168,37 @@ class Page extends _channelOwner.ChannelOwner {
_onWorker(worker) { _onWorker(worker) {
this._workers.add(worker); this._workers.add(worker);
worker._page = this; worker._page = this;
this.emit(_events.Events.Page.Worker, worker); this.emit(import_events.Events.Page.Worker, worker);
} }
_onClose() { _onClose() {
this._closed = true; this._closed = true;
this._browserContext._pages.delete(this); this._browserContext._pages.delete(this);
this._browserContext._backgroundPages.delete(this); this._browserContext._backgroundPages.delete(this);
this._disposeHarRouters(); this._disposeHarRouters();
this.emit(_events.Events.Page.Close, this); this.emit(import_events.Events.Page.Close, this);
} }
_onCrash() { _onCrash() {
this.emit(_events.Events.Page.Crash, this); this.emit(import_events.Events.Page.Crash, this);
} }
context() { context() {
return this._browserContext; return this._browserContext;
} }
async opener() { async opener() {
if (!this._opener || this._opener.isClosed()) return null; if (!this._opener || this._opener.isClosed())
return null;
return this._opener; return this._opener;
} }
mainFrame() { mainFrame() {
return this._mainFrame; return this._mainFrame;
} }
frame(frameSelector) { frame(frameSelector) {
const name = (0, _rtti.isString)(frameSelector) ? frameSelector : frameSelector.name; const name = (0, import_rtti.isString)(frameSelector) ? frameSelector : frameSelector.name;
const url = (0, _rtti.isObject)(frameSelector) ? frameSelector.url : undefined; const url = (0, import_rtti.isObject)(frameSelector) ? frameSelector.url : void 0;
(0, _assert.assert)(name || url, 'Either name or url matcher should be specified'); (0, import_assert.assert)(name || url, "Either name or url matcher should be specified");
return this.frames().find(f => { return this.frames().find((f) => {
if (name) return f.name() === name; if (name)
return (0, _urlMatch.urlMatches)(this._browserContext._options.baseURL, f.url(), url); return f.name() === name;
return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, f.url(), url);
}) || null; }) || null;
} }
frames() { frames() {
@@ -224,28 +207,25 @@ class Page extends _channelOwner.ChannelOwner {
setDefaultNavigationTimeout(timeout) { setDefaultNavigationTimeout(timeout) {
this._timeoutSettings.setDefaultNavigationTimeout(timeout); this._timeoutSettings.setDefaultNavigationTimeout(timeout);
this._wrapApiCall(async () => { this._wrapApiCall(async () => {
await this._channel.setDefaultNavigationTimeoutNoReply({ await this._channel.setDefaultNavigationTimeoutNoReply({ timeout });
timeout }, true).catch(() => {
}); });
}, true).catch(() => {});
} }
setDefaultTimeout(timeout) { setDefaultTimeout(timeout) {
this._timeoutSettings.setDefaultTimeout(timeout); this._timeoutSettings.setDefaultTimeout(timeout);
this._wrapApiCall(async () => { this._wrapApiCall(async () => {
await this._channel.setDefaultTimeoutNoReply({ await this._channel.setDefaultTimeoutNoReply({ timeout });
timeout }, true).catch(() => {
}); });
}, true).catch(() => {});
} }
_forceVideo() { _forceVideo() {
if (!this._video) this._video = new _video.Video(this, this._connection); if (!this._video)
this._video = new import_video.Video(this, this._connection);
return this._video; return this._video;
} }
video() { video() {
// Note: we are creating Video object lazily, because we do not know if (!this._browserContext._options.recordVideo)
// BrowserContextOptions when constructing the page - it is assigned return null;
// too late during launchPersistentContext.
if (!this._browserContext._options.recordVideo) return null;
return this._forceVideo(); return this._forceVideo();
} }
async $(selector, options) { async $(selector, options) {
@@ -258,15 +238,15 @@ class Page extends _channelOwner.ChannelOwner {
return await this._mainFrame.dispatchEvent(selector, type, eventInit, options); return await this._mainFrame.dispatchEvent(selector, type, eventInit, options);
} }
async evaluateHandle(pageFunction, arg) { async evaluateHandle(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2); (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
return await this._mainFrame.evaluateHandle(pageFunction, arg); return await this._mainFrame.evaluateHandle(pageFunction, arg);
} }
async $eval(selector, pageFunction, arg) { async $eval(selector, pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 3); (0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
return await this._mainFrame.$eval(selector, pageFunction, arg); return await this._mainFrame.$eval(selector, pageFunction, arg);
} }
async $$eval(selector, pageFunction, arg) { async $$eval(selector, pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 3); (0, import_jsHandle.assertMaxArguments)(arguments.length, 3);
return await this._mainFrame.$$eval(selector, pageFunction, arg); return await this._mainFrame.$$eval(selector, pageFunction, arg);
} }
async $$(selector) { async $$(selector) {
@@ -279,24 +259,17 @@ class Page extends _channelOwner.ChannelOwner {
return await this._mainFrame.addStyleTag(options); return await this._mainFrame.addStyleTag(options);
} }
async exposeFunction(name, callback) { async exposeFunction(name, callback) {
await this._channel.exposeBinding({ await this._channel.exposeBinding({ name });
name
});
const binding = (source, ...args) => callback(...args); const binding = (source, ...args) => callback(...args);
this._bindings.set(name, binding); this._bindings.set(name, binding);
} }
async exposeBinding(name, callback, options = {}) { async exposeBinding(name, callback, options = {}) {
await this._channel.exposeBinding({ await this._channel.exposeBinding({ name, needsHandle: options.handle });
name,
needsHandle: options.handle
});
this._bindings.set(name, callback); this._bindings.set(name, callback);
} }
async setExtraHTTPHeaders(headers) { async setExtraHTTPHeaders(headers) {
(0, _network.validateHeaders)(headers); (0, import_network.validateHeaders)(headers);
await this._channel.setExtraHTTPHeaders({ await this._channel.setExtraHTTPHeaders({ headers: (0, import_headers.headersObjectToArray)(headers) });
headers: (0, _headers.headersObjectToArray)(headers)
});
} }
url() { url() {
return this._mainFrame.url(); return this._mainFrame.url();
@@ -311,51 +284,40 @@ class Page extends _channelOwner.ChannelOwner {
return await this._mainFrame.goto(url, options); return await this._mainFrame.goto(url, options);
} }
async reload(options = {}) { async reload(options = {}) {
const waitUntil = (0, _frame.verifyLoadState)('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return _network.Response.fromNullable((await this._channel.reload({ return import_network.Response.fromNullable((await this._channel.reload({ ...options, waitUntil })).response);
...options,
waitUntil
})).response);
} }
async addLocatorHandler(locator, handler, options = {}) { async addLocatorHandler(locator, handler, options = {}) {
if (locator._frame !== this._mainFrame) throw new Error(`Locator must belong to the main frame of this page`); if (locator._frame !== this._mainFrame)
if (options.times === 0) return; throw new Error(`Locator must belong to the main frame of this page`);
const { if (options.times === 0)
uid return;
} = await this._channel.registerLocatorHandler({ const { uid } = await this._channel.registerLocatorHandler({ selector: locator._selector, noWaitAfter: options.noWaitAfter });
selector: locator._selector, this._locatorHandlers.set(uid, { locator, handler, times: options.times });
noWaitAfter: options.noWaitAfter
});
this._locatorHandlers.set(uid, {
locator,
handler,
times: options.times
});
} }
async _onLocatorHandlerTriggered(uid) { async _onLocatorHandlerTriggered(uid) {
let remove = false; let remove = false;
try { try {
const handler = this._locatorHandlers.get(uid); const handler = this._locatorHandlers.get(uid);
if (handler && handler.times !== 0) { if (handler && handler.times !== 0) {
if (handler.times !== undefined) handler.times--; if (handler.times !== void 0)
handler.times--;
await handler.handler(handler.locator); await handler.handler(handler.locator);
} }
remove = (handler === null || handler === void 0 ? void 0 : handler.times) === 0; remove = handler?.times === 0;
} finally { } finally {
if (remove) this._locatorHandlers.delete(uid); if (remove)
this._wrapApiCall(() => this._channel.resolveLocatorHandlerNoReply({ this._locatorHandlers.delete(uid);
uid, this._wrapApiCall(() => this._channel.resolveLocatorHandlerNoReply({ uid, remove }), true).catch(() => {
remove });
}), true).catch(() => {});
} }
} }
async removeLocatorHandler(locator) { async removeLocatorHandler(locator) {
for (const [uid, data] of this._locatorHandlers) { for (const [uid, data] of this._locatorHandlers) {
if (data.locator._equals(locator)) { if (data.locator._equals(locator)) {
this._locatorHandlers.delete(uid); this._locatorHandlers.delete(uid);
await this._channel.unregisterLocatorHandler({ await this._channel.unregisterLocatorHandler({ uid }).catch(() => {
uid });
}).catch(() => {});
} }
} }
} }
@@ -369,179 +331,165 @@ class Page extends _channelOwner.ChannelOwner {
return await this._mainFrame.waitForURL(url, options); return await this._mainFrame.waitForURL(url, options);
} }
async waitForRequest(urlOrPredicate, options = {}) { async waitForRequest(urlOrPredicate, options = {}) {
const predicate = async request => { const predicate = async (request) => {
if ((0, _rtti.isString)(urlOrPredicate) || (0, _rtti.isRegExp)(urlOrPredicate)) return (0, _urlMatch.urlMatches)(this._browserContext._options.baseURL, request.url(), urlOrPredicate); if ((0, import_rtti.isString)(urlOrPredicate) || (0, import_rtti.isRegExp)(urlOrPredicate))
return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, request.url(), urlOrPredicate);
return await urlOrPredicate(request); return await urlOrPredicate(request);
}; };
const trimmedUrl = trimUrl(urlOrPredicate); const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for request ${trimmedUrl}` : undefined; const logLine = trimmedUrl ? `waiting for request ${trimmedUrl}` : void 0;
return await this._waitForEvent(_events.Events.Page.Request, { return await this._waitForEvent(import_events.Events.Page.Request, { predicate, timeout: options.timeout }, logLine);
predicate,
timeout: options.timeout
}, logLine);
} }
async waitForResponse(urlOrPredicate, options = {}) { async waitForResponse(urlOrPredicate, options = {}) {
const predicate = async response => { const predicate = async (response) => {
if ((0, _rtti.isString)(urlOrPredicate) || (0, _rtti.isRegExp)(urlOrPredicate)) return (0, _urlMatch.urlMatches)(this._browserContext._options.baseURL, response.url(), urlOrPredicate); if ((0, import_rtti.isString)(urlOrPredicate) || (0, import_rtti.isRegExp)(urlOrPredicate))
return (0, import_urlMatch.urlMatches)(this._browserContext._options.baseURL, response.url(), urlOrPredicate);
return await urlOrPredicate(response); return await urlOrPredicate(response);
}; };
const trimmedUrl = trimUrl(urlOrPredicate); const trimmedUrl = trimUrl(urlOrPredicate);
const logLine = trimmedUrl ? `waiting for response ${trimmedUrl}` : undefined; const logLine = trimmedUrl ? `waiting for response ${trimmedUrl}` : void 0;
return await this._waitForEvent(_events.Events.Page.Response, { return await this._waitForEvent(import_events.Events.Page.Response, { predicate, timeout: options.timeout }, logLine);
predicate,
timeout: options.timeout
}, logLine);
} }
async waitForEvent(event, optionsOrPredicate = {}) { async waitForEvent(event, optionsOrPredicate = {}) {
return await this._waitForEvent(event, optionsOrPredicate, `waiting for event "${event}"`); return await this._waitForEvent(event, optionsOrPredicate, `waiting for event "${event}"`);
} }
_closeErrorWithReason() { _closeErrorWithReason() {
return new _errors.TargetClosedError(this._closeReason || this._browserContext._effectiveCloseReason()); return new import_errors.TargetClosedError(this._closeReason || this._browserContext._effectiveCloseReason());
} }
async _waitForEvent(event, optionsOrPredicate, logLine) { async _waitForEvent(event, optionsOrPredicate, logLine) {
return await this._wrapApiCall(async () => { return await this._wrapApiCall(async () => {
const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate); const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate; const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
const waiter = _waiter.Waiter.createForEvent(this, event); const waiter = import_waiter.Waiter.createForEvent(this, event);
if (logLine) waiter.log(logLine); if (logLine)
waiter.log(logLine);
waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`); waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
if (event !== _events.Events.Page.Crash) waiter.rejectOnEvent(this, _events.Events.Page.Crash, new Error('Page crashed')); if (event !== import_events.Events.Page.Crash)
if (event !== _events.Events.Page.Close) waiter.rejectOnEvent(this, _events.Events.Page.Close, () => this._closeErrorWithReason()); waiter.rejectOnEvent(this, import_events.Events.Page.Crash, new Error("Page crashed"));
if (event !== import_events.Events.Page.Close)
waiter.rejectOnEvent(this, import_events.Events.Page.Close, () => this._closeErrorWithReason());
const result = await waiter.waitForEvent(this, event, predicate); const result = await waiter.waitForEvent(this, event, predicate);
waiter.dispose(); waiter.dispose();
return result; return result;
}); });
} }
async goBack(options = {}) { async goBack(options = {}) {
const waitUntil = (0, _frame.verifyLoadState)('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return _network.Response.fromNullable((await this._channel.goBack({ return import_network.Response.fromNullable((await this._channel.goBack({ ...options, waitUntil })).response);
...options,
waitUntil
})).response);
} }
async goForward(options = {}) { async goForward(options = {}) {
const waitUntil = (0, _frame.verifyLoadState)('waitUntil', options.waitUntil === undefined ? 'load' : options.waitUntil); const waitUntil = (0, import_frame.verifyLoadState)("waitUntil", options.waitUntil === void 0 ? "load" : options.waitUntil);
return _network.Response.fromNullable((await this._channel.goForward({ return import_network.Response.fromNullable((await this._channel.goForward({ ...options, waitUntil })).response);
...options,
waitUntil
})).response);
} }
async requestGC() { async requestGC() {
await this._channel.requestGC(); await this._channel.requestGC();
} }
async emulateMedia(options = {}) { async emulateMedia(options = {}) {
await this._channel.emulateMedia({ await this._channel.emulateMedia({
media: options.media === null ? 'no-override' : options.media, media: options.media === null ? "no-override" : options.media,
colorScheme: options.colorScheme === null ? 'no-override' : options.colorScheme, colorScheme: options.colorScheme === null ? "no-override" : options.colorScheme,
reducedMotion: options.reducedMotion === null ? 'no-override' : options.reducedMotion, reducedMotion: options.reducedMotion === null ? "no-override" : options.reducedMotion,
forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors, forcedColors: options.forcedColors === null ? "no-override" : options.forcedColors,
contrast: options.contrast === null ? 'no-override' : options.contrast contrast: options.contrast === null ? "no-override" : options.contrast
}); });
} }
async setViewportSize(viewportSize) { async setViewportSize(viewportSize) {
this._viewportSize = viewportSize; this._viewportSize = viewportSize;
await this._channel.setViewportSize({ await this._channel.setViewportSize({ viewportSize });
viewportSize
});
} }
viewportSize() { viewportSize() {
return this._viewportSize; return this._viewportSize;
} }
async evaluate(pageFunction, arg) { async evaluate(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2); (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
return await this._mainFrame.evaluate(pageFunction, arg); return await this._mainFrame.evaluate(pageFunction, arg);
} }
async addInitScript(script, arg) { async addInitScript(script, arg) {
const source = await (0, _clientHelper.evaluationScript)(this._platform, script, arg); const source = await (0, import_clientHelper.evaluationScript)(this._platform, script, arg);
await this._channel.addInitScript({ await this._channel.addInitScript({ source });
source
});
} }
async route(url, handler, options = {}) { async route(url, handler, options = {}) {
this._routes.unshift(new _network.RouteHandler(this._platform, this._browserContext._options.baseURL, url, handler, options.times)); this._routes.unshift(new import_network.RouteHandler(this._platform, this._browserContext._options.baseURL, url, handler, options.times));
await this._updateInterceptionPatterns(); await this._updateInterceptionPatterns();
} }
async routeFromHAR(har, options = {}) { async routeFromHAR(har, options = {}) {
const localUtils = this._connection.localUtils(); const localUtils = this._connection.localUtils();
if (!localUtils) throw new Error('Route from har is not supported in thin clients'); if (!localUtils)
throw new Error("Route from har is not supported in thin clients");
if (options.update) { if (options.update) {
await this._browserContext._recordIntoHAR(har, this, options); await this._browserContext._recordIntoHAR(har, this, options);
return; return;
} }
const harRouter = await _harRouter.HarRouter.create(localUtils, har, options.notFound || 'abort', { const harRouter = await import_harRouter.HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url });
urlMatch: options.url
});
this._harRouters.push(harRouter); this._harRouters.push(harRouter);
await harRouter.addPageRoute(this); await harRouter.addPageRoute(this);
} }
async routeWebSocket(url, handler) { async routeWebSocket(url, handler) {
this._webSocketRoutes.unshift(new _network.WebSocketRouteHandler(this._browserContext._options.baseURL, url, handler)); this._webSocketRoutes.unshift(new import_network.WebSocketRouteHandler(this._browserContext._options.baseURL, url, handler));
await this._updateWebSocketInterceptionPatterns(); await this._updateWebSocketInterceptionPatterns();
} }
_disposeHarRouters() { _disposeHarRouters() {
this._harRouters.forEach(router => router.dispose()); this._harRouters.forEach((router) => router.dispose());
this._harRouters = []; this._harRouters = [];
} }
async unrouteAll(options) { async unrouteAll(options) {
await this._unrouteInternal(this._routes, [], options === null || options === void 0 ? void 0 : options.behavior); await this._unrouteInternal(this._routes, [], options?.behavior);
this._disposeHarRouters(); this._disposeHarRouters();
} }
async unroute(url, handler) { async unroute(url, handler) {
const removed = []; const removed = [];
const remaining = []; const remaining = [];
for (const route of this._routes) { for (const route of this._routes) {
if ((0, _urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler)) removed.push(route);else remaining.push(route); if ((0, import_urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler))
removed.push(route);
else
remaining.push(route);
} }
await this._unrouteInternal(removed, remaining, 'default'); await this._unrouteInternal(removed, remaining, "default");
} }
async _unrouteInternal(removed, remaining, behavior) { async _unrouteInternal(removed, remaining, behavior) {
this._routes = remaining; this._routes = remaining;
await this._updateInterceptionPatterns(); await this._updateInterceptionPatterns();
if (!behavior || behavior === 'default') return; if (!behavior || behavior === "default")
const promises = removed.map(routeHandler => routeHandler.stop(behavior)); return;
const promises = removed.map((routeHandler) => routeHandler.stop(behavior));
await Promise.all(promises); await Promise.all(promises);
} }
async _updateInterceptionPatterns() { async _updateInterceptionPatterns() {
const patterns = _network.RouteHandler.prepareInterceptionPatterns(this._routes); const patterns = import_network.RouteHandler.prepareInterceptionPatterns(this._routes);
await this._channel.setNetworkInterceptionPatterns({ await this._channel.setNetworkInterceptionPatterns({ patterns });
patterns
});
} }
async _updateWebSocketInterceptionPatterns() { async _updateWebSocketInterceptionPatterns() {
const patterns = _network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes); const patterns = import_network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
await this._channel.setWebSocketInterceptionPatterns({ await this._channel.setWebSocketInterceptionPatterns({ patterns });
patterns
});
} }
async screenshot(options = {}) { async screenshot(options = {}) {
const mask = options.mask; const mask = options.mask;
const copy = { const copy = { ...options, mask: void 0 };
...options, if (!copy.type)
mask: undefined copy.type = (0, import_elementHandle.determineScreenshotType)(options);
};
if (!copy.type) copy.type = (0, _elementHandle.determineScreenshotType)(options);
if (mask) { if (mask) {
copy.mask = mask.map(locator => ({ copy.mask = mask.map((locator) => ({
frame: locator._frame._channel, frame: locator._frame._channel,
selector: locator._selector selector: locator._selector
})); }));
} }
const result = await this._channel.screenshot(copy); const result = await this._channel.screenshot(copy);
if (options.path) { if (options.path) {
await (0, _fileUtils.mkdirIfNeeded)(this._platform, options.path); await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
await this._platform.fs().promises.writeFile(options.path, result.binary); await this._platform.fs().promises.writeFile(options.path, result.binary);
} }
return result.binary; return result.binary;
} }
async _expectScreenshot(options) { async _expectScreenshot(options) {
const mask = options !== null && options !== void 0 && options.mask ? options === null || options === void 0 ? void 0 : options.mask.map(locator => ({ const mask = options?.mask ? options?.mask.map((locator2) => ({
frame: locator._frame._channel, frame: locator2._frame._channel,
selector: locator._selector selector: locator2._selector
})) : undefined; })) : void 0;
const locator = options.locator ? { const locator = options.locator ? {
frame: options.locator._frame._channel, frame: options.locator._frame._channel,
selector: options.locator._selector selector: options.locator._selector
} : undefined; } : void 0;
return await this._channel.expectScreenshot({ return await this._channel.expectScreenshot({
...options, ...options,
isNot: !!options.isNot, isNot: !!options.isNot,
@@ -562,9 +510,13 @@ class Page extends _channelOwner.ChannelOwner {
this._closeReason = options.reason; this._closeReason = options.reason;
this._closeWasCalled = true; this._closeWasCalled = true;
try { try {
if (this._ownedContext) await this._ownedContext.close();else await this._channel.close(options); if (this._ownedContext)
await this._ownedContext.close();
else
await this._channel.close(options);
} catch (e) { } catch (e) {
if ((0, _errors.isTargetClosedError)(e) && !options.runBeforeUnload) return; if ((0, import_errors.isTargetClosedError)(e) && !options.runBeforeUnload)
return;
throw e; throw e;
} }
} }
@@ -683,45 +635,40 @@ class Page extends _channelOwner.ChannelOwner {
return [...this._workers]; return [...this._workers];
} }
async pause(_options) { async pause(_options) {
var _this$_instrumentatio; if (this._platform.isJSDebuggerAttached())
if (this._platform.isJSDebuggerAttached()) return; return;
const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout(); const defaultNavigationTimeout = this._browserContext._timeoutSettings.defaultNavigationTimeout();
const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout(); const defaultTimeout = this._browserContext._timeoutSettings.defaultTimeout();
this._browserContext.setDefaultNavigationTimeout(0); this._browserContext.setDefaultNavigationTimeout(0);
this._browserContext.setDefaultTimeout(0); this._browserContext.setDefaultTimeout(0);
(_this$_instrumentatio = this._instrumentation) === null || _this$_instrumentatio === void 0 || _this$_instrumentatio.onWillPause({ this._instrumentation?.onWillPause({ keepTestTimeout: !!_options?.__testHookKeepTestTimeout });
keepTestTimeout: !!(_options !== null && _options !== void 0 && _options.__testHookKeepTestTimeout)
});
await this._closedOrCrashedScope.safeRace(this.context()._channel.pause()); await this._closedOrCrashedScope.safeRace(this.context()._channel.pause());
this._browserContext.setDefaultNavigationTimeout(defaultNavigationTimeout); this._browserContext.setDefaultNavigationTimeout(defaultNavigationTimeout);
this._browserContext.setDefaultTimeout(defaultTimeout); this._browserContext.setDefaultTimeout(defaultTimeout);
} }
async pdf(options = {}) { async pdf(options = {}) {
const transportOptions = { const transportOptions = { ...options };
...options if (transportOptions.margin)
}; transportOptions.margin = { ...transportOptions.margin };
if (transportOptions.margin) transportOptions.margin = { if (typeof options.width === "number")
...transportOptions.margin transportOptions.width = options.width + "px";
}; if (typeof options.height === "number")
if (typeof options.width === 'number') transportOptions.width = options.width + 'px'; transportOptions.height = options.height + "px";
if (typeof options.height === 'number') transportOptions.height = options.height + 'px'; for (const margin of ["top", "right", "bottom", "left"]) {
for (const margin of ['top', 'right', 'bottom', 'left']) {
const index = margin; const index = margin;
if (options.margin && typeof options.margin[index] === 'number') transportOptions.margin[index] = transportOptions.margin[index] + 'px'; if (options.margin && typeof options.margin[index] === "number")
transportOptions.margin[index] = transportOptions.margin[index] + "px";
} }
const result = await this._channel.pdf(transportOptions); const result = await this._channel.pdf(transportOptions);
if (options.path) { if (options.path) {
const platform = this._platform; const platform = this._platform;
await platform.fs().promises.mkdir(platform.path().dirname(options.path), { await platform.fs().promises.mkdir(platform.path().dirname(options.path), { recursive: true });
recursive: true
});
await platform.fs().promises.writeFile(options.path, result.pdf); await platform.fs().promises.writeFile(options.path, result.pdf);
} }
return result.pdf; return result.pdf;
} }
} }
exports.Page = Page; class BindingCall extends import_channelOwner.ChannelOwner {
class BindingCall extends _channelOwner.ChannelOwner {
static from(channel) { static from(channel) {
return channel._object; return channel._object;
} }
@@ -730,26 +677,33 @@ class BindingCall extends _channelOwner.ChannelOwner {
} }
async call(func) { async call(func) {
try { try {
const frame = _frame.Frame.from(this._initializer.frame); const frame = import_frame.Frame.from(this._initializer.frame);
const source = { const source = {
context: frame._page.context(), context: frame._page.context(),
page: frame._page, page: frame._page,
frame frame
}; };
let result; let result;
if (this._initializer.handle) result = await func(source, _jsHandle.JSHandle.from(this._initializer.handle));else result = await func(source, ...this._initializer.args.map(_jsHandle.parseResult)); if (this._initializer.handle)
this._channel.resolve({ result = await func(source, import_jsHandle.JSHandle.from(this._initializer.handle));
result: (0, _jsHandle.serializeArgument)(result) else
}).catch(() => {}); result = await func(source, ...this._initializer.args.map(import_jsHandle.parseResult));
this._channel.resolve({ result: (0, import_jsHandle.serializeArgument)(result) }).catch(() => {
});
} catch (e) { } catch (e) {
this._channel.reject({ this._channel.reject({ error: (0, import_errors.serializeError)(e) }).catch(() => {
error: (0, _errors.serializeError)(e) });
}).catch(() => {});
} }
} }
} }
exports.BindingCall = BindingCall;
function trimUrl(param) { function trimUrl(param) {
if ((0, _rtti.isRegExp)(param)) return `/${(0, _stringUtils.trimStringWithEllipsis)(param.source, 50)}/${param.flags}`; if ((0, import_rtti.isRegExp)(param))
if ((0, _rtti.isString)(param)) return `"${(0, _stringUtils.trimStringWithEllipsis)(param, 50)}"`; return `/${(0, import_stringUtils.trimStringWithEllipsis)(param.source, 50)}/${param.flags}`;
if ((0, import_rtti.isString)(param))
return `"${(0, import_stringUtils.trimStringWithEllipsis)(param, 50)}"`;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
BindingCall,
Page
});

View File

@@ -1,80 +1,62 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
}); var __hasOwnProp = Object.prototype.hasOwnProperty;
exports.Playwright = void 0; var __export = (target, all) => {
var _android = require("./android"); for (var name in all)
var _browser = require("./browser"); __defProp(target, name, { get: all[name], enumerable: true });
var _browserType = require("./browserType");
var _channelOwner = require("./channelOwner");
var _electron = require("./electron");
var _errors = require("./errors");
var _fetch = require("./fetch");
var _selectors = require("./selectors");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Playwright extends _channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
var _this$_connection$loc, _this$_connection$loc2;
super(parent, type, guid, initializer);
this._android = void 0;
this._electron = void 0;
this._bidiChromium = void 0;
this._bidiFirefox = void 0;
this.chromium = void 0;
this.firefox = void 0;
this.webkit = void 0;
this.devices = void 0;
this.selectors = void 0;
this.request = void 0;
this.errors = void 0;
// Instrumentation.
this._defaultLaunchOptions = void 0;
this._defaultContextOptions = void 0;
this._defaultContextTimeout = void 0;
this._defaultContextNavigationTimeout = void 0;
this.request = new _fetch.APIRequest(this);
this.chromium = _browserType.BrowserType.from(initializer.chromium);
this.chromium._playwright = this;
this.firefox = _browserType.BrowserType.from(initializer.firefox);
this.firefox._playwright = this;
this.webkit = _browserType.BrowserType.from(initializer.webkit);
this.webkit._playwright = this;
this._android = _android.Android.from(initializer.android);
this._electron = _electron.Electron.from(initializer.electron);
this._bidiChromium = _browserType.BrowserType.from(initializer.bidiChromium);
this._bidiChromium._playwright = this;
this._bidiFirefox = _browserType.BrowserType.from(initializer.bidiFirefox);
this._bidiFirefox._playwright = this;
this.devices = (_this$_connection$loc = (_this$_connection$loc2 = this._connection.localUtils()) === null || _this$_connection$loc2 === void 0 ? void 0 : _this$_connection$loc2.devices) !== null && _this$_connection$loc !== void 0 ? _this$_connection$loc : {};
this.selectors = new _selectors.Selectors();
this.errors = {
TimeoutError: _errors.TimeoutError
}; };
const selectorsOwner = _selectors.SelectorsOwner.from(initializer.selectors); var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var playwright_exports = {};
__export(playwright_exports, {
Playwright: () => Playwright
});
module.exports = __toCommonJS(playwright_exports);
var import_android = require("./android");
var import_browser = require("./browser");
var import_browserType = require("./browserType");
var import_channelOwner = require("./channelOwner");
var import_electron = require("./electron");
var import_errors = require("./errors");
var import_fetch = require("./fetch");
var import_selectors = require("./selectors");
class Playwright extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer);
this.request = new import_fetch.APIRequest(this);
this.chromium = import_browserType.BrowserType.from(initializer.chromium);
this.chromium._playwright = this;
this.firefox = import_browserType.BrowserType.from(initializer.firefox);
this.firefox._playwright = this;
this.webkit = import_browserType.BrowserType.from(initializer.webkit);
this.webkit._playwright = this;
this._android = import_android.Android.from(initializer.android);
this._electron = import_electron.Electron.from(initializer.electron);
this._bidiChromium = import_browserType.BrowserType.from(initializer.bidiChromium);
this._bidiChromium._playwright = this;
this._bidiFirefox = import_browserType.BrowserType.from(initializer.bidiFirefox);
this._bidiFirefox._playwright = this;
this.devices = this._connection.localUtils()?.devices ?? {};
this.selectors = new import_selectors.Selectors();
this.errors = { TimeoutError: import_errors.TimeoutError };
const selectorsOwner = import_selectors.SelectorsOwner.from(initializer.selectors);
this.selectors._addChannel(selectorsOwner); this.selectors._addChannel(selectorsOwner);
this._connection.on('close', () => { this._connection.on("close", () => {
this.selectors._removeChannel(selectorsOwner); this.selectors._removeChannel(selectorsOwner);
}); });
global._playwrightInstance = this; global._playwrightInstance = this;
} }
_setSelectors(selectors) { _setSelectors(selectors) {
const selectorsOwner = _selectors.SelectorsOwner.from(this._initializer.selectors); const selectorsOwner = import_selectors.SelectorsOwner.from(this._initializer.selectors);
this.selectors._removeChannel(selectorsOwner); this.selectors._removeChannel(selectorsOwner);
this.selectors = selectors; this.selectors = selectors;
this.selectors._addChannel(selectorsOwner); this.selectors._addChannel(selectorsOwner);
@@ -86,15 +68,18 @@ class Playwright extends _channelOwner.ChannelOwner {
return [this.chromium, this.firefox, this.webkit, this._bidiChromium, this._bidiFirefox]; return [this.chromium, this.firefox, this.webkit, this._bidiChromium, this._bidiFirefox];
} }
_preLaunchedBrowser() { _preLaunchedBrowser() {
const browser = _browser.Browser.from(this._initializer.preLaunchedBrowser); const browser = import_browser.Browser.from(this._initializer.preLaunchedBrowser);
browser._browserType = this[browser._name]; browser._browserType = this[browser._name];
return browser; return browser;
} }
_allContexts() { _allContexts() {
return this._browserTypes().flatMap(type => [...type._contexts]); return this._browserTypes().flatMap((type) => [...type._contexts]);
} }
_allPages() { _allPages() {
return this._allContexts().flatMap(context => context.pages()); return this._allContexts().flatMap((context) => context.pages());
} }
} }
exports.Playwright = Playwright; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Playwright
});

View File

@@ -1,73 +1,75 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var selectors_exports = {};
__export(selectors_exports, {
Selectors: () => Selectors,
SelectorsOwner: () => SelectorsOwner,
setPlatformForSelectors: () => setPlatformForSelectors
}); });
exports.SelectorsOwner = exports.Selectors = void 0; module.exports = __toCommonJS(selectors_exports);
exports.setPlatformForSelectors = setPlatformForSelectors; var import_channelOwner = require("./channelOwner");
var _channelOwner = require("./channelOwner"); var import_clientHelper = require("./clientHelper");
var _clientHelper = require("./clientHelper"); var import_locator = require("./locator");
var _locator = require("./locator"); var import_platform = require("./platform");
var _platform = require("./platform"); let platform = import_platform.emptyPlatform;
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let platform = _platform.emptyPlatform;
function setPlatformForSelectors(p) { function setPlatformForSelectors(p) {
platform = p; platform = p;
} }
class Selectors { class Selectors {
constructor() { constructor() {
this._channels = new Set(); this._channels = /* @__PURE__ */ new Set();
this._registrations = []; this._registrations = [];
} }
async register(name, script, options = {}) { async register(name, script, options = {}) {
const source = await (0, _clientHelper.evaluationScript)(platform, script, undefined, false); const source = await (0, import_clientHelper.evaluationScript)(platform, script, void 0, false);
const params = { const params = { ...options, name, source };
...options, for (const channel of this._channels)
name, await channel._channel.register(params);
source
};
for (const channel of this._channels) await channel._channel.register(params);
this._registrations.push(params); this._registrations.push(params);
} }
setTestIdAttribute(attributeName) { setTestIdAttribute(attributeName) {
(0, _locator.setTestIdAttribute)(attributeName); (0, import_locator.setTestIdAttribute)(attributeName);
for (const channel of this._channels) channel._channel.setTestIdAttributeName({ for (const channel of this._channels)
testIdAttributeName: attributeName channel._channel.setTestIdAttributeName({ testIdAttributeName: attributeName }).catch(() => {
}).catch(() => {}); });
} }
_addChannel(channel) { _addChannel(channel) {
this._channels.add(channel); this._channels.add(channel);
for (const params of this._registrations) { for (const params of this._registrations) {
// This should not fail except for connection closure, but just in case we catch. channel._channel.register(params).catch(() => {
channel._channel.register(params).catch(() => {}); });
channel._channel.setTestIdAttributeName({ channel._channel.setTestIdAttributeName({ testIdAttributeName: (0, import_locator.testIdAttributeName)() }).catch(() => {
testIdAttributeName: (0, _locator.testIdAttributeName)() });
}).catch(() => {});
} }
} }
_removeChannel(channel) { _removeChannel(channel) {
this._channels.delete(channel); this._channels.delete(channel);
} }
} }
exports.Selectors = Selectors; class SelectorsOwner extends import_channelOwner.ChannelOwner {
class SelectorsOwner extends _channelOwner.ChannelOwner {
static from(browser) { static from(browser) {
return browser._object; return browser._object;
} }
} }
exports.SelectorsOwner = SelectorsOwner; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Selectors,
SelectorsOwner,
setPlatformForSelectors
});

View File

@@ -1,29 +1,30 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var stream_exports = {};
__export(stream_exports, {
Stream: () => Stream
}); });
exports.Stream = void 0; module.exports = __toCommonJS(stream_exports);
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
/** class Stream extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation. static from(Stream2) {
* return Stream2._object;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Stream extends _channelOwner.ChannelOwner {
static from(Stream) {
return Stream._object;
} }
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
@@ -32,4 +33,7 @@ class Stream extends _channelOwner.ChannelOwner {
return this._platform.streamReadable(this._channel); return this._platform.streamReadable(this._channel);
} }
} }
exports.Stream = Stream; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Stream
});

View File

@@ -1,39 +1,38 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
}); var __hasOwnProp = Object.prototype.hasOwnProperty;
exports.Tracing = void 0; var __export = (target, all) => {
var _artifact = require("./artifact"); for (var name in all)
var _channelOwner = require("./channelOwner"); __defProp(target, name, { get: all[name], enumerable: true });
/** };
* Copyright (c) Microsoft Corporation. var __copyProps = (to, from, except, desc) => {
* if (from && typeof from === "object" || typeof from === "function") {
* Licensed under the Apache License, Version 2.0 (the "License"); for (let key of __getOwnPropNames(from))
* you may not use this file except in compliance with the License. if (!__hasOwnProp.call(to, key) && key !== except)
* You may obtain a copy of the License at __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Tracing extends _channelOwner.ChannelOwner {
static from(channel) {
return channel._object;
} }
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var tracing_exports = {};
__export(tracing_exports, {
Tracing: () => Tracing
});
module.exports = __toCommonJS(tracing_exports);
var import_artifact = require("./artifact");
var import_channelOwner = require("./channelOwner");
class Tracing extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._includeSources = false; this._includeSources = false;
this._tracesDir = void 0;
this._stacksId = void 0;
this._isTracing = false; this._isTracing = false;
this.markAsInternalType(); this.markAsInternalType();
} }
static from(channel) {
return channel._object;
}
async start(options = {}) { async start(options = {}) {
this._includeSources = !!options.sources; this._includeSources = !!options.sources;
await this._channel.tracingStart({ await this._channel.tracingStart({
@@ -42,26 +41,16 @@ class Tracing extends _channelOwner.ChannelOwner {
screenshots: options.screenshots, screenshots: options.screenshots,
live: options._live live: options._live
}); });
const { const { traceName } = await this._channel.tracingStartChunk({ name: options.name, title: options.title });
traceName
} = await this._channel.tracingStartChunk({
name: options.name,
title: options.title
});
await this._startCollectingStacks(traceName); await this._startCollectingStacks(traceName);
} }
async startChunk(options = {}) { async startChunk(options = {}) {
const { const { traceName } = await this._channel.tracingStartChunk(options);
traceName
} = await this._channel.tracingStartChunk(options);
await this._startCollectingStacks(traceName); await this._startCollectingStacks(traceName);
} }
async group(name, options = {}) { async group(name, options = {}) {
await this._wrapApiCall(async () => { await this._wrapApiCall(async () => {
await this._channel.tracingGroup({ await this._channel.tracingGroup({ name, location: options.location });
name,
location: options.location
});
}, false); }, false);
} }
async groupEnd() { async groupEnd() {
@@ -70,16 +59,12 @@ class Tracing extends _channelOwner.ChannelOwner {
}, false); }, false);
} }
async _startCollectingStacks(traceName) { async _startCollectingStacks(traceName) {
var _this$_connection$loc;
if (!this._isTracing) { if (!this._isTracing) {
this._isTracing = true; this._isTracing = true;
this._connection.setIsTracing(true); this._connection.setIsTracing(true);
} }
const result = await ((_this$_connection$loc = this._connection.localUtils()) === null || _this$_connection$loc === void 0 ? void 0 : _this$_connection$loc.tracingStarted({ const result = await this._connection.localUtils()?.tracingStarted({ tracesDir: this._tracesDir, traceName });
tracesDir: this._tracesDir, this._stacksId = result?.stacksId;
traceName
}));
this._stacksId = result === null || result === void 0 ? void 0 : result.stacksId;
} }
async stopChunk(options = {}) { async stopChunk(options = {}) {
await this._doStopChunk(options.path); await this._doStopChunk(options.path);
@@ -91,54 +76,30 @@ class Tracing extends _channelOwner.ChannelOwner {
async _doStopChunk(filePath) { async _doStopChunk(filePath) {
this._resetStackCounter(); this._resetStackCounter();
if (!filePath) { if (!filePath) {
// Not interested in artifacts. await this._channel.tracingStopChunk({ mode: "discard" });
await this._channel.tracingStopChunk({ if (this._stacksId)
mode: 'discard' await this._connection.localUtils().traceDiscarded({ stacksId: this._stacksId });
});
if (this._stacksId) await this._connection.localUtils().traceDiscarded({
stacksId: this._stacksId
});
return; return;
} }
const localUtils = this._connection.localUtils(); const localUtils = this._connection.localUtils();
if (!localUtils) throw new Error('Cannot save trace in thin clients'); if (!localUtils)
throw new Error("Cannot save trace in thin clients");
const isLocal = !this._connection.isRemote(); const isLocal = !this._connection.isRemote();
if (isLocal) { if (isLocal) {
const result = await this._channel.tracingStopChunk({ const result2 = await this._channel.tracingStopChunk({ mode: "entries" });
mode: 'entries' await localUtils.zip({ zipFile: filePath, entries: result2.entries, mode: "write", stacksId: this._stacksId, includeSources: this._includeSources });
});
await localUtils.zip({
zipFile: filePath,
entries: result.entries,
mode: 'write',
stacksId: this._stacksId,
includeSources: this._includeSources
});
return; return;
} }
const result = await this._channel.tracingStopChunk({ const result = await this._channel.tracingStopChunk({ mode: "archive" });
mode: 'archive'
});
// The artifact may be missing if the browser closed while stopping tracing.
if (!result.artifact) { if (!result.artifact) {
if (this._stacksId) await localUtils.traceDiscarded({ if (this._stacksId)
stacksId: this._stacksId await localUtils.traceDiscarded({ stacksId: this._stacksId });
});
return; return;
} }
const artifact = import_artifact.Artifact.from(result.artifact);
// Save trace to the final local file.
const artifact = _artifact.Artifact.from(result.artifact);
await artifact.saveAs(filePath); await artifact.saveAs(filePath);
await artifact.delete(); await artifact.delete();
await localUtils.zip({ await localUtils.zip({ zipFile: filePath, entries: [], mode: "append", stacksId: this._stacksId, includeSources: this._includeSources });
zipFile: filePath,
entries: [],
mode: 'append',
stacksId: this._stacksId,
includeSources: this._includeSources
});
} }
_resetStackCounter() { _resetStackCounter() {
if (this._isTracing) { if (this._isTracing) {
@@ -147,4 +108,7 @@ class Tracing extends _channelOwner.ChannelOwner {
} }
} }
} }
exports.Tracing = Tracing; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Tracing
});

View File

@@ -1,24 +1,28 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var types_exports = {};
__export(types_exports, {
kLifecycleEvents: () => kLifecycleEvents
});
module.exports = __toCommonJS(types_exports);
const kLifecycleEvents = /* @__PURE__ */ new Set(["load", "domcontentloaded", "networkidle", "commit"]);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
kLifecycleEvents
}); });
exports.kLifecycleEvents = void 0;
/**
* Copyright 2018 Google Inc. All rights reserved.
* Modifications copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const kLifecycleEvents = exports.kLifecycleEvents = new Set(['load', 'domcontentloaded', 'networkidle', 'commit']);

View File

@@ -1,30 +1,31 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var video_exports = {};
__export(video_exports, {
Video: () => Video
}); });
exports.Video = void 0; module.exports = __toCommonJS(video_exports);
var _manualPromise = require("../utils/isomorphic/manualPromise"); var import_manualPromise = require("../utils/isomorphic/manualPromise");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Video { class Video {
constructor(page, connection) { constructor(page, connection) {
this._artifact = null; this._artifact = null;
this._artifactReadyPromise = new _manualPromise.ManualPromise(); this._artifactReadyPromise = new import_manualPromise.ManualPromise();
this._isRemote = false; this._isRemote = false;
this._isRemote = connection.isRemote(); this._isRemote = connection.isRemote();
this._artifact = page._closedOrCrashedScope.safeRace(this._artifactReadyPromise); this._artifact = page._closedOrCrashedScope.safeRace(this._artifactReadyPromise);
@@ -33,19 +34,26 @@ class Video {
this._artifactReadyPromise.resolve(artifact); this._artifactReadyPromise.resolve(artifact);
} }
async path() { async path() {
if (this._isRemote) throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`); if (this._isRemote)
throw new Error(`Path is not available when connecting remotely. Use saveAs() to save a local copy.`);
const artifact = await this._artifact; const artifact = await this._artifact;
if (!artifact) throw new Error('Page did not produce any video frames'); if (!artifact)
throw new Error("Page did not produce any video frames");
return artifact._initializer.absolutePath; return artifact._initializer.absolutePath;
} }
async saveAs(path) { async saveAs(path) {
const artifact = await this._artifact; const artifact = await this._artifact;
if (!artifact) throw new Error('Page did not produce any video frames'); if (!artifact)
throw new Error("Page did not produce any video frames");
return await artifact.saveAs(path); return await artifact.saveAs(path);
} }
async delete() { async delete() {
const artifact = await this._artifact; const artifact = await this._artifact;
if (artifact) await artifact.delete(); if (artifact)
await artifact.delete();
} }
} }
exports.Video = Video; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Video
});

View File

@@ -1,131 +1,110 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var waiter_exports = {};
__export(waiter_exports, {
Waiter: () => Waiter
}); });
exports.Waiter = void 0; module.exports = __toCommonJS(waiter_exports);
var _errors = require("./errors"); var import_errors = require("./errors");
var _stackTrace = require("../utils/isomorphic/stackTrace"); var import_stackTrace = require("../utils/isomorphic/stackTrace");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Waiter { class Waiter {
constructor(channelOwner, event) { constructor(channelOwner, event) {
this._dispose = void 0;
this._failures = []; this._failures = [];
this._immediateError = void 0;
this._logs = []; this._logs = [];
this._channelOwner = void 0;
this._waitId = void 0;
this._error = void 0;
this._savedZone = void 0;
this._waitId = channelOwner._platform.createGuid(); this._waitId = channelOwner._platform.createGuid();
this._channelOwner = channelOwner; this._channelOwner = channelOwner;
this._savedZone = channelOwner._platform.zones.current().pop(); this._savedZone = channelOwner._platform.zones.current().pop();
this._channelOwner._channel.waitForEventInfo({ this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "before", event } }).catch(() => {
info: {
waitId: this._waitId,
phase: 'before',
event
}
}).catch(() => {});
this._dispose = [() => this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({
info: {
waitId: this._waitId,
phase: 'after',
error: this._error
}
}); });
}, true).catch(() => {})]; this._dispose = [
() => this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "after", error: this._error } });
}, true).catch(() => {
})
];
} }
static createForEvent(channelOwner, event) { static createForEvent(channelOwner, event) {
return new Waiter(channelOwner, event); return new Waiter(channelOwner, event);
} }
async waitForEvent(emitter, event, predicate) { async waitForEvent(emitter, event, predicate) {
const { const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate);
promise,
dispose
} = waitForEvent(emitter, event, this._savedZone, predicate);
return await this.waitForPromise(promise, dispose); return await this.waitForPromise(promise, dispose);
} }
rejectOnEvent(emitter, event, error, predicate) { rejectOnEvent(emitter, event, error, predicate) {
const { const { promise, dispose } = waitForEvent(emitter, event, this._savedZone, predicate);
promise,
dispose
} = waitForEvent(emitter, event, this._savedZone, predicate);
this._rejectOn(promise.then(() => { this._rejectOn(promise.then(() => {
throw typeof error === 'function' ? error() : error; throw typeof error === "function" ? error() : error;
}), dispose); }), dispose);
} }
rejectOnTimeout(timeout, message) { rejectOnTimeout(timeout, message) {
if (!timeout) return; if (!timeout)
const { return;
promise, const { promise, dispose } = waitForTimeout(timeout);
dispose
} = waitForTimeout(timeout);
this._rejectOn(promise.then(() => { this._rejectOn(promise.then(() => {
throw new _errors.TimeoutError(message); throw new import_errors.TimeoutError(message);
}), dispose); }), dispose);
} }
rejectImmediately(error) { rejectImmediately(error) {
this._immediateError = error; this._immediateError = error;
} }
dispose() { dispose() {
for (const dispose of this._dispose) dispose(); for (const dispose of this._dispose)
dispose();
} }
async waitForPromise(promise, dispose) { async waitForPromise(promise, dispose) {
try { try {
if (this._immediateError) throw this._immediateError; if (this._immediateError)
throw this._immediateError;
const result = await Promise.race([promise, ...this._failures]); const result = await Promise.race([promise, ...this._failures]);
if (dispose) dispose(); if (dispose)
dispose();
return result; return result;
} catch (e) { } catch (e) {
if (dispose) dispose(); if (dispose)
dispose();
this._error = e.message; this._error = e.message;
this.dispose(); this.dispose();
(0, _stackTrace.rewriteErrorMessage)(e, e.message + formatLogRecording(this._logs)); (0, import_stackTrace.rewriteErrorMessage)(e, e.message + formatLogRecording(this._logs));
throw e; throw e;
} }
} }
log(s) { log(s) {
this._logs.push(s); this._logs.push(s);
this._channelOwner._wrapApiCall(async () => { this._channelOwner._wrapApiCall(async () => {
await this._channelOwner._channel.waitForEventInfo({ await this._channelOwner._channel.waitForEventInfo({ info: { waitId: this._waitId, phase: "log", message: s } });
info: { }, true).catch(() => {
waitId: this._waitId,
phase: 'log',
message: s
}
}); });
}, true).catch(() => {});
} }
_rejectOn(promise, dispose) { _rejectOn(promise, dispose) {
this._failures.push(promise); this._failures.push(promise);
if (dispose) this._dispose.push(dispose); if (dispose)
this._dispose.push(dispose);
} }
} }
exports.Waiter = Waiter;
function waitForEvent(emitter, event, savedZone, predicate) { function waitForEvent(emitter, event, savedZone, predicate) {
let listener; let listener;
const promise = new Promise((resolve, reject) => { const promise = new Promise((resolve, reject) => {
listener = async eventArg => { listener = async (eventArg) => {
await savedZone.run(async () => { await savedZone.run(async () => {
try { try {
if (predicate && !(await predicate(eventArg))) return; if (predicate && !await predicate(eventArg))
return;
emitter.removeListener(event, listener); emitter.removeListener(event, listener);
resolve(eventArg); resolve(eventArg);
} catch (e) { } catch (e) {
@@ -137,25 +116,27 @@ function waitForEvent(emitter, event, savedZone, predicate) {
emitter.addListener(event, listener); emitter.addListener(event, listener);
}); });
const dispose = () => emitter.removeListener(event, listener); const dispose = () => emitter.removeListener(event, listener);
return { return { promise, dispose };
promise,
dispose
};
} }
function waitForTimeout(timeout) { function waitForTimeout(timeout) {
let timeoutId; let timeoutId;
const promise = new Promise(resolve => timeoutId = setTimeout(resolve, timeout)); const promise = new Promise((resolve) => timeoutId = setTimeout(resolve, timeout));
const dispose = () => clearTimeout(timeoutId); const dispose = () => clearTimeout(timeoutId);
return { return { promise, dispose };
promise,
dispose
};
} }
function formatLogRecording(log) { function formatLogRecording(log) {
if (!log.length) return ''; if (!log.length)
return "";
const header = ` logs `; const header = ` logs `;
const headerLength = 60; const headerLength = 60;
const leftLength = (headerLength - header.length) / 2; const leftLength = (headerLength - header.length) / 2;
const rightLength = headerLength - header.length - leftLength; const rightLength = headerLength - header.length - leftLength;
return `\n${'='.repeat(leftLength)}${header}${'='.repeat(rightLength)}\n${log.join('\n')}\n${'='.repeat(headerLength)}`; return `
${"=".repeat(leftLength)}${header}${"=".repeat(rightLength)}
${log.join("\n")}
${"=".repeat(headerLength)}`;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Waiter
});

View File

@@ -1,29 +1,28 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var webError_exports = {};
__export(webError_exports, {
WebError: () => WebError
}); });
exports.WebError = void 0; module.exports = __toCommonJS(webError_exports);
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class WebError { class WebError {
constructor(page, error) { constructor(page, error) {
this._page = void 0;
this._error = void 0;
this._page = page; this._page = page;
this._error = error; this._error = error;
} }
@@ -34,4 +33,7 @@ class WebError {
return this._error; return this._error;
} }
} }
exports.WebError = WebError; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
WebError
});

View File

@@ -1,71 +1,63 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
}); var __hasOwnProp = Object.prototype.hasOwnProperty;
exports.Worker = void 0; var __export = (target, all) => {
var _channelOwner = require("./channelOwner"); for (var name in all)
var _errors = require("./errors"); __defProp(target, name, { get: all[name], enumerable: true });
var _events = require("./events"); };
var _jsHandle = require("./jsHandle"); var __copyProps = (to, from, except, desc) => {
var _manualPromise = require("../utils/isomorphic/manualPromise"); if (from && typeof from === "object" || typeof from === "function") {
/** for (let key of __getOwnPropNames(from))
* Copyright (c) Microsoft Corporation. if (!__hasOwnProp.call(to, key) && key !== except)
* __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class Worker extends _channelOwner.ChannelOwner {
static from(worker) {
return worker._object;
} }
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var worker_exports = {};
__export(worker_exports, {
Worker: () => Worker
});
module.exports = __toCommonJS(worker_exports);
var import_channelOwner = require("./channelOwner");
var import_errors = require("./errors");
var import_events = require("./events");
var import_jsHandle = require("./jsHandle");
var import_manualPromise = require("../utils/isomorphic/manualPromise");
class Worker extends import_channelOwner.ChannelOwner {
constructor(parent, type, guid, initializer) { constructor(parent, type, guid, initializer) {
super(parent, type, guid, initializer); super(parent, type, guid, initializer);
this._page = void 0;
// Set for web workers.
this._context = void 0;
// Set for service workers. // Set for service workers.
this._closedScope = new _manualPromise.LongStandingScope(); this._closedScope = new import_manualPromise.LongStandingScope();
this._channel.on('close', () => { this._channel.on("close", () => {
if (this._page) this._page._workers.delete(this); if (this._page)
if (this._context) this._context._serviceWorkers.delete(this); this._page._workers.delete(this);
this.emit(_events.Events.Worker.Close, this); if (this._context)
}); this._context._serviceWorkers.delete(this);
this.once(_events.Events.Worker.Close, () => { this.emit(import_events.Events.Worker.Close, this);
var _this$_page;
return this._closedScope.close(((_this$_page = this._page) === null || _this$_page === void 0 ? void 0 : _this$_page._closeErrorWithReason()) || new _errors.TargetClosedError());
}); });
this.once(import_events.Events.Worker.Close, () => this._closedScope.close(this._page?._closeErrorWithReason() || new import_errors.TargetClosedError()));
}
static from(worker) {
return worker._object;
} }
url() { url() {
return this._initializer.url; return this._initializer.url;
} }
async evaluate(pageFunction, arg) { async evaluate(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2); (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpression({ const result = await this._channel.evaluateExpression({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
expression: String(pageFunction), return (0, import_jsHandle.parseResult)(result.value);
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
return (0, _jsHandle.parseResult)(result.value);
} }
async evaluateHandle(pageFunction, arg) { async evaluateHandle(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2); (0, import_jsHandle.assertMaxArguments)(arguments.length, 2);
const result = await this._channel.evaluateExpressionHandle({ const result = await this._channel.evaluateExpressionHandle({ expression: String(pageFunction), isFunction: typeof pageFunction === "function", arg: (0, import_jsHandle.serializeArgument)(arg) });
expression: String(pageFunction), return import_jsHandle.JSHandle.from(result.handle);
isFunction: typeof pageFunction === 'function', }
arg: (0, _jsHandle.serializeArgument)(arg) }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Worker
}); });
return _jsHandle.JSHandle.from(result.handle);
}
}
exports.Worker = Worker;

View File

@@ -1,27 +1,28 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var writableStream_exports = {};
__export(writableStream_exports, {
WritableStream: () => WritableStream
}); });
exports.WritableStream = void 0; module.exports = __toCommonJS(writableStream_exports);
var _channelOwner = require("./channelOwner"); var import_channelOwner = require("./channelOwner");
/** class WritableStream extends import_channelOwner.ChannelOwner {
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class WritableStream extends _channelOwner.ChannelOwner {
static from(Stream) { static from(Stream) {
return Stream._object; return Stream._object;
} }
@@ -32,4 +33,7 @@ class WritableStream extends _channelOwner.ChannelOwner {
return this._platform.streamWritable(this._channel); return this._platform.streamWritable(this._channel);
} }
} }
exports.WritableStream = WritableStream; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
WritableStream
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,59 +1,59 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var inProcessFactory_exports = {};
__export(inProcessFactory_exports, {
createInProcessPlaywright: () => createInProcessPlaywright
}); });
exports.createInProcessPlaywright = createInProcessPlaywright; module.exports = __toCommonJS(inProcessFactory_exports);
var _androidServerImpl = require("./androidServerImpl"); var import_androidServerImpl = require("./androidServerImpl");
var _browserServerImpl = require("./browserServerImpl"); var import_browserServerImpl = require("./browserServerImpl");
var _server = require("./server"); var import_server = require("./server");
var _nodePlatform = require("./server/utils/nodePlatform"); var import_nodePlatform = require("./server/utils/nodePlatform");
var _connection = require("./client/connection"); var import_connection = require("./client/connection");
var _selectors = require("./client/selectors"); var import_selectors = require("./client/selectors");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function createInProcessPlaywright() { function createInProcessPlaywright() {
const playwright = (0, _server.createPlaywright)({ const playwright = (0, import_server.createPlaywright)({ sdkLanguage: process.env.PW_LANG_NAME || "javascript" });
sdkLanguage: process.env.PW_LANG_NAME || 'javascript' (0, import_selectors.setPlatformForSelectors)(import_nodePlatform.nodePlatform);
}); const clientConnection = new import_connection.Connection(import_nodePlatform.nodePlatform);
(0, _selectors.setPlatformForSelectors)(_nodePlatform.nodePlatform);
const clientConnection = new _connection.Connection(_nodePlatform.nodePlatform);
clientConnection.useRawBuffers(); clientConnection.useRawBuffers();
const dispatcherConnection = new _server.DispatcherConnection(true /* local */); const dispatcherConnection = new import_server.DispatcherConnection(
true
// Dispatch synchronously at first. /* local */
dispatcherConnection.onmessage = message => clientConnection.dispatch(message); );
clientConnection.onmessage = message => dispatcherConnection.dispatch(message); dispatcherConnection.onmessage = (message) => clientConnection.dispatch(message);
const rootScope = new _server.RootDispatcher(dispatcherConnection); clientConnection.onmessage = (message) => dispatcherConnection.dispatch(message);
const rootScope = new import_server.RootDispatcher(dispatcherConnection);
// Initialize Playwright channel. new import_server.PlaywrightDispatcher(rootScope, playwright);
new _server.PlaywrightDispatcher(rootScope, playwright); const playwrightAPI = clientConnection.getObjectWithKnownName("Playwright");
const playwrightAPI = clientConnection.getObjectWithKnownName('Playwright'); playwrightAPI.chromium._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("chromium");
playwrightAPI.chromium._serverLauncher = new _browserServerImpl.BrowserServerLauncherImpl('chromium'); playwrightAPI.firefox._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("firefox");
playwrightAPI.firefox._serverLauncher = new _browserServerImpl.BrowserServerLauncherImpl('firefox'); playwrightAPI.webkit._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("webkit");
playwrightAPI.webkit._serverLauncher = new _browserServerImpl.BrowserServerLauncherImpl('webkit'); playwrightAPI._android._serverLauncher = new import_androidServerImpl.AndroidServerLauncherImpl();
playwrightAPI._android._serverLauncher = new _androidServerImpl.AndroidServerLauncherImpl(); playwrightAPI._bidiChromium._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("bidiChromium");
playwrightAPI._bidiChromium._serverLauncher = new _browserServerImpl.BrowserServerLauncherImpl('bidiChromium'); playwrightAPI._bidiFirefox._serverLauncher = new import_browserServerImpl.BrowserServerLauncherImpl("bidiFirefox");
playwrightAPI._bidiFirefox._serverLauncher = new _browserServerImpl.BrowserServerLauncherImpl('bidiFirefox'); dispatcherConnection.onmessage = (message) => setImmediate(() => clientConnection.dispatch(message));
clientConnection.onmessage = (message) => setImmediate(() => dispatcherConnection.dispatch(message));
// Switch to async dispatch after we got Playwright object. clientConnection.toImpl = (x) => x ? dispatcherConnection._dispatchers.get(x._guid)._object : dispatcherConnection._dispatchers.get("");
dispatcherConnection.onmessage = message => setImmediate(() => clientConnection.dispatch(message));
clientConnection.onmessage = message => setImmediate(() => dispatcherConnection.dispatch(message));
clientConnection.toImpl = x => x ? dispatcherConnection._dispatchers.get(x._guid)._object : dispatcherConnection._dispatchers.get('');
playwrightAPI._toImpl = clientConnection.toImpl; playwrightAPI._toImpl = clientConnection.toImpl;
return playwrightAPI; return playwrightAPI;
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createInProcessPlaywright
});

View File

@@ -1,20 +1,3 @@
"use strict"; "use strict";
var import_inProcessFactory = require("./inProcessFactory");
var _inProcessFactory = require("./inProcessFactory"); module.exports = (0, import_inProcessFactory.createInProcessPlaywright)();
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the 'License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = (0, _inProcessFactory.createInProcessPlaywright)();

View File

@@ -1,52 +1,55 @@
"use strict"; "use strict";
var __create = Object.create;
Object.defineProperty(exports, "__esModule", { var __defProp = Object.defineProperty;
value: true var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var outofprocess_exports = {};
__export(outofprocess_exports, {
start: () => start
}); });
exports.start = start; module.exports = __toCommonJS(outofprocess_exports);
var childProcess = _interopRequireWildcard(require("child_process")); var childProcess = __toESM(require("child_process"));
var _path = _interopRequireDefault(require("path")); var import_path = __toESM(require("path"));
var _connection = require("./client/connection"); var import_connection = require("./client/connection");
var _pipeTransport = require("./server/utils/pipeTransport"); var import_pipeTransport = require("./server/utils/pipeTransport");
var _manualPromise = require("./utils/isomorphic/manualPromise"); var import_manualPromise = require("./utils/isomorphic/manualPromise");
var _nodePlatform = require("./server/utils/nodePlatform"); var import_nodePlatform = require("./server/utils/nodePlatform");
var _selectors = require("./client/selectors"); var import_selectors = require("./client/selectors");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
async function start(env = {}) { async function start(env = {}) {
(0, _selectors.setPlatformForSelectors)(_nodePlatform.nodePlatform); (0, import_selectors.setPlatformForSelectors)(import_nodePlatform.nodePlatform);
const client = new PlaywrightClient(env); const client = new PlaywrightClient(env);
const playwright = await client._playwright; const playwright = await client._playwright;
playwright.driverProcess = client._driverProcess; playwright.driverProcess = client._driverProcess;
return { return { playwright, stop: () => client.stop() };
playwright,
stop: () => client.stop()
};
} }
class PlaywrightClient { class PlaywrightClient {
constructor(env) { constructor(env) {
this._playwright = void 0; this._closePromise = new import_manualPromise.ManualPromise();
this._driverProcess = void 0; this._driverProcess = childProcess.fork(import_path.default.join(__dirname, "..", "cli.js"), ["run-driver"], {
this._closePromise = new _manualPromise.ManualPromise(); stdio: "pipe",
this._driverProcess = childProcess.fork(_path.default.join(__dirname, '..', 'cli.js'), ['run-driver'], {
stdio: 'pipe',
detached: true, detached: true,
env: { env: {
...process.env, ...process.env,
@@ -54,11 +57,11 @@ class PlaywrightClient {
} }
}); });
this._driverProcess.unref(); this._driverProcess.unref();
this._driverProcess.stderr.on('data', data => process.stderr.write(data)); this._driverProcess.stderr.on("data", (data) => process.stderr.write(data));
const connection = new _connection.Connection(_nodePlatform.nodePlatform); const connection = new import_connection.Connection(import_nodePlatform.nodePlatform);
const transport = new _pipeTransport.PipeTransport(this._driverProcess.stdin, this._driverProcess.stdout); const transport = new import_pipeTransport.PipeTransport(this._driverProcess.stdin, this._driverProcess.stdout);
connection.onmessage = message => transport.send(JSON.stringify(message)); connection.onmessage = (message) => transport.send(JSON.stringify(message));
transport.onmessage = message => connection.dispatch(JSON.parse(message)); transport.onmessage = (message) => connection.dispatch(JSON.parse(message));
transport.onclose = () => this._closePromise.resolve(); transport.onclose = () => this._closePromise.resolve();
this._playwright = connection.initializePlaywright(); this._playwright = connection.initializePlaywright();
} }
@@ -69,3 +72,7 @@ class PlaywrightClient {
await this._closePromise; await this._closePromise;
} }
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
start
});

View File

@@ -1,27 +1,211 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var debug_exports = {};
__export(debug_exports, {
commandsWithTracingSnapshots: () => commandsWithTracingSnapshots,
pausesBeforeInputActions: () => pausesBeforeInputActions,
slowMoActions: () => slowMoActions
});
module.exports = __toCommonJS(debug_exports);
const slowMoActions = /* @__PURE__ */ new Set([
"Page.goBack",
"Page.goForward",
"Page.reload",
"Page.keyboardDown",
"Page.keyboardUp",
"Page.keyboardInsertText",
"Page.keyboardType",
"Page.keyboardPress",
"Page.mouseMove",
"Page.mouseDown",
"Page.mouseUp",
"Page.mouseClick",
"Page.mouseWheel",
"Page.touchscreenTap",
"Frame.blur",
"Frame.check",
"Frame.click",
"Frame.dragAndDrop",
"Frame.dblclick",
"Frame.dispatchEvent",
"Frame.fill",
"Frame.focus",
"Frame.goto",
"Frame.hover",
"Frame.press",
"Frame.selectOption",
"Frame.setInputFiles",
"Frame.tap",
"Frame.type",
"Frame.uncheck",
"ElementHandle.check",
"ElementHandle.click",
"ElementHandle.dblclick",
"ElementHandle.dispatchEvent",
"ElementHandle.fill",
"ElementHandle.focus",
"ElementHandle.hover",
"ElementHandle.press",
"ElementHandle.scrollIntoViewIfNeeded",
"ElementHandle.selectOption",
"ElementHandle.selectText",
"ElementHandle.setInputFiles",
"ElementHandle.tap",
"ElementHandle.type",
"ElementHandle.uncheck"
]);
const commandsWithTracingSnapshots = /* @__PURE__ */ new Set([
"EventTarget.waitForEventInfo",
"BrowserContext.waitForEventInfo",
"Page.waitForEventInfo",
"WebSocket.waitForEventInfo",
"ElectronApplication.waitForEventInfo",
"AndroidDevice.waitForEventInfo",
"Page.emulateMedia",
"Page.goBack",
"Page.goForward",
"Page.reload",
"Page.expectScreenshot",
"Page.screenshot",
"Page.setViewportSize",
"Page.keyboardDown",
"Page.keyboardUp",
"Page.keyboardInsertText",
"Page.keyboardType",
"Page.keyboardPress",
"Page.mouseMove",
"Page.mouseDown",
"Page.mouseUp",
"Page.mouseClick",
"Page.mouseWheel",
"Page.touchscreenTap",
"Page.accessibilitySnapshot",
"Frame.evalOnSelector",
"Frame.evalOnSelectorAll",
"Frame.addScriptTag",
"Frame.addStyleTag",
"Frame.ariaSnapshot",
"Frame.blur",
"Frame.check",
"Frame.click",
"Frame.content",
"Frame.dragAndDrop",
"Frame.dblclick",
"Frame.dispatchEvent",
"Frame.evaluateExpression",
"Frame.evaluateExpressionHandle",
"Frame.fill",
"Frame.focus",
"Frame.getAttribute",
"Frame.goto",
"Frame.hover",
"Frame.innerHTML",
"Frame.innerText",
"Frame.inputValue",
"Frame.isChecked",
"Frame.isDisabled",
"Frame.isEnabled",
"Frame.isHidden",
"Frame.isVisible",
"Frame.isEditable",
"Frame.press",
"Frame.querySelector",
"Frame.querySelectorAll",
"Frame.queryCount",
"Frame.selectOption",
"Frame.setContent",
"Frame.setInputFiles",
"Frame.tap",
"Frame.textContent",
"Frame.type",
"Frame.uncheck",
"Frame.waitForTimeout",
"Frame.waitForFunction",
"Frame.waitForSelector",
"Frame.expect",
"JSHandle.evaluateExpression",
"ElementHandle.evaluateExpression",
"JSHandle.evaluateExpressionHandle",
"ElementHandle.evaluateExpressionHandle",
"ElementHandle.evalOnSelector",
"ElementHandle.evalOnSelectorAll",
"ElementHandle.boundingBox",
"ElementHandle.check",
"ElementHandle.click",
"ElementHandle.contentFrame",
"ElementHandle.dblclick",
"ElementHandle.dispatchEvent",
"ElementHandle.fill",
"ElementHandle.focus",
"ElementHandle.hover",
"ElementHandle.innerHTML",
"ElementHandle.innerText",
"ElementHandle.inputValue",
"ElementHandle.isChecked",
"ElementHandle.isDisabled",
"ElementHandle.isEditable",
"ElementHandle.isEnabled",
"ElementHandle.isHidden",
"ElementHandle.isVisible",
"ElementHandle.press",
"ElementHandle.querySelector",
"ElementHandle.querySelectorAll",
"ElementHandle.screenshot",
"ElementHandle.scrollIntoViewIfNeeded",
"ElementHandle.selectOption",
"ElementHandle.selectText",
"ElementHandle.setInputFiles",
"ElementHandle.tap",
"ElementHandle.textContent",
"ElementHandle.type",
"ElementHandle.uncheck",
"ElementHandle.waitForElementState",
"ElementHandle.waitForSelector"
]);
const pausesBeforeInputActions = /* @__PURE__ */ new Set([
"Frame.check",
"Frame.click",
"Frame.dragAndDrop",
"Frame.dblclick",
"Frame.fill",
"Frame.hover",
"Frame.press",
"Frame.selectOption",
"Frame.setInputFiles",
"Frame.tap",
"Frame.type",
"Frame.uncheck",
"ElementHandle.check",
"ElementHandle.click",
"ElementHandle.dblclick",
"ElementHandle.fill",
"ElementHandle.hover",
"ElementHandle.press",
"ElementHandle.selectOption",
"ElementHandle.setInputFiles",
"ElementHandle.tap",
"ElementHandle.type",
"ElementHandle.uncheck"
]);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
commandsWithTracingSnapshots,
pausesBeforeInputActions,
slowMoActions
}); });
exports.slowMoActions = exports.pausesBeforeInputActions = exports.commandsWithTracingSnapshots = void 0;
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// This file is generated by generate_channels.js, do not edit manually.
const slowMoActions = exports.slowMoActions = new Set(['Page.goBack', 'Page.goForward', 'Page.reload', 'Page.keyboardDown', 'Page.keyboardUp', 'Page.keyboardInsertText', 'Page.keyboardType', 'Page.keyboardPress', 'Page.mouseMove', 'Page.mouseDown', 'Page.mouseUp', 'Page.mouseClick', 'Page.mouseWheel', 'Page.touchscreenTap', 'Frame.blur', 'Frame.check', 'Frame.click', 'Frame.dragAndDrop', 'Frame.dblclick', 'Frame.dispatchEvent', 'Frame.fill', 'Frame.focus', 'Frame.goto', 'Frame.hover', 'Frame.press', 'Frame.selectOption', 'Frame.setInputFiles', 'Frame.tap', 'Frame.type', 'Frame.uncheck', 'ElementHandle.check', 'ElementHandle.click', 'ElementHandle.dblclick', 'ElementHandle.dispatchEvent', 'ElementHandle.fill', 'ElementHandle.focus', 'ElementHandle.hover', 'ElementHandle.press', 'ElementHandle.scrollIntoViewIfNeeded', 'ElementHandle.selectOption', 'ElementHandle.selectText', 'ElementHandle.setInputFiles', 'ElementHandle.tap', 'ElementHandle.type', 'ElementHandle.uncheck']);
const commandsWithTracingSnapshots = exports.commandsWithTracingSnapshots = new Set(['EventTarget.waitForEventInfo', 'BrowserContext.waitForEventInfo', 'Page.waitForEventInfo', 'WebSocket.waitForEventInfo', 'ElectronApplication.waitForEventInfo', 'AndroidDevice.waitForEventInfo', 'Page.emulateMedia', 'Page.goBack', 'Page.goForward', 'Page.reload', 'Page.expectScreenshot', 'Page.screenshot', 'Page.setViewportSize', 'Page.keyboardDown', 'Page.keyboardUp', 'Page.keyboardInsertText', 'Page.keyboardType', 'Page.keyboardPress', 'Page.mouseMove', 'Page.mouseDown', 'Page.mouseUp', 'Page.mouseClick', 'Page.mouseWheel', 'Page.touchscreenTap', 'Page.accessibilitySnapshot', 'Frame.evalOnSelector', 'Frame.evalOnSelectorAll', 'Frame.addScriptTag', 'Frame.addStyleTag', 'Frame.ariaSnapshot', 'Frame.blur', 'Frame.check', 'Frame.click', 'Frame.content', 'Frame.dragAndDrop', 'Frame.dblclick', 'Frame.dispatchEvent', 'Frame.evaluateExpression', 'Frame.evaluateExpressionHandle', 'Frame.fill', 'Frame.focus', 'Frame.getAttribute', 'Frame.goto', 'Frame.hover', 'Frame.innerHTML', 'Frame.innerText', 'Frame.inputValue', 'Frame.isChecked', 'Frame.isDisabled', 'Frame.isEnabled', 'Frame.isHidden', 'Frame.isVisible', 'Frame.isEditable', 'Frame.press', 'Frame.querySelector', 'Frame.querySelectorAll', 'Frame.queryCount', 'Frame.selectOption', 'Frame.setContent', 'Frame.setInputFiles', 'Frame.tap', 'Frame.textContent', 'Frame.type', 'Frame.uncheck', 'Frame.waitForTimeout', 'Frame.waitForFunction', 'Frame.waitForSelector', 'Frame.expect', 'JSHandle.evaluateExpression', 'ElementHandle.evaluateExpression', 'JSHandle.evaluateExpressionHandle', 'ElementHandle.evaluateExpressionHandle', 'ElementHandle.evalOnSelector', 'ElementHandle.evalOnSelectorAll', 'ElementHandle.boundingBox', 'ElementHandle.check', 'ElementHandle.click', 'ElementHandle.contentFrame', 'ElementHandle.dblclick', 'ElementHandle.dispatchEvent', 'ElementHandle.fill', 'ElementHandle.focus', 'ElementHandle.hover', 'ElementHandle.innerHTML', 'ElementHandle.innerText', 'ElementHandle.inputValue', 'ElementHandle.isChecked', 'ElementHandle.isDisabled', 'ElementHandle.isEditable', 'ElementHandle.isEnabled', 'ElementHandle.isHidden', 'ElementHandle.isVisible', 'ElementHandle.press', 'ElementHandle.querySelector', 'ElementHandle.querySelectorAll', 'ElementHandle.screenshot', 'ElementHandle.scrollIntoViewIfNeeded', 'ElementHandle.selectOption', 'ElementHandle.selectText', 'ElementHandle.setInputFiles', 'ElementHandle.tap', 'ElementHandle.textContent', 'ElementHandle.type', 'ElementHandle.uncheck', 'ElementHandle.waitForElementState', 'ElementHandle.waitForSelector']);
const pausesBeforeInputActions = exports.pausesBeforeInputActions = new Set(['Frame.check', 'Frame.click', 'Frame.dragAndDrop', 'Frame.dblclick', 'Frame.fill', 'Frame.hover', 'Frame.press', 'Frame.selectOption', 'Frame.setInputFiles', 'Frame.tap', 'Frame.type', 'Frame.uncheck', 'ElementHandle.check', 'ElementHandle.click', 'ElementHandle.dblclick', 'ElementHandle.fill', 'ElementHandle.hover', 'ElementHandle.press', 'ElementHandle.selectOption', 'ElementHandle.setInputFiles', 'ElementHandle.tap', 'ElementHandle.type', 'ElementHandle.uncheck']);

View File

@@ -1,173 +1,192 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var serializers_exports = {};
__export(serializers_exports, {
parseSerializedValue: () => parseSerializedValue,
serializeValue: () => serializeValue
}); });
exports.parseSerializedValue = parseSerializedValue; module.exports = __toCommonJS(serializers_exports);
exports.serializeValue = serializeValue;
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function parseSerializedValue(value, handles) { function parseSerializedValue(value, handles) {
return innerParseSerializedValue(value, handles, new Map()); return innerParseSerializedValue(value, handles, /* @__PURE__ */ new Map(), []);
} }
function innerParseSerializedValue(value, handles, refs) { function innerParseSerializedValue(value, handles, refs, accessChain) {
if (value.ref !== undefined) return refs.get(value.ref); if (value.ref !== void 0)
if (value.n !== undefined) return value.n; return refs.get(value.ref);
if (value.s !== undefined) return value.s; if (value.n !== void 0)
if (value.b !== undefined) return value.b; return value.n;
if (value.v !== undefined) { if (value.s !== void 0)
if (value.v === 'undefined') return undefined; return value.s;
if (value.v === 'null') return null; if (value.b !== void 0)
if (value.v === 'NaN') return NaN; return value.b;
if (value.v === 'Infinity') return Infinity; if (value.v !== void 0) {
if (value.v === '-Infinity') return -Infinity; if (value.v === "undefined")
if (value.v === '-0') return -0; return void 0;
if (value.v === "null")
return null;
if (value.v === "NaN")
return NaN;
if (value.v === "Infinity")
return Infinity;
if (value.v === "-Infinity")
return -Infinity;
if (value.v === "-0")
return -0;
} }
if (value.d !== undefined) return new Date(value.d); if (value.d !== void 0)
if (value.u !== undefined) return new URL(value.u); return new Date(value.d);
if (value.bi !== undefined) return BigInt(value.bi); if (value.u !== void 0)
if (value.e !== undefined) { return new URL(value.u);
if (value.bi !== void 0)
return BigInt(value.bi);
if (value.e !== void 0) {
const error = new Error(value.e.m); const error = new Error(value.e.m);
error.name = value.e.n; error.name = value.e.n;
error.stack = value.e.s; error.stack = value.e.s;
return error; return error;
} }
if (value.r !== undefined) return new RegExp(value.r.p, value.r.f); if (value.r !== void 0)
if (value.a !== undefined) { return new RegExp(value.r.p, value.r.f);
if (value.ta !== void 0) {
const ctor = typedArrayKindToConstructor[value.ta.k];
return new ctor(value.ta.b.buffer, value.ta.b.byteOffset, value.ta.b.length / ctor.BYTES_PER_ELEMENT);
}
if (value.a !== void 0) {
const result = []; const result = [];
refs.set(value.id, result); refs.set(value.id, result);
for (const v of value.a) result.push(innerParseSerializedValue(v, handles, refs)); for (let i = 0; i < value.a.length; i++)
result.push(innerParseSerializedValue(value.a[i], handles, refs, [...accessChain, i]));
return result; return result;
} }
if (value.o !== undefined) { if (value.o !== void 0) {
const result = {}; const result = {};
refs.set(value.id, result); refs.set(value.id, result);
for (const { for (const { k, v } of value.o)
k, result[k] = innerParseSerializedValue(v, handles, refs, [...accessChain, k]);
v
} of value.o) result[k] = innerParseSerializedValue(v, handles, refs);
return result; return result;
} }
if (value.h !== undefined) { if (value.h !== void 0) {
if (handles === undefined) throw new Error('Unexpected handle'); if (handles === void 0)
throw new Error("Unexpected handle");
return handles[value.h]; return handles[value.h];
} }
throw new Error('Unexpected value'); throw new Error(`Attempting to deserialize unexpected value${accessChainToDisplayString(accessChain)}: ${value}`);
} }
function serializeValue(value, handleSerializer) { function serializeValue(value, handleSerializer) {
return innerSerializeValue(value, handleSerializer, { return innerSerializeValue(value, handleSerializer, { lastId: 0, visited: /* @__PURE__ */ new Map() }, []);
lastId: 0,
visited: new Map()
});
} }
function innerSerializeValue(value, handleSerializer, visitorInfo) { function innerSerializeValue(value, handleSerializer, visitorInfo, accessChain) {
const handle = handleSerializer(value); const handle = handleSerializer(value);
if ('fallThrough' in handle) value = handle.fallThrough;else return handle; if ("fallThrough" in handle)
if (typeof value === 'symbol') return { value = handle.fallThrough;
v: 'undefined' else
}; return handle;
if (Object.is(value, undefined)) return { if (typeof value === "symbol")
v: 'undefined' return { v: "undefined" };
}; if (Object.is(value, void 0))
if (Object.is(value, null)) return { return { v: "undefined" };
v: 'null' if (Object.is(value, null))
}; return { v: "null" };
if (Object.is(value, NaN)) return { if (Object.is(value, NaN))
v: 'NaN' return { v: "NaN" };
}; if (Object.is(value, Infinity))
if (Object.is(value, Infinity)) return { return { v: "Infinity" };
v: 'Infinity' if (Object.is(value, -Infinity))
}; return { v: "-Infinity" };
if (Object.is(value, -Infinity)) return { if (Object.is(value, -0))
v: '-Infinity' return { v: "-0" };
}; if (typeof value === "boolean")
if (Object.is(value, -0)) return { return { b: value };
v: '-0' if (typeof value === "number")
}; return { n: value };
if (typeof value === 'boolean') return { if (typeof value === "string")
b: value return { s: value };
}; if (typeof value === "bigint")
if (typeof value === 'number') return { return { bi: value.toString() };
n: value if (isError(value))
}; return { e: { n: value.name, m: value.message, s: value.stack || "" } };
if (typeof value === 'string') return { if (isDate(value))
s: value return { d: value.toJSON() };
}; if (isURL(value))
if (typeof value === 'bigint') return { return { u: value.toJSON() };
bi: value.toString() if (isRegExp(value))
}; return { r: { p: value.source, f: value.flags } };
if (isError(value)) return { const typedArrayKind = constructorToTypedArrayKind.get(value.constructor);
e: { if (typedArrayKind)
n: value.name, return { ta: { b: Buffer.from(value.buffer, value.byteOffset, value.byteLength), k: typedArrayKind } };
m: value.message,
s: value.stack || ''
}
};
if (isDate(value)) return {
d: value.toJSON()
};
if (isURL(value)) return {
u: value.toJSON()
};
if (isRegExp(value)) return {
r: {
p: value.source,
f: value.flags
}
};
const id = visitorInfo.visited.get(value); const id = visitorInfo.visited.get(value);
if (id) return { if (id)
ref: id return { ref: id };
};
if (Array.isArray(value)) { if (Array.isArray(value)) {
const a = []; const a = [];
const id = ++visitorInfo.lastId; const id2 = ++visitorInfo.lastId;
visitorInfo.visited.set(value, id); visitorInfo.visited.set(value, id2);
for (let i = 0; i < value.length; ++i) a.push(innerSerializeValue(value[i], handleSerializer, visitorInfo)); for (let i = 0; i < value.length; ++i)
return { a.push(innerSerializeValue(value[i], handleSerializer, visitorInfo, [...accessChain, i]));
a, return { a, id: id2 };
id
};
} }
if (typeof value === 'object') { if (typeof value === "object") {
const o = []; const o = [];
const id = ++visitorInfo.lastId; const id2 = ++visitorInfo.lastId;
visitorInfo.visited.set(value, id); visitorInfo.visited.set(value, id2);
for (const name of Object.keys(value)) o.push({ for (const name of Object.keys(value))
k: name, o.push({ k: name, v: innerSerializeValue(value[name], handleSerializer, visitorInfo, [...accessChain, name]) });
v: innerSerializeValue(value[name], handleSerializer, visitorInfo) return { o, id: id2 };
});
return {
o,
id
};
} }
throw new Error('Unexpected value'); throw new Error(`Attempting to serialize unexpected value${accessChainToDisplayString(accessChain)}: ${value}`);
}
function accessChainToDisplayString(accessChain) {
const chainString = accessChain.map((accessor, i) => {
if (typeof accessor === "string")
return i ? `.${accessor}` : accessor;
return `[${accessor}]`;
}).join("");
return chainString.length > 0 ? ` at position "${chainString}"` : "";
} }
function isRegExp(obj) { function isRegExp(obj) {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]'; return obj instanceof RegExp || Object.prototype.toString.call(obj) === "[object RegExp]";
} }
function isDate(obj) { function isDate(obj) {
return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]'; return obj instanceof Date || Object.prototype.toString.call(obj) === "[object Date]";
} }
function isURL(obj) { function isURL(obj) {
return obj instanceof URL || Object.prototype.toString.call(obj) === '[object URL]'; return obj instanceof URL || Object.prototype.toString.call(obj) === "[object URL]";
} }
function isError(obj) { function isError(obj) {
const proto = obj ? Object.getPrototypeOf(obj) : null; const proto = obj ? Object.getPrototypeOf(obj) : null;
return obj instanceof Error || (proto === null || proto === void 0 ? void 0 : proto.name) === 'Error' || proto && isError(proto); return obj instanceof Error || proto?.name === "Error" || proto && isError(proto);
} }
const typedArrayKindToConstructor = {
i8: Int8Array,
ui8: Uint8Array,
ui8c: Uint8ClampedArray,
i16: Int16Array,
ui16: Uint16Array,
i32: Int32Array,
ui32: Uint32Array,
f32: Float32Array,
f64: Float64Array,
bi64: BigInt64Array,
bui64: BigUint64Array
};
const constructorToTypedArrayKind = new Map(Object.entries(typedArrayKindToConstructor).map(([k, v]) => [v, k]));
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
parseSerializedValue,
serializeValue
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,138 +1,179 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var validatorPrimitives_exports = {};
__export(validatorPrimitives_exports, {
ValidationError: () => ValidationError,
createMetadataValidator: () => createMetadataValidator,
findValidator: () => findValidator,
maybeFindValidator: () => maybeFindValidator,
scheme: () => scheme,
tAny: () => tAny,
tArray: () => tArray,
tBinary: () => tBinary,
tBoolean: () => tBoolean,
tChannel: () => tChannel,
tEnum: () => tEnum,
tNumber: () => tNumber,
tObject: () => tObject,
tOptional: () => tOptional,
tString: () => tString,
tType: () => tType,
tUndefined: () => tUndefined
}); });
exports.ValidationError = void 0; module.exports = __toCommonJS(validatorPrimitives_exports);
exports.createMetadataValidator = createMetadataValidator; class ValidationError extends Error {
exports.findValidator = findValidator; }
exports.maybeFindValidator = maybeFindValidator; const scheme = {};
exports.tUndefined = exports.tType = exports.tString = exports.tOptional = exports.tObject = exports.tNumber = exports.tEnum = exports.tChannel = exports.tBoolean = exports.tBinary = exports.tArray = exports.tAny = exports.scheme = void 0;
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ValidationError extends Error {}
exports.ValidationError = ValidationError;
const scheme = exports.scheme = {};
function findValidator(type, method, kind) { function findValidator(type, method, kind) {
const validator = maybeFindValidator(type, method, kind); const validator = maybeFindValidator(type, method, kind);
if (!validator) throw new ValidationError(`Unknown scheme for ${kind}: ${type}.${method}`); if (!validator)
throw new ValidationError(`Unknown scheme for ${kind}: ${type}.${method}`);
return validator; return validator;
} }
function maybeFindValidator(type, method, kind) { function maybeFindValidator(type, method, kind) {
const schemeName = type + (kind === 'Initializer' ? '' : method[0].toUpperCase() + method.substring(1)) + kind; const schemeName = type + (kind === "Initializer" ? "" : method[0].toUpperCase() + method.substring(1)) + kind;
return scheme[schemeName]; return scheme[schemeName];
} }
function createMetadataValidator() { function createMetadataValidator() {
return tOptional(scheme['Metadata']); return tOptional(scheme["Metadata"]);
} }
const tNumber = (arg, path, context) => { const tNumber = (arg, path, context) => {
if (arg instanceof Number) return arg.valueOf(); if (arg instanceof Number)
if (typeof arg === 'number') return arg; return arg.valueOf();
if (typeof arg === "number")
return arg;
throw new ValidationError(`${path}: expected number, got ${typeof arg}`); throw new ValidationError(`${path}: expected number, got ${typeof arg}`);
}; };
exports.tNumber = tNumber;
const tBoolean = (arg, path, context) => { const tBoolean = (arg, path, context) => {
if (arg instanceof Boolean) return arg.valueOf(); if (arg instanceof Boolean)
if (typeof arg === 'boolean') return arg; return arg.valueOf();
if (typeof arg === "boolean")
return arg;
throw new ValidationError(`${path}: expected boolean, got ${typeof arg}`); throw new ValidationError(`${path}: expected boolean, got ${typeof arg}`);
}; };
exports.tBoolean = tBoolean;
const tString = (arg, path, context) => { const tString = (arg, path, context) => {
if (arg instanceof String) return arg.valueOf(); if (arg instanceof String)
if (typeof arg === 'string') return arg; return arg.valueOf();
if (typeof arg === "string")
return arg;
throw new ValidationError(`${path}: expected string, got ${typeof arg}`); throw new ValidationError(`${path}: expected string, got ${typeof arg}`);
}; };
exports.tString = tString;
const tBinary = (arg, path, context) => { const tBinary = (arg, path, context) => {
if (context.binary === 'fromBase64') { if (context.binary === "fromBase64") {
if (arg instanceof String) return Buffer.from(arg.valueOf(), 'base64'); if (arg instanceof String)
if (typeof arg === 'string') return Buffer.from(arg, 'base64'); return Buffer.from(arg.valueOf(), "base64");
if (typeof arg === "string")
return Buffer.from(arg, "base64");
throw new ValidationError(`${path}: expected base64-encoded buffer, got ${typeof arg}`); throw new ValidationError(`${path}: expected base64-encoded buffer, got ${typeof arg}`);
} }
if (context.binary === 'toBase64') { if (context.binary === "toBase64") {
if (!(arg instanceof Buffer)) throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`); if (!(arg instanceof Buffer))
return arg.toString('base64'); throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
return arg.toString("base64");
} }
if (context.binary === 'buffer') { if (context.binary === "buffer") {
if (!(arg instanceof Buffer)) throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`); if (!(arg instanceof Buffer))
throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
return arg; return arg;
} }
throw new ValidationError(`Unsupported binary behavior "${context.binary}"`); throw new ValidationError(`Unsupported binary behavior "${context.binary}"`);
}; };
exports.tBinary = tBinary;
const tUndefined = (arg, path, context) => { const tUndefined = (arg, path, context) => {
if (Object.is(arg, undefined)) return arg; if (Object.is(arg, void 0))
return arg;
throw new ValidationError(`${path}: expected undefined, got ${typeof arg}`); throw new ValidationError(`${path}: expected undefined, got ${typeof arg}`);
}; };
exports.tUndefined = tUndefined;
const tAny = (arg, path, context) => { const tAny = (arg, path, context) => {
return arg; return arg;
}; };
exports.tAny = tAny; const tOptional = (v) => {
const tOptional = v => {
return (arg, path, context) => { return (arg, path, context) => {
if (Object.is(arg, undefined)) return arg; if (Object.is(arg, void 0))
return arg;
return v(arg, path, context); return v(arg, path, context);
}; };
}; };
exports.tOptional = tOptional; const tArray = (v) => {
const tArray = v => {
return (arg, path, context) => { return (arg, path, context) => {
if (!Array.isArray(arg)) throw new ValidationError(`${path}: expected array, got ${typeof arg}`); if (!Array.isArray(arg))
return arg.map((x, index) => v(x, path + '[' + index + ']', context)); throw new ValidationError(`${path}: expected array, got ${typeof arg}`);
return arg.map((x, index) => v(x, path + "[" + index + "]", context));
}; };
}; };
exports.tArray = tArray; const tObject = (s) => {
const tObject = s => {
return (arg, path, context) => { return (arg, path, context) => {
if (Object.is(arg, null)) throw new ValidationError(`${path}: expected object, got null`); if (Object.is(arg, null))
if (typeof arg !== 'object') throw new ValidationError(`${path}: expected object, got ${typeof arg}`); throw new ValidationError(`${path}: expected object, got null`);
if (typeof arg !== "object")
throw new ValidationError(`${path}: expected object, got ${typeof arg}`);
const result = {}; const result = {};
for (const [key, v] of Object.entries(s)) { for (const [key, v] of Object.entries(s)) {
const value = v(arg[key], path ? path + '.' + key : key, context); const value = v(arg[key], path ? path + "." + key : key, context);
if (!Object.is(value, undefined)) result[key] = value; if (!Object.is(value, void 0))
result[key] = value;
} }
if (context.isUnderTest()) { if (context.isUnderTest()) {
for (const [key, value] of Object.entries(arg)) { for (const [key, value] of Object.entries(arg)) {
if (key.startsWith('__testHook')) result[key] = value; if (key.startsWith("__testHook"))
result[key] = value;
} }
} }
return result; return result;
}; };
}; };
exports.tObject = tObject; const tEnum = (e) => {
const tEnum = e => {
return (arg, path, context) => { return (arg, path, context) => {
if (!e.includes(arg)) throw new ValidationError(`${path}: expected one of (${e.join('|')})`); if (!e.includes(arg))
throw new ValidationError(`${path}: expected one of (${e.join("|")})`);
return arg; return arg;
}; };
}; };
exports.tEnum = tEnum; const tChannel = (names) => {
const tChannel = names => {
return (arg, path, context) => { return (arg, path, context) => {
return context.tChannelImpl(names, arg, path, context); return context.tChannelImpl(names, arg, path, context);
}; };
}; };
exports.tChannel = tChannel; const tType = (name) => {
const tType = name => {
return (arg, path, context) => { return (arg, path, context) => {
const v = scheme[name]; const v = scheme[name];
if (!v) throw new ValidationError(path + ': unknown type "' + name + '"'); if (!v)
throw new ValidationError(path + ': unknown type "' + name + '"');
return v(arg, path, context); return v(arg, path, context);
}; };
}; };
exports.tType = tType; // Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ValidationError,
createMetadataValidator,
findValidator,
maybeFindValidator,
scheme,
tAny,
tArray,
tBinary,
tBoolean,
tChannel,
tEnum,
tNumber,
tObject,
tOptional,
tString,
tType,
tUndefined
});

View File

@@ -1,230 +1,205 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var playwrightConnection_exports = {};
__export(playwrightConnection_exports, {
PlaywrightConnection: () => PlaywrightConnection
}); });
exports.PlaywrightConnection = void 0; module.exports = __toCommonJS(playwrightConnection_exports);
var _socksProxy = require("../server/utils/socksProxy"); var import_socksProxy = require("../server/utils/socksProxy");
var _server = require("../server"); var import_server = require("../server");
var _android = require("../server/android/android"); var import_android = require("../server/android/android");
var _browser = require("../server/browser"); var import_browser = require("../server/browser");
var _debugControllerDispatcher = require("../server/dispatchers/debugControllerDispatcher"); var import_debugControllerDispatcher = require("../server/dispatchers/debugControllerDispatcher");
var _instrumentation = require("../server/instrumentation"); var import_instrumentation = require("../server/instrumentation");
var _assert = require("../utils/isomorphic/assert"); var import_assert = require("../utils/isomorphic/assert");
var _debug = require("../server/utils/debug"); var import_debug = require("../server/utils/debug");
var _profiler = require("../server/utils/profiler"); var import_profiler = require("../server/utils/profiler");
var _utils = require("../utils"); var import_utils = require("../utils");
var _debugLogger = require("../server/utils/debugLogger"); var import_debugLogger = require("../server/utils/debugLogger");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PlaywrightConnection { class PlaywrightConnection {
constructor(lock, clientType, ws, options, preLaunched, id, onClose) { constructor(lock, clientType, ws, options, preLaunched, id, onClose) {
this._ws = void 0;
this._onClose = void 0;
this._dispatcherConnection = void 0;
this._cleanups = []; this._cleanups = [];
this._id = void 0;
this._disconnected = false; this._disconnected = false;
this._preLaunched = void 0;
this._options = void 0;
this._root = void 0;
this._profileName = void 0;
this._ws = ws; this._ws = ws;
this._preLaunched = preLaunched; this._preLaunched = preLaunched;
this._options = options; this._options = options;
options.launchOptions = filterLaunchOptions(options.launchOptions, options.allowFSPaths); options.launchOptions = filterLaunchOptions(options.launchOptions, options.allowFSPaths);
if (clientType === 'reuse-browser' || clientType === 'pre-launched-browser-or-android') (0, _assert.assert)(preLaunched.playwright); if (clientType === "reuse-browser" || clientType === "pre-launched-browser-or-android")
if (clientType === 'pre-launched-browser-or-android') (0, _assert.assert)(preLaunched.browser || preLaunched.androidDevice); (0, import_assert.assert)(preLaunched.playwright);
if (clientType === "pre-launched-browser-or-android")
(0, import_assert.assert)(preLaunched.browser || preLaunched.androidDevice);
this._onClose = onClose; this._onClose = onClose;
this._id = id; this._id = id;
this._profileName = `${new Date().toISOString()}-${clientType}`; this._profileName = `${(/* @__PURE__ */ new Date()).toISOString()}-${clientType}`;
this._dispatcherConnection = new _server.DispatcherConnection(); this._dispatcherConnection = new import_server.DispatcherConnection();
this._dispatcherConnection.onmessage = async message => { this._dispatcherConnection.onmessage = async (message) => {
await lock; await lock;
if (ws.readyState !== ws.CLOSING) { if (ws.readyState !== ws.CLOSING) {
const messageString = JSON.stringify(message); const messageString = JSON.stringify(message);
if (_debugLogger.debugLogger.isEnabled('server:channel')) _debugLogger.debugLogger.log('server:channel', `[${this._id}] ${(0, _utils.monotonicTime)() * 1000} SEND ► ${messageString}`); if (import_debugLogger.debugLogger.isEnabled("server:channel"))
if (_debugLogger.debugLogger.isEnabled('server:metadata')) this.logServerMetadata(message, messageString, 'SEND'); import_debugLogger.debugLogger.log("server:channel", `[${this._id}] ${(0, import_utils.monotonicTime)() * 1e3} SEND \u25BA ${messageString}`);
if (import_debugLogger.debugLogger.isEnabled("server:metadata"))
this.logServerMetadata(message, messageString, "SEND");
ws.send(messageString); ws.send(messageString);
} }
}; };
ws.on('message', async message => { ws.on("message", async (message) => {
await lock; await lock;
const messageString = Buffer.from(message).toString(); const messageString = Buffer.from(message).toString();
const jsonMessage = JSON.parse(messageString); const jsonMessage = JSON.parse(messageString);
if (_debugLogger.debugLogger.isEnabled('server:channel')) _debugLogger.debugLogger.log('server:channel', `[${this._id}] ${(0, _utils.monotonicTime)() * 1000} ◀ RECV ${messageString}`); if (import_debugLogger.debugLogger.isEnabled("server:channel"))
if (_debugLogger.debugLogger.isEnabled('server:metadata')) this.logServerMetadata(jsonMessage, messageString, 'RECV'); import_debugLogger.debugLogger.log("server:channel", `[${this._id}] ${(0, import_utils.monotonicTime)() * 1e3} \u25C0 RECV ${messageString}`);
if (import_debugLogger.debugLogger.isEnabled("server:metadata"))
this.logServerMetadata(jsonMessage, messageString, "RECV");
this._dispatcherConnection.dispatch(jsonMessage); this._dispatcherConnection.dispatch(jsonMessage);
}); });
ws.on('close', () => this._onDisconnect()); ws.on("close", () => this._onDisconnect());
ws.on('error', error => this._onDisconnect(error)); ws.on("error", (error) => this._onDisconnect(error));
if (clientType === 'controller') { if (clientType === "controller") {
this._root = this._initDebugControllerMode(); this._root = this._initDebugControllerMode();
return; return;
} }
this._root = new _server.RootDispatcher(this._dispatcherConnection, async (scope, options) => { this._root = new import_server.RootDispatcher(this._dispatcherConnection, async (scope, options2) => {
await (0, _profiler.startProfiling)(); await (0, import_profiler.startProfiling)();
if (clientType === 'reuse-browser') return await this._initReuseBrowsersMode(scope); if (clientType === "reuse-browser")
if (clientType === 'pre-launched-browser-or-android') return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope); return await this._initReuseBrowsersMode(scope);
if (clientType === 'launch-browser') return await this._initLaunchBrowserMode(scope, options); if (clientType === "pre-launched-browser-or-android")
throw new Error('Unsupported client type: ' + clientType); return this._preLaunched.browser ? await this._initPreLaunchedBrowserMode(scope) : await this._initPreLaunchedAndroidMode(scope);
if (clientType === "launch-browser")
return await this._initLaunchBrowserMode(scope, options2);
throw new Error("Unsupported client type: " + clientType);
}); });
} }
async _initLaunchBrowserMode(scope, options) { async _initLaunchBrowserMode(scope, options) {
_debugLogger.debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`); import_debugLogger.debugLogger.log("server", `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
const playwright = (0, _server.createPlaywright)({ const playwright = (0, import_server.createPlaywright)({ sdkLanguage: options.sdkLanguage, isServer: true });
sdkLanguage: options.sdkLanguage,
isServer: true
});
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright); const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
let browserName = this._options.browserName; let browserName = this._options.browserName;
if ('bidi' === browserName) { if ("bidi" === browserName) {
var _this$_options$launch; if (this._options.launchOptions?.channel?.toLocaleLowerCase().includes("firefox"))
if ((_this$_options$launch = this._options.launchOptions) !== null && _this$_options$launch !== void 0 && (_this$_options$launch = _this$_options$launch.channel) !== null && _this$_options$launch !== void 0 && _this$_options$launch.toLocaleLowerCase().includes('firefox')) browserName = 'bidiFirefox';else browserName = 'bidiChromium'; browserName = "bidiFirefox";
else
browserName = "bidiChromium";
} }
const browser = await playwright[browserName].launch((0, _instrumentation.serverSideCallMetadata)(), this._options.launchOptions); const browser = await playwright[browserName].launch((0, import_instrumentation.serverSideCallMetadata)(), this._options.launchOptions);
this._cleanups.push(async () => { this._cleanups.push(async () => {
for (const browser of playwright.allBrowsers()) await browser.close({ for (const browser2 of playwright.allBrowsers())
reason: 'Connection terminated' await browser2.close({ reason: "Connection terminated" });
}); });
browser.on(import_browser.Browser.Events.Disconnected, () => {
this.close({ code: 1001, reason: "Browser closed" });
}); });
browser.on(_browser.Browser.Events.Disconnected, () => { return new import_server.PlaywrightDispatcher(scope, playwright, ownedSocksProxy, browser);
// Underlying browser did close for some reason - force disconnect the client.
this.close({
code: 1001,
reason: 'Browser closed'
});
});
return new _server.PlaywrightDispatcher(scope, playwright, ownedSocksProxy, browser);
} }
async _initPreLaunchedBrowserMode(scope) { async _initPreLaunchedBrowserMode(scope) {
var _this$_preLaunched$so; import_debugLogger.debugLogger.log("server", `[${this._id}] engaged pre-launched (browser) mode`);
_debugLogger.debugLogger.log('server', `[${this._id}] engaged pre-launched (browser) mode`);
const playwright = this._preLaunched.playwright; const playwright = this._preLaunched.playwright;
this._preLaunched.socksProxy?.setPattern(this._options.socksProxyPattern);
// Note: connected client owns the socks proxy and configures the pattern.
(_this$_preLaunched$so = this._preLaunched.socksProxy) === null || _this$_preLaunched$so === void 0 || _this$_preLaunched$so.setPattern(this._options.socksProxyPattern);
const browser = this._preLaunched.browser; const browser = this._preLaunched.browser;
browser.on(_browser.Browser.Events.Disconnected, () => { browser.on(import_browser.Browser.Events.Disconnected, () => {
// Underlying browser did close for some reason - force disconnect the client. this.close({ code: 1001, reason: "Browser closed" });
this.close({
code: 1001,
reason: 'Browser closed'
}); });
}); const playwrightDispatcher = new import_server.PlaywrightDispatcher(scope, playwright, this._preLaunched.socksProxy, browser);
const playwrightDispatcher = new _server.PlaywrightDispatcher(scope, playwright, this._preLaunched.socksProxy, browser);
// In pre-launched mode, keep only the pre-launched browser.
for (const b of playwright.allBrowsers()) { for (const b of playwright.allBrowsers()) {
if (b !== browser) await b.close({ if (b !== browser)
reason: 'Connection terminated' await b.close({ reason: "Connection terminated" });
});
} }
this._cleanups.push(() => playwrightDispatcher.cleanup()); this._cleanups.push(() => playwrightDispatcher.cleanup());
return playwrightDispatcher; return playwrightDispatcher;
} }
async _initPreLaunchedAndroidMode(scope) { async _initPreLaunchedAndroidMode(scope) {
_debugLogger.debugLogger.log('server', `[${this._id}] engaged pre-launched (Android) mode`); import_debugLogger.debugLogger.log("server", `[${this._id}] engaged pre-launched (Android) mode`);
const playwright = this._preLaunched.playwright; const playwright = this._preLaunched.playwright;
const androidDevice = this._preLaunched.androidDevice; const androidDevice = this._preLaunched.androidDevice;
androidDevice.on(_android.AndroidDevice.Events.Close, () => { androidDevice.on(import_android.AndroidDevice.Events.Close, () => {
// Underlying browser did close for some reason - force disconnect the client. this.close({ code: 1001, reason: "Android device disconnected" });
this.close({
code: 1001,
reason: 'Android device disconnected'
}); });
}); const playwrightDispatcher = new import_server.PlaywrightDispatcher(scope, playwright, void 0, void 0, androidDevice);
const playwrightDispatcher = new _server.PlaywrightDispatcher(scope, playwright, undefined, undefined, androidDevice);
this._cleanups.push(() => playwrightDispatcher.cleanup()); this._cleanups.push(() => playwrightDispatcher.cleanup());
return playwrightDispatcher; return playwrightDispatcher;
} }
_initDebugControllerMode() { _initDebugControllerMode() {
_debugLogger.debugLogger.log('server', `[${this._id}] engaged reuse controller mode`); import_debugLogger.debugLogger.log("server", `[${this._id}] engaged reuse controller mode`);
const playwright = this._preLaunched.playwright; const playwright = this._preLaunched.playwright;
// Always create new instance based on the reused Playwright instance. return new import_debugControllerDispatcher.DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController);
return new _debugControllerDispatcher.DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController);
} }
async _initReuseBrowsersMode(scope) { async _initReuseBrowsersMode(scope) {
// Note: reuse browser mode does not support socks proxy, because import_debugLogger.debugLogger.log("server", `[${this._id}] engaged reuse browsers mode for ${this._options.browserName}`);
// clients come and go, while the browser stays the same.
_debugLogger.debugLogger.log('server', `[${this._id}] engaged reuse browsers mode for ${this._options.browserName}`);
const playwright = this._preLaunched.playwright; const playwright = this._preLaunched.playwright;
const requestedOptions = launchOptionsHash(this._options.launchOptions); const requestedOptions = launchOptionsHash(this._options.launchOptions);
let browser = playwright.allBrowsers().find(b => { let browser = playwright.allBrowsers().find((b) => {
if (b.options.name !== this._options.browserName) return false; if (b.options.name !== this._options.browserName)
return false;
const existingOptions = launchOptionsHash(b.options.originalLaunchOptions); const existingOptions = launchOptionsHash(b.options.originalLaunchOptions);
return existingOptions === requestedOptions; return existingOptions === requestedOptions;
}); });
// Close remaining browsers of this type+channel. Keep different browser types for the speed.
for (const b of playwright.allBrowsers()) { for (const b of playwright.allBrowsers()) {
if (b === browser) continue; if (b === browser)
if (b.options.name === this._options.browserName && b.options.channel === this._options.launchOptions.channel) await b.close({ continue;
reason: 'Connection terminated' if (b.options.name === this._options.browserName && b.options.channel === this._options.launchOptions.channel)
}); await b.close({ reason: "Connection terminated" });
} }
if (!browser) { if (!browser) {
browser = await playwright[this._options.browserName || 'chromium'].launch((0, _instrumentation.serverSideCallMetadata)(), { browser = await playwright[this._options.browserName || "chromium"].launch((0, import_instrumentation.serverSideCallMetadata)(), {
...this._options.launchOptions, ...this._options.launchOptions,
headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS headless: !!process.env.PW_DEBUG_CONTROLLER_HEADLESS
}); });
browser.on(_browser.Browser.Events.Disconnected, () => { browser.on(import_browser.Browser.Events.Disconnected, () => {
// Underlying browser did close for some reason - force disconnect the client. this.close({ code: 1001, reason: "Browser closed" });
this.close({
code: 1001,
reason: 'Browser closed'
});
}); });
} }
this._cleanups.push(async () => { this._cleanups.push(async () => {
// Don't close the pages so that user could debug them, for (const browser2 of playwright.allBrowsers()) {
// but close all the empty browsers and contexts to clean up. for (const context of browser2.contexts()) {
for (const browser of playwright.allBrowsers()) { if (!context.pages().length)
for (const context of browser.contexts()) { await context.close({ reason: "Connection terminated" });
if (!context.pages().length) await context.close({ else
reason: 'Connection terminated' await context.stopPendingOperations("Connection closed");
});else await context.stopPendingOperations('Connection closed');
} }
if (!browser.contexts()) await browser.close({ if (!browser2.contexts())
reason: 'Connection terminated' await browser2.close({ reason: "Connection terminated" });
});
} }
}); });
const playwrightDispatcher = new _server.PlaywrightDispatcher(scope, playwright, undefined, browser); const playwrightDispatcher = new import_server.PlaywrightDispatcher(scope, playwright, void 0, browser);
return playwrightDispatcher; return playwrightDispatcher;
} }
async _createOwnedSocksProxy(playwright) { async _createOwnedSocksProxy(playwright) {
if (!this._options.socksProxyPattern) return; if (!this._options.socksProxyPattern)
const socksProxy = new _socksProxy.SocksProxy(); return;
const socksProxy = new import_socksProxy.SocksProxy();
socksProxy.setPattern(this._options.socksProxyPattern); socksProxy.setPattern(this._options.socksProxyPattern);
playwright.options.socksProxyPort = await socksProxy.listen(0); playwright.options.socksProxyPort = await socksProxy.listen(0);
_debugLogger.debugLogger.log('server', `[${this._id}] started socks proxy on port ${playwright.options.socksProxyPort}`); import_debugLogger.debugLogger.log("server", `[${this._id}] started socks proxy on port ${playwright.options.socksProxyPort}`);
this._cleanups.push(() => socksProxy.close()); this._cleanups.push(() => socksProxy.close());
return socksProxy; return socksProxy;
} }
async _onDisconnect(error) { async _onDisconnect(error) {
this._disconnected = true; this._disconnected = true;
_debugLogger.debugLogger.log('server', `[${this._id}] disconnected. error: ${error}`); import_debugLogger.debugLogger.log("server", `[${this._id}] disconnected. error: ${error}`);
this._root._dispose(); this._root._dispose();
_debugLogger.debugLogger.log('server', `[${this._id}] starting cleanup`); import_debugLogger.debugLogger.log("server", `[${this._id}] starting cleanup`);
for (const cleanup of this._cleanups) await cleanup().catch(() => {}); for (const cleanup of this._cleanups)
await (0, _profiler.stopProfiling)(this._profileName); await cleanup().catch(() => {
});
await (0, import_profiler.stopProfiling)(this._profileName);
this._onClose(); this._onClose();
_debugLogger.debugLogger.log('server', `[${this._id}] finished cleanup`); import_debugLogger.debugLogger.log("server", `[${this._id}] finished cleanup`);
} }
logServerMetadata(message, messageString, direction) { logServerMetadata(message, messageString, direction) {
const serverLogMetadata = { const serverLogMetadata = {
@@ -232,28 +207,29 @@ class PlaywrightConnection {
id: message.id, id: message.id,
guid: message.guid, guid: message.guid,
method: message.method, method: message.method,
payloadSizeInBytes: Buffer.byteLength(messageString, 'utf-8') payloadSizeInBytes: Buffer.byteLength(messageString, "utf-8")
}; };
_debugLogger.debugLogger.log('server:metadata', (direction === 'SEND' ? 'SEND ► ' : '◀ RECV ') + JSON.stringify(serverLogMetadata)); import_debugLogger.debugLogger.log("server:metadata", (direction === "SEND" ? "SEND \u25BA " : "\u25C0 RECV ") + JSON.stringify(serverLogMetadata));
} }
async close(reason) { async close(reason) {
if (this._disconnected) return; if (this._disconnected)
_debugLogger.debugLogger.log('server', `[${this._id}] force closing connection: ${(reason === null || reason === void 0 ? void 0 : reason.reason) || ''} (${(reason === null || reason === void 0 ? void 0 : reason.code) || 0})`); return;
import_debugLogger.debugLogger.log("server", `[${this._id}] force closing connection: ${reason?.reason || ""} (${reason?.code || 0})`);
try { try {
this._ws.close(reason === null || reason === void 0 ? void 0 : reason.code, reason === null || reason === void 0 ? void 0 : reason.reason); this._ws.close(reason?.code, reason?.reason);
} catch (e) {} } catch (e) {
}
} }
} }
exports.PlaywrightConnection = PlaywrightConnection;
function launchOptionsHash(options) { function launchOptionsHash(options) {
const copy = { const copy = { ...options };
...options
};
for (const k of Object.keys(copy)) { for (const k of Object.keys(copy)) {
const key = k; const key = k;
if (copy[key] === defaultLaunchOptions[key]) delete copy[key]; if (copy[key] === defaultLaunchOptions[key])
delete copy[key];
} }
for (const key of optionsThatAllowBrowserReuse) delete copy[key]; for (const key of optionsThatAllowBrowserReuse)
delete copy[key];
return JSON.stringify(copy); return JSON.stringify(copy);
} }
function filterLaunchOptions(options, allowFSPaths) { function filterLaunchOptions(options, allowFSPaths) {
@@ -268,8 +244,8 @@ function filterLaunchOptions(options, allowFSPaths) {
chromiumSandbox: options.chromiumSandbox, chromiumSandbox: options.chromiumSandbox,
firefoxUserPrefs: options.firefoxUserPrefs, firefoxUserPrefs: options.firefoxUserPrefs,
slowMo: options.slowMo, slowMo: options.slowMo,
executablePath: (0, _debug.isUnderTest)() || allowFSPaths ? options.executablePath : undefined, executablePath: (0, import_debug.isUnderTest)() || allowFSPaths ? options.executablePath : void 0,
downloadsPath: allowFSPaths ? options.downloadsPath : undefined downloadsPath: allowFSPaths ? options.downloadsPath : void 0
}; };
} }
const defaultLaunchOptions = { const defaultLaunchOptions = {
@@ -280,4 +256,11 @@ const defaultLaunchOptions = {
headless: true, headless: true,
devtools: false devtools: false
}; };
const optionsThatAllowBrowserReuse = ['headless', 'tracesDir']; const optionsThatAllowBrowserReuse = [
"headless",
"tracesDir"
];
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PlaywrightConnection
});

View File

@@ -1,104 +1,105 @@
"use strict"; "use strict";
var __defProp = Object.defineProperty;
Object.defineProperty(exports, "__esModule", { var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
value: true var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var playwrightServer_exports = {};
__export(playwrightServer_exports, {
PlaywrightServer: () => PlaywrightServer
}); });
exports.PlaywrightServer = void 0; module.exports = __toCommonJS(playwrightServer_exports);
var _playwrightConnection = require("./playwrightConnection"); var import_playwrightConnection = require("./playwrightConnection");
var _playwright = require("../server/playwright"); var import_playwright = require("../server/playwright");
var _debugLogger = require("../server/utils/debugLogger"); var import_debugLogger = require("../server/utils/debugLogger");
var _semaphore = require("../utils/isomorphic/semaphore"); var import_semaphore = require("../utils/isomorphic/semaphore");
var _wsServer = require("../server/utils/wsServer"); var import_wsServer = require("../server/utils/wsServer");
var _ascii = require("../server/utils/ascii"); var import_ascii = require("../server/utils/ascii");
var _userAgent = require("../server/utils/userAgent"); var import_userAgent = require("../server/utils/userAgent");
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class PlaywrightServer { class PlaywrightServer {
constructor(options) { constructor(options) {
this._preLaunchedPlaywright = void 0;
this._options = void 0;
this._wsServer = void 0;
this._options = options; this._options = options;
if (options.preLaunchedBrowser) this._preLaunchedPlaywright = options.preLaunchedBrowser.attribution.playwright; if (options.preLaunchedBrowser)
if (options.preLaunchedAndroidDevice) this._preLaunchedPlaywright = options.preLaunchedAndroidDevice._android.attribution.playwright; this._preLaunchedPlaywright = options.preLaunchedBrowser.attribution.playwright;
const browserSemaphore = new _semaphore.Semaphore(this._options.maxConnections); if (options.preLaunchedAndroidDevice)
const controllerSemaphore = new _semaphore.Semaphore(1); this._preLaunchedPlaywright = options.preLaunchedAndroidDevice._android.attribution.playwright;
const reuseBrowserSemaphore = new _semaphore.Semaphore(1); const browserSemaphore = new import_semaphore.Semaphore(this._options.maxConnections);
this._wsServer = new _wsServer.WSServer({ const controllerSemaphore = new import_semaphore.Semaphore(1);
const reuseBrowserSemaphore = new import_semaphore.Semaphore(1);
this._wsServer = new import_wsServer.WSServer({
onUpgrade: (request, socket) => { onUpgrade: (request, socket) => {
const uaError = userAgentVersionMatchesErrorMessage(request.headers['user-agent'] || ''); const uaError = userAgentVersionMatchesErrorMessage(request.headers["user-agent"] || "");
if (uaError) return { if (uaError)
error: `HTTP/${request.httpVersion} 428 Precondition Required\r\n\r\n${uaError}` return { error: `HTTP/${request.httpVersion} 428 Precondition Required\r
}; \r
${uaError}` };
}, },
onHeaders: headers => { onHeaders: (headers) => {
if (process.env.PWTEST_SERVER_WS_HEADERS) headers.push(process.env.PWTEST_SERVER_WS_HEADERS); if (process.env.PWTEST_SERVER_WS_HEADERS)
headers.push(process.env.PWTEST_SERVER_WS_HEADERS);
}, },
onConnection: (request, url, ws, id) => { onConnection: (request, url, ws, id) => {
const browserHeader = request.headers['x-playwright-browser']; const browserHeader = request.headers["x-playwright-browser"];
const browserName = url.searchParams.get('browser') || (Array.isArray(browserHeader) ? browserHeader[0] : browserHeader) || null; const browserName = url.searchParams.get("browser") || (Array.isArray(browserHeader) ? browserHeader[0] : browserHeader) || null;
const proxyHeader = request.headers['x-playwright-proxy']; const proxyHeader = request.headers["x-playwright-proxy"];
const proxyValue = url.searchParams.get('proxy') || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader); const proxyValue = url.searchParams.get("proxy") || (Array.isArray(proxyHeader) ? proxyHeader[0] : proxyHeader);
const launchOptionsHeader = request.headers['x-playwright-launch-options'] || ''; const launchOptionsHeader = request.headers["x-playwright-launch-options"] || "";
const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader; const launchOptionsHeaderValue = Array.isArray(launchOptionsHeader) ? launchOptionsHeader[0] : launchOptionsHeader;
const launchOptionsParam = url.searchParams.get('launch-options'); const launchOptionsParam = url.searchParams.get("launch-options");
let launchOptions = {}; let launchOptions = {};
try { try {
launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue); launchOptions = JSON.parse(launchOptionsParam || launchOptionsHeaderValue);
} catch (e) {} } catch (e) {
// Instantiate playwright for the extension modes.
const isExtension = this._options.mode === 'extension';
if (isExtension) {
if (!this._preLaunchedPlaywright) this._preLaunchedPlaywright = (0, _playwright.createPlaywright)({
sdkLanguage: 'javascript',
isServer: true
});
} }
let clientType = 'launch-browser'; const isExtension = this._options.mode === "extension";
if (isExtension) {
if (!this._preLaunchedPlaywright)
this._preLaunchedPlaywright = (0, import_playwright.createPlaywright)({ sdkLanguage: "javascript", isServer: true });
}
let clientType = "launch-browser";
let semaphore = browserSemaphore; let semaphore = browserSemaphore;
if (isExtension && url.searchParams.has('debug-controller')) { if (isExtension && url.searchParams.has("debug-controller")) {
clientType = 'controller'; clientType = "controller";
semaphore = controllerSemaphore; semaphore = controllerSemaphore;
} else if (isExtension) { } else if (isExtension) {
clientType = 'reuse-browser'; clientType = "reuse-browser";
semaphore = reuseBrowserSemaphore; semaphore = reuseBrowserSemaphore;
} else if (this._options.mode === 'launchServer') { } else if (this._options.mode === "launchServer") {
clientType = 'pre-launched-browser-or-android'; clientType = "pre-launched-browser-or-android";
semaphore = browserSemaphore; semaphore = browserSemaphore;
} }
return new _playwrightConnection.PlaywrightConnection(semaphore.acquire(), clientType, ws, { return new import_playwrightConnection.PlaywrightConnection(
socksProxyPattern: proxyValue, semaphore.acquire(),
browserName, clientType,
launchOptions, ws,
allowFSPaths: this._options.mode === 'extension' { socksProxyPattern: proxyValue, browserName, launchOptions, allowFSPaths: this._options.mode === "extension" },
}, { {
playwright: this._preLaunchedPlaywright, playwright: this._preLaunchedPlaywright,
browser: this._options.preLaunchedBrowser, browser: this._options.preLaunchedBrowser,
androidDevice: this._options.preLaunchedAndroidDevice, androidDevice: this._options.preLaunchedAndroidDevice,
socksProxy: this._options.preLaunchedSocksProxy socksProxy: this._options.preLaunchedSocksProxy
}, id, () => semaphore.release()); },
id,
() => semaphore.release()
);
}, },
onClose: async () => { onClose: async () => {
_debugLogger.debugLogger.log('server', 'closing browsers'); import_debugLogger.debugLogger.log("server", "closing browsers");
if (this._preLaunchedPlaywright) await Promise.all(this._preLaunchedPlaywright.allBrowsers().map(browser => browser.close({ if (this._preLaunchedPlaywright)
reason: 'Playwright Server stopped' await Promise.all(this._preLaunchedPlaywright.allBrowsers().map((browser) => browser.close({ reason: "Playwright Server stopped" })));
}))); import_debugLogger.debugLogger.log("server", "closed browsers");
_debugLogger.debugLogger.log('server', 'closed browsers');
} }
}); });
} }
@@ -109,16 +110,30 @@ class PlaywrightServer {
await this._wsServer.close(); await this._wsServer.close();
} }
} }
exports.PlaywrightServer = PlaywrightServer;
function userAgentVersionMatchesErrorMessage(userAgent) { function userAgentVersionMatchesErrorMessage(userAgent) {
const match = userAgent.match(/^Playwright\/(\d+\.\d+\.\d+)/); const match = userAgent.match(/^Playwright\/(\d+\.\d+\.\d+)/);
if (!match) { if (!match) {
// Cannot parse user agent - be lax.
return; return;
} }
const received = match[1].split('.').slice(0, 2).join('.'); const received = match[1].split(".").slice(0, 2).join(".");
const expected = (0, _userAgent.getPlaywrightVersion)(true); const expected = (0, import_userAgent.getPlaywrightVersion)(true);
if (received !== expected) { if (received !== expected) {
return (0, _ascii.wrapInASCIIBox)([`Playwright version mismatch:`, ` - server version: v${expected}`, ` - client version: v${received}`, ``, `If you are using VSCode extension, restart VSCode.`, ``, `If you are connecting to a remote service,`, `keep your local Playwright version in sync`, `with the remote service version.`, ``, `<3 Playwright Team`].join('\n'), 1); return (0, import_ascii.wrapInASCIIBox)([
`Playwright version mismatch:`,
` - server version: v${expected}`,
` - client version: v${received}`,
``,
`If you are using VSCode extension, restart VSCode.`,
``,
`If you are connecting to a remote service,`,
`keep your local Playwright version in sync`,
`with the remote service version.`,
``,
`<3 Playwright Team`
].join("\n"), 1);
} }
} }
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
PlaywrightServer
});

Some files were not shown because too many files have changed in this diff Show More