SPDXVersion: SPDX-2.1
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: project
DocumentNamespace: http://spdx.org/spdxdocs/spdx-v2.1-3a04fc02-a4f7-4c15-a5ae-58db60153704
Creator: Person: Anonymous ()
Creator: Organization: Anonymous ()
Creator: Tool: reuse-6.1.0
Created: 2026-08-03T07:23:59Z
CreatorComment: This document was created automatically using available reuse information consistent with REUSE.
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-1330e351b78126aebaaaa8c028a6bd71
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-b64eb1c1ff5d615fa4b71edbe8e20c0c
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-1e0e53f3a4cda9a5e63b935421a2660d
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-0b2420fdd66596965bf84debe9c8e0e9
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-796f380d9c91350db031938a4b4cd437
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-a702e225b6d73cda48195ba37116c1f6
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-7e01dbe24bef256885c4e75740e2143b
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-d45d9eb1a6cccbef71aad8f2328c497c
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-ce8c62b8550b10c2031f1cd904ef4348
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-4d0c274515c41683a8b7c0a669c02c11
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-0f9b80057ede865ceb6b73863de0aaa1
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-b1a9506bad4275dc0d4110b2e0548973
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-b76ed24579509532b5f703bfc091f6d9
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-1385712e105271a03b927fa8963d4708
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-154d551fbc375c842b68e0b2c6dc409e
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-bada5d5d541b0161db52fa6c508ec884
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-8924cc4781bc09e5a4d73e575c5b4b73
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-37dd4126913f67a63b0d69683b78b9cd
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-cc45bc3e84ffff91b52c85ed8edf18cb
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-bced871a599551c9032c84d00e3332c4
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-76df032e5fe3223cb4ae6ea8fc89bbe4
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-42e1de2661683e985f6c3442a4c3a65c
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-14395c47d2272b9abb9bd2baf0e460ea
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-ec974ca993628cf5a135d08f503c290f
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-807a29049d7da634399b64f94127075d
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2f1590981a87ca492d09a1e59729fdbb
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-6d24bcb0216880a7e5fa1445b31318e8
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-ce0df55b19ef8cded1a7b1a94d284c4d
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-7c6f181dee750807551959a0c65bb2a0
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-6851e9e71bb93a7c2825f2146e8afdd4
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-49ce20e991f71c92ee84728660a79b3a
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-e703eb19f9a02f31d3f039864ec096b7
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-66a6fc078c8c740355932bb5c5cd0dfc
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-9fd4ff3158e9af15c2214ad72c97a6f6
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-e97dd1537547db6f86b5441e043738f9
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-0aa661fb36377a38ce3c3f61c1264dc6
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-4add95c20c276b6e8f4b47003104e0e2
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-1dff51634be2ad0c1eba1bdae5613ca9
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-971e194092b5a697f855e6dadd32b1ff
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-3655e1caec108fa552d0b65052b51220
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-93f281e7a0802638959b6e251827db6f
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-929f6c429218543c679db04787ff6c45
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-0bde88a48fe27d07460ea97c9ae1f22e
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-035bd2e2fd3d2ea1eee6bb34c90cac75
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-7da954c1f84e13b3011bae211e1f159f
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-a627df9bc7cb119e55327a2218410384
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-32025b529bbfb6ef1cb2edd84fe996cf
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-51ab3fd561ebb124c08c41a989c64520
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-3538735102bcfc73eb59ba1f129a5255
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-e43673088fe4ef695726c1bb586c34f0
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2b0935a9f42457a75a1873f5f0c910ae
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-3f4be2c92720e01b6817cdb3cb6521d5
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-409d8af50c602975227d724f57ad69d3
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-7eb1ac325c38e20f20ca5ac99a7f1a01
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-329ce8202f06b53c345497cb80fc1636
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-dbfe5d93a40fc13ba2daf8a8f0c28f56
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-83e556734de9a8666875ad5d4fb3b62a
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2ef4b12593aabd3df85cd4cd778b75b3
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-991eeb069b8ceb6e899ed317aca06e5f
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-3aaf51398847f731a8dc029d3e32e25d
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-c57d50374edfa1066338ea5e2f477fca
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-4086d1e5a09f11ec8ec2b0b8fccb613b
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-dc18b8d4e529327fa5e74f9115644f2a
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-6521134c5145a3917a7c08118d66cf4c
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-e50a92407bdb8bb8d584ba7e2d2a00b7
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-f90579d2bb465f48633c90cc85fb4de8
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-9825741af945d3446fdc9a016107b662
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-2a646d9527efdc0f74701c1ae9fb963f
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-02527d98c93360056118c8141d392b6a
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-69dd4b213e1317fa8ca19ba1b4908906
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-b641698918f270e72bdf4d8b2c39213b
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-5ae105db61896415f9f4efc55d16063a
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-18d1bda186eb3b4b6cefd6bce61be263
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-89e0ab5dfaf66bb7dd2e56bded736ae6
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-62dcc55893dc20cf7dfef8c653f9c47b
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-1051021259eb53de2053b0cb8681605a
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-641b10b6d4bd41a625661883aa2c5e63
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-84f05f6daed21abed5dc273fc123b54b
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-808df7ff77ffb26033a9f59cd517f495
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-d39d34f9416aa9c743f15778445b35c4
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-76a5dfd0bf15861b6128e7efc2e734e9
FileName: ./.env.example
SPDXID: SPDXRef-1330e351b78126aebaaaa8c028a6bd71
FileChecksum: SHA1: ada775214c753084737b30ef80be486507923c3a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.gitattributes
SPDXID: SPDXRef-b64eb1c1ff5d615fa4b71edbe8e20c0c
FileChecksum: SHA1: be01d5b88e947024c348095f7d6398cbf663ceae
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.github/codeql/codeql-config.yml
SPDXID: SPDXRef-1e0e53f3a4cda9a5e63b935421a2660d
FileChecksum: SHA1: c0727b183e2d32db2a0bff0648aaa8f8844cfc3e
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.github/workflows/check-dist.yml
SPDXID: SPDXRef-0b2420fdd66596965bf84debe9c8e0e9
FileChecksum: SHA1: 4b1892e8a453af63d0a3d2ea35c77a22eaccab99
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.github/workflows/ci.yml
SPDXID: SPDXRef-796f380d9c91350db031938a4b4cd437
FileChecksum: SHA1: 6ce0f8c248c7bdd64aaa5a1c6f783aabf1505587
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.github/workflows/codeql.yml
SPDXID: SPDXRef-a702e225b6d73cda48195ba37116c1f6
FileChecksum: SHA1: 2918e38402e131cf4e83d97d3e168144506a31f4
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.github/workflows/release.yml
SPDXID: SPDXRef-7e01dbe24bef256885c4e75740e2143b
FileChecksum: SHA1: 9a64662fb8f593e50de601882c4386bd0121385a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.gitignore
SPDXID: SPDXRef-d45d9eb1a6cccbef71aad8f2328c497c
FileChecksum: SHA1: d3937b8b39fbb272542a198081477e49ca52e317
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.node-version
SPDXID: SPDXRef-ce8c62b8550b10c2031f1cd904ef4348
FileChecksum: SHA1: b9cea7ef7ff058452c8bb501657aa7c7c20c40b2
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./.yaml-lint.yml
SPDXID: SPDXRef-4d0c274515c41683a8b7c0a669c02c11
FileChecksum: SHA1: cd827ce9c71283a1ee12113357ee278d7ef28bbf
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./AGENTS.md
SPDXID: SPDXRef-0f9b80057ede865ceb6b73863de0aaa1
FileChecksum: SHA1: 381b5467884b3ec5e63feeb0b9b02c657d5284e6
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./README.md
SPDXID: SPDXRef-b1a9506bad4275dc0d4110b2e0548973
FileChecksum: SHA1: 93cc17c383ee5129fc546a80286461b9b51fe2a2
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: Copyright and
FileName: ./SECURITY.md
SPDXID: SPDXRef-b76ed24579509532b5f703bfc091f6d9
FileChecksum: SHA1: 63ccbdec9916bd24b20291cdcb042bad4c3ffbd7
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/android/README.md
SPDXID: SPDXRef-1385712e105271a03b927fa8963d4708
FileChecksum: SHA1: 1537bbcbde1bf6e81d7ff654a45b90b995bea29c
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/android/install-time-permanent-modules.aab
SPDXID: SPDXRef-154d551fbc375c842b68e0b2c6dc409e
FileChecksum: SHA1: 1b34f00ec78011c5fb32110da788dcdf76da5d0c
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: The Android Open Source Project
FileName: ./__fixtures__/android/pixel2_spec.json
SPDXID: SPDXRef-bada5d5d541b0161db52fa6c508ec884
FileChecksum: SHA1: ba999a07128cc7ca33b67e7cbf7ed1676b5d24a8
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: The Android Open Source Project
FileName: ./__fixtures__/build-apks.ts
SPDXID: SPDXRef-8924cc4781bc09e5a4d73e575c5b4b73
FileChecksum: SHA1: 8f4c0c06ce79f89348c9671d74d876451ae40ca2
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/core.ts
SPDXID: SPDXRef-37dd4126913f67a63b0d69683b78b9cd
FileChecksum: SHA1: 65e4a889547b87638215d34b72edc1ac2cf3e0c6
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/extract-apks.ts
SPDXID: SPDXRef-cc45bc3e84ffff91b52c85ed8edf18cb
FileChecksum: SHA1: 548f1eb134d679beaf6ca11212d0622f64119168
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/installer.ts
SPDXID: SPDXRef-bced871a599551c9032c84d00e3332c4
FileChecksum: SHA1: 30996c705f0ff4f809b884e7bae79b96030728e8
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/java.ts
SPDXID: SPDXRef-76df032e5fe3223cb4ae6ea8fc89bbe4
FileChecksum: SHA1: dc697f79331b492c4ace82ed0e606cf836476fd0
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/signing.ts
SPDXID: SPDXRef-42e1de2661683e985f6c3442a4c3a65c
FileChecksum: SHA1: 326100d57e68d473e214ba75bf7c1093ea0e2094
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__fixtures__/universal-apk.ts
SPDXID: SPDXRef-14395c47d2272b9abb9bd2baf0e460ea
FileChecksum: SHA1: bc19732ccad14e8bdb3dc9176d2fff147d4a5ca0
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/args.test.ts
SPDXID: SPDXRef-ec974ca993628cf5a135d08f503c290f
FileChecksum: SHA1: 47b943f8cddec93d64580830335ca31aa1fb040a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/build-apks.test.ts
SPDXID: SPDXRef-807a29049d7da634399b64f94127075d
FileChecksum: SHA1: 29d6947bd06c66f02cfabecb96ab0e8d03ea1561
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/cleanup.test.ts
SPDXID: SPDXRef-2f1590981a87ca492d09a1e59729fdbb
FileChecksum: SHA1: 1e628aef180ede5a490949c4686ba8269ec1c2c5
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/extract-apks.test.ts
SPDXID: SPDXRef-6d24bcb0216880a7e5fa1445b31318e8
FileChecksum: SHA1: 79799b31bbd933b61fd7e2f135fa38016b16de5f
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/inputs.test.ts
SPDXID: SPDXRef-ce0df55b19ef8cded1a7b1a94d284c4d
FileChecksum: SHA1: 9f68d101289ed9844d84d9941b070d8d1ac73429
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/installer.test.ts
SPDXID: SPDXRef-7c6f181dee750807551959a0c65bb2a0
FileChecksum: SHA1: 221f6415baa37c72038d01f8e4a356673e499019
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/java.test.ts
SPDXID: SPDXRef-6851e9e71bb93a7c2825f2146e8afdd4
FileChecksum: SHA1: 88a3c653d5bc520df8f990d24ee60fe0767bbcf5
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/logger.test.ts
SPDXID: SPDXRef-49ce20e991f71c92ee84728660a79b3a
FileChecksum: SHA1: 6aef400e58f4c0acbc736190fe95d4c77bb882d9
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/main.test.ts
SPDXID: SPDXRef-e703eb19f9a02f31d3f039864ec096b7
FileChecksum: SHA1: ed8ce48838e380e0009148a68625ebdf6b6b1b03
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/passwords.test.ts
SPDXID: SPDXRef-66a6fc078c8c740355932bb5c5cd0dfc
FileChecksum: SHA1: 20a7c4ed220192dbe07bbd29239755dc61a5e9b1
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/paths.test.ts
SPDXID: SPDXRef-9fd4ff3158e9af15c2214ad72c97a6f6
FileChecksum: SHA1: 696b54cf5de58ed44fefd1439577e8fc146c22bb
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/redact.test.ts
SPDXID: SPDXRef-e97dd1537547db6f86b5441e043738f9
FileChecksum: SHA1: af778bb1a943e284ed9769793ce8ffe224b50861
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/release-client.test.ts
SPDXID: SPDXRef-0aa661fb36377a38ce3c3f61c1264dc6
FileChecksum: SHA1: ba59f54a13de50a03c12d017ea24f160050f4bef
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/runner.test.ts
SPDXID: SPDXRef-4add95c20c276b6e8f4b47003104e0e2
FileChecksum: SHA1: 4807e76e7737bb32b8d7af8345d940a5d986134e
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/sha256.test.ts
SPDXID: SPDXRef-1dff51634be2ad0c1eba1bdae5613ca9
FileChecksum: SHA1: b5f438f461c9bc44909d39bb729b718ab899de00
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/signing.test.ts
SPDXID: SPDXRef-971e194092b5a697f855e6dadd32b1ff
FileChecksum: SHA1: 1838cecccc925bc1db3319b04ac1bd5c6db2ab4c
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/universal-apk-paths.test.ts
SPDXID: SPDXRef-3655e1caec108fa552d0b65052b51220
FileChecksum: SHA1: e37e429309a65269a96c67c8b120f801d943d56d
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/universal-apk.test.ts
SPDXID: SPDXRef-93f281e7a0802638959b6e251827db6f
FileChecksum: SHA1: e2e928c4516a7b0155dac9f4bc6f5012dfc87f24
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./__tests__/zip.test.ts
SPDXID: SPDXRef-929f6c429218543c679db04787ff6c45
FileChecksum: SHA1: 3c3b774476d1017ee477303a658321fbdb573b62
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./action.yml
SPDXID: SPDXRef-0bde88a48fe27d07460ea97c9ae1f22e
FileChecksum: SHA1: a03118cfd701d07fc6ad70c61cd848cede34d01c
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./actionlint.yml
SPDXID: SPDXRef-035bd2e2fd3d2ea1eee6bb34c90cac75
FileChecksum: SHA1: ffeda3a080f21cbc2fcecc07133fbbda2334f3f2
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./badges/coverage.svg
SPDXID: SPDXRef-7da954c1f84e13b3011bae211e1f159f
FileChecksum: SHA1: d0572e53067a91b34217e53fc4622577eda0e513
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./biome.json
SPDXID: SPDXRef-a627df9bc7cb119e55327a2218410384
FileChecksum: SHA1: 2237ce70b0e754251c441b66d020b9f82ec10d68
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./cliff.toml
SPDXID: SPDXRef-32025b529bbfb6ef1cb2edd84fe996cf
FileChecksum: SHA1: 663924bb7d805cf6df7bec18e1a246726d67cd98
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./dist/index.js
SPDXID: SPDXRef-51ab3fd561ebb124c08c41a989c64520
FileChecksum: SHA1: 26e42baeeb52430824927f6dce069479e2f8859a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: Copyright (C) 2011 Einar Otto Stangvik
Copyright (C) 2013 Arnout Kazemier and contributors
Copyright (C) 2016 Luigi Pinca and contributors
Copyright Joyent, Inc. and other Node contributors. All rights reserved.
FileName: ./dist/index.js.map
SPDXID: SPDXRef-3538735102bcfc73eb59ba1f129a5255
FileChecksum: SHA1: eceaba70c53b8fe47ac8122f19d4d471d5917e38
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: Copyright (C) 2011, 2013, 2016, 1009, 1009, 1009, 1009, 1008, 1002, 1007, 1000, 3000, 4999, 1000, 3000 Einar Otto Stangvik \n// Copyright (c) Arnout Kazemier and contributors\n// Copyright (c) Luigi Pinca and contributors\n\nclass ByteParser extends Writable {\n #buffers = []\n #fragmentsBytes = 0\n #byteOffset = 0\n #loop = false\n\n #state = parserStates.INFO\n\n #info = {}\n #fragments = []\n\n /** @type {Map} */\n #extensions\n\n /** @type {number} */\n #maxFragments\n\n /** @type {number} */\n #maxPayloadSize\n\n /**\n * @param {import('./websocket').WebSocket} ws\n * @param {Map|null} extensions\n * @param {{ maxFragments?: number, maxPayloadSize?: number }} [options]\n */\n constructor (ws, extensions, options = {}) {\n super()\n\n this.ws = ws\n this.#extensions = extensions == null ? new Map() : extensions\n this.#maxFragments = options.maxFragments ?? 0\n this.#maxPayloadSize = options.maxPayloadSize ?? 0\n\n if (this.#extensions.has('permessage-deflate')) {\n this.#extensions.set('permessage-deflate', new PerMessageDeflate(extensions, options))\n }\n }\n\n /**\n * @param {Buffer} chunk\n * @param {() => void} callback\n */\n _write (chunk, _, callback) {\n this.#buffers.push(chunk)\n this.#byteOffset += chunk.length\n this.#loop = true\n\n this.run(callback)\n }\n\n #validatePayloadLength () {\n if (\n this.#maxPayloadSize > 0 &&\n !isControlFrame(this.#info.opcode) &&\n this.#info.payloadLength + this.#fragmentsBytes > this.#maxPayloadSize\n ) {\n failWebsocketConnectionWithCode(this.ws, 'Payload size exceeds maximum allowed size')\n return false\n }\n\n return true\n }\n\n /**\n * Runs whenever a new chunk is received.\n * Callback is called whenever there are no more chunks buffering,\n * or not enough bytes are buffered to parse.\n */\n run (callback) {\n while (this.#loop) {\n if (this.#state === parserStates.INFO) {\n // If there aren't enough bytes to parse the payload length, etc.\n if (this.#byteOffset < 2) {\n return callback()\n }\n\n const buffer = this.consume(2)\n const fin = (buffer[0] & 0x80) !== 0\n const opcode = buffer[0] & 0x0F\n const masked = (buffer[1] & 0x80) === 0x80\n\n const fragmented = !fin && opcode !== opcodes.CONTINUATION\n const payloadLength = buffer[1] & 0x7F\n\n const rsv1 = buffer[0] & 0x40\n const rsv2 = buffer[0] & 0x20\n const rsv3 = buffer[0] & 0x10\n\n if (!isValidOpcode(opcode)) {\n failWebsocketConnection(this.ws, 'Invalid opcode received')\n return callback()\n }\n\n if (masked) {\n failWebsocketConnection(this.ws, 'Frame cannot be masked')\n return callback()\n }\n\n // MUST be 0 unless an extension is negotiated that defines meanings\n // for non-zero values. If a nonzero value is received and none of\n // the negotiated extensions defines the meaning of such a nonzero\n // value, the receiving endpoint MUST _Fail the WebSocket\n // Connection_.\n // This document allocates the RSV1 bit of the WebSocket header for\n // PMCEs and calls the bit the \"Per-Message Compressed\" bit. On a\n // WebSocket connection where a PMCE is in use, this bit indicates\n // whether a message is compressed or not.\n if (rsv1 !== 0 && !this.#extensions.has('permessage-deflate')) {\n failWebsocketConnection(this.ws, 'Expected RSV1 to be clear.')\n return\n }\n\n if (rsv2 !== 0 || rsv3 !== 0) {\n failWebsocketConnection(this.ws, 'RSV1, RSV2, RSV3 must be clear')\n return\n }\n\n if (fragmented && !isTextBinaryFrame(opcode)) {\n // Only text and binary frames can be fragmented\n failWebsocketConnection(this.ws, 'Invalid frame type was fragmented.')\n return\n }\n\n // If we are already parsing a text/binary frame and do not receive either\n // a continuation frame or close frame, fail the connection.\n if (isTextBinaryFrame(opcode) && this.#fragments.length > 0) {\n failWebsocketConnection(this.ws, 'Expected continuation frame')\n return\n }\n\n if (this.#info.fragmented && fragmented) {\n // A fragmented frame can't be fragmented itself\n failWebsocketConnection(this.ws, 'Fragmented frame exceeded 125 bytes.')\n return\n }\n\n // \"All control frames MUST have a payload length of 125 bytes or less\n // and MUST NOT be fragmented.\"\n if ((payloadLength > 125 || fragmented) && isControlFrame(opcode)) {\n failWebsocketConnection(this.ws, 'Control frame either too large or fragmented')\n return\n }\n\n if (isContinuationFrame(opcode) && this.#fragments.length === 0 && !this.#info.compressed) {\n failWebsocketConnection(this.ws, 'Unexpected continuation frame')\n return\n }\n\n if (payloadLength <= 125) {\n this.#info.payloadLength = payloadLength\n this.#state = parserStates.READ_DATA\n\n if (!this.#validatePayloadLength()) {\n return\n }\n } else if (payloadLength === 126) {\n this.#state = parserStates.PAYLOADLENGTH_16\n } else if (payloadLength === 127) {\n this.#state = parserStates.PAYLOADLENGTH_64\n }\n\n if (isTextBinaryFrame(opcode)) {\n this.#info.binaryType = opcode\n this.#info.compressed = rsv1 !== 0\n }\n\n this.#info.opcode = opcode\n this.#info.masked = masked\n this.#info.fin = fin\n this.#info.fragmented = fragmented\n } else if (this.#state === parserStates.PAYLOADLENGTH_16) {\n if (this.#byteOffset < 2) {\n return callback()\n }\n\n const buffer = this.consume(2)\n\n this.#info.payloadLength = buffer.readUInt16BE(0)\n this.#state = parserStates.READ_DATA\n\n if (!this.#validatePayloadLength()) {\n return\n }\n } else if (this.#state === parserStates.PAYLOADLENGTH_64) {\n if (this.#byteOffset < 8) {\n return callback()\n }\n\n const buffer = this.consume(8)\n const upper = buffer.readUInt32BE(0)\n const lower = buffer.readUInt32BE(4)\n\n // 2^31 is the maximum bytes an arraybuffer can contain\n // on 32-bit systems. Although, on 64-bit systems, this is\n // 2^53-1 bytes.\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length\n // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275\n // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e\n if (upper !== 0 || lower > 2 ** 31 - 1) {\n failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.')\n return\n }\n\n this.#info.payloadLength = lower\n this.#state = parserStates.READ_DATA\n\n if (!this.#validatePayloadLength()) {\n return\n }\n } else if (this.#state === parserStates.READ_DATA) {\n if (this.#byteOffset < this.#info.payloadLength) {\n return callback()\n }\n\n const body = this.consume(this.#info.payloadLength)\n\n if (isControlFrame(this.#info.opcode)) {\n this.#loop = this.parseControlFrame(body)\n this.#state = parserStates.INFO\n } else {\n if (!this.#info.compressed) {\n if (!this.writeFragments(body)) {\n return\n }\n\n if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {\n failWebsocketConnectionWithCode(this.ws, new MessageSizeExceededError().message)\n return\n }\n\n // If the frame is not fragmented, a message has been received.\n // If the frame is fragmented, it will terminate with a fin bit set\n // and an opcode of 0 (continuation), therefore we handle that when\n // parsing continuation frames, not here.\n if (!this.#info.fragmented && this.#info.fin) {\n websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())\n }\n\n this.#state = parserStates.INFO\n } else {\n this.#extensions.get('permessage-deflate').decompress(\n body,\n this.#info.fin,\n (error, data) => {\n if (error) {\n const code = error instanceof MessageSizeExceededError ? : 1007\n failWebsocketConnectionWithCode(this.ws, code, error.message)\n return\n }\n\n if (!this.writeFragments(data)) {\n return\n }\n\n if (this.#maxPayloadSize > 0 && this.#fragmentsBytes > this.#maxPayloadSize) {\n failWebsocketConnectionWithCode(this.ws, new MessageSizeExceededError().message)\n return\n }\n\n if (!this.#info.fin) {\n this.#state = parserStates.INFO\n this.#loop = true\n this.run(callback)\n return\n }\n\n websocketMessageReceived(this.ws, this.#info.binaryType, this.consumeFragments())\n\n this.#loop = true\n this.#state = parserStates.INFO\n this.run(callback)\n }\n )\n\n this.#loop = false\n break\n }\n }\n }\n }\n }\n\n /**\n * Take n bytes from the buffered Buffers\n * @param {number} n\n * @returns {Buffer}\n */\n consume (n) {\n if (n > this.#byteOffset) {\n throw new Error('Called consume() before buffers satiated.')\n } else if (n === 0) {\n return emptyBuffer\n }\n\n if (this.#buffers[0].length === n) {\n this.#byteOffset -= this.#buffers[0].length\n return this.#buffers.shift()\n }\n\n const buffer = Buffer.allocUnsafe(n)\n let offset = 0\n\n while (offset !== n) {\n const next = this.#buffers[0]\n const { length } = next\n\n if (length + offset === n) {\n buffer.set(this.#buffers.shift(), offset)\n break\n } else if (length + offset > n) {\n buffer.set(next.subarray(0, n - offset), offset)\n this.#buffers[0] = next.subarray(n - offset)\n break\n } else {\n buffer.set(this.#buffers.shift(), offset)\n offset += next.length\n }\n }\n\n this.#byteOffset -= n\n\n return buffer\n }\n\n writeFragments (fragment) {\n if (\n this.#maxFragments > 0 &&\n this.#fragments.length === this.#maxFragments\n ) {\n failWebsocketConnectionWithCode(this.ws, 'Too many message fragments')\n return false\n }\n\n this.#fragmentsBytes += fragment.length\n this.#fragments.push(fragment)\n return true\n }\n\n consumeFragments () {\n const fragments = this.#fragments\n\n if (fragments.length === 1) {\n this.#fragmentsBytes = 0\n return fragments.shift()\n }\n\n const output = Buffer.concat(fragments, this.#fragmentsBytes)\n this.#fragments = []\n this.#fragmentsBytes = 0\n\n return output\n }\n\n parseCloseBody (data) {\n assert(data.length !== 1)\n\n // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5\n /** @type {number|undefined} */\n let code\n\n if (data.length >= 2) {\n // _The WebSocket Connection Close Code_ is\n // defined as the status code (Section 7.4) contained in the first Close\n // control frame received by the application\n code = data.readUInt16BE(0)\n }\n\n if (code !== undefined && !isValidStatusCode(code)) {\n return { code:, reason: 'Invalid status code', error: true }\n }\n\n // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6\n /** @type {Buffer} */\n let reason = data.subarray(2)\n\n // Remove BOM\n if (reason[0] === 0xEF && reason[1] === 0xBB && reason[2] === 0xBF) {\n reason = reason.subarray(3)\n }\n\n try {\n reason = utf8Decode(reason)\n } catch {\n return { code:, reason: 'Invalid UTF-8', error: true }\n }\n\n return { code, reason, error: false }\n }\n\n /**\n * Parses control frames.\n * @param {Buffer} body\n */\n parseControlFrame (body) {\n const { opcode, payloadLength } = this.#info\n\n if (opcode === opcodes.CLOSE) {\n if (payloadLength === 1) {\n failWebsocketConnection(this.ws, 'Received close frame with a 1-byte body.')\n return false\n }\n\n this.#info.closeInfo = this.parseCloseBody(body)\n\n if (this.#info.closeInfo.error) {\n const { code, reason } = this.#info.closeInfo\n\n closeWebSocketConnection(this.ws, code, reason, reason.length)\n failWebsocketConnection(this.ws, reason)\n return false\n }\n\n if (this.ws[kSentClose] !== sentCloseFrameState.SENT) {\n // If an endpoint receives a Close frame and did not previously send a\n // Close frame, the endpoint MUST send a Close frame in response. (When\n // sending a Close frame in response, the endpoint typically echos the\n // status code it received.)\n let body = emptyBuffer\n if (this.#info.closeInfo.code) {\n body = Buffer.allocUnsafe(2)\n body.writeUInt16BE(this.#info.closeInfo.code, 0)\n }\n const closeFrame = new WebsocketFrameSend(body)\n\n this.ws[kResponse].socket.write(\n closeFrame.createFrame(opcodes.CLOSE),\n (err) => {\n if (!err) {\n this.ws[kSentClose] = sentCloseFrameState.SENT\n }\n }\n )\n }\n\n // Upon either sending or receiving a Close control frame, it is said\n // that _The WebSocket Closing Handshake is Started_ and that the\n // WebSocket connection is in the CLOSING state.\n this.ws[kReadyState] = states.CLOSING\n this.ws[kReceivedClose] = true\n\n return false\n } else if (opcode === opcodes.PING) {\n // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in\n // response, unless it already received a Close frame.\n // A Pong frame sent in response to a Ping frame must have identical\n // \"Application data\"\n\n if (!this.ws[kReceivedClose]) {\n const frame = new WebsocketFrameSend(body)\n\n this.ws[kResponse].socket.write(frame.createFrame(opcodes.PONG))\n\n if (channels.ping.hasSubscribers) {\n channels.ping.publish({\n payload: body\n })\n }\n }\n } else if (opcode === opcodes.PONG) {\n // A Pong frame MAY be sent unsolicited. This serves as a\n // unidirectional heartbeat. A response to an unsolicited Pong frame is\n // not expected.\n\n if (channels.pong.hasSubscribers) {\n channels.pong.publish({\n payload: body\n })\n }\n }\n\n return true\n }\n\n get closingInfo () {\n return this.#info.closeInfo\n }\n}\n\nmodule.exports = {\n ByteParser\n}\n","'use strict'\n\nconst { WebsocketFrameSend } = require('./frame')\nconst { opcodes, sendHints } = require('./constants')\nconst FixedQueue = require('../../dispatcher/fixed-queue')\n\n/** @type {typeof Uint8Array} */\nconst FastBuffer = Buffer[Symbol.species]\n\n/**\n * @typedef {object} SendQueueNode\n * @property {Promise | null} promise\n * @property {((...args: any[]) => any)} callback\n * @property {Buffer | null} frame\n */\n\nclass SendQueue {\n /**\n * @type {FixedQueue}\n */\n #queue = new FixedQueue()\n\n /**\n * @type {boolean}\n */\n #running = false\n\n /** @type {import('node:net').Socket} */\n #socket\n\n constructor (socket) {\n this.#socket = socket\n }\n\n add (item, cb, hint) {\n if (hint !== sendHints.blob) {\n const frame = createFrame(item, hint)\n if (!this.#running) {\n // fast-path\n this.#socket.write(frame, cb)\n } else {\n /** @type {SendQueueNode} */\n const node = {\n promise: null,\n callback: cb,\n frame\n }\n this.#queue.push(node)\n }\n return\n }\n\n /** @type {SendQueueNode} */\n const node = {\n promise: item.arrayBuffer().then((ab) => {\n node.promise = null\n node.frame = createFrame(ab, hint)\n }),\n callback: cb,\n frame: null\n }\n\n this.#queue.push(node)\n\n if (!this.#running) {\n this.#run()\n }\n }\n\n async #run () {\n this.#running = true\n const queue = this.#queue\n while (!queue.isEmpty()) {\n const node = queue.shift()\n // wait pending promise\n if (node.promise !== null) {\n await node.promise\n }\n // write\n this.#socket.write(node.frame, node.callback)\n // cleanup\n node.callback = node.frame = null\n }\n this.#running = false\n }\n}\n\nfunction createFrame (data, hint) {\n return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY)\n}\n\nfunction toBuffer (data, hint) {\n switch (hint) {\n case sendHints.string:\n return Buffer.from(data)\n case sendHints.arrayBuffer:\n case sendHints.blob:\n return new FastBuffer(data)\n case sendHints.typedArray:\n return new FastBuffer(data.buffer, data.byteOffset, data.byteLength)\n }\n}\n\nmodule.exports = { SendQueue }\n","'use strict'\n\nconst { webidl } = require('../fetch/webidl')\nconst { URLSerializer } = require('../fetch/data-url')\nconst { environmentSettingsObject } = require('../fetch/util')\nconst { staticPropertyDescriptors, states, sentCloseFrameState, sendHints } = require('./constants')\nconst {\n kWebSocketURL,\n kReadyState,\n kController,\n kBinaryType,\n kResponse,\n kSentClose,\n kByteParser\n} = require('./symbols')\nconst {\n isConnecting,\n isEstablished,\n isClosing,\n isValidSubprotocol,\n fireEvent\n} = require('./util')\nconst { establishWebSocketConnection, closeWebSocketConnection } = require('./connection')\nconst { ByteParser } = require('./receiver')\nconst { kEnumerableProperty, isBlobLike } = require('../../core/util')\nconst { getGlobalDispatcher } = require('../../global')\nconst { types } = require('node:util')\nconst { ErrorEvent, CloseEvent } = require('./events')\nconst { SendQueue } = require('./sender')\n\n// https://websockets.spec.whatwg.org/#interface-definition\nclass WebSocket extends EventTarget {\n #events = {\n open: null,\n error: null,\n close: null,\n message: null\n }\n\n #bufferedAmount = 0\n #protocol = ''\n #extensions = ''\n\n /** @type {SendQueue} */\n #sendQueue\n\n /**\n * @param {string} url\n * @param {string|string[]} protocols\n */\n constructor (url, protocols = []) {\n super()\n\n webidl.util.markAsUncloneable(this)\n\n const prefix = 'WebSocket constructor'\n webidl.argumentLengthCheck(arguments, 1, prefix)\n\n const options = webidl.converters['DOMString or sequence or WebSocketInit'](protocols, prefix, 'options')\n\n url = webidl.converters.USVString(url, prefix, 'url')\n protocols = options.protocols\n\n // 1. Let baseURL be this's relevant settings object's API base URL.\n const baseURL = environmentSettingsObject.settingsObject.baseUrl\n\n // 1. Let urlRecord be the result of applying the URL parser to url with baseURL.\n let urlRecord\n\n try {\n urlRecord = new URL(url, baseURL)\n } catch (e) {\n // 3. If urlRecord is failure, then throw a \"SyntaxError\" DOMException.\n throw new DOMException(e, 'SyntaxError')\n }\n\n // 4. If urlRecord’s scheme is \"http\", then set urlRecord’s scheme to \"ws\".\n if (urlRecord.protocol === 'http:') {\n urlRecord.protocol = 'ws:'\n } else if (urlRecord.protocol === 'https:') {\n // 5. Otherwise, if urlRecord’s scheme is \"https\", set urlRecord’s scheme to \"wss\".\n urlRecord.protocol = 'wss:'\n }\n\n // 6. If urlRecord’s scheme is not \"ws\" or \"wss\", then throw a \"SyntaxError\" DOMException.\n if (urlRecord.protocol !== 'ws:' && urlRecord.protocol !== 'wss:') {\n throw new DOMException(\n `Expected a ws: or wss: protocol, got ${urlRecord.protocol}`,\n 'SyntaxError'\n )\n }\n\n // 7. If urlRecord’s fragment is non-null, then throw a \"SyntaxError\"\n // DOMException.\n if (urlRecord.hash || urlRecord.href.endsWith('#')) {\n throw new DOMException('Got fragment', 'SyntaxError')\n }\n\n // 8. If protocols is a string, set protocols to a sequence consisting\n // of just that string.\n if (typeof protocols === 'string') {\n protocols = [protocols]\n }\n\n // 9. If any of the values in protocols occur more than once or otherwise\n // fail to match the requirements for elements that comprise the value\n // of `Sec-WebSocket-Protocol` fields as defined by The WebSocket\n // protocol, then throw a \"SyntaxError\" DOMException.\n if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) {\n throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')\n }\n\n if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) {\n throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError')\n }\n\n // 10. Set this's url to urlRecord.\n this[kWebSocketURL] = new URL(urlRecord.href)\n\n // 11. Let client be this's relevant settings object.\n const client = environmentSettingsObject.settingsObject\n\n // 12. Run this step in parallel:\n\n // 1. Establish a WebSocket connection given urlRecord, protocols,\n // and client.\n this[kController] = establishWebSocketConnection(\n urlRecord,\n protocols,\n client,\n this,\n (response, extensions) => this.#onConnectionEstablished(response, extensions),\n options\n )\n\n // Each WebSocket object has an associated ready state, which is a\n // number representing the state of the connection. Initially it must\n // be CONNECTING (0).\n this[kReadyState] = WebSocket.CONNECTING\n\n this[kSentClose] = sentCloseFrameState.NOT_SENT\n\n // The extensions attribute must initially return the empty string.\n\n // The protocol attribute must initially return the empty string.\n\n // Each WebSocket object has an associated binary type, which is a\n // BinaryType. Initially it must be \"blob\".\n this[kBinaryType] = 'blob'\n }\n\n /**\n * @see https://websockets.spec.whatwg.org/#dom-websocket-close\n * @param {number|undefined} code\n * @param {string|undefined} reason\n */\n close (code = undefined, reason = undefined) {\n webidl.brandCheck(this, WebSocket)\n\n const prefix = 'WebSocket.close'\n\n if (code !== undefined) {\n code = webidl.converters['unsigned short'](code, prefix, 'code', { clamp: true })\n }\n\n if (reason !== undefined) {\n reason = webidl.converters.USVString(reason, prefix, 'reason')\n }\n\n // 1. If code is present, but is neither an integer equal to nor an\n // integer in the range to, inclusive, throw an\n // \"InvalidAccessError\" DOMException.\n if (code !== undefined) {\n if (code !== && (code < || code > 4999)) {\n throw new DOMException('invalid code', 'InvalidAccessError')\n }\n }\n\n let reasonByteLength = 0\n\n // 2. If reason is present, then run these substeps:\n if (reason !== undefined) {\n // 1. Let reasonBytes be the result of encoding reason.\n // 2. If reasonBytes is longer than 123 bytes, then throw a\n // \"SyntaxError\" DOMException.\n reasonByteLength = Buffer.byteLength(reason)\n\n if (reasonByteLength > 123) {\n throw new DOMException(\n `Reason must be less than 123 bytes; received ${reasonByteLength}`,\n 'SyntaxError'\n )\n }\n }\n\n // 3. Run the first matching steps from the following list:\n closeWebSocketConnection(this, code, reason, reasonByteLength)\n }\n\n /**\n * @see https://websockets.spec.whatwg.org/#dom-websocket-send\n * @param {NodeJS.TypedArray|ArrayBuffer|Blob|string} data\n */\n send (data) {\n webidl.brandCheck(this, WebSocket)\n\n const prefix = 'WebSocket.send'\n webidl.argumentLengthCheck(arguments, 1, prefix)\n\n data = webidl.converters.WebSocketSendData(data, prefix, 'data')\n\n // 1. If this's ready state is CONNECTING, then throw an\n // \"InvalidStateError\" DOMException.\n if (isConnecting(this)) {\n throw new DOMException('Sent before connected.', 'InvalidStateError')\n }\n\n // 2. Run the appropriate set of steps from the following list:\n // https://datatracker.ietf.org/doc/html/rfc6455#section-6.1\n // https://datatracker.ietf.org/doc/html/rfc6455#section-5.2\n\n if (!isEstablished(this) || isClosing(this)) {\n return\n }\n\n // If data is a string\n if (typeof data === 'string') {\n // If the WebSocket connection is established and the WebSocket\n // closing handshake has not yet started, then the user agent\n // must send a WebSocket Message comprised of the data argument\n // using a text frame opcode; if the data cannot be sent, e.g.\n // because it would need to be buffered but the buffer is full,\n // the user agent must flag the WebSocket as full and then close\n // the WebSocket connection. Any invocation of this method with a\n // string argument that does not throw an exception must increase\n // the bufferedAmount attribute by the number of bytes needed to\n // express the argument as UTF-8.\n\n const length = Buffer.byteLength(data)\n\n this.#bufferedAmount += length\n this.#sendQueue.add(data, () => {\n this.#bufferedAmount -= length\n }, sendHints.string)\n } else if (types.isArrayBuffer(data)) {\n // If the WebSocket connection is established, and the WebSocket\n // closing handshake has not yet started, then the user agent must\n // send a WebSocket Message comprised of data using a binary frame\n // opcode; if the data cannot be sent, e.g. because it would need\n // to be buffered but the buffer is full, the user agent must flag\n // the WebSocket as full and then close the WebSocket connection.\n // The data to be sent is the data stored in the buffer described\n // by the ArrayBuffer object. Any invocation of this method with an\n // ArrayBuffer argument that does not throw an exception must\n // increase the bufferedAmount attribute by the length of the\n // ArrayBuffer in bytes.\n\n this.#bufferedAmount += data.byteLength\n this.#sendQueue.add(data, () => {\n this.#bufferedAmount -= data.byteLength\n }, sendHints.arrayBuffer)\n } else if (ArrayBuffer.isView(data)) {\n // If the WebSocket connection is established, and the WebSocket\n // closing handshake has not yet started, then the user agent must\n // send a WebSocket Message comprised of data using a binary frame\n // opcode; if the data cannot be sent, e.g. because it would need to\n // be buffered but the buffer is full, the user agent must flag the\n // WebSocket as full and then close the WebSocket connection. The\n // data to be sent is the data stored in the section of the buffer\n // described by the ArrayBuffer object that data references. Any\n // invocation of this method with this kind of argument that does\n // not throw an exception must increase the bufferedAmount attribute\n // by the length of data’s buffer in bytes.\n\n this.#bufferedAmount += data.byteLength\n this.#sendQueue.add(data, () => {\n this.#bufferedAmount -= data.byteLength\n }, sendHints.typedArray)\n } else if (isBlobLike(data)) {\n // If the WebSocket connection is established, and the WebSocket\n // closing handshake has not yet started, then the user agent must\n // send a WebSocket Message comprised of data using a binary frame\n // opcode; if the data cannot be sent, e.g. because it would need to\n // be buffered but the buffer is full, the user agent must flag the\n // WebSocket as full and then close the WebSocket connection. The data\n // to be sent is the raw data represented by the Blob object. Any\n // invocation of this method with a Blob argument that does not throw\n // an exception must increase the bufferedAmount attribute by the size\n // of the Blob object’s raw data, in bytes.\n\n this.#bufferedAmount += data.size\n this.#
Copyright Joyent, Inc. and other Node contributors. All rights reserved.\n //\n // Permission is hereby granted, free of charge, to any person obtaining a copy\n // of this software and associated documentation files (the \"Software\"), to\n // deal in the Software without restriction, including without limitation the\n // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n // sell copies of the Software, and to permit persons to whom the Software is\n // furnished to do so, subject to the following conditions:\n //\n // The above copyright notice and this permission notice shall be included in\n // all copies or substantial portions of the Software.\n //\n // THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n // IN THE SOFTWARE.\n if (!arg) {\n // Need double quotation for empty argument\n return '\"\"';\n }\n if (!arg.includes(' ') && !arg.includes('\\t') && !arg.includes('\"')) {\n // No quotation needed\n return arg;\n }\n if (!arg.includes('\"') && !arg.includes('\\\\')) {\n // No embedded double quotes or backslashes, so I can just wrap\n // quote marks around the whole thing.\n return `\"${arg}\"`;\n }\n // Expected input/output:\n // input : hello\"world\n // output: \"hello\\\"world\"\n // input : hello\"\"world\n // output: \"hello\\\"\\\"world\"\n // input : hello\\world\n // output: hello\\world\n // input : hello\\\\world\n // output: hello\\\\world\n // input : hello\\\"world\n // output: \"hello\\\\\\\"world\"\n // input : hello\\\\\"world\n // output: \"hello\\\\\\\\\\\"world\"\n // input : hello world\\\n // output: \"hello world\\\\\" - note the comment in libuv actually reads \"hello world\\\"\n // but it appears the comment is wrong, it should be \"hello world\\\\\"\n let reverse = '\"';\n let quoteHit = true;\n for (let i = arg.length; i > 0; i--) {\n // walk the string in reverse\n reverse += arg[i - 1];\n if (quoteHit && arg[i - 1] === '\\\\') {\n reverse += '\\\\';\n }\n else if (arg[i - 1] === '\"') {\n quoteHit = true;\n reverse += '\\\\';\n }\n else {\n quoteHit = false;\n }\n }\n reverse += '\"';\n return reverse.split('').reverse().join('');\n }\n _cloneExecOptions(options) {\n options = options || {};\n const result = {\n cwd: options.cwd || process.cwd(),\n env: options.env || process.env,\n silent: options.silent || false,\n windowsVerbatimArguments: options.windowsVerbatimArguments || false,\n failOnStdErr: options.failOnStdErr || false,\n ignoreReturnCode: options.ignoreReturnCode || false,\n delay: options.delay || 10000\n };\n result.outStream = options.outStream || process.stdout;\n result.errStream = options.errStream || process.stderr;\n return result;\n }\n _getSpawnOptions(options, toolPath) {\n options = options || {};\n const result = {};\n result.cwd = options.cwd;\n result.env = options.env;\n result['windowsVerbatimArguments'] =\n options.windowsVerbatimArguments || this._isCmdFile();\n if (options.windowsVerbatimArguments) {\n result.argv0 = `\"${toolPath}\"`;\n }\n return result;\n }\n /**\n * Exec a tool.\n * Output will be streamed to the live console.\n * Returns promise with return code\n *\n * @param tool path to tool to exec\n * @param options optional exec options. See ExecOptions\n * @returns number\n */\n exec() {\n return __awaiter(this, void 0, void 0, function* () {\n // root the tool path if it is unrooted and contains relative pathing\n if (!ioUtil.isRooted(this.toolPath) &&\n (this.toolPath.includes('/') ||\n (IS_WINDOWS && this.toolPath.includes('\\\\')))) {\n // prefer options.cwd if it is specified, however options.cwd may also need to be rooted\n this.toolPath = path.resolve(process.cwd(), this.options.cwd || process.cwd(), this.toolPath);\n }\n // if the tool is only a file name, then resolve it from the PATH\n // otherwise verify it exists (add extension on Windows if necessary)\n this.toolPath = yield io.which(this.toolPath, true);\n return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {\n this._debug(`exec tool: ${this.toolPath}`);\n this._debug('arguments:');\n for (const arg of this.args) {\n this._debug(` ${arg}`);\n }\n const optionsNonNull = this._cloneExecOptions(this.options);\n if (!optionsNonNull.silent && optionsNonNull.outStream) {\n optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);\n }\n const state = new ExecState(optionsNonNull, this.toolPath);\n state.on('debug', (message) => {\n this._debug(message);\n });\n if (this.options.cwd && !(yield ioUtil.exists(this.options.cwd))) {\n return reject(new Error(`The cwd: ${this.options.cwd} does not exist!`));\n }\n const fileName = this._getSpawnFileName();\n const cp = child.spawn(fileName, this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(this.options, fileName));\n let stdbuffer = '';\n if (cp.stdout) {\n cp.stdout.on('data', (data) => {\n if (this.options.listeners && this.options.listeners.stdout) {\n this.options.listeners.stdout(data);\n }\n if (!optionsNonNull.silent && optionsNonNull.outStream) {\n optionsNonNull.outStream.write(data);\n }\n stdbuffer = this._processLineBuffer(data, stdbuffer, (line) => {\n if (this.options.listeners && this.options.listeners.stdline) {\n this.options.listeners.stdline(line);\n }\n });\n });\n }\n let errbuffer = '';\n if (cp.stderr) {\n cp.stderr.on('data', (data) => {\n state.processStderr = true;\n if (this.options.listeners && this.options.listeners.stderr) {\n this.options.listeners.stderr(data);\n }\n if (!optionsNonNull.silent &&\n optionsNonNull.errStream &&\n optionsNonNull.outStream) {\n const s = optionsNonNull.failOnStdErr\n ? optionsNonNull.errStream\n : optionsNonNull.outStream;\n s.write(data);\n }\n errbuffer = this._processLineBuffer(data, errbuffer, (line) => {\n if (this.options.listeners && this.options.listeners.errline) {\n this.options.listeners.errline(line);\n }\n });\n });\n }\n cp.on('error', (err) => {\n state.processError = err.message;\n state.processExited = true;\n state.processClosed = true;\n state.CheckComplete();\n });\n cp.on('exit', (code) => {\n state.processExitCode = code;\n state.processExited = true;\n this._debug(`Exit code ${code} received from tool '${this.toolPath}'`);\n state.CheckComplete();\n });\n cp.on('close', (code) => {\n state.processExitCode = code;\n state.processExited = true;\n state.processClosed = true;\n this._debug(`STDIO streams have closed for tool '${this.toolPath}'`);\n state.CheckComplete();\n });\n state.on('done', (error, exitCode) => {\n if (stdbuffer.length > 0) {\n this.emit('stdline', stdbuffer);\n }\n if (errbuffer.length > 0) {\n this.emit('errline', errbuffer);\n }\n cp.removeAllListeners();\n if (error) {\n reject(error);\n }\n else {\n resolve(exitCode);\n }\n });\n if (this.options.input) {\n if (!cp.stdin) {\n throw new Error('child process missing stdin');\n }\n cp.stdin.end(this.options.input);\n }\n }));\n });\n }\n}\n/**\n * Convert an arg string to an array of args. Handles escaping\n *\n * @param argString string of arguments\n * @returns string[] array of arguments\n */\nexport function argStringToArray(argString) {\n const args = [];\n let inQuotes = false;\n let escaped = false;\n let arg = '';\n function append(c) {\n // we only escape double quotes.\n if (escaped && c !== '\"') {\n arg += '\\\\';\n }\n arg += c;\n escaped = false;\n }\n for (let i = 0; i < argString.length; i++) {\n const c = argString.charAt(i);\n if (c === '\"') {\n if (!escaped) {\n inQuotes = !inQuotes;\n }\n else {\n append(c);\n }\n continue;\n }\n if (c === '\\\\' && escaped) {\n append(c);\n continue;\n }\n if (c === '\\\\' && inQuotes) {\n escaped = true;\n continue;\n }\n if (c === ' ' && !inQuotes) {\n if (arg.length > 0) {\n args.push(arg);\n arg = '';\n }\n continue;\n }\n append(c);\n }\n if (arg.length > 0) {\n args.push(arg.trim());\n }\n return args;\n}\nclass ExecState extends events.EventEmitter {\n constructor(options, toolPath) {\n super();\n this.processClosed = false; // tracks whether the process has exited and stdio is closed\n this.processError = '';\n this.processExitCode = 0;\n this.processExited = false; // tracks whether the process has exited\n this.processStderr = false; // tracks whether stderr was written to\n this.delay = 10000; // 10 seconds\n this.done = false;\n this.timeout = null;\n if (!toolPath) {\n throw new Error('toolPath must not be empty');\n }\n this.options = options;\n this.toolPath = toolPath;\n if (options.delay) {\n this.delay = options.delay;\n }\n }\n CheckComplete() {\n if (this.done) {\n return;\n }\n if (this.processClosed) {\n this._setResult();\n }\n else if (this.processExited) {\n this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this);\n }\n }\n _debug(message) {\n this.emit('debug', message);\n }\n _setResult() {\n // determine whether there is an error\n let error;\n if (this.processExited) {\n if (this.processError) {\n error = new Error(`There was an error when attempting to execute the process '${this.toolPath}'. This may indicate the process failed to start. Error: ${this.processError}`);\n }\n else if (this.processExitCode !== 0 && !this.options.ignoreReturnCode) {\n error = new Error(`The process '${this.toolPath}' failed with exit code ${this.processExitCode}`);\n }\n else if (this.processStderr && this.options.failOnStdErr) {\n error = new Error(`The process '${this.toolPath}' failed because one or more lines were written to the STDERR stream`);\n }\n }\n // clear the timeout\n if (this.timeout) {\n clearTimeout(this.timeout);\n this.timeout = null;\n }\n this.done = true;\n this.emit('done', error, this.processExitCode);\n }\n static HandleTimeout(state) {\n if (state.done) {\n return;\n }\n if (!state.processClosed && state.processExited) {\n const message = `The STDIO streams did not close within ${state.delay / 1000} seconds of the exit event from process '${state.toolPath}'. This may indicate a child process inherited the STDIO streams and has not yet exited.`;\n state._debug(message);\n }\n state._setResult();\n }\n}\n//# sourceMappingURL=toolrunner.js.map","var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e);
FileName: ./package.json
SPDXID: SPDXRef-e43673088fe4ef695726c1bb586c34f0
FileChecksum: SHA1: 05f0b9ac6b17e08397ac54db08e6e4e441d2ca83
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./pnpm-lock.yaml
SPDXID: SPDXRef-2b0935a9f42457a75a1873f5f0c910ae
FileChecksum: SHA1: f4ab611b0a9bf163926c0a7bc127d67872baae7a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./pnpm-workspace.yaml
SPDXID: SPDXRef-3f4be2c92720e01b6817cdb3cb6521d5
FileChecksum: SHA1: cad956d87ab8267dafc2c73a87ad13d66a3bfdae
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./renovate.json
SPDXID: SPDXRef-409d8af50c602975227d724f57ad69d3
FileChecksum: SHA1: e14a9b6f3ff6b4116e756a961e73dc90394bef0a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./rollup.config.ts
SPDXID: SPDXRef-7eb1ac325c38e20f20ca5ac99a7f1a01
FileChecksum: SHA1: 80997e01e159f0d751f02549a4f199b34182b042
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./script/release
SPDXID: SPDXRef-329ce8202f06b53c345497cb80fc1636
FileChecksum: SHA1: f7e736cfe557a784b528f769894c0e2ef07d3df3
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/artifacts/paths.ts
SPDXID: SPDXRef-dbfe5d93a40fc13ba2daf8a8f0c28f56
FileChecksum: SHA1: d2711ec6864cbd4d6064bca0ca6c5d9daaa47082
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/artifacts/universal-apk.ts
SPDXID: SPDXRef-83e556734de9a8666875ad5d4fb3b62a
FileChecksum: SHA1: 4eb28b1afc109f9ad6822182c4c1cb280779e0e6
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/artifacts/zip.ts
SPDXID: SPDXRef-2ef4b12593aabd3df85cd4cd778b75b3
FileChecksum: SHA1: b05194fb8cf55e992426f05648cb4136631b6c69
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/args.ts
SPDXID: SPDXRef-991eeb069b8ceb6e899ed317aca06e5f
FileChecksum: SHA1: e2ebe3493d2a720929ae3bb425c481532bd1c0df
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/commands/build-apks.ts
SPDXID: SPDXRef-3aaf51398847f731a8dc029d3e32e25d
FileChecksum: SHA1: b27f40613a3dcdbe88fdc283e0d032977569718f
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/commands/extract-apks.ts
SPDXID: SPDXRef-c57d50374edfa1066338ea5e2f477fca
FileChecksum: SHA1: de6f5fbaa61cf1d1a37fb0fbfb48c3df39e379ed
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/installer.ts
SPDXID: SPDXRef-4086d1e5a09f11ec8ec2b0b8fccb613b
FileChecksum: SHA1: 3a98f964fc18965d235d13ac14678d5c55fa5993
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/java.ts
SPDXID: SPDXRef-dc18b8d4e529327fa5e74f9115644f2a
FileChecksum: SHA1: 3e7743d232cd45e05e10b9abeefc51f799a68bfe
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/paths.ts
SPDXID: SPDXRef-6521134c5145a3917a7c08118d66cf4c
FileChecksum: SHA1: f642d0c3df68a6bec4dad6c9404cf15dec70d766
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/release-client.ts
SPDXID: SPDXRef-e50a92407bdb8bb8d584ba7e2d2a00b7
FileChecksum: SHA1: 5fad9ac76e00133c0f064848bf448f1ec03d3f50
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/runner.ts
SPDXID: SPDXRef-f90579d2bb465f48633c90cc85fb4de8
FileChecksum: SHA1: ace2ed910405546849909f30505f022f36ff7360
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/bundletool/sha256.ts
SPDXID: SPDXRef-9825741af945d3446fdc9a016107b662
FileChecksum: SHA1: e4443021b6e3952830209a8b83502ea60949409e
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/cleanup.ts
SPDXID: SPDXRef-2a646d9527efdc0f74701c1ae9fb963f
FileChecksum: SHA1: 7b5a6bd248eecd30b1c3e958e05388a2d7bb3bee
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/config/constants.ts
SPDXID: SPDXRef-02527d98c93360056118c8141d392b6a
FileChecksum: SHA1: e2cecaa9d05f2c178cd464e0c714e77fd03a2f0e
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/config/inputs.ts
SPDXID: SPDXRef-69dd4b213e1317fa8ca19ba1b4908906
FileChecksum: SHA1: 99816977d71292696cd878a454d4b52cbc2127bf
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/config/redact.ts
SPDXID: SPDXRef-b641698918f270e72bdf4d8b2c39213b
FileChecksum: SHA1: 4f8e4cd3ca6db6de93d399e46adc68cc63876bb0
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/config/types.ts
SPDXID: SPDXRef-5ae105db61896415f9f4efc55d16063a
FileChecksum: SHA1: 883eea3dfc7beae626479e394e93e3604da6f55c
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/config/validate.ts
SPDXID: SPDXRef-18d1bda186eb3b4b6cefd6bce61be263
FileChecksum: SHA1: 387a9f9cb791a0ee176415510b4a2bb948c53d9c
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/errors.ts
SPDXID: SPDXRef-89e0ab5dfaf66bb7dd2e56bded736ae6
FileChecksum: SHA1: 846434f324e819cd8e4a8e747374d6e98824752a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/index.ts
SPDXID: SPDXRef-62dcc55893dc20cf7dfef8c653f9c47b
FileChecksum: SHA1: f264866fcc135cbab5f6d89fa7a2292eb8c31a3f
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/logging/logger.ts
SPDXID: SPDXRef-1051021259eb53de2053b0cb8681605a
FileChecksum: SHA1: 68018d925880ef0efeeb48495c052bddd35d1f6d
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/main.ts
SPDXID: SPDXRef-641b10b6d4bd41a625661883aa2c5e63
FileChecksum: SHA1: 91cbfa6286c55230317427e41052016192e25b3e
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/signing/keystore.ts
SPDXID: SPDXRef-84f05f6daed21abed5dc273fc123b54b
FileChecksum: SHA1: ec7ce5d8ca7da8a7f3004d00c9a672d94a08c208
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./src/signing/passwords.ts
SPDXID: SPDXRef-808df7ff77ffb26033a9f59cd517f495
FileChecksum: SHA1: 408ac984a30e9766de44af6f5abaa2fdf84961f2
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./tsconfig.json
SPDXID: SPDXRef-d39d34f9416aa9c743f15778445b35c4
FileChecksum: SHA1: cdee700ae8f924c5980688e3d5a3b95c3f41f70a
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl
FileName: ./vitest.config.ts
SPDXID: SPDXRef-76a5dfd0bf15861b6128e7efc2e734e9
FileChecksum: SHA1: ddf62a1536ff60229c810f22e14cb77a75dc1d00
LicenseConcluded: NOASSERTION
LicenseInfoInFile: Apache-2.0
FileCopyrightText: SPDX-FileCopyrightText: 2026 Robert Eggl