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