diff --git a/.github/workflows/scrape.yml b/.github/workflows/scrape.yml index cd31b4a1..aaef86a6 100644 --- a/.github/workflows/scrape.yml +++ b/.github/workflows/scrape.yml @@ -15,6 +15,10 @@ on: default: "auto_moneyman" required: false description: "The name of the worksheet to write to" + parallelScrapes: + default: "1" + required: false + description: "Number of parallel scrapes to run" schedule: - cron: "33 10 * * *" env: @@ -57,6 +61,7 @@ jobs: -e BUXFER_ACCOUNTS -e TRANSACTION_HASH_TYPE -e WEB_POST_URL + -e MAX_PARALLEL_SCRAPERS ${{ env.REGISTRY }}/${{ steps.normalize-repository-name.outputs.repository }}:latest env: DEBUG: "" @@ -85,3 +90,4 @@ jobs: BUXFER_ACCOUNTS: ${{ secrets.BUXFER_ACCOUNTS }} TRANSACTION_HASH_TYPE: ${{ secrets.TRANSACTION_HASH_TYPE }} WEB_POST_URL: ${{ secrets.WEB_POST_URL }} + MAX_PARALLEL_SCRAPERS: ${{ github.event.inputs.parallelScrapes }} diff --git a/README.md b/README.md index fbf494a8..4d442306 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ Example: | `TRANSACTION_HASH_TYPE` | `` | The hash type to use for the transaction hash. Can be `moneyman` or empty. The default will be changed to `moneyman` in the upcoming versions | | `HIDDEN_DEPRECATIONS` | '' | A comma separated list of deprecations to hide | | `PUPPETEER_EXECUTABLE_PATH` | `undefined` | An ExecutablePath for the scraper. if undefined defaults to system. | +| `MAX_PARALLEL_SCRAPERS` | `1` | The maximum number of parallel scrapers to run | ### Get notified in telegram diff --git a/package-lock.json b/package-lock.json index 644c3e8d..d7b7b991 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "ISC", "dependencies": { + "async": "^3.2.6", "azure-kusto-data": "^6.0.2", "azure-kusto-ingest": "^6.0.2", "buxfer-ts-client": "^1.1.0", @@ -20,11 +21,12 @@ "google-auth-library": "^9.14.1", "google-spreadsheet": "^4.1.4", "hash-it": "^6.0.0", - "israeli-bank-scrapers": "^5.1.4", + "israeli-bank-scrapers": "^5.2.0", "telegraf": "^4.16.3", "ynab": "^2.5.0" }, "devDependencies": { + "@types/async": "^3.2.24", "@types/debug": "^4.1.12", "@types/jest": "^29.5.13", "husky": "^9.1.6", @@ -1580,6 +1582,12 @@ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" }, + "node_modules/@types/async": { + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.24.tgz", + "integrity": "sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", @@ -1876,10 +1884,9 @@ } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "node_modules/asynckit": { "version": "0.4.0", @@ -1953,9 +1960,9 @@ } }, "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" }, "node_modules/babel-jest": { "version": "29.7.0", @@ -2070,15 +2077,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", "optional": true }, "node_modules/bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "optional": true, "dependencies": { "bare-events": "^2.0.0", @@ -2087,9 +2094,9 @@ } }, "node_modules/bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "optional": true }, "node_modules/bare-path": { @@ -2102,12 +2109,13 @@ } }, "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.0.tgz", + "integrity": "sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==", "optional": true, "dependencies": { - "streamx": "^2.18.0" + "b4a": "^1.6.6", + "streamx": "^2.20.0" } }, "node_modules/base64-js": { @@ -2469,9 +2477,9 @@ "dev": true }, "node_modules/core-js": { - "version": "3.32.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", - "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -3665,9 +3673,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/israeli-bank-scrapers": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/israeli-bank-scrapers/-/israeli-bank-scrapers-5.1.4.tgz", - "integrity": "sha512-0ChR13o/VQNWSY8oGm3hVIsjS1LrwdS820wlG6hdLly9gJe+DEt2Do1sTQ4IRoie7pwXGFVdmNT4KOcdI2R4gA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/israeli-bank-scrapers/-/israeli-bank-scrapers-5.2.0.tgz", + "integrity": "sha512-ombgTVVy1RramVP7H4vRAVZaHFru6mWkYcaCoWr8VYuBLY/bVvm1s2Pb88j79SxDc0+VEoKOf08TpN6gUvDcYg==", "dependencies": { "build-url": "^2.0.0", "core-js": "^3.1.4", @@ -4873,17 +4881,17 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "node_modules/moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "engines": { "node": "*" } }, "node_modules/moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", "dependencies": { "moment": "^2.29.4" }, @@ -5514,9 +5522,9 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -5944,9 +5952,9 @@ } }, "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", "dependencies": { "fast-fifo": "^1.3.2", "queue-tick": "^1.0.1", @@ -6163,9 +6171,9 @@ } }, "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", + "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", "dependencies": { "b4a": "^1.6.4" } @@ -7895,6 +7903,12 @@ "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" }, + "@types/async": { + "version": "3.2.24", + "resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.24.tgz", + "integrity": "sha512-8iHVLHsCCOBKjCF2KwFe0p9Z3rfM9mL+sSP8btyR5vTjJRAqpBYD28/ZLgXPf0pjG1VxOvtCV/BgXkQbpSe8Hw==", + "dev": true + }, "@types/babel__core": { "version": "7.20.2", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", @@ -8164,10 +8178,9 @@ } }, "async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==" }, "asynckit": { "version": "0.4.0", @@ -8232,9 +8245,9 @@ } }, "b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==" + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" }, "babel-jest": { "version": "29.7.0", @@ -8327,15 +8340,15 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.5.0.tgz", + "integrity": "sha512-/E8dDe9dsbLyh2qrZ64PEPadOQ0F4gbl1sUJOrmph7xOiIxfY8vwab/4bFLh4Y88/Hk/ujKcrQKc+ps0mv873A==", "optional": true }, "bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "optional": true, "requires": { "bare-events": "^2.0.0", @@ -8344,9 +8357,9 @@ } }, "bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "optional": true }, "bare-path": { @@ -8359,12 +8372,13 @@ } }, "bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.0.tgz", + "integrity": "sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==", "optional": true, "requires": { - "streamx": "^2.18.0" + "b4a": "^1.6.6", + "streamx": "^2.20.0" } }, "base64-js": { @@ -8616,9 +8630,9 @@ "dev": true }, "core-js": { - "version": "3.32.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz", - "integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==" + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==" }, "core-util-is": { "version": "1.0.3", @@ -9447,9 +9461,9 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "israeli-bank-scrapers": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/israeli-bank-scrapers/-/israeli-bank-scrapers-5.1.4.tgz", - "integrity": "sha512-0ChR13o/VQNWSY8oGm3hVIsjS1LrwdS820wlG6hdLly9gJe+DEt2Do1sTQ4IRoie7pwXGFVdmNT4KOcdI2R4gA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/israeli-bank-scrapers/-/israeli-bank-scrapers-5.2.0.tgz", + "integrity": "sha512-ombgTVVy1RramVP7H4vRAVZaHFru6mWkYcaCoWr8VYuBLY/bVvm1s2Pb88j79SxDc0+VEoKOf08TpN6gUvDcYg==", "requires": { "build-url": "^2.0.0", "core-js": "^3.1.4", @@ -10374,14 +10388,14 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" }, "moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", "requires": { "moment": "^2.29.4" } @@ -10820,9 +10834,9 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -11148,9 +11162,9 @@ } }, "streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.1.tgz", + "integrity": "sha512-uTa0mU6WUC65iUvzKH4X9hEdvSW7rbPxPtwfWiLMSj3qTdQbAiUboZTxauKfpFuGIGa1C2BYijZ7wgdUXICJhA==", "requires": { "bare-events": "^2.2.0", "fast-fifo": "^1.3.2", @@ -11311,9 +11325,9 @@ } }, "text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.0.tgz", + "integrity": "sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==", "requires": { "b4a": "^1.6.4" } diff --git a/package.json b/package.json index 559ef734..7debd9b3 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ }, "homepage": "https://github.com/daniel-hauser/moneyman#readme", "dependencies": { + "async": "^3.2.6", "azure-kusto-data": "^6.0.2", "azure-kusto-ingest": "^6.0.2", "buxfer-ts-client": "^1.1.0", @@ -38,11 +39,12 @@ "google-auth-library": "^9.14.1", "google-spreadsheet": "^4.1.4", "hash-it": "^6.0.0", - "israeli-bank-scrapers": "^5.1.4", + "israeli-bank-scrapers": "^5.2.0", "telegraf": "^4.16.3", "ynab": "^2.5.0" }, "devDependencies": { + "@types/async": "^3.2.24", "@types/debug": "^4.1.12", "@types/jest": "^29.5.13", "husky": "^9.1.6", diff --git a/patches/israeli-bank-scrapers+5.2.0.patch b/patches/israeli-bank-scrapers+5.2.0.patch new file mode 100644 index 00000000..196fbf32 --- /dev/null +++ b/patches/israeli-bank-scrapers+5.2.0.patch @@ -0,0 +1,274 @@ +diff --git a/node_modules/israeli-bank-scrapers/lib/scrapers/base-scraper-with-browser.js b/node_modules/israeli-bank-scrapers/lib/scrapers/base-scraper-with-browser.js +index 1287e84..9f99cf9 100644 +--- a/node_modules/israeli-bank-scrapers/lib/scrapers/base-scraper-with-browser.js ++++ b/node_modules/israeli-bank-scrapers/lib/scrapers/base-scraper-with-browser.js +@@ -76,9 +76,7 @@ function createGeneralError() { + class BaseScraperWithBrowser extends _baseScraper.BaseScraper { + constructor(...args) { + super(...args); +- // NOTICE - it is discouraged to use bang (!) in general. It is used here because +- // all the classes that inherit from this base assume is it mandatory. +- _defineProperty(this, "browser", void 0); ++ _defineProperty(this, "cleanups", []); + // NOTICE - it is discouraged to use bang (!) in general. It is used here because + // all the classes that inherit from this base assume is it mandatory. + _defineProperty(this, "page", void 0); +@@ -89,51 +87,68 @@ class BaseScraperWithBrowser extends _baseScraper.BaseScraper { + height: VIEWPORT_HEIGHT + }; + } +- async initialize() { +- await super.initialize(); +- debug('initialize scraper'); +- this.emitProgress(_definitions.ScraperProgressTypes.Initializing); +- let env; +- if (this.options.verbose) { +- env = _objectSpread({ +- DEBUG: '*' +- }, process.env); ++ async initializePage() { ++ debug('initialize browser page'); ++ if ('browserContext' in this.options) { ++ debug('Using the browser context provided in options'); ++ return this.options.browserContext.newPage(); + } +- if (typeof this.options.browser !== 'undefined' && this.options.browser !== null) { +- debug('use custom browser instance provided in options'); +- this.browser = this.options.browser; +- } else { +- const executablePath = this.options.executablePath || undefined; +- const args = this.options.args || []; ++ if ('browser' in this.options) { ++ debug('Using the browser instance provided in options'); + const { +- timeout ++ browser + } = this.options; +- const headless = !this.options.showBrowser; +- debug(`launch a browser with headless mode = ${headless}`); +- this.browser = await _puppeteer.default.launch({ +- env, +- headless, +- executablePath, +- args, +- timeout +- }); ++ ++ /** ++ * For backward compatibility, we will close the browser even if we didn't create it ++ */ ++ if (!this.options.skipCloseBrowser) { ++ this.cleanups.push(async () => { ++ debug('closing the browser'); ++ await browser.close(); ++ }); ++ } ++ return browser.newPage(); + } ++ const { ++ timeout, ++ args, ++ executablePath, ++ showBrowser ++ } = this.options; ++ const headless = !showBrowser; ++ debug(`launch a browser with headless mode = ${headless}`); ++ const browser = await _puppeteer.default.launch({ ++ env: this.options.verbose ? _objectSpread({ ++ DEBUG: '*' ++ }, process.env) : undefined, ++ headless, ++ executablePath, ++ args, ++ timeout ++ }); ++ this.cleanups.push(async () => { ++ debug('closing the browser'); ++ await browser.close(); ++ }); + if (this.options.prepareBrowser) { + debug("execute 'prepareBrowser' interceptor provided in options"); +- await this.options.prepareBrowser(this.browser); ++ await this.options.prepareBrowser(browser); + } +- if (!this.browser) { +- debug('failed to initiate a browser, exit'); ++ debug('create a new browser page'); ++ return browser.newPage(); ++ } ++ async initialize() { ++ await super.initialize(); ++ debug('initialize scraper'); ++ this.emitProgress(_definitions.ScraperProgressTypes.Initializing); ++ const page = await this.initializePage(); ++ if (!page) { ++ debug('failed to initiate a browser page, exit'); + return; + } +- const pages = await this.browser.pages(); +- if (pages.length) { +- debug('browser has already pages open, use the first one'); +- [this.page] = pages; +- } else { +- debug('create a new browser page'); +- this.page = await this.browser.newPage(); +- } ++ this.page = page; ++ this.cleanups.push(() => page.close()); + if (this.options.defaultTimeout) { + this.page.setDefaultTimeout(this.options.defaultTimeout); + } +@@ -241,10 +256,8 @@ class BaseScraperWithBrowser extends _baseScraper.BaseScraper { + fullPage: true + }); + } +- if (!this.browser) { +- return; +- } +- await this.browser.close(); ++ await Promise.all(this.cleanups.reverse().map(cleanup => cleanup())); ++ this.cleanups = []; + } + handleLoginResult(loginResult) { + switch (loginResult) { +@@ -273,4 +286,4 @@ class BaseScraperWithBrowser extends _baseScraper.BaseScraper { + } + } + exports.BaseScraperWithBrowser = BaseScraperWithBrowser; +-//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcHVwcGV0ZWVyIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfZGVmaW5pdGlvbnMiLCJfZGVidWciLCJfZWxlbWVudHNJbnRlcmFjdGlvbnMiLCJfbmF2aWdhdGlvbiIsIl9iYXNlU2NyYXBlciIsIl9lcnJvcnMiLCJlIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJvd25LZXlzIiwiciIsInQiLCJPYmplY3QiLCJrZXlzIiwiZ2V0T3duUHJvcGVydHlTeW1ib2xzIiwibyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZGVmaW5lUHJvcGVydHkiLCJfdG9Qcm9wZXJ0eUtleSIsInZhbHVlIiwiY29uZmlndXJhYmxlIiwid3JpdGFibGUiLCJpIiwiX3RvUHJpbWl0aXZlIiwiU3ltYm9sIiwidG9QcmltaXRpdmUiLCJjYWxsIiwiVHlwZUVycm9yIiwiU3RyaW5nIiwiTnVtYmVyIiwiX29iamVjdFdpdGhvdXRQcm9wZXJ0aWVzIiwiX29iamVjdFdpdGhvdXRQcm9wZXJ0aWVzTG9vc2UiLCJuIiwiaW5kZXhPZiIsInByb3BlcnR5SXNFbnVtZXJhYmxlIiwiaGFzT3duUHJvcGVydHkiLCJWSUVXUE9SVF9XSURUSCIsIlZJRVdQT1JUX0hFSUdIVCIsIk9LX1NUQVRVUyIsImRlYnVnIiwiZ2V0RGVidWciLCJMb2dpbkJhc2VSZXN1bHRzIiwiVGltZW91dCIsIkdlbmVyaWMiLCJHZW5lcmFsIiwiU2NyYXBlckVycm9yVHlwZXMiLCJyZXN0IiwiTG9naW5SZXN1bHRzIiwiZXhwb3J0cyIsImdldEtleUJ5VmFsdWUiLCJvYmplY3QiLCJwYWdlIiwia2V5IiwiY29uZGl0aW9ucyIsImNvbmRpdGlvbiIsInJlc3VsdCIsIlJlZ0V4cCIsInRlc3QiLCJ0b0xvd2VyQ2FzZSIsIlByb21pc2UiLCJyZXNvbHZlIiwiVW5rbm93bkVycm9yIiwiY3JlYXRlR2VuZXJhbEVycm9yIiwic3VjY2VzcyIsImVycm9yVHlwZSIsIkJhc2VTY3JhcGVyV2l0aEJyb3dzZXIiLCJCYXNlU2NyYXBlciIsImNvbnN0cnVjdG9yIiwiYXJncyIsImdldFZpZXdQb3J0Iiwid2lkdGgiLCJoZWlnaHQiLCJpbml0aWFsaXplIiwiZW1pdFByb2dyZXNzIiwiU2NyYXBlclByb2dyZXNzVHlwZXMiLCJJbml0aWFsaXppbmciLCJlbnYiLCJvcHRpb25zIiwidmVyYm9zZSIsIkRFQlVHIiwicHJvY2VzcyIsImJyb3dzZXIiLCJleGVjdXRhYmxlUGF0aCIsInVuZGVmaW5lZCIsInRpbWVvdXQiLCJoZWFkbGVzcyIsInNob3dCcm93c2VyIiwicHVwcGV0ZWVyIiwibGF1bmNoIiwicHJlcGFyZUJyb3dzZXIiLCJwYWdlcyIsIm5ld1BhZ2UiLCJkZWZhdWx0VGltZW91dCIsInNldERlZmF1bHRUaW1lb3V0IiwicHJlcGFyZVBhZ2UiLCJ2aWV3cG9ydCIsInNldFZpZXdwb3J0Iiwib24iLCJyZXF1ZXN0IiwiX3JlcXVlc3QkZmFpbHVyZSIsImZhaWx1cmUiLCJlcnJvclRleHQiLCJ1cmwiLCJuYXZpZ2F0ZVRvIiwid2FpdFVudGlsIiwicGFnZVRvVXNlIiwicmVzcG9uc2UiLCJnb3RvIiwic3RhdHVzIiwiRXJyb3IiLCJnZXRMb2dpbk9wdGlvbnMiLCJfY3JlZGVudGlhbHMiLCJjb21wYW55SWQiLCJmaWxsSW5wdXRzIiwicGFnZU9yRnJhbWUiLCJmaWVsZHMiLCJtb2RpZmllZCIsImlucHV0Iiwic2hpZnQiLCJmaWxsSW5wdXQiLCJzZWxlY3RvciIsImxvZ2luIiwiY3JlZGVudGlhbHMiLCJsb2dpbk9wdGlvbnMiLCJ1c2VyQWdlbnQiLCJzZXRVc2VyQWdlbnQiLCJsb2dpblVybCIsImNoZWNrUmVhZGluZXNzIiwic3VibWl0QnV0dG9uU2VsZWN0b3IiLCJ3YWl0VW50aWxFbGVtZW50Rm91bmQiLCJsb2dpbkZyYW1lT3JQYWdlIiwicHJlQWN0aW9uIiwiY2xpY2tCdXR0b24iLCJMb2dnaW5nSW4iLCJwb3N0QWN0aW9uIiwid2FpdEZvck5hdmlnYXRpb24iLCJjdXJyZW50IiwiZ2V0Q3VycmVudFVybCIsImxvZ2luUmVzdWx0IiwicG9zc2libGVSZXN1bHRzIiwiaGFuZGxlTG9naW5SZXN1bHQiLCJ0ZXJtaW5hdGUiLCJfc3VjY2VzcyIsIlRlcm1pbmF0aW5nIiwic3RvcmVGYWlsdXJlU2NyZWVuU2hvdFBhdGgiLCJzY3JlZW5zaG90IiwicGF0aCIsImZ1bGxQYWdlIiwiY2xvc2UiLCJTdWNjZXNzIiwiTG9naW5TdWNjZXNzIiwiSW52YWxpZFBhc3N3b3JkIiwiTG9naW5GYWlsZWQiLCJlcnJvck1lc3NhZ2UiLCJDaGFuZ2VQYXNzd29yZCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zY3JhcGVycy9iYXNlLXNjcmFwZXItd2l0aC1icm93c2VyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBwdXBwZXRlZXIsIHtcbiAgdHlwZSBCcm93c2VyLCB0eXBlIEZyYW1lLCB0eXBlIEdvVG9PcHRpb25zLCB0eXBlIFBhZ2UsIHR5cGUgUHVwcGV0ZWVyTGlmZUN5Y2xlRXZlbnQsXG59IGZyb20gJ3B1cHBldGVlcic7XG5cbmltcG9ydCB7IFNjcmFwZXJQcm9ncmVzc1R5cGVzIH0gZnJvbSAnLi4vZGVmaW5pdGlvbnMnO1xuaW1wb3J0IHsgZ2V0RGVidWcgfSBmcm9tICcuLi9oZWxwZXJzL2RlYnVnJztcbmltcG9ydCB7IGNsaWNrQnV0dG9uLCBmaWxsSW5wdXQsIHdhaXRVbnRpbEVsZW1lbnRGb3VuZCB9IGZyb20gJy4uL2hlbHBlcnMvZWxlbWVudHMtaW50ZXJhY3Rpb25zJztcbmltcG9ydCB7IGdldEN1cnJlbnRVcmwsIHdhaXRGb3JOYXZpZ2F0aW9uIH0gZnJvbSAnLi4vaGVscGVycy9uYXZpZ2F0aW9uJztcbmltcG9ydCB7IEJhc2VTY3JhcGVyIH0gZnJvbSAnLi9iYXNlLXNjcmFwZXInO1xuaW1wb3J0IHsgU2NyYXBlckVycm9yVHlwZXMgfSBmcm9tICcuL2Vycm9ycyc7XG5pbXBvcnQgeyB0eXBlIFNjcmFwZXJDcmVkZW50aWFscywgdHlwZSBTY3JhcGVyU2NyYXBpbmdSZXN1bHQgfSBmcm9tICcuL2ludGVyZmFjZSc7XG5cbmNvbnN0IFZJRVdQT1JUX1dJRFRIID0gMTAyNDtcbmNvbnN0IFZJRVdQT1JUX0hFSUdIVCA9IDc2ODtcbmNvbnN0IE9LX1NUQVRVUyA9IDIwMDtcblxuY29uc3QgZGVidWcgPSBnZXREZWJ1ZygnYmFzZS1zY3JhcGVyLXdpdGgtYnJvd3NlcicpO1xuXG5lbnVtIExvZ2luQmFzZVJlc3VsdHMge1xuICBTdWNjZXNzID0gJ1NVQ0NFU1MnLFxuICBVbmtub3duRXJyb3IgPSAnVU5LTk9XTl9FUlJPUicsXG59XG5cbmNvbnN0IHtcbiAgVGltZW91dCwgR2VuZXJpYywgR2VuZXJhbCwgLi4ucmVzdFxufSA9IFNjcmFwZXJFcnJvclR5cGVzO1xuZXhwb3J0IGNvbnN0IExvZ2luUmVzdWx0cyA9IHtcbiAgLi4ucmVzdCxcbiAgLi4uTG9naW5CYXNlUmVzdWx0cyxcbn07XG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVkZWNsYXJlXG5leHBvcnQgdHlwZSBMb2dpblJlc3VsdHMgPVxuICB8IEV4Y2x1ZGU8U2NyYXBlckVycm9yVHlwZXMsIFNjcmFwZXJFcnJvclR5cGVzLlRpbWVvdXQgfCBTY3JhcGVyRXJyb3JUeXBlcy5HZW5lcmljIHwgU2NyYXBlckVycm9yVHlwZXMuR2VuZXJhbD5cbiAgfCBMb2dpbkJhc2VSZXN1bHRzO1xuXG5leHBvcnQgdHlwZSBQb3NzaWJsZUxvZ2luUmVzdWx0cyA9IHtcbiAgW2tleSBpbiBMb2dpblJlc3VsdHNdPzogKHN0cmluZyB8IFJlZ0V4cCB8ICgob3B0aW9ucz86IHsgcGFnZT86IFBhZ2UgfSkgPT4gUHJvbWlzZTxib29sZWFuPikpW107XG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIExvZ2luT3B0aW9ucyB7XG4gIGxvZ2luVXJsOiBzdHJpbmc7XG4gIGNoZWNrUmVhZGluZXNzPzogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgZmllbGRzOiB7IHNlbGVjdG9yOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcgfVtdO1xuICBzdWJtaXRCdXR0b25TZWxlY3Rvcjogc3RyaW5nIHwgKCgpID0+IFByb21pc2U8dm9pZD4pO1xuICBwcmVBY3Rpb24/OiAoKSA9PiBQcm9taXNlPEZyYW1lIHwgdm9pZD47XG4gIHBvc3RBY3Rpb24/OiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xuICBwb3NzaWJsZVJlc3VsdHM6IFBvc3NpYmxlTG9naW5SZXN1bHRzO1xuICB1c2VyQWdlbnQ/OiBzdHJpbmc7XG4gIHdhaXRVbnRpbD86IFB1cHBldGVlckxpZmVDeWNsZUV2ZW50O1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRLZXlCeVZhbHVlKG9iamVjdDogUG9zc2libGVMb2dpblJlc3VsdHMsIHZhbHVlOiBzdHJpbmcsIHBhZ2U6IFBhZ2UpOiBQcm9taXNlPExvZ2luUmVzdWx0cz4ge1xuICBjb25zdCBrZXlzID0gT2JqZWN0LmtleXMob2JqZWN0KTtcbiAgZm9yIChjb25zdCBrZXkgb2Yga2V5cykge1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICBjb25zdCBjb25kaXRpb25zID0gb2JqZWN0W2tleV07XG5cbiAgICBmb3IgKGNvbnN0IGNvbmRpdGlvbiBvZiBjb25kaXRpb25zKSB7XG4gICAgICBsZXQgcmVzdWx0ID0gZmFsc2U7XG5cbiAgICAgIGlmIChjb25kaXRpb24gaW5zdGFuY2VvZiBSZWdFeHApIHtcbiAgICAgICAgcmVzdWx0ID0gY29uZGl0aW9uLnRlc3QodmFsdWUpO1xuICAgICAgfSBlbHNlIGlmICh0eXBlb2YgY29uZGl0aW9uID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IGNvbmRpdGlvbih7IHBhZ2UsIHZhbHVlIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmVzdWx0ID0gdmFsdWUudG9Mb3dlckNhc2UoKSA9PT0gY29uZGl0aW9uLnRvTG93ZXJDYXNlKCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGtleSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShMb2dpblJlc3VsdHMuVW5rbm93bkVycm9yKTtcbn1cblxuZnVuY3Rpb24gY3JlYXRlR2VuZXJhbEVycm9yKCk6IFNjcmFwZXJTY3JhcGluZ1Jlc3VsdCB7XG4gIHJldHVybiB7XG4gICAgc3VjY2VzczogZmFsc2UsXG4gICAgZXJyb3JUeXBlOiBTY3JhcGVyRXJyb3JUeXBlcy5HZW5lcmFsLFxuICB9O1xufVxuXG5jbGFzcyBCYXNlU2NyYXBlcldpdGhCcm93c2VyPFRDcmVkZW50aWFscyBleHRlbmRzIFNjcmFwZXJDcmVkZW50aWFscz4gZXh0ZW5kcyBCYXNlU2NyYXBlcjxUQ3JlZGVudGlhbHM+IHtcbiAgLy8gTk9USUNFIC0gaXQgaXMgZGlzY291cmFnZWQgdG8gdXNlIGJhbmcgKCEpIGluIGdlbmVyYWwuIEl0IGlzIHVzZWQgaGVyZSBiZWNhdXNlXG4gIC8vIGFsbCB0aGUgY2xhc3NlcyB0aGF0IGluaGVyaXQgZnJvbSB0aGlzIGJhc2UgYXNzdW1lIGlzIGl0IG1hbmRhdG9yeS5cbiAgcHJvdGVjdGVkIGJyb3dzZXIhOiBCcm93c2VyO1xuXG4gIC8vIE5PVElDRSAtIGl0IGlzIGRpc2NvdXJhZ2VkIHRvIHVzZSBiYW5nICghKSBpbiBnZW5lcmFsLiBJdCBpcyB1c2VkIGhlcmUgYmVjYXVzZVxuICAvLyBhbGwgdGhlIGNsYXNzZXMgdGhhdCBpbmhlcml0IGZyb20gdGhpcyBiYXNlIGFzc3VtZSBpcyBpdCBtYW5kYXRvcnkuXG4gIHByb3RlY3RlZCBwYWdlITogUGFnZTtcblxuICBwcm90ZWN0ZWQgZ2V0Vmlld1BvcnQoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIHdpZHRoOiBWSUVXUE9SVF9XSURUSCxcbiAgICAgIGhlaWdodDogVklFV1BPUlRfSEVJR0hULFxuICAgIH07XG4gIH1cblxuICBhc3luYyBpbml0aWFsaXplKCkge1xuICAgIGF3YWl0IHN1cGVyLmluaXRpYWxpemUoKTtcbiAgICBkZWJ1ZygnaW5pdGlhbGl6ZSBzY3JhcGVyJyk7XG4gICAgdGhpcy5lbWl0UHJvZ3Jlc3MoU2NyYXBlclByb2dyZXNzVHlwZXMuSW5pdGlhbGl6aW5nKTtcblxuICAgIGxldCBlbnY6IFJlY29yZDxzdHJpbmcsIGFueT4gfCB1bmRlZmluZWQ7XG4gICAgaWYgKHRoaXMub3B0aW9ucy52ZXJib3NlKSB7XG4gICAgICBlbnYgPSB7IERFQlVHOiAnKicsIC4uLnByb2Nlc3MuZW52IH07XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiB0aGlzLm9wdGlvbnMuYnJvd3NlciAhPT0gJ3VuZGVmaW5lZCcgJiYgdGhpcy5vcHRpb25zLmJyb3dzZXIgIT09IG51bGwpIHtcbiAgICAgIGRlYnVnKCd1c2UgY3VzdG9tIGJyb3dzZXIgaW5zdGFuY2UgcHJvdmlkZWQgaW4gb3B0aW9ucycpO1xuICAgICAgdGhpcy5icm93c2VyID0gdGhpcy5vcHRpb25zLmJyb3dzZXI7XG4gICAgfSBlbHNlIHtcbiAgICAgIGNvbnN0IGV4ZWN1dGFibGVQYXRoID0gdGhpcy5vcHRpb25zLmV4ZWN1dGFibGVQYXRoIHx8IHVuZGVmaW5lZDtcbiAgICAgIGNvbnN0IGFyZ3MgPSB0aGlzLm9wdGlvbnMuYXJncyB8fCBbXTtcbiAgICAgIGNvbnN0IHsgdGltZW91dCB9ID0gdGhpcy5vcHRpb25zO1xuXG4gICAgICBjb25zdCBoZWFkbGVzcyA9ICF0aGlzLm9wdGlvbnMuc2hvd0Jyb3dzZXI7XG4gICAgICBkZWJ1ZyhgbGF1bmNoIGEgYnJvd3NlciB3aXRoIGhlYWRsZXNzIG1vZGUgPSAke2hlYWRsZXNzfWApO1xuICAgICAgdGhpcy5icm93c2VyID0gYXdhaXQgcHVwcGV0ZWVyLmxhdW5jaCh7XG4gICAgICAgIGVudixcbiAgICAgICAgaGVhZGxlc3MsXG4gICAgICAgIGV4ZWN1dGFibGVQYXRoLFxuICAgICAgICBhcmdzLFxuICAgICAgICB0aW1lb3V0LFxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgaWYgKHRoaXMub3B0aW9ucy5wcmVwYXJlQnJvd3Nlcikge1xuICAgICAgZGVidWcoXCJleGVjdXRlICdwcmVwYXJlQnJvd3NlcicgaW50ZXJjZXB0b3IgcHJvdmlkZWQgaW4gb3B0aW9uc1wiKTtcbiAgICAgIGF3YWl0IHRoaXMub3B0aW9ucy5wcmVwYXJlQnJvd3Nlcih0aGlzLmJyb3dzZXIpO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5icm93c2VyKSB7XG4gICAgICBkZWJ1ZygnZmFpbGVkIHRvIGluaXRpYXRlIGEgYnJvd3NlciwgZXhpdCcpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IHBhZ2VzID0gYXdhaXQgdGhpcy5icm93c2VyLnBhZ2VzKCk7XG4gICAgaWYgKHBhZ2VzLmxlbmd0aCkge1xuICAgICAgZGVidWcoJ2Jyb3dzZXIgaGFzIGFscmVhZHkgcGFnZXMgb3BlbiwgdXNlIHRoZSBmaXJzdCBvbmUnKTtcbiAgICAgIFt0aGlzLnBhZ2VdID0gcGFnZXM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGRlYnVnKCdjcmVhdGUgYSBuZXcgYnJvd3NlciBwYWdlJyk7XG4gICAgICB0aGlzLnBhZ2UgPSBhd2FpdCB0aGlzLmJyb3dzZXIubmV3UGFnZSgpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm9wdGlvbnMuZGVmYXVsdFRpbWVvdXQpIHtcbiAgICAgIHRoaXMucGFnZS5zZXREZWZhdWx0VGltZW91dCh0aGlzLm9wdGlvbnMuZGVmYXVsdFRpbWVvdXQpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLm9wdGlvbnMucHJlcGFyZVBhZ2UpIHtcbiAgICAgIGRlYnVnKFwiZXhlY3V0ZSAncHJlcGFyZVBhZ2UnIGludGVyY2VwdG9yIHByb3ZpZGVkIGluIG9wdGlvbnNcIik7XG4gICAgICBhd2FpdCB0aGlzLm9wdGlvbnMucHJlcGFyZVBhZ2UodGhpcy5wYWdlKTtcbiAgICB9XG5cbiAgICBjb25zdCB2aWV3cG9ydCA9IHRoaXMuZ2V0Vmlld1BvcnQoKTtcbiAgICBkZWJ1Zyhgc2V0IHZpZXdwb3J0IHRvIHdpZHRoICR7dmlld3BvcnQud2lkdGh9LCBoZWlnaHQgJHt2aWV3cG9ydC5oZWlnaHR9YCk7XG4gICAgYXdhaXQgdGhpcy5wYWdlLnNldFZpZXdwb3J0KHtcbiAgICAgIHdpZHRoOiB2aWV3cG9ydC53aWR0aCxcbiAgICAgIGhlaWdodDogdmlld3BvcnQuaGVpZ2h0LFxuICAgIH0pO1xuXG4gICAgdGhpcy5wYWdlLm9uKCdyZXF1ZXN0ZmFpbGVkJywgKHJlcXVlc3QpID0+IHtcbiAgICAgIGRlYnVnKCdSZXF1ZXN0IGZhaWxlZDogJXMgJXMnLCByZXF1ZXN0LmZhaWx1cmUoKT8uZXJyb3JUZXh0LCByZXF1ZXN0LnVybCgpKTtcbiAgICB9KTtcbiAgfVxuXG4gIGFzeW5jIG5hdmlnYXRlVG8oXG4gICAgdXJsOiBzdHJpbmcsXG4gICAgcGFnZT86IFBhZ2UsXG4gICAgdGltZW91dD86IG51bWJlcixcbiAgICB3YWl0VW50aWw6IFB1cHBldGVlckxpZmVDeWNsZUV2ZW50IHwgdW5kZWZpbmVkID0gJ2xvYWQnLFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBwYWdlVG9Vc2UgPSBwYWdlIHx8IHRoaXMucGFnZTtcblxuICAgIGlmICghcGFnZVRvVXNlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3Qgb3B0aW9uczogR29Ub09wdGlvbnMgPSB7IC4uLih0aW1lb3V0ID09PSBudWxsID8gbnVsbCA6IHsgdGltZW91dCB9KSwgd2FpdFVudGlsIH07XG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBwYWdlVG9Vc2UuZ290byh1cmwsIG9wdGlvbnMpO1xuXG4gICAgLy8gbm90ZTogcmVzcG9uc2Ugd2lsbCBiZSBudWxsIHdoZW4gbmF2aWdhdGluZyB0byBzYW1lIHVybCB3aGlsZSBjaGFuZ2luZyB0aGUgaGFzaCBwYXJ0LiB0aGUgY29uZGl0aW9uIGJlbG93IHdpbGwgYWx3YXlzIGFjY2VwdCBudWxsIGFzIHZhbGlkIHJlc3VsdC5cbiAgICBpZiAocmVzcG9uc2UgIT09IG51bGwgJiYgKHJlc3BvbnNlID09PSB1bmRlZmluZWQgfHwgcmVzcG9uc2Uuc3RhdHVzKCkgIT09IE9LX1NUQVRVUykpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgRXJyb3Igd2hpbGUgdHJ5aW5nIHRvIG5hdmlnYXRlIHRvIHVybCAke3VybH1gKTtcbiAgICB9XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC12YXJzXG4gIGdldExvZ2luT3B0aW9ucyhfY3JlZGVudGlhbHM6IFNjcmFwZXJDcmVkZW50aWFscyk6IExvZ2luT3B0aW9ucyB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBnZXRMb2dpbk9wdGlvbnMoKSBpcyBub3QgY3JlYXRlZCBpbiAke3RoaXMub3B0aW9ucy5jb21wYW55SWR9YCk7XG4gIH1cblxuICBhc3luYyBmaWxsSW5wdXRzKHBhZ2VPckZyYW1lOiBQYWdlIHwgRnJhbWUsIGZpZWxkczogeyBzZWxlY3Rvcjogc3RyaW5nLCB2YWx1ZTogc3RyaW5nIH1bXSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG1vZGlmaWVkID0gWy4uLmZpZWxkc107XG4gICAgY29uc3QgaW5wdXQgPSBtb2RpZmllZC5zaGlmdCgpO1xuXG4gICAgaWYgKCFpbnB1dCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBhd2FpdCBmaWxsSW5wdXQocGFnZU9yRnJhbWUsIGlucHV0LnNlbGVjdG9yLCBpbnB1dC52YWx1ZSk7XG4gICAgaWYgKG1vZGlmaWVkLmxlbmd0aCkge1xuICAgICAgYXdhaXQgdGhpcy5maWxsSW5wdXRzKHBhZ2VPckZyYW1lLCBtb2RpZmllZCk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgbG9naW4oY3JlZGVudGlhbHM6IFNjcmFwZXJDcmVkZW50aWFscyk6IFByb21pc2U8U2NyYXBlclNjcmFwaW5nUmVzdWx0PiB7XG4gICAgaWYgKCFjcmVkZW50aWFscyB8fCAhdGhpcy5wYWdlKSB7XG4gICAgICByZXR1cm4gY3JlYXRlR2VuZXJhbEVycm9yKCk7XG4gICAgfVxuXG4gICAgZGVidWcoJ2V4ZWN1dGUgbG9naW4gcHJvY2VzcycpO1xuICAgIGNvbnN0IGxvZ2luT3B0aW9ucyA9IHRoaXMuZ2V0TG9naW5PcHRpb25zKGNyZWRlbnRpYWxzKTtcblxuICAgIGlmIChsb2dpbk9wdGlvbnMudXNlckFnZW50KSB7XG4gICAgICBkZWJ1Zygnc2V0IGN1c3RvbSB1c2VyIGFnZW50IHByb3ZpZGVkIGluIG9wdGlvbnMnKTtcbiAgICAgIGF3YWl0IHRoaXMucGFnZS5zZXRVc2VyQWdlbnQobG9naW5PcHRpb25zLnVzZXJBZ2VudCk7XG4gICAgfVxuXG4gICAgZGVidWcoJ25hdmlnYXRlIHRvIGxvZ2luIHVybCcpO1xuICAgIGF3YWl0IHRoaXMubmF2aWdhdGVUbyhsb2dpbk9wdGlvbnMubG9naW5VcmwsIHVuZGVmaW5lZCwgdW5kZWZpbmVkLCBsb2dpbk9wdGlvbnMud2FpdFVudGlsKTtcbiAgICBpZiAobG9naW5PcHRpb25zLmNoZWNrUmVhZGluZXNzKSB7XG4gICAgICBkZWJ1ZyhcImV4ZWN1dGUgJ2NoZWNrUmVhZGluZXNzJyBpbnRlcmNlcHRvciBwcm92aWRlZCBpbiBsb2dpbiBvcHRpb25zXCIpO1xuICAgICAgYXdhaXQgbG9naW5PcHRpb25zLmNoZWNrUmVhZGluZXNzKCk7XG4gICAgfSBlbHNlIGlmICh0eXBlb2YgbG9naW5PcHRpb25zLnN1Ym1pdEJ1dHRvblNlbGVjdG9yID09PSAnc3RyaW5nJykge1xuICAgICAgZGVidWcoJ3dhaXQgdW50aWwgc3VibWl0IGJ1dHRvbiBpcyBhdmFpbGFibGUnKTtcbiAgICAgIGF3YWl0IHdhaXRVbnRpbEVsZW1lbnRGb3VuZCh0aGlzLnBhZ2UsIGxvZ2luT3B0aW9ucy5zdWJtaXRCdXR0b25TZWxlY3Rvcik7XG4gICAgfVxuXG4gICAgbGV0IGxvZ2luRnJhbWVPclBhZ2U6IFBhZ2UgfCBGcmFtZSB8IG51bGwgPSB0aGlzLnBhZ2U7XG4gICAgaWYgKGxvZ2luT3B0aW9ucy5wcmVBY3Rpb24pIHtcbiAgICAgIGRlYnVnKFwiZXhlY3V0ZSAncHJlQWN0aW9uJyBpbnRlcmNlcHRvciBwcm92aWRlZCBpbiBsb2dpbiBvcHRpb25zXCIpO1xuICAgICAgbG9naW5GcmFtZU9yUGFnZSA9IChhd2FpdCBsb2dpbk9wdGlvbnMucHJlQWN0aW9uKCkpIHx8IHRoaXMucGFnZTtcbiAgICB9XG5cbiAgICBkZWJ1ZygnZmlsbCBsb2dpbiBjb21wb25lbnRzIGlucHV0IHdpdGggcmVsZXZhbnQgdmFsdWVzJyk7XG4gICAgYXdhaXQgdGhpcy5maWxsSW5wdXRzKGxvZ2luRnJhbWVPclBhZ2UsIGxvZ2luT3B0aW9ucy5maWVsZHMpO1xuICAgIGRlYnVnKCdjbGljayBvbiBsb2dpbiBzdWJtaXQgYnV0dG9uJyk7XG4gICAgaWYgKHR5cGVvZiBsb2dpbk9wdGlvbnMuc3VibWl0QnV0dG9uU2VsZWN0b3IgPT09ICdzdHJpbmcnKSB7XG4gICAgICBhd2FpdCBjbGlja0J1dHRvbihsb2dpbkZyYW1lT3JQYWdlLCBsb2dpbk9wdGlvbnMuc3VibWl0QnV0dG9uU2VsZWN0b3IpO1xuICAgIH0gZWxzZSB7XG4gICAgICBhd2FpdCBsb2dpbk9wdGlvbnMuc3VibWl0QnV0dG9uU2VsZWN0b3IoKTtcbiAgICB9XG4gICAgdGhpcy5lbWl0UHJvZ3Jlc3MoU2NyYXBlclByb2dyZXNzVHlwZXMuTG9nZ2luZ0luKTtcblxuICAgIGlmIChsb2dpbk9wdGlvbnMucG9zdEFjdGlvbikge1xuICAgICAgZGVidWcoXCJleGVjdXRlICdwb3N0QWN0aW9uJyBpbnRlcmNlcHRvciBwcm92aWRlZCBpbiBsb2dpbiBvcHRpb25zXCIpO1xuICAgICAgYXdhaXQgbG9naW5PcHRpb25zLnBvc3RBY3Rpb24oKTtcbiAgICB9IGVsc2Uge1xuICAgICAgZGVidWcoJ3dhaXQgZm9yIHBhZ2UgbmF2aWdhdGlvbicpO1xuICAgICAgYXdhaXQgd2FpdEZvck5hdmlnYXRpb24odGhpcy5wYWdlKTtcbiAgICB9XG5cbiAgICBkZWJ1ZygnY2hlY2sgbG9naW4gcmVzdWx0Jyk7XG4gICAgY29uc3QgY3VycmVudCA9IGF3YWl0IGdldEN1cnJlbnRVcmwodGhpcy5wYWdlLCB0cnVlKTtcbiAgICBjb25zdCBsb2dpblJlc3VsdCA9IGF3YWl0IGdldEtleUJ5VmFsdWUobG9naW5PcHRpb25zLnBvc3NpYmxlUmVzdWx0cywgY3VycmVudCwgdGhpcy5wYWdlKTtcbiAgICBkZWJ1ZyhgaGFuZGxlIGxvZ2luIHJlc3VsdHMgJHtsb2dpblJlc3VsdH1gKTtcbiAgICByZXR1cm4gdGhpcy5oYW5kbGVMb2dpblJlc3VsdChsb2dpblJlc3VsdCk7XG4gIH1cblxuICBhc3luYyB0ZXJtaW5hdGUoX3N1Y2Nlc3M6IGJvb2xlYW4pIHtcbiAgICBkZWJ1ZyhgdGVybWluYXRpbmcgYnJvd3NlciB3aXRoIHN1Y2Nlc3MgPSAke19zdWNjZXNzfWApO1xuICAgIHRoaXMuZW1pdFByb2dyZXNzKFNjcmFwZXJQcm9ncmVzc1R5cGVzLlRlcm1pbmF0aW5nKTtcblxuICAgIGlmICghX3N1Y2Nlc3MgJiYgISF0aGlzLm9wdGlvbnMuc3RvcmVGYWlsdXJlU2NyZWVuU2hvdFBhdGgpIHtcbiAgICAgIGRlYnVnKGBjcmVhdGUgYSBzbmFwc2hvdCBiZWZvcmUgdGVybWluYXRlZCBpbiAke3RoaXMub3B0aW9ucy5zdG9yZUZhaWx1cmVTY3JlZW5TaG90UGF0aH1gKTtcbiAgICAgIGF3YWl0IHRoaXMucGFnZS5zY3JlZW5zaG90KHtcbiAgICAgICAgcGF0aDogdGhpcy5vcHRpb25zLnN0b3JlRmFpbHVyZVNjcmVlblNob3RQYXRoLFxuICAgICAgICBmdWxsUGFnZTogdHJ1ZSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGlmICghdGhpcy5icm93c2VyKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgYXdhaXQgdGhpcy5icm93c2VyLmNsb3NlKCk7XG4gIH1cblxuICBwcml2YXRlIGhhbmRsZUxvZ2luUmVzdWx0KGxvZ2luUmVzdWx0OiBMb2dpblJlc3VsdHMpIHtcbiAgICBzd2l0Y2ggKGxvZ2luUmVzdWx0KSB7XG4gICAgICBjYXNlIExvZ2luUmVzdWx0cy5TdWNjZXNzOlxuICAgICAgICB0aGlzLmVtaXRQcm9ncmVzcyhTY3JhcGVyUHJvZ3Jlc3NUeXBlcy5Mb2dpblN1Y2Nlc3MpO1xuICAgICAgICByZXR1cm4geyBzdWNjZXNzOiB0cnVlIH07XG4gICAgICBjYXNlIExvZ2luUmVzdWx0cy5JbnZhbGlkUGFzc3dvcmQ6XG4gICAgICBjYXNlIExvZ2luUmVzdWx0cy5Vbmtub3duRXJyb3I6XG4gICAgICAgIHRoaXMuZW1pdFByb2dyZXNzKFNjcmFwZXJQcm9ncmVzc1R5cGVzLkxvZ2luRmFpbGVkKTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICBlcnJvclR5cGU6XG4gICAgICAgICAgICBsb2dpblJlc3VsdCA9PT0gTG9naW5SZXN1bHRzLkludmFsaWRQYXNzd29yZCA/XG4gICAgICAgICAgICAgIFNjcmFwZXJFcnJvclR5cGVzLkludmFsaWRQYXNzd29yZCA6XG4gICAgICAgICAgICAgIFNjcmFwZXJFcnJvclR5cGVzLkdlbmVyYWwsXG4gICAgICAgICAgZXJyb3JNZXNzYWdlOiBgTG9naW4gZmFpbGVkIHdpdGggJHtsb2dpblJlc3VsdH0gZXJyb3JgLFxuICAgICAgICB9O1xuICAgICAgY2FzZSBMb2dpblJlc3VsdHMuQ2hhbmdlUGFzc3dvcmQ6XG4gICAgICAgIHRoaXMuZW1pdFByb2dyZXNzKFNjcmFwZXJQcm9ncmVzc1R5cGVzLkNoYW5nZVBhc3N3b3JkKTtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcbiAgICAgICAgICBlcnJvclR5cGU6IFNjcmFwZXJFcnJvclR5cGVzLkNoYW5nZVBhc3N3b3JkLFxuICAgICAgICB9O1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGB1bmV4cGVjdGVkIGxvZ2luIHJlc3VsdCBcIiR7bG9naW5SZXN1bHR9XCJgKTtcbiAgICB9XG4gIH1cbn1cblxuZXhwb3J0IHsgQmFzZVNjcmFwZXJXaXRoQnJvd3NlciB9O1xuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7O0FBQUEsSUFBQUEsVUFBQSxHQUFBQyxzQkFBQSxDQUFBQyxPQUFBO0FBSUEsSUFBQUMsWUFBQSxHQUFBRCxPQUFBO0FBQ0EsSUFBQUUsTUFBQSxHQUFBRixPQUFBO0FBQ0EsSUFBQUcscUJBQUEsR0FBQUgsT0FBQTtBQUNBLElBQUFJLFdBQUEsR0FBQUosT0FBQTtBQUNBLElBQUFLLFlBQUEsR0FBQUwsT0FBQTtBQUNBLElBQUFNLE9BQUEsR0FBQU4sT0FBQTtBQUE2QyxTQUFBRCx1QkFBQVEsQ0FBQSxXQUFBQSxDQUFBLElBQUFBLENBQUEsQ0FBQUMsVUFBQSxHQUFBRCxDQUFBLEtBQUFFLE9BQUEsRUFBQUYsQ0FBQTtBQUFBLFNBQUFHLFFBQUFILENBQUEsRUFBQUksQ0FBQSxRQUFBQyxDQUFBLEdBQUFDLE1BQUEsQ0FBQUMsSUFBQSxDQUFBUCxDQUFBLE9BQUFNLE1BQUEsQ0FBQUUscUJBQUEsUUFBQUMsQ0FBQSxHQUFBSCxNQUFBLENBQUFFLHFCQUFBLENBQUFSLENBQUEsR0FBQUksQ0FBQSxLQUFBSyxDQUFBLEdBQUFBLENBQUEsQ0FBQUMsTUFBQSxXQUFBTixDQUFBLFdBQUFFLE1BQUEsQ0FBQUssd0JBQUEsQ0FBQVgsQ0FBQSxFQUFBSSxDQUFBLEVBQUFRLFVBQUEsT0FBQVAsQ0FBQSxDQUFBUSxJQUFBLENBQUFDLEtBQUEsQ0FBQVQsQ0FBQSxFQUFBSSxDQUFBLFlBQUFKLENBQUE7QUFBQSxTQUFBVSxjQUFBZixDQUFBLGFBQUFJLENBQUEsTUFBQUEsQ0FB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 +\ No newline at end of file ++//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcHVwcGV0ZWVyIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfZGVmaW5pdGlvbnMiLCJfZGVidWciLCJfZWxlbWVudHNJbnRlcmFjdGlvbnMiLCJfbmF2aWdhdGlvbiIsIl9iYXNlU2NyYXBlciIsIl9lcnJvcnMiLCJlIiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJvd25LZXlzIiwiciIsInQiLCJPYmplY3QiLCJrZXlzIiwiZ2V0T3duUHJvcGVydHlTeW1ib2xzIiwibyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZGVmaW5lUHJvcGVydHkiLCJfdG9Qcm9wZXJ0eUtleSIsInZhbHVlIiwiY29uZmlndXJhYmxlIiwid3JpdGFibGUiLCJpIiwiX3RvUHJpbWl0aXZlIiwiU3ltYm9sIiwidG9QcmltaXRpdmUiLCJjYWxsIiwiVHlwZUVycm9yIiwiU3RyaW5nIiwiTnVtYmVyIiwiX29iamVjdFdpdGhvdXRQcm9wZXJ0aWVzIiwiX29iamVjdFdpdGhvdXRQcm9wZXJ0aWVzTG9vc2UiLCJuIiwiaW5kZXhPZiIsInByb3BlcnR5SXNFbnVtZXJhYmxlIiwiaGFzT3duUHJvcGVydHkiLCJWSUVXUE9SVF9XSURUSCIsIlZJRVdQT1JUX0hFSUdIVCIsIk9LX1NUQVRVUyIsImRlYnVnIiwiZ2V0RGVidWciLCJMb2dpbkJhc2VSZXN1bHRzIiwiVGltZW91dCIsIkdlbmVyaWMiLCJHZW5lcmFsIiwiU2NyYXBlckVycm9yVHlwZXMiLCJyZXN0IiwiTG9naW5SZXN1bHRzIiwiZXhwb3J0cyIsImdldEtleUJ5VmFsdWUiLCJvYmplY3QiLCJwYWdlIiwia2V5IiwiY29uZGl0aW9ucyIsImNvbmRpdGlvbiIsInJlc3VsdCIsIlJlZ0V4cCIsInRlc3QiLCJ0b0xvd2VyQ2FzZSIsIlByb21pc2UiLCJyZXNvbHZlIiwiVW5rbm93bkVycm9yIiwiY3JlYXRlR2VuZXJhbEVycm9yIiwic3VjY2VzcyIsImVycm9yVHlwZSIsIkJhc2VTY3JhcGVyV2l0aEJyb3dzZXIiLCJCYXNlU2NyYXBlciIsImNvbnN0cnVjdG9yIiwiYXJncyIsImdldFZpZXdQb3J0Iiwid2lkdGgiLCJoZWlnaHQiLCJpbml0aWFsaXplUGFnZSIsIm9wdGlvbnMiLCJicm93c2VyQ29udGV4dCIsIm5ld1BhZ2UiLCJicm93c2VyIiwic2tpcENsb3NlQnJvd3NlciIsImNsZWFudXBzIiwiY2xvc2UiLCJ0aW1lb3V0IiwiZXhlY3V0YWJsZVBhdGgiLCJzaG93QnJvd3NlciIsImhlYWRsZXNzIiwicHVwcGV0ZWVyIiwibGF1bmNoIiwiZW52IiwidmVyYm9zZSIsIkRFQlVHIiwicHJvY2VzcyIsInVuZGVmaW5lZCIsInByZXBhcmVCcm93c2VyIiwiaW5pdGlhbGl6ZSIsImVtaXRQcm9ncmVzcyIsIlNjcmFwZXJQcm9ncmVzc1R5cGVzIiwiSW5pdGlhbGl6aW5nIiwiZGVmYXVsdFRpbWVvdXQiLCJzZXREZWZhdWx0VGltZW91dCIsInByZXBhcmVQYWdlIiwidmlld3BvcnQiLCJzZXRWaWV3cG9ydCIsIm9uIiwicmVxdWVzdCIsIl9yZXF1ZXN0JGZhaWx1cmUiLCJmYWlsdXJlIiwiZXJyb3JUZXh0IiwidXJsIiwibmF2aWdhdGVUbyIsIndhaXRVbnRpbCIsInBhZ2VUb1VzZSIsInJlc3BvbnNlIiwiZ290byIsInN0YXR1cyIsIkVycm9yIiwiZ2V0TG9naW5PcHRpb25zIiwiX2NyZWRlbnRpYWxzIiwiY29tcGFueUlkIiwiZmlsbElucHV0cyIsInBhZ2VPckZyYW1lIiwiZmllbGRzIiwibW9kaWZpZWQiLCJpbnB1dCIsInNoaWZ0IiwiZmlsbElucHV0Iiwic2VsZWN0b3IiLCJsb2dpbiIsImNyZWRlbnRpYWxzIiwibG9naW5PcHRpb25zIiwidXNlckFnZW50Iiwic2V0VXNlckFnZW50IiwibG9naW5VcmwiLCJjaGVja1JlYWRpbmVzcyIsInN1Ym1pdEJ1dHRvblNlbGVjdG9yIiwid2FpdFVudGlsRWxlbWVudEZvdW5kIiwibG9naW5GcmFtZU9yUGFnZSIsInByZUFjdGlvbiIsImNsaWNrQnV0dG9uIiwiTG9nZ2luZ0luIiwicG9zdEFjdGlvbiIsIndhaXRGb3JOYXZpZ2F0aW9uIiwiY3VycmVudCIsImdldEN1cnJlbnRVcmwiLCJsb2dpblJlc3VsdCIsInBvc3NpYmxlUmVzdWx0cyIsImhhbmRsZUxvZ2luUmVzdWx0IiwidGVybWluYXRlIiwiX3N1Y2Nlc3MiLCJUZXJtaW5hdGluZyIsInN0b3JlRmFpbHVyZVNjcmVlblNob3RQYXRoIiwic2NyZWVuc2hvdCIsInBhdGgiLCJmdWxsUGFnZSIsImFsbCIsInJldmVyc2UiLCJtYXAiLCJjbGVhbnVwIiwiU3VjY2VzcyIsIkxvZ2luU3VjY2VzcyIsIkludmFsaWRQYXNzd29yZCIsIkxvZ2luRmFpbGVkIiwiZXJyb3JNZXNzYWdlIiwiQ2hhbmdlUGFzc3dvcmQiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvc2NyYXBlcnMvYmFzZS1zY3JhcGVyLXdpdGgtYnJvd3Nlci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgcHVwcGV0ZWVyLCB7IHR5cGUgRnJhbWUsIHR5cGUgR29Ub09wdGlvbnMsIHR5cGUgUGFnZSwgdHlwZSBQdXBwZXRlZXJMaWZlQ3ljbGVFdmVudCB9IGZyb20gJ3B1cHBldGVlcic7XHJcbmltcG9ydCB7IFNjcmFwZXJQcm9ncmVzc1R5cGVzIH0gZnJvbSAnLi4vZGVmaW5pdGlvbnMnO1xyXG5pbXBvcnQgeyBnZXREZWJ1ZyB9IGZyb20gJy4uL2hlbHBlcnMvZGVidWcnO1xyXG5pbXBvcnQgeyBjbGlja0J1dHRvbiwgZmlsbElucHV0LCB3YWl0VW50aWxFbGVtZW50Rm91bmQgfSBmcm9tICcuLi9oZWxwZXJzL2VsZW1lbnRzLWludGVyYWN0aW9ucyc7XHJcbmltcG9ydCB7IGdldEN1cnJlbnRVcmwsIHdhaXRGb3JOYXZpZ2F0aW9uIH0gZnJvbSAnLi4vaGVscGVycy9uYXZpZ2F0aW9uJztcclxuaW1wb3J0IHsgQmFzZVNjcmFwZXIgfSBmcm9tICcuL2Jhc2Utc2NyYXBlcic7XHJcbmltcG9ydCB7IFNjcmFwZXJFcnJvclR5cGVzIH0gZnJvbSAnLi9lcnJvcnMnO1xyXG5pbXBvcnQgeyB0eXBlIFNjcmFwZXJDcmVkZW50aWFscywgdHlwZSBTY3JhcGVyU2NyYXBpbmdSZXN1bHQgfSBmcm9tICcuL2ludGVyZmFjZSc7XHJcblxyXG5jb25zdCBWSUVXUE9SVF9XSURUSCA9IDEwMjQ7XHJcbmNvbnN0IFZJRVdQT1JUX0hFSUdIVCA9IDc2ODtcclxuY29uc3QgT0tfU1RBVFVTID0gMjAwO1xyXG5cclxuY29uc3QgZGVidWcgPSBnZXREZWJ1ZygnYmFzZS1zY3JhcGVyLXdpdGgtYnJvd3NlcicpO1xyXG5cclxuZW51bSBMb2dpbkJhc2VSZXN1bHRzIHtcclxuICBTdWNjZXNzID0gJ1NVQ0NFU1MnLFxyXG4gIFVua25vd25FcnJvciA9ICdVTktOT1dOX0VSUk9SJyxcclxufVxyXG5cclxuY29uc3Qge1xyXG4gIFRpbWVvdXQsIEdlbmVyaWMsIEdlbmVyYWwsIC4uLnJlc3RcclxufSA9IFNjcmFwZXJFcnJvclR5cGVzO1xyXG5leHBvcnQgY29uc3QgTG9naW5SZXN1bHRzID0ge1xyXG4gIC4uLnJlc3QsXHJcbiAgLi4uTG9naW5CYXNlUmVzdWx0cyxcclxufTtcclxuXHJcbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tcmVkZWNsYXJlXHJcbmV4cG9ydCB0eXBlIExvZ2luUmVzdWx0cyA9XHJcbiAgfCBFeGNsdWRlPFNjcmFwZXJFcnJvclR5cGVzLCBTY3JhcGVyRXJyb3JUeXBlcy5UaW1lb3V0IHwgU2NyYXBlckVycm9yVHlwZXMuR2VuZXJpYyB8IFNjcmFwZXJFcnJvclR5cGVzLkdlbmVyYWw+XHJcbiAgfCBMb2dpbkJhc2VSZXN1bHRzO1xyXG5cclxuZXhwb3J0IHR5cGUgUG9zc2libGVMb2dpblJlc3VsdHMgPSB7XHJcbiAgW2tleSBpbiBMb2dpblJlc3VsdHNdPzogKHN0cmluZyB8IFJlZ0V4cCB8ICgob3B0aW9ucz86IHsgcGFnZT86IFBhZ2UgfSkgPT4gUHJvbWlzZTxib29sZWFuPikpW107XHJcbn07XHJcblxyXG5leHBvcnQgaW50ZXJmYWNlIExvZ2luT3B0aW9ucyB7XHJcbiAgbG9naW5Vcmw6IHN0cmluZztcclxuICBjaGVja1JlYWRpbmVzcz86ICgpID0+IFByb21pc2U8dm9pZD47XHJcbiAgZmllbGRzOiB7IHNlbGVjdG9yOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcgfVtdO1xyXG4gIHN1Ym1pdEJ1dHRvblNlbGVjdG9yOiBzdHJpbmcgfCAoKCkgPT4gUHJvbWlzZTx2b2lkPik7XHJcbiAgcHJlQWN0aW9uPzogKCkgPT4gUHJvbWlzZTxGcmFtZSB8IHZvaWQ+O1xyXG4gIHBvc3RBY3Rpb24/OiAoKSA9PiBQcm9taXNlPHZvaWQ+O1xyXG4gIHBvc3NpYmxlUmVzdWx0czogUG9zc2libGVMb2dpblJlc3VsdHM7XHJcbiAgdXNlckFnZW50Pzogc3RyaW5nO1xyXG4gIHdhaXRVbnRpbD86IFB1cHBldGVlckxpZmVDeWNsZUV2ZW50O1xyXG59XHJcblxyXG5hc3luYyBmdW5jdGlvbiBnZXRLZXlCeVZhbHVlKG9iamVjdDogUG9zc2libGVMb2dpblJlc3VsdHMsIHZhbHVlOiBzdHJpbmcsIHBhZ2U6IFBhZ2UpOiBQcm9taXNlPExvZ2luUmVzdWx0cz4ge1xyXG4gIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhvYmplY3QpO1xyXG4gIGZvciAoY29uc3Qga2V5IG9mIGtleXMpIHtcclxuICAgIC8vIEB0cy1pZ25vcmVcclxuICAgIGNvbnN0IGNvbmRpdGlvbnMgPSBvYmplY3Rba2V5XTtcclxuXHJcbiAgICBmb3IgKGNvbnN0IGNvbmRpdGlvbiBvZiBjb25kaXRpb25zKSB7XHJcbiAgICAgIGxldCByZXN1bHQgPSBmYWxzZTtcclxuXHJcbiAgICAgIGlmIChjb25kaXRpb24gaW5zdGFuY2VvZiBSZWdFeHApIHtcclxuICAgICAgICByZXN1bHQgPSBjb25kaXRpb24udGVzdCh2YWx1ZSk7XHJcbiAgICAgIH0gZWxzZSBpZiAodHlwZW9mIGNvbmRpdGlvbiA9PT0gJ2Z1bmN0aW9uJykge1xyXG4gICAgICAgIHJlc3VsdCA9IGF3YWl0IGNvbmRpdGlvbih7IHBhZ2UsIHZhbHVlIH0pO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIHJlc3VsdCA9IHZhbHVlLnRvTG93ZXJDYXNlKCkgPT09IGNvbmRpdGlvbi50b0xvd2VyQ2FzZSgpO1xyXG4gICAgICB9XHJcblxyXG4gICAgICBpZiAocmVzdWx0KSB7XHJcbiAgICAgICAgLy8gQHRzLWlnbm9yZVxyXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoa2V5KTtcclxuICAgICAgfVxyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShMb2dpblJlc3VsdHMuVW5rbm93bkVycm9yKTtcclxufVxyXG5cclxuZnVuY3Rpb24gY3JlYXRlR2VuZXJhbEVycm9yKCk6IFNjcmFwZXJTY3JhcGluZ1Jlc3VsdCB7XHJcbiAgcmV0dXJuIHtcclxuICAgIHN1Y2Nlc3M6IGZhbHNlLFxyXG4gICAgZXJyb3JUeXBlOiBTY3JhcGVyRXJyb3JUeXBlcy5HZW5lcmFsLFxyXG4gIH07XHJcbn1cclxuXHJcbmNsYXNzIEJhc2VTY3JhcGVyV2l0aEJyb3dzZXI8VENyZWRlbnRpYWxzIGV4dGVuZHMgU2NyYXBlckNyZWRlbnRpYWxzPiBleHRlbmRzIEJhc2VTY3JhcGVyPFRDcmVkZW50aWFscz4ge1xyXG4gIHByaXZhdGUgY2xlYW51cHM6IEFycmF5PCgpID0+IFByb21pc2U8dm9pZD4+ID0gW107XHJcblxyXG4gIC8vIE5PVElDRSAtIGl0IGlzIGRpc2NvdXJhZ2VkIHRvIHVzZSBiYW5nICghKSBpbiBnZW5lcmFsLiBJdCBpcyB1c2VkIGhlcmUgYmVjYXVzZVxyXG4gIC8vIGFsbCB0aGUgY2xhc3NlcyB0aGF0IGluaGVyaXQgZnJvbSB0aGlzIGJhc2UgYXNzdW1lIGlzIGl0IG1hbmRhdG9yeS5cclxuICBwcm90ZWN0ZWQgcGFnZSE6IFBhZ2U7XHJcblxyXG4gIHByb3RlY3RlZCBnZXRWaWV3UG9ydCgpIHtcclxuICAgIHJldHVybiB7XHJcbiAgICAgIHdpZHRoOiBWSUVXUE9SVF9XSURUSCxcclxuICAgICAgaGVpZ2h0OiBWSUVXUE9SVF9IRUlHSFQsXHJcbiAgICB9O1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBhc3luYyBpbml0aWFsaXplUGFnZSgpIHtcclxuICAgIGRlYnVnKCdpbml0aWFsaXplIGJyb3dzZXIgcGFnZScpO1xyXG4gICAgaWYgKCdicm93c2VyQ29udGV4dCcgaW4gdGhpcy5vcHRpb25zKSB7XHJcbiAgICAgIGRlYnVnKCdVc2luZyB0aGUgYnJvd3NlciBjb250ZXh0IHByb3ZpZGVkIGluIG9wdGlvbnMnKTtcclxuICAgICAgcmV0dXJuIHRoaXMub3B0aW9ucy5icm93c2VyQ29udGV4dC5uZXdQYWdlKCk7XHJcbiAgICB9XHJcblxyXG4gICAgaWYgKCdicm93c2VyJyBpbiB0aGlzLm9wdGlvbnMpIHtcclxuICAgICAgZGVidWcoJ1VzaW5nIHRoZSBicm93c2VyIGluc3RhbmNlIHByb3ZpZGVkIGluIG9wdGlvbnMnKTtcclxuICAgICAgY29uc3QgeyBicm93c2VyIH0gPSB0aGlzLm9wdGlvbnM7XHJcblxyXG4gICAgICAvKipcclxuICAgICAgICogRm9yIGJhY2t3YXJkIGNvbXBhdGliaWxpdHksIHdlIHdpbGwgY2xvc2UgdGhlIGJyb3dzZXIgZXZlbiBpZiB3ZSBkaWRuJ3QgY3JlYXRlIGl0XHJcbiAgICAgICAqL1xyXG4gICAgICBpZiAoIXRoaXMub3B0aW9ucy5za2lwQ2xvc2VCcm93c2VyKSB7XHJcbiAgICAgICAgdGhpcy5jbGVhbnVwcy5wdXNoKGFzeW5jICgpID0+IHtcclxuICAgICAgICAgIGRlYnVnKCdjbG9zaW5nIHRoZSBicm93c2VyJyk7XHJcbiAgICAgICAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgICAgICAgfSk7XHJcbiAgICAgIH1cclxuXHJcbiAgICAgIHJldHVybiBicm93c2VyLm5ld1BhZ2UoKTtcclxuICAgIH0gXHJcblxyXG4gICAgY29uc3QgeyB0aW1lb3V0LCBhcmdzLCBleGVjdXRhYmxlUGF0aCwgc2hvd0Jyb3dzZXIgfSA9IHRoaXMub3B0aW9ucztcclxuXHJcbiAgICBjb25zdCBoZWFkbGVzcyA9ICFzaG93QnJvd3NlcjtcclxuICAgIGRlYnVnKGBsYXVuY2ggYSBicm93c2VyIHdpdGggaGVhZGxlc3MgbW9kZSA9ICR7aGVhZGxlc3N9YCk7XHJcblxyXG4gICAgY29uc3QgYnJvd3NlciA9IGF3YWl0IHB1cHBldGVlci5sYXVuY2goe1xyXG4gICAgICBlbnY6IHRoaXMub3B0aW9ucy52ZXJib3NlID8geyBERUJVRzogJyonLCAuLi5wcm9jZXNzLmVudiB9IDogdW5kZWZpbmVkLFxyXG4gICAgICBoZWFkbGVzcyxcclxuICAgICAgZXhlY3V0YWJsZVBhdGgsXHJcbiAgICAgIGFyZ3MsXHJcbiAgICAgIHRpbWVvdXQsXHJcbiAgICB9KTtcclxuXHJcbiAgICB0aGlzLmNsZWFudXBzLnB1c2goYXN5bmMgKCkgPT4ge1xyXG4gICAgICBkZWJ1ZygnY2xvc2luZyB0aGUgYnJvd3NlcicpO1xyXG4gICAgICBhd2FpdCBicm93c2VyLmNsb3NlKCk7XHJcbiAgICB9KTtcclxuXHJcbiAgICBpZiAodGhpcy5vcHRpb25zLnByZXBhcmVCcm93c2VyKSB7XHJcbiAgICAgIGRlYnVnKFwiZXhlY3V0ZSAncHJlcGFyZUJyb3dzZXInIGludGVyY2VwdG9yIHByb3ZpZGVkIGluIG9wdGlvbnNcIik7XHJcbiAgICAgIGF3YWl0IHRoaXMub3B0aW9ucy5wcmVwYXJlQnJvd3Nlcihicm93c2VyKTtcclxuICAgIH1cclxuXHJcbiAgICBkZWJ1ZygnY3JlYXRlIGEgbmV3IGJyb3dzZXIgcGFnZScpO1xyXG4gICAgcmV0dXJuIGJyb3dzZXIubmV3UGFnZSgpO1xyXG4gIH1cclxuXHJcbiAgYXN5bmMgaW5pdGlhbGl6ZSgpIHtcclxuICAgIGF3YWl0IHN1cGVyLmluaXRpYWxpemUoKTtcclxuICAgIGRlYnVnKCdpbml0aWFsaXplIHNjcmFwZXInKTtcclxuICAgIHRoaXMuZW1pdFByb2dyZXNzKFNjcmFwZXJQcm9ncmVzc1R5cGVzLkluaXRpYWxpemluZyk7XHJcblxyXG4gICAgY29uc3QgcGFnZSA9IGF3YWl0IHRoaXMuaW5pdGlhbGl6ZVBhZ2UoKTtcclxuICAgIGlmICghcGFnZSkge1xyXG4gICAgICBkZWJ1ZygnZmFpbGVkIHRvIGluaXRpYXRlIGEgYnJvd3NlciBwYWdlLCBleGl0Jyk7XHJcbiAgICAgIHJldHVybjtcclxuICAgIH1cclxuXHJcbiAgICB0aGlzLnBhZ2UgPSBwYWdlO1xyXG5cclxuICAgIHRoaXMuY2xlYW51cHMucHVzaCggKCkgPT4gcGFnZS5jbG9zZSgpKTtcclxuXHJcbiAgICBpZiAodGhpcy5vcHRpb25zLmRlZmF1bHRUaW1lb3V0KSB7XHJcbiAgICAgIHRoaXMucGFnZS5zZXREZWZhdWx0VGltZW91dCh0aGlzLm9wdGlvbnMuZGVmYXVsdFRpbWVvdXQpO1xyXG4gICAgfVxyXG5cclxuICAgIGlmICh0aGlzLm9wdGlvbnMucHJlcGFyZVBhZ2UpIHtcclxuICAgICAgZGVidWcoXCJleGVjdXRlICdwcmVwYXJlUGFnZScgaW50ZXJjZXB0b3IgcHJvdmlkZWQgaW4gb3B0aW9uc1wiKTtcclxuICAgICAgYXdhaXQgdGhpcy5vcHRpb25zLnByZXBhcmVQYWdlKHRoaXMucGFnZSk7XHJcbiAgICB9XHJcblxyXG4gICAgY29uc3Qgdmlld3BvcnQgPSB0aGlzLmdldFZpZXdQb3J0KCk7XHJcbiAgICBkZWJ1Zyhgc2V0IHZpZXdwb3J0IHRvIHdpZHRoICR7dmlld3BvcnQud2lkdGh9LCBoZWlnaHQgJHt2aWV3cG9ydC5oZWlnaHR9YCk7XHJcbiAgICBhd2FpdCB0aGlzLnBhZ2Uuc2V0Vmlld3BvcnQoe1xyXG4gICAgICB3aWR0aDogdmlld3BvcnQud2lkdGgsXHJcbiAgICAgIGhlaWdodDogdmlld3BvcnQuaGVpZ2h0LFxyXG4gICAgfSk7XHJcblxyXG4gICAgdGhpcy5wYWdlLm9uKCdyZXF1ZXN0ZmFpbGVkJywgKHJlcXVlc3QpID0+IHtcclxuICAgICAgZGVidWcoJ1JlcXVlc3QgZmFpbGVkOiAlcyAlcycsIHJlcXVlc3QuZmFpbHVyZSgpPy5lcnJvclRleHQsIHJlcXVlc3QudXJsKCkpO1xyXG4gICAgfSk7XHJcbiAgfVxyXG5cclxuICBhc3luYyBuYXZpZ2F0ZVRvKFxyXG4gICAgdXJsOiBzdHJpbmcsXHJcbiAgICBwYWdlPzogUGFnZSxcclxuICAgIHRpbWVvdXQ/OiBudW1iZXIsXHJcbiAgICB3YWl0VW50aWw6IFB1cHBldGVlckxpZmVDeWNsZUV2ZW50IHwgdW5kZWZpbmVkID0gJ2xvYWQnLFxyXG4gICk6IFByb21pc2U8dm9pZD4ge1xyXG4gICAgY29uc3QgcGFnZVRvVXNlID0gcGFnZSB8fCB0aGlzLnBhZ2U7XHJcblxyXG4gICAgaWYgKCFwYWdlVG9Vc2UpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG5cclxuICAgIGNvbnN0IG9wdGlvbnM6IEdvVG9PcHRpb25zID0geyAuLi4odGltZW91dCA9PT0gbnVsbCA/IG51bGwgOiB7IHRpbWVvdXQgfSksIHdhaXRVbnRpbCB9O1xyXG4gICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBwYWdlVG9Vc2UuZ290byh1cmwsIG9wdGlvbnMpO1xyXG5cclxuICAgIC8vIG5vdGU6IHJlc3BvbnNlIHdpbGwgYmUgbnVsbCB3aGVuIG5hdmlnYXRpbmcgdG8gc2FtZSB1cmwgd2hpbGUgY2hhbmdpbmcgdGhlIGhhc2ggcGFydC4gdGhlIGNvbmRpdGlvbiBiZWxvdyB3aWxsIGFsd2F5cyBhY2NlcHQgbnVsbCBhcyB2YWxpZCByZXN1bHQuXHJcbiAgICBpZiAocmVzcG9uc2UgIT09IG51bGwgJiYgKHJlc3BvbnNlID09PSB1bmRlZmluZWQgfHwgcmVzcG9uc2Uuc3RhdHVzKCkgIT09IE9LX1NUQVRVUykpIHtcclxuICAgICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciB3aGlsZSB0cnlpbmcgdG8gbmF2aWdhdGUgdG8gdXJsICR7dXJsfWApO1xyXG4gICAgfVxyXG4gIH1cclxuXHJcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnVzZWQtdmFyc1xyXG4gIGdldExvZ2luT3B0aW9ucyhfY3JlZGVudGlhbHM6IFNjcmFwZXJDcmVkZW50aWFscyk6IExvZ2luT3B0aW9ucyB7XHJcbiAgICB0aHJvdyBuZXcgRXJyb3IoYGdldExvZ2luT3B0aW9ucygpIGlzIG5vdCBjcmVhdGVkIGluICR7dGhpcy5vcHRpb25zLmNvbXBhbnlJZH1gKTtcclxuICB9XHJcblxyXG4gIGFzeW5jIGZpbGxJbnB1dHMocGFnZU9yRnJhbWU6IFBhZ2UgfCBGcmFtZSwgZmllbGRzOiB7IHNlbGVjdG9yOiBzdHJpbmcsIHZhbHVlOiBzdHJpbmcgfVtdKTogUHJvbWlzZTx2b2lkPiB7XHJcbiAgICBjb25zdCBtb2RpZmllZCA9IFsuLi5maWVsZHNdO1xyXG4gICAgY29uc3QgaW5wdXQgPSBtb2RpZmllZC5zaGlmdCgpO1xyXG5cclxuICAgIGlmICghaW5wdXQpIHtcclxuICAgICAgcmV0dXJuO1xyXG4gICAgfVxyXG4gICAgYXdhaXQgZmlsbElucHV0KHBhZ2VPckZyYW1lLCBpbnB1dC5zZWxlY3RvciwgaW5wdXQudmFsdWUpO1xyXG4gICAgaWYgKG1vZGlmaWVkLmxlbmd0aCkge1xyXG4gICAgICBhd2FpdCB0aGlzLmZpbGxJbnB1dHMocGFnZU9yRnJhbWUsIG1vZGlmaWVkKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIGFzeW5jIGxvZ2luKGNyZWRlbnRpYWxzOiBTY3JhcGVyQ3JlZGVudGlhbHMpOiBQcm9taXNlPFNjcmFwZXJTY3JhcGluZ1Jlc3VsdD4ge1xyXG4gICAgaWYgKCFjcmVkZW50aWFscyB8fCAhdGhpcy5wYWdlKSB7XHJcbiAgICAgIHJldHVybiBjcmVhdGVHZW5lcmFsRXJyb3IoKTtcclxuICAgIH1cclxuXHJcbiAgICBkZWJ1ZygnZXhlY3V0ZSBsb2dpbiBwcm9jZXNzJyk7XHJcbiAgICBjb25zdCBsb2dpbk9wdGlvbnMgPSB0aGlzLmdldExvZ2luT3B0aW9ucyhjcmVkZW50aWFscyk7XHJcblxyXG4gICAgaWYgKGxvZ2luT3B0aW9ucy51c2VyQWdlbnQpIHtcclxuICAgICAgZGVidWcoJ3NldCBjdXN0b20gdXNlciBhZ2VudCBwcm92aWRlZCBpbiBvcHRpb25zJyk7XHJcbiAgICAgIGF3YWl0IHRoaXMucGFnZS5zZXRVc2VyQWdlbnQobG9naW5PcHRpb25zLnVzZXJBZ2VudCk7XHJcbiAgICB9XHJcblxyXG4gICAgZGVidWcoJ25hdmlnYXRlIHRvIGxvZ2luIHVybCcpO1xyXG4gICAgYXdhaXQgdGhpcy5uYXZpZ2F0ZVRvKGxvZ2luT3B0aW9ucy5sb2dpblVybCwgdW5kZWZpbmVkLCB1bmRlZmluZWQsIGxvZ2luT3B0aW9ucy53YWl0VW50aWwpO1xyXG4gICAgaWYgKGxvZ2luT3B0aW9ucy5jaGVja1JlYWRpbmVzcykge1xyXG4gICAgICBkZWJ1ZyhcImV4ZWN1dGUgJ2NoZWNrUmVhZGluZXNzJyBpbnRlcmNlcHRvciBwcm92aWRlZCBpbiBsb2dpbiBvcHRpb25zXCIpO1xyXG4gICAgICBhd2FpdCBsb2dpbk9wdGlvbnMuY2hlY2tSZWFkaW5lc3MoKTtcclxuICAgIH0gZWxzZSBpZiAodHlwZW9mIGxvZ2luT3B0aW9ucy5zdWJtaXRCdXR0b25TZWxlY3RvciA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgZGVidWcoJ3dhaXQgdW50aWwgc3VibWl0IGJ1dHRvbiBpcyBhdmFpbGFibGUnKTtcclxuICAgICAgYXdhaXQgd2FpdFVudGlsRWxlbWVudEZvdW5kKHRoaXMucGFnZSwgbG9naW5PcHRpb25zLnN1Ym1pdEJ1dHRvblNlbGVjdG9yKTtcclxuICAgIH1cclxuXHJcbiAgICBsZXQgbG9naW5GcmFtZU9yUGFnZTogUGFnZSB8IEZyYW1lIHwgbnVsbCA9IHRoaXMucGFnZTtcclxuICAgIGlmIChsb2dpbk9wdGlvbnMucHJlQWN0aW9uKSB7XHJcbiAgICAgIGRlYnVnKFwiZXhlY3V0ZSAncHJlQWN0aW9uJyBpbnRlcmNlcHRvciBwcm92aWRlZCBpbiBsb2dpbiBvcHRpb25zXCIpO1xyXG4gICAgICBsb2dpbkZyYW1lT3JQYWdlID0gKGF3YWl0IGxvZ2luT3B0aW9ucy5wcmVBY3Rpb24oKSkgfHwgdGhpcy5wYWdlO1xyXG4gICAgfVxyXG5cclxuICAgIGRlYnVnKCdmaWxsIGxvZ2luIGNvbXBvbmVudHMgaW5wdXQgd2l0aCByZWxldmFudCB2YWx1ZXMnKTtcclxuICAgIGF3YWl0IHRoaXMuZmlsbElucHV0cyhsb2dpbkZyYW1lT3JQYWdlLCBsb2dpbk9wdGlvbnMuZmllbGRzKTtcclxuICAgIGRlYnVnKCdjbGljayBvbiBsb2dpbiBzdWJtaXQgYnV0dG9uJyk7XHJcbiAgICBpZiAodHlwZW9mIGxvZ2luT3B0aW9ucy5zdWJtaXRCdXR0b25TZWxlY3RvciA9PT0gJ3N0cmluZycpIHtcclxuICAgICAgYXdhaXQgY2xpY2tCdXR0b24obG9naW5GcmFtZU9yUGFnZSwgbG9naW5PcHRpb25zLnN1Ym1pdEJ1dHRvblNlbGVjdG9yKTtcclxuICAgIH0gZWxzZSB7XHJcbiAgICAgIGF3YWl0IGxvZ2luT3B0aW9ucy5zdWJtaXRCdXR0b25TZWxlY3RvcigpO1xyXG4gICAgfVxyXG4gICAgdGhpcy5lbWl0UHJvZ3Jlc3MoU2NyYXBlclByb2dyZXNzVHlwZXMuTG9nZ2luZ0luKTtcclxuXHJcbiAgICBpZiAobG9naW5PcHRpb25zLnBvc3RBY3Rpb24pIHtcclxuICAgICAgZGVidWcoXCJleGVjdXRlICdwb3N0QWN0aW9uJyBpbnRlcmNlcHRvciBwcm92aWRlZCBpbiBsb2dpbiBvcHRpb25zXCIpO1xyXG4gICAgICBhd2FpdCBsb2dpbk9wdGlvbnMucG9zdEFjdGlvbigpO1xyXG4gICAgfSBlbHNlIHtcclxuICAgICAgZGVidWcoJ3dhaXQgZm9yIHBhZ2UgbmF2aWdhdGlvbicpO1xyXG4gICAgICBhd2FpdCB3YWl0Rm9yTmF2aWdhdGlvbih0aGlzLnBhZ2UpO1xyXG4gICAgfVxyXG5cclxuICAgIGRlYnVnKCdjaGVjayBsb2dpbiByZXN1bHQnKTtcclxuICAgIGNvbnN0IGN1cnJlbnQgPSBhd2FpdCBnZXRDdXJyZW50VXJsKHRoaXMucGFnZSwgdHJ1ZSk7XHJcbiAgICBjb25zdCBsb2dpblJlc3VsdCA9IGF3YWl0IGdldEtleUJ5VmFsdWUobG9naW5PcHRpb25zLnBvc3NpYmxlUmVzdWx0cywgY3VycmVudCwgdGhpcy5wYWdlKTtcclxuICAgIGRlYnVnKGBoYW5kbGUgbG9naW4gcmVzdWx0cyAke2xvZ2luUmVzdWx0fWApO1xyXG4gICAgcmV0dXJuIHRoaXMuaGFuZGxlTG9naW5SZXN1bHQobG9naW5SZXN1bHQpO1xyXG4gIH1cclxuXHJcbiAgYXN5bmMgdGVybWluYXRlKF9zdWNjZXNzOiBib29sZWFuKSB7XHJcbiAgICBkZWJ1ZyhgdGVybWluYXRpbmcgYnJvd3NlciB3aXRoIHN1Y2Nlc3MgPSAke19zdWNjZXNzfWApO1xyXG4gICAgdGhpcy5lbWl0UHJvZ3Jlc3MoU2NyYXBlclByb2dyZXNzVHlwZXMuVGVybWluYXRpbmcpO1xyXG5cclxuICAgIGlmICghX3N1Y2Nlc3MgJiYgISF0aGlzLm9wdGlvbnMuc3RvcmVGYWlsdXJlU2NyZWVuU2hvdFBhdGgpIHtcclxuICAgICAgZGVidWcoYGNyZWF0ZSBhIHNuYXBzaG90IGJlZm9yZSB0ZXJtaW5hdGVkIGluICR7dGhpcy5vcHRpb25zLnN0b3JlRmFpbHVyZVNjcmVlblNob3RQYXRofWApO1xyXG4gICAgICBhd2FpdCB0aGlzLnBhZ2Uuc2NyZWVuc2hvdCh7XHJcbiAgICAgICAgcGF0aDogdGhpcy5vcHRpb25zLnN0b3JlRmFpbHVyZVNjcmVlblNob3RQYXRoLFxyXG4gICAgICAgIGZ1bGxQYWdlOiB0cnVlLFxyXG4gICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBhd2FpdCBQcm9taXNlLmFsbCh0aGlzLmNsZWFudXBzLnJldmVyc2UoKS5tYXAoKGNsZWFudXApID0+IGNsZWFudXAoKSkpO1xyXG4gICAgdGhpcy5jbGVhbnVwcyA9IFtdO1xyXG4gIH1cclxuXHJcbiAgcHJpdmF0ZSBoYW5kbGVMb2dpblJlc3VsdChsb2dpblJlc3VsdDogTG9naW5SZXN1bHRzKSB7XHJcbiAgICBzd2l0Y2ggKGxvZ2luUmVzdWx0KSB7XHJcbiAgICAgIGNhc2UgTG9naW5SZXN1bHRzLlN1Y2Nlc3M6XHJcbiAgICAgICAgdGhpcy5lbWl0UHJvZ3Jlc3MoU2NyYXBlclByb2dyZXNzVHlwZXMuTG9naW5TdWNjZXNzKTtcclxuICAgICAgICByZXR1cm4geyBzdWNjZXNzOiB0cnVlIH07XHJcbiAgICAgIGNhc2UgTG9naW5SZXN1bHRzLkludmFsaWRQYXNzd29yZDpcclxuICAgICAgY2FzZSBMb2dpblJlc3VsdHMuVW5rbm93bkVycm9yOlxyXG4gICAgICAgIHRoaXMuZW1pdFByb2dyZXNzKFNjcmFwZXJQcm9ncmVzc1R5cGVzLkxvZ2luRmFpbGVkKTtcclxuICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgc3VjY2VzczogZmFsc2UsXHJcbiAgICAgICAgICBlcnJvclR5cGU6XHJcbiAgICAgICAgICAgIGxvZ2luUmVzdWx0ID09PSBMb2dpblJlc3VsdHMuSW52YWxpZFBhc3N3b3JkID9cclxuICAgICAgICAgICAgICBTY3JhcGVyRXJyb3JUeXBlcy5JbnZhbGlkUGFzc3dvcmQgOlxyXG4gICAgICAgICAgICAgIFNjcmFwZXJFcnJvclR5cGVzLkdlbmVyYWwsXHJcbiAgICAgICAgICBlcnJvck1lc3NhZ2U6IGBMb2dpbiBmYWlsZWQgd2l0aCAke2xvZ2luUmVzdWx0fSBlcnJvcmAsXHJcbiAgICAgICAgfTtcclxuICAgICAgY2FzZSBMb2dpblJlc3VsdHMuQ2hhbmdlUGFzc3dvcmQ6XHJcbiAgICAgICAgdGhpcy5lbWl0UHJvZ3Jlc3MoU2NyYXBlclByb2dyZXNzVHlwZXMuQ2hhbmdlUGFzc3dvcmQpO1xyXG4gICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICBzdWNjZXNzOiBmYWxzZSxcclxuICAgICAgICAgIGVycm9yVHlwZTogU2NyYXBlckVycm9yVHlwZXMuQ2hhbmdlUGFzc3dvcmQsXHJcbiAgICAgICAgfTtcclxuICAgICAgZGVmYXVsdDpcclxuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYHVuZXhwZWN0ZWQgbG9naW4gcmVzdWx0IFwiJHtsb2dpblJlc3VsdH1cImApO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG5cclxuZXhwb3J0IHsgQmFzZVNjcmFwZXJXaXRoQnJvd3NlciB9O1xyXG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7QUFBQSxJQUFBQSxVQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBQyxZQUFBLEdBQUFELE9BQUE7QUFDQSxJQUFBRSxNQUFBLEdBQUFGLE9BQUE7QUFDQSxJQUFBRyxxQkFBQSxHQUFBSCxPQUFBO0FBQ0EsSUFBQUksV0FBQSxHQUFBSixPQUFBO0FBQ0EsSUFBQUssWUFBQSxHQUFBTCxPQUFBO0FBQ0EsSUFBQU0sT0FBQSxHQUFBTixPQUFBO0FBQTZDLFNBQUFELHVCQUFBUSxDQUFBLFdBQUFBLENBQUEsSUFBQUEsQ0FBQSxDQUFBQyxVQUFBLEdBQUFELENBQUEsS0FBQUUsT0FBQSxFQUFBRixDQUFBO0FBQUEsU0FBQUcsUUFBQUgsQ0FBQSxFQUFBSSxDQUFBLFFBQUFDLENBQUEsR0FBQUMsTUFBQSxDQUFBQyxJQUFBLENBQUFQLENBQUEsT0FBQU0sTUFBQSxDQUFBRSxxQkFBQSxRQUFBQyxDQUFBLEdBQUFILE1BQUEsQ0FBQUUscUJBQUEsQ0FBQVIsQ0FBQSxHQUFBSSxDQUFBLEtBQUFLLENBQUEsR0FBQUEsQ0FBQSxDQUFBQyxNQUFBLFdBQUFOLENBQUEsV0FBQUUsTUFBQSxDQUFBSyx3QkFBQSxDQUFBWCxDQUFBLEVBQUFJLENBQUEsRUFBQVEsVUFBQSxPQUFBUCxDQUFBLENBQUFRLElBQUEsQ0FBQUMsS0FBQSxDQUFBVCxDQUFBLEVBQUFJLENBQUEs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 +\ No newline at end of file +diff --git a/node_modules/israeli-bank-scrapers/lib/scrapers/interface.d.ts b/node_modules/israeli-bank-scrapers/lib/scrapers/interface.d.ts +index f871354..9278821 100644 +--- a/node_modules/israeli-bank-scrapers/lib/scrapers/interface.d.ts ++++ b/node_modules/israeli-bank-scrapers/lib/scrapers/interface.d.ts +@@ -1,4 +1,4 @@ +-import { type Browser, type Page } from 'puppeteer'; ++import { type BrowserContext, type Browser, type Page } from 'puppeteer'; + import { type CompanyTypes, type ScraperProgressTypes } from '../definitions'; + import { type TransactionsAccount } from '../transactions'; + import { type ErrorResult, type ScraperErrorTypes } from './errors'; +@@ -38,7 +38,7 @@ export interface FutureDebit { + chargeDate?: string; + bankAccountNumber?: string; + } +-export interface ScraperOptions { ++export type ScraperOptions = ScraperBrowserOptions & { + /** + * The company you want to scrape + */ +@@ -51,28 +51,65 @@ export interface ScraperOptions { + * the date to fetch transactions from (can't be before the minimum allowed time difference for the scraper) + */ + startDate: Date; +- /** +- * shows the browser while scraping, good for debugging (default false) +- */ +- showBrowser?: boolean; + /** + * scrape transactions to be processed X months in the future + */ + futureMonthsToScrape?: number; + /** +- * option from init puppeteer browser instance outside the libary scope. you can get +- * browser diretly from puppeteer via `puppeteer.launch()` ++ * if set to true, all installment transactions will be combine into the first one ++ */ ++ combineInstallments?: boolean; ++ /** ++ * if set, store a screenshot if failed to scrape. Used for debug purposes ++ */ ++ storeFailureScreenShotPath?: string; ++ /** ++ * if set, will set the timeout in milliseconds of puppeteer's `page.setDefaultTimeout`. ++ */ ++ defaultTimeout?: number; ++ /** ++ * Options for manipulation of output data ++ */ ++ outputData?: OutputDataOptions; ++ /** ++ * Perform additional operation for each transaction to get more information (Like category) about it. ++ * Please note: It will take more time to finish the process. ++ */ ++ additionalTransactionInformation?: boolean; ++ /** ++ * adjust the page instance before it is being used. ++ * ++ * @param page ++ */ ++ preparePage?: (page: Page) => Promise; ++}; ++type ScraperBrowserOptions = { ++ /** ++ * An externally created browser instance. ++ * you can get a browser directly from puppeteer via `puppeteer.launch()` ++ * ++ * Note: The browser will be closed by the library after the scraper finishes unless `skipCloseBrowser` is set to true ++ */ ++ browser: Browser; ++ /** ++ * If true, the browser will not be closed by the library after the scraper finishes ++ */ ++ skipCloseBrowser?: boolean; ++} | { ++ /** ++ * An externally managed browser context. This is useful when you want to manage the browser + */ +- browser?: any; ++ browserContext: BrowserContext; ++} | { ++ /** ++ * shows the browser while scraping, good for debugging (default false) ++ */ ++ showBrowser?: boolean; + /** + * provide a patch to local chromium to be used by puppeteer. Relevant when using + * `israeli-bank-scrapers-core` library + */ + executablePath?: string; +- /** +- * if set to true, all installment transactions will be combine into the first one +- */ +- combineInstallments?: boolean; + /** + * additional arguments to pass to the browser instance. The list of flags can be found in + * +@@ -91,30 +128,7 @@ export interface ScraperOptions { + * @param browser + */ + prepareBrowser?: (browser: Browser) => Promise; +- /** +- * adjust the page instance before it is being used. +- * +- * @param page +- */ +- preparePage?: (page: Page) => Promise; +- /** +- * if set, store a screenshot if failed to scrape. Used for debug purposes +- */ +- storeFailureScreenShotPath?: string; +- /** +- * if set, will set the timeout in milliseconds of puppeteer's `page.setDefaultTimeout`. +- */ +- defaultTimeout?: number; +- /** +- * Options for manipulation of output data +- */ +- outputData?: OutputDataOptions; +- /** +- * Perform additional operation for each transaction to get more information (Like category) about it. +- * Please note: It will take more time to finish the process. +- */ +- additionalTransactionInformation?: boolean; +-} ++}; + export interface OutputDataOptions { + /** + * if true, the result wouldn't be filtered out by date, and you will return unfiltered scrapped data. +@@ -149,3 +163,4 @@ export interface ScraperLoginResult { + errorMessage?: string; + persistentOtpToken?: string; + } ++export {}; diff --git a/src/browser.ts b/src/browser.ts new file mode 100644 index 00000000..75218bf2 --- /dev/null +++ b/src/browser.ts @@ -0,0 +1,21 @@ +import puppeteer, { + type Browser, + type PuppeteerLaunchOptions, +} from "puppeteer"; +import { createLogger } from "./utils/logger.js"; + +export const browserArgs = ["--disable-dev-shm-usage", "--no-sandbox"]; +export const browserExecutablePath = + process.env.PUPPETEER_EXECUTABLE_PATH || undefined; + +const logger = createLogger("browser"); + +export async function createBrowser(): Promise { + const options = { + args: browserArgs, + executablePath: browserExecutablePath, + } satisfies PuppeteerLaunchOptions; + + logger("Creating browser", options); + return puppeteer.launch(options); +} diff --git a/src/config.ts b/src/config.ts index 437f9d8e..bf91488f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -25,6 +25,7 @@ const { BUXFER_ACCOUNTS = "", TRANSACTION_HASH_TYPE = "", WEB_POST_URL = "", + MAX_PARALLEL_SCRAPERS = "", } = process.env; /** @@ -34,6 +35,7 @@ export const daysBackToScrape = DAYS_BACK || 10; export const worksheetName = WORKSHEET_NAME || "_moneyman"; export const futureMonthsToScrape = parseInt(FUTURE_MONTHS, 10); export const systemTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; +export const parallelScrapers = MAX_PARALLEL_SCRAPERS || 1; const accountsToScrape = ACCOUNTS_TO_SCRAPE.split(",") .filter(Boolean) diff --git a/src/data/index.ts b/src/data/index.ts index 131770a5..5fc07682 100644 --- a/src/data/index.ts +++ b/src/data/index.ts @@ -2,8 +2,15 @@ import { performance } from "perf_hooks"; import { getAccountTransactions } from "./scrape.js"; import { AccountConfig, AccountScrapeResult } from "../types.js"; import { createLogger } from "../utils/logger.js"; +import { createBrowser } from "../browser.js"; +import { send, sendError } from "../notifier.js"; +import { getFailureScreenShotPath } from "../utils/failureScreenshot.js"; +import { ScraperOptions } from "israeli-bank-scrapers"; +import { parallelScrapers } from "../config.js"; +import { parallelLimit } from "async"; +import os from "node:os"; -const logger = createLogger("data"); +const logger = createLogger("scraper"); export async function scrapeAccounts( accounts: Array, @@ -18,67 +25,74 @@ export async function scrapeAccounts( logger(`scraping %d accounts`, accounts.length); logger(`start date %s`, startDate.toISOString()); + + let futureMonths: number | undefined = undefined; if (!Number.isNaN(futureMonthsToScrape)) { logger(`months to scrap: %d`, futureMonthsToScrape); + futureMonths = futureMonthsToScrape; } const status: Array = []; - const results: Array = []; - - for (let i = 0; i < accounts.length; i++) { - const account = accounts[i]; - - logger(`scraping account #${i} (type=${account.companyId})`); - const result = await scrapeAccount( - account, - startDate, - futureMonthsToScrape, - async (message) => { - status[i] = message; - await scrapeStatusChanged?.(status); - }, - ); - results.push({ - companyId: account.companyId, - result, - }); - logger(`scraping account #${i} ended`); + logger("Creating a browser"); + const browser = await createBrowser(); + logger(`Browser created, starting to scrape ${accounts.length} accounts`); + + if (Number(parallelScrapers) > 1) { + logger(`Running with ${parallelScrapers} parallel scrapers`); + send( + `System info: ${JSON.stringify( + { + parallelScrapers: Number(parallelScrapers), + availableParallelism: os.availableParallelism(), + totalMemoryGB: (os.totalmem() / 1000000000).toFixed(2), + freeMemoryGB: (os.freemem() / 1000000000).toFixed(2), + cpus: os.cpus().length, + }, + null, + 2, + )}`, + ); } - logger(`scraping ended`); - const stats = getStats(results); - logger( - `Got ${stats.transactions} transactions from ${stats.accounts} accounts`, + const results = await parallelLimit( + accounts.map( + (account, i) => async () => + scrapeAccount( + logger.extend(`#${i} (${account.companyId})`), + account, + { + browserContext: await browser.createBrowserContext(), + startDate, + companyId: account.companyId, + futureMonthsToScrape: futureMonths, + storeFailureScreenShotPath: getFailureScreenShotPath( + account.companyId, + ), + }, + async (message, append = false) => { + status[i] = append ? `${status[i]} ${message}` : message; + return scrapeStatusChanged?.(status); + }, + ), + ), + Number(parallelScrapers), ); const duration = (performance.now() - start) / 1000; - logger(`total duration: ${duration}s`); - + logger(`scraping ended, total duration: ${duration.toFixed(1)}s`); await scrapeStatusChanged?.(status, duration); - return results; -} - -export async function scrapeAccount( - account: AccountConfig, - startDate: Date, - futureMonthsToScrape: number, - setStatusMessage: (message: string) => Promise, -) { - let message = ""; - const start = performance.now(); - const result = await getAccountTransactions( - account, - startDate, - futureMonthsToScrape, - (cid, step) => setStatusMessage((message = `[${cid}] ${step}`)), - ); + try { + logger(`closing browser`); + await browser?.close(); + } catch (e) { + sendError(e, "browser.close"); + logger(`failed to close browser`, e); + } - const duration = (performance.now() - start) / 1000; - logger(`scraping took ${duration.toFixed(1)}s`); - await setStatusMessage(`${message}, took ${duration.toFixed(1)}s`); - return result; + logger(getStats(results)); + return results; } function getStats(results: Array) { @@ -99,3 +113,28 @@ function getStats(results: Array) { transactions, }; } + +async function scrapeAccount( + logger: debug.IDebugger, + account: AccountConfig, + scraperOptions: ScraperOptions, + setStatusMessage: (message: string, append?: boolean) => Promise, +) { + logger(`scraping`); + + const scraperStart = performance.now(); + const result = await getAccountTransactions( + account, + scraperOptions, + (cid, step) => setStatusMessage(`[${cid}] ${step}`), + ); + + const duration = (performance.now() - scraperStart) / 1000; + logger(`scraping ended, took ${duration.toFixed(1)}s`); + await setStatusMessage(`, took ${duration.toFixed(1)}s`, true); + + return { + companyId: account.companyId, + result, + }; +} diff --git a/src/data/scrape.ts b/src/data/scrape.ts index e71cdffe..affda685 100644 --- a/src/data/scrape.ts +++ b/src/data/scrape.ts @@ -1,29 +1,22 @@ -import { createScraper, ScraperScrapingResult } from "israeli-bank-scrapers"; +import { + createScraper, + ScraperOptions, + ScraperScrapingResult, +} from "israeli-bank-scrapers"; import { AccountConfig } from "../types.js"; import { ScraperErrorTypes } from "israeli-bank-scrapers/lib/scrapers/errors.js"; import { createLogger } from "../utils/logger.js"; -import { getFailureScreenShotPath } from "../utils/failureScreenshot.js"; const logger = createLogger("scrape"); export async function getAccountTransactions( account: AccountConfig, - startDate: Date, - futureMonthsToScrape: number, + options: ScraperOptions, onProgress: (companyId: string, status: string) => void, ): Promise { logger(`started`); try { - const scraper = createScraper({ - executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined, - startDate, - companyId: account.companyId, - args: ["--disable-dev-shm-usage", "--no-sandbox"], - futureMonthsToScrape: Number.isNaN(futureMonthsToScrape) - ? undefined - : futureMonthsToScrape, - storeFailureScreenShotPath: getFailureScreenShotPath(account.companyId), - }); + const scraper = createScraper(options); scraper.onProgress((companyId, { type }) => { logger(`[${companyId}] ${type}`); diff --git a/src/utils/failureScreenshot.ts b/src/utils/failureScreenshot.ts index a571f452..004eb505 100644 --- a/src/utils/failureScreenshot.ts +++ b/src/utils/failureScreenshot.ts @@ -15,7 +15,7 @@ export function getFailureScreenShotPath(companyId: string) { } const filePath = path.join(companyDir, `${companyId}-${Date.now()}.png`); - logger("getFailureScreenShotPath", { filePath }); + logger("getFailureScreenShotPath %o", filePath); return filePath; }