Scouting: Add ability to scout offline
Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Change-Id: I0e0c0be033824c05b6c239d38eb79d95745fceec
diff --git a/package.json b/package.json
index b8fb10b..6853a14 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
"@angular/core": "v16-lts",
"@angular/forms": "v16-lts",
"@angular/platform-browser": "v16-lts",
+ "@angular/service-worker": "v16-lts",
"@angular/cli": "v16-lts",
"@babel/cli": "^7.16.0",
"@babel/core": "^7.16.0",
@@ -26,6 +27,7 @@
"requirejs": "2.3.6",
"rollup": "4.12.0",
"rxjs": "7.5.7",
+ "dexie": "^3.2.5",
"@rollup/plugin-node-resolve": "15.2.3",
"@types/flatbuffers": "1.10.0",
"@types/node": "20.11.19",
@@ -33,4 +35,4 @@
"terser": "5.16.4",
"zone.js": "^0.13.0"
}
-}
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 13df2c5..bf6a429 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -32,6 +32,9 @@
'@angular/platform-browser':
specifier: v16-lts
version: 16.2.12(@angular/animations@16.2.12)(@angular/common@16.2.12)(@angular/core@16.2.12)
+ '@angular/service-worker':
+ specifier: v16-lts
+ version: 16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12)
'@babel/cli':
specifier: ^7.16.0
version: 7.23.9(@babel/core@7.23.9)
@@ -65,6 +68,9 @@
cypress:
specifier: 13.3.1
version: 13.3.1
+ dexie:
+ specifier: ^3.2.5
+ version: 3.2.5(karma@6.4.3)
html-insert-assets:
specifier: 0.14.3
version: 0.14.3
@@ -341,6 +347,19 @@
'@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
tslib: 2.6.0
+ /@angular/service-worker@16.2.12(@angular/common@16.2.12)(@angular/core@16.2.12):
+ resolution: {integrity: sha512-o0z0s4c76NmRASa+mUHn/q6vUKQNa06mGmLBDKm84vRQ1sQ2TJv+R1p8K9WkiM5mGy6tjQCDOgaz13TcxMFWOQ==}
+ engines: {node: ^16.14.0 || >=18.10.0}
+ hasBin: true
+ peerDependencies:
+ '@angular/common': 16.2.12
+ '@angular/core': 16.2.12
+ dependencies:
+ '@angular/common': 16.2.12(@angular/core@16.2.12)(rxjs@7.5.7)
+ '@angular/core': 16.2.12(rxjs@7.5.7)(zone.js@0.13.3)
+ tslib: 2.6.0
+ dev: true
+
/@babel/cli@7.23.9(@babel/core@7.23.9):
resolution: {integrity: sha512-vB1UXmGDNEhcf1jNAHKT9IlYk1R+hehVTLFlCLHBi8gfuHQGP6uRjgXVYU0EVlI/qwAWpstqkBdf2aez3/z/5Q==}
engines: {node: '>=6.9.0'}
@@ -594,7 +613,6 @@
engines: {node: '>=0.1.90'}
requiresBuild: true
dev: true
- optional: true
/@cypress/request@3.0.1:
resolution: {integrity: sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==}
@@ -952,6 +970,10 @@
- supports-color
dev: true
+ /@socket.io/component-emitter@3.1.0:
+ resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
+ dev: true
+
/@tootallnate/once@2.0.0:
resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==}
engines: {node: '>= 10'}
@@ -999,6 +1021,16 @@
'@babel/types': 7.23.9
dev: true
+ /@types/cookie@0.4.1:
+ resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
+ dev: true
+
+ /@types/cors@2.8.17:
+ resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
+ dependencies:
+ '@types/node': 20.11.19
+ dev: true
+
/@types/estree@1.0.5:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: true
@@ -1011,8 +1043,8 @@
resolution: {integrity: sha512-SWyMrjgdAUHNQmutvDcKablrJhkDLy4wunTme8oYLjKp41GnHGxMRXr2MQMvy/qy8H3LdzwQk9gH4hZ6T++H8g==}
dev: true
- /@types/node@18.19.18:
- resolution: {integrity: sha512-80CP7B8y4PzZF0GWx15/gVWRrB5y/bIjNI84NK3cmQJu0WZwvmj2WMA5LcofQFVfLqqCSp545+U2LsrVzX36Zg==}
+ /@types/node@18.19.22:
+ resolution: {integrity: sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==}
dependencies:
undici-types: 5.26.5
dev: true
@@ -1054,6 +1086,14 @@
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
dev: true
+ /accepts@1.3.8:
+ resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+ engines: {node: '>= 0.6'}
+ dependencies:
+ mime-types: 2.1.35
+ negotiator: 0.6.3
+ dev: true
+
/acorn@8.9.0:
resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
engines: {node: '>=0.4.0'}
@@ -1224,6 +1264,11 @@
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
dev: true
+ /base64id@2.0.0:
+ resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
+ engines: {node: ^4.5.0 || >= 5.9}
+ dev: true
+
/bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
dependencies:
@@ -1251,6 +1296,26 @@
resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
dev: true
+ /body-parser@1.20.2:
+ resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
+ engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+ dependencies:
+ bytes: 3.1.2
+ content-type: 1.0.5
+ debug: 2.6.9
+ depd: 2.0.0
+ destroy: 1.2.0
+ http-errors: 2.0.0
+ iconv-lite: 0.4.24
+ on-finished: 2.4.1
+ qs: 6.11.0
+ raw-body: 2.5.2
+ type-is: 1.6.18
+ unpipe: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@@ -1308,6 +1373,11 @@
semver: 7.6.0
dev: true
+ /bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
/cacache@16.1.3:
resolution: {integrity: sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==}
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -1475,6 +1545,14 @@
wrap-ansi: 6.2.0
dev: true
+ /cliui@7.0.4:
+ resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+ dev: true
+
/cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
@@ -1549,10 +1627,27 @@
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
+ /connect@3.7.0:
+ resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==}
+ engines: {node: '>= 0.10.0'}
+ dependencies:
+ debug: 2.6.9
+ finalhandler: 1.1.2
+ parseurl: 1.3.3
+ utils-merge: 1.0.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/console-control-strings@1.1.0:
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
dev: true
+ /content-type@1.0.5:
+ resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+ engines: {node: '>= 0.6'}
+ dev: true
+
/convert-source-map@1.9.0:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
dev: true
@@ -1561,10 +1656,23 @@
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
dev: true
+ /cookie@0.4.2:
+ resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
+ engines: {node: '>= 0.6'}
+ dev: true
+
/core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
dev: true
+ /cors@2.8.5:
+ resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+ engines: {node: '>= 0.10'}
+ dependencies:
+ object-assign: 4.1.1
+ vary: 1.1.2
+ dev: true
+
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -1574,6 +1682,10 @@
which: 2.0.2
dev: true
+ /custom-event@1.0.1:
+ resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==}
+ dev: true
+
/cypress@13.3.1:
resolution: {integrity: sha512-g4mJLZxYN+UAF2LMy3Znd4LBnUmS59Vynd81VES59RdW48Yt+QtR2cush3melOoVNz0PPbADpWr8DcUx6mif8Q==}
engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
@@ -1582,7 +1694,7 @@
dependencies:
'@cypress/request': 3.0.1
'@cypress/xvfb': 1.2.4(supports-color@8.1.1)
- '@types/node': 18.19.18
+ '@types/node': 18.19.22
'@types/sinonjs__fake-timers': 8.1.1
'@types/sizzle': 2.3.3
arch: 2.2.0
@@ -1632,10 +1744,26 @@
assert-plus: 1.0.0
dev: true
+ /date-format@4.0.14:
+ resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==}
+ engines: {node: '>=4.0'}
+ dev: true
+
/dayjs@1.11.9:
resolution: {integrity: sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==}
dev: true
+ /debug@2.6.9:
+ resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ dependencies:
+ ms: 2.0.0
+ dev: true
+
/debug@3.2.7(supports-color@8.1.1):
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
peerDependencies:
@@ -1691,10 +1819,42 @@
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
dev: true
+ /depd@2.0.0:
+ resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
+ /destroy@1.2.0:
+ resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+ engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+ dev: true
+
+ /dexie@3.2.5(karma@6.4.3):
+ resolution: {integrity: sha512-MA7vYQvXxWN2+G50D0GLS4FqdYUyRYQsN0FikZIVebOmRoNCSCL9+eUbIF80dqrfns3kmY+83+hE2GN9CnAGyA==}
+ engines: {node: '>=6.0'}
+ dependencies:
+ karma-safari-launcher: 1.0.0(karma@6.4.3)
+ transitivePeerDependencies:
+ - karma
+ dev: true
+
+ /di@0.0.1:
+ resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==}
+ dev: true
+
/dijkstrajs@1.0.3:
resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==}
dev: true
+ /dom-serialize@2.2.1:
+ resolution: {integrity: sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==}
+ dependencies:
+ custom-event: 1.0.1
+ ent: 2.2.0
+ extend: 3.0.2
+ void-elements: 2.0.1
+ dev: true
+
/eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true
@@ -1706,6 +1866,10 @@
safer-buffer: 2.1.2
dev: true
+ /ee-first@1.1.1:
+ resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+ dev: true
+
/electron-to-chromium@1.4.679:
resolution: {integrity: sha512-NhQMsz5k0d6m9z3qAxnsOR/ebal4NAGsrNVRwcDo4Kc/zQ7KdsTKZUxZoygHcVRb0QDW3waEDIcE3isZ79RP6g==}
dev: true
@@ -1722,6 +1886,11 @@
resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
dev: true
+ /encodeurl@1.0.2:
+ resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
/encoding@0.1.13:
resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==}
requiresBuild: true
@@ -1736,6 +1905,31 @@
once: 1.4.0
dev: true
+ /engine.io-parser@5.2.2:
+ resolution: {integrity: sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==}
+ engines: {node: '>=10.0.0'}
+ dev: true
+
+ /engine.io@6.5.4:
+ resolution: {integrity: sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==}
+ engines: {node: '>=10.2.0'}
+ dependencies:
+ '@types/cookie': 0.4.1
+ '@types/cors': 2.8.17
+ '@types/node': 20.11.19
+ accepts: 1.3.8
+ base64id: 2.0.0
+ cookie: 0.4.2
+ cors: 2.8.5
+ debug: 4.3.4(supports-color@8.1.1)
+ engine.io-parser: 5.2.2
+ ws: 8.11.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ dev: true
+
/enquirer@2.3.6:
resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
engines: {node: '>=8.6'}
@@ -1743,6 +1937,10 @@
ansi-colors: 4.1.3
dev: true
+ /ent@2.2.0:
+ resolution: {integrity: sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==}
+ dev: true
+
/env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
engines: {node: '>=6'}
@@ -1757,6 +1955,10 @@
engines: {node: '>=6'}
dev: true
+ /escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+ dev: true
+
/escape-string-regexp@1.0.5:
resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
engines: {node: '>=0.8.0'}
@@ -1770,6 +1972,10 @@
resolution: {integrity: sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==}
dev: true
+ /eventemitter3@4.0.7:
+ resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+ dev: true
+
/execa@4.1.0:
resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
engines: {node: '>=10'}
@@ -1852,6 +2058,21 @@
to-regex-range: 5.0.1
dev: true
+ /finalhandler@1.1.2:
+ resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ debug: 2.6.9
+ encodeurl: 1.0.2
+ escape-html: 1.0.3
+ on-finished: 2.3.0
+ parseurl: 1.3.3
+ statuses: 1.5.0
+ unpipe: 1.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/find-up@4.1.0:
resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
engines: {node: '>=8'}
@@ -1860,6 +2081,20 @@
path-exists: 4.0.0
dev: true
+ /flatted@3.3.1:
+ resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
+ dev: true
+
+ /follow-redirects@1.15.5:
+ resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+ dev: true
+
/foreground-child@3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
@@ -1881,6 +2116,15 @@
mime-types: 2.1.35
dev: true
+ /fs-extra@8.1.0:
+ resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==}
+ engines: {node: '>=6 <7 || >=8'}
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 4.0.0
+ universalify: 0.1.2
+ dev: true
+
/fs-extra@9.1.0:
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
engines: {node: '>=10'}
@@ -2095,6 +2339,17 @@
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
dev: true
+ /http-errors@2.0.0:
+ resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.1
+ toidentifier: 1.0.1
+ dev: true
+
/http-proxy-agent@5.0.0:
resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==}
engines: {node: '>= 6'}
@@ -2106,6 +2361,17 @@
- supports-color
dev: true
+ /http-proxy@1.18.1:
+ resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
+ engines: {node: '>=8.0.0'}
+ dependencies:
+ eventemitter3: 4.0.7
+ follow-redirects: 1.15.5
+ requires-port: 1.0.0
+ transitivePeerDependencies:
+ - debug
+ dev: true
+
/http-signature@1.3.6:
resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==}
engines: {node: '>=0.10'}
@@ -2331,6 +2597,11 @@
is-docker: 2.2.1
dev: true
+ /isbinaryfile@4.0.10:
+ resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==}
+ engines: {node: '>= 8.0.0'}
+ dev: true
+
/isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
dev: true
@@ -2389,6 +2660,12 @@
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
dev: true
+ /jsonfile@4.0.0:
+ resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+ optionalDependencies:
+ graceful-fs: 4.2.11
+ dev: true
+
/jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
dependencies:
@@ -2412,6 +2689,50 @@
verror: 1.10.0
dev: true
+ /karma-safari-launcher@1.0.0(karma@6.4.3):
+ resolution: {integrity: sha512-qmypLWd6F2qrDJfAETvXDfxHvKDk+nyIjpH9xIeI3/hENr0U3nuqkxaftq73PfXZ4aOuOChA6SnLW4m4AxfRjQ==}
+ peerDependencies:
+ karma: '>=0.9'
+ dependencies:
+ karma: 6.4.3
+ dev: true
+
+ /karma@6.4.3:
+ resolution: {integrity: sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==}
+ engines: {node: '>= 10'}
+ hasBin: true
+ dependencies:
+ '@colors/colors': 1.5.0
+ body-parser: 1.20.2
+ braces: 3.0.2
+ chokidar: 3.5.3
+ connect: 3.7.0
+ di: 0.0.1
+ dom-serialize: 2.2.1
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ http-proxy: 1.18.1
+ isbinaryfile: 4.0.10
+ lodash: 4.17.21
+ log4js: 6.9.1
+ mime: 2.6.0
+ minimatch: 3.1.2
+ mkdirp: 0.5.6
+ qjobs: 1.2.0
+ range-parser: 1.2.1
+ rimraf: 3.0.2
+ socket.io: 4.7.4
+ source-map: 0.6.1
+ tmp: 0.2.1
+ ua-parser-js: 0.7.37
+ yargs: 16.2.0
+ transitivePeerDependencies:
+ - bufferutil
+ - debug
+ - supports-color
+ - utf-8-validate
+ dev: true
+
/lazy-ass@1.6.0:
resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==}
engines: {node: '> 0.8'}
@@ -2470,6 +2791,19 @@
wrap-ansi: 6.2.0
dev: true
+ /log4js@6.9.1:
+ resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==}
+ engines: {node: '>=8.0'}
+ dependencies:
+ date-format: 4.0.14
+ debug: 4.3.4(supports-color@8.1.1)
+ flatted: 3.3.1
+ rfdc: 1.3.0
+ streamroller: 3.1.5
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/lru-cache@10.2.0:
resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==}
engines: {node: 14 || >=16.14}
@@ -2556,6 +2890,11 @@
- supports-color
dev: true
+ /media-typer@0.3.0:
+ resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+ engines: {node: '>= 0.6'}
+ dev: true
+
/merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: true
@@ -2572,6 +2911,12 @@
mime-db: 1.52.0
dev: true
+ /mime@2.6.0:
+ resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==}
+ engines: {node: '>=4.0.0'}
+ hasBin: true
+ dev: true
+
/mimic-fn@2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
@@ -2683,12 +3028,23 @@
yallist: 4.0.0
dev: true
+ /mkdirp@0.5.6:
+ resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+ hasBin: true
+ dependencies:
+ minimist: 1.2.8
+ dev: true
+
/mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
hasBin: true
dev: true
+ /ms@2.0.0:
+ resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+ dev: true
+
/ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
@@ -2832,10 +3188,29 @@
set-blocking: 2.0.0
dev: true
+ /object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/object-inspect@1.12.3:
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
dev: true
+ /on-finished@2.3.0:
+ resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ ee-first: 1.1.1
+ dev: true
+
+ /on-finished@2.4.1:
+ resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ ee-first: 1.1.1
+ dev: true
+
/once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
dependencies:
@@ -2943,6 +3318,11 @@
resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
dev: true
+ /parseurl@1.3.3:
+ resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
/path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -3060,6 +3440,11 @@
engines: {node: '>=6'}
dev: true
+ /qjobs@1.2.0:
+ resolution: {integrity: sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==}
+ engines: {node: '>=0.9'}
+ dev: true
+
/qrcode@1.5.3:
resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==}
engines: {node: '>=10.13.0'}
@@ -3078,10 +3463,32 @@
side-channel: 1.0.4
dev: true
+ /qs@6.11.0:
+ resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
+ engines: {node: '>=0.6'}
+ dependencies:
+ side-channel: 1.0.4
+ dev: true
+
/querystringify@2.2.0:
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
dev: true
+ /range-parser@1.2.1:
+ resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+ engines: {node: '>= 0.6'}
+ dev: true
+
+ /raw-body@2.5.2:
+ resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ bytes: 3.1.2
+ http-errors: 2.0.0
+ iconv-lite: 0.4.24
+ unpipe: 1.0.0
+ dev: true
+
/read-package-json-fast@3.0.2:
resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -3260,6 +3667,10 @@
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
dev: true
+ /setprototypeof@1.2.0:
+ resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+ dev: true
+
/shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -3331,6 +3742,44 @@
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
dev: true
+ /socket.io-adapter@2.5.4:
+ resolution: {integrity: sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==}
+ dependencies:
+ debug: 4.3.4(supports-color@8.1.1)
+ ws: 8.11.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ dev: true
+
+ /socket.io-parser@4.2.4:
+ resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
+ engines: {node: '>=10.0.0'}
+ dependencies:
+ '@socket.io/component-emitter': 3.1.0
+ debug: 4.3.4(supports-color@8.1.1)
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /socket.io@4.7.4:
+ resolution: {integrity: sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==}
+ engines: {node: '>=10.2.0'}
+ dependencies:
+ accepts: 1.3.8
+ base64id: 2.0.0
+ cors: 2.8.5
+ debug: 4.3.4(supports-color@8.1.1)
+ engine.io: 6.5.4
+ socket.io-adapter: 2.5.4
+ socket.io-parser: 4.2.4
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ dev: true
+
/socks-proxy-agent@7.0.0:
resolution: {integrity: sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==}
engines: {node: '>= 10'}
@@ -3419,6 +3868,27 @@
minipass: 3.3.6
dev: true
+ /statuses@1.5.0:
+ resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
+ engines: {node: '>= 0.6'}
+ dev: true
+
+ /statuses@2.0.1:
+ resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
+ /streamroller@3.1.5:
+ resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==}
+ engines: {node: '>=8.0'}
+ dependencies:
+ date-format: 4.0.14
+ debug: 4.3.4(supports-color@8.1.1)
+ fs-extra: 8.1.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -3550,6 +4020,11 @@
is-number: 7.0.0
dev: true
+ /toidentifier@1.0.1:
+ resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+ engines: {node: '>=0.6'}
+ dev: true
+
/tough-cookie@4.1.3:
resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==}
engines: {node: '>=6'}
@@ -3589,12 +4064,24 @@
engines: {node: '>=10'}
dev: true
+ /type-is@1.6.18:
+ resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+ engines: {node: '>= 0.6'}
+ dependencies:
+ media-typer: 0.3.0
+ mime-types: 2.1.35
+ dev: true
+
/typescript@5.1.6:
resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
+ /ua-parser-js@0.7.37:
+ resolution: {integrity: sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==}
+ dev: true
+
/undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
dev: true
@@ -3627,6 +4114,11 @@
imurmurhash: 0.1.4
dev: true
+ /universalify@0.1.2:
+ resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+ engines: {node: '>= 4.0.0'}
+ dev: true
+
/universalify@0.2.0:
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
engines: {node: '>= 4.0.0'}
@@ -3637,6 +4129,11 @@
engines: {node: '>= 10.0.0'}
dev: true
+ /unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
/untildify@4.0.0:
resolution: {integrity: sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==}
engines: {node: '>=8'}
@@ -3670,6 +4167,11 @@
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
dev: true
+ /utils-merge@1.0.1:
+ resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+ engines: {node: '>= 0.4.0'}
+ dev: true
+
/uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
@@ -3689,6 +4191,11 @@
builtins: 5.0.1
dev: true
+ /vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+ dev: true
+
/verror@1.10.0:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0}
@@ -3698,6 +4205,11 @@
extsprintf: 1.3.0
dev: true
+ /void-elements@2.0.1:
+ resolution: {integrity: sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
dependencies:
@@ -3761,6 +4273,19 @@
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
dev: true
+ /ws@8.11.0:
+ resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ dev: true
+
/y18n@4.0.3:
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
dev: true
@@ -3786,6 +4311,11 @@
decamelize: 1.2.0
dev: true
+ /yargs-parser@20.2.9:
+ resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+ engines: {node: '>=10'}
+ dev: true
+
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -3808,6 +4338,19 @@
yargs-parser: 18.1.3
dev: true
+ /yargs@16.2.0:
+ resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+ engines: {node: '>=10'}
+ dependencies:
+ cliui: 7.0.4
+ escalade: 3.1.1
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 20.2.9
+ dev: true
+
/yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
diff --git a/scouting/scouting_test.cy.js b/scouting/scouting_test.cy.js
index 31ca935..d15cdfc 100644
--- a/scouting/scouting_test.cy.js
+++ b/scouting/scouting_test.cy.js
@@ -117,15 +117,27 @@
clickButton('Submit');
headerShouldBe(teamNumber + ' Success ');
}
+function visit(path) {
+ cy.visit(path, {
+ onBeforeLoad(win) {
+ // The service worker seems to interfere with Cypress somehow. There
+ // doesn't seem to be a proper fix for this issue. Work around it with
+ // this hack that disables the service worker.
+ // https://github.com/cypress-io/cypress/issues/16192#issuecomment-870421667
+ // https://github.com/cypress-io/cypress/issues/702#issuecomment-587127275
+ delete win.navigator.__proto__.serviceWorker;
+ },
+ });
+}
before(() => {
- cy.visit('/');
+ visit('/');
disableAlerts();
cy.title().should('eq', 'FRC971 Scouting Application');
});
beforeEach(() => {
- cy.visit('/');
+ visit('/');
disableAlerts();
});
diff --git a/scouting/www/BUILD b/scouting/www/BUILD
index b46f265..a6ca0a1 100644
--- a/scouting/www/BUILD
+++ b/scouting/www/BUILD
@@ -1,10 +1,19 @@
load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file")
+load("@aspect_rules_js//js:defs.bzl", "js_binary", "js_run_binary")
+load("@npm//:@angular/service-worker/package_json.bzl", angular_service_worker = "bin")
load("@npm//:defs.bzl", "npm_link_all_packages")
load("//tools/build_rules:js.bzl", "ng_application")
-load(":defs.bzl", "assemble_static_files")
+load(":defs.bzl", "assemble_service_worker_files", "assemble_static_files")
npm_link_all_packages(name = "node_modules")
+assemble_service_worker_files(
+ name = "service_worker_files",
+ outs = [
+ "ngsw-worker.js",
+ ],
+)
+
OPENCV_VERSION = "4.9.0"
copy_file(
@@ -16,6 +25,7 @@
ng_application(
name = "app",
assets = [
+ "manifest.json",
":opencv.js",
],
extra_srcs = [
@@ -23,9 +33,11 @@
],
html_assets = [
"favicon.ico",
+ "assets/971_144.png",
],
deps = [
"//:node_modules/@angular/animations",
+ "//:node_modules/@angular/service-worker",
"//scouting/www/driver_ranking",
"//scouting/www/entry",
"//scouting/www/match_list",
@@ -42,6 +54,8 @@
app_files = ":app",
pictures = [
"//third_party/y2024/field:pictures",
+ ":ngsw-worker.js",
+ ":ngsw.json",
],
replace_prefixes = {
"prod": "",
@@ -60,3 +74,35 @@
out = "app/common.css",
visibility = ["//scouting/www:__subpackages__"],
)
+
+angular_service_worker.ngsw_config_binary(
+ name = "ngsw_config_binary",
+)
+
+js_binary(
+ name = "ngsw_config_wrapper",
+ data = [
+ ":ngsw_config_binary",
+ ],
+ entry_point = "ngsw_config_wrapper.js",
+)
+
+js_run_binary(
+ name = "ngsw_config",
+ srcs = [
+ "manifest.json",
+ "ngsw-config.json",
+ ":app",
+ ":ngsw_config_binary",
+ ],
+ outs = [
+ "ngsw.json",
+ ],
+ args = [
+ "$(rootpath :ngsw_config_binary)",
+ "$(rootpath :ngsw.json)",
+ "$(rootpath :prod)",
+ "$(rootpath ngsw-config.json)",
+ ],
+ tool = ":ngsw_config_wrapper",
+)
diff --git a/scouting/www/app/app.module.ts b/scouting/www/app/app.module.ts
index ccc4840..decd1f3 100644
--- a/scouting/www/app/app.module.ts
+++ b/scouting/www/app/app.module.ts
@@ -1,6 +1,7 @@
-import {NgModule} from '@angular/core';
+import {NgModule, isDevMode} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+import {ServiceWorkerModule} from '@angular/service-worker';
import {App} from './app';
import {EntryModule} from '../entry';
@@ -17,6 +18,12 @@
imports: [
BrowserModule,
BrowserAnimationsModule,
+ ServiceWorkerModule.register('./ngsw-worker.js', {
+ enabled: !isDevMode(),
+ // Register the ServiceWorker as soon as the application is stable
+ // or after 30 seconds (whichever comes first).
+ registrationStrategy: 'registerWhenStable:30000',
+ }),
EntryModule,
NotesModule,
MatchListModule,
diff --git a/scouting/www/app/app.ts b/scouting/www/app/app.ts
index 895336b..ab15ae5 100644
--- a/scouting/www/app/app.ts
+++ b/scouting/www/app/app.ts
@@ -1,4 +1,4 @@
-import {Component, ElementRef, ViewChild} from '@angular/core';
+import {Component, ElementRef, ViewChild, isDevMode} from '@angular/core';
type Tab =
| 'MatchList'
@@ -40,6 +40,8 @@
@ViewChild('block_alerts') block_alerts: ElementRef;
constructor() {
+ console.log(`Using development mode: ${isDevMode()}`);
+
window.addEventListener('beforeunload', (e) => {
if (!unguardedTabs.includes(this.tab)) {
if (!this.block_alerts.nativeElement.checked) {
diff --git a/scouting/www/assets/971_144.png b/scouting/www/assets/971_144.png
new file mode 100644
index 0000000..881edfa
--- /dev/null
+++ b/scouting/www/assets/971_144.png
Binary files differ
diff --git a/scouting/www/defs.bzl b/scouting/www/defs.bzl
index 828f30a..e7fb44a 100644
--- a/scouting/www/defs.bzl
+++ b/scouting/www/defs.bzl
@@ -27,6 +27,7 @@
),
"pictures": attr.label_list(
mandatory = True,
+ allow_files = True,
),
"replace_prefixes": attr.string_dict(
mandatory = True,
@@ -34,3 +35,45 @@
},
toolchains = ["@aspect_bazel_lib//lib:copy_to_directory_toolchain_type"],
)
+
+def _assemble_service_worker_files_impl(ctx):
+ args = ctx.actions.args()
+ args.add_all(ctx.attr._package.files, before_each = "--input_dir", expand_directories = False)
+ args.add_all(ctx.outputs.outs, before_each = "--output")
+ args.add_all(ctx.attr.outs_as_strings, before_each = "--relative_output")
+ ctx.actions.run(
+ inputs = ctx.attr._package.files,
+ outputs = ctx.outputs.outs,
+ executable = ctx.executable._tool,
+ arguments = [args],
+ mnemonic = "AssembleAngularServiceWorker",
+ )
+
+_assemble_service_worker_files = rule(
+ implementation = _assemble_service_worker_files_impl,
+ attrs = {
+ "outs": attr.output_list(
+ allow_empty = False,
+ mandatory = True,
+ ),
+ "outs_as_strings": attr.string_list(
+ allow_empty = False,
+ mandatory = True,
+ ),
+ "_package": attr.label(
+ default = "//:node_modules/@angular/service-worker",
+ ),
+ "_tool": attr.label(
+ default = "//tools/build_rules/js:assemble_service_worker_files",
+ cfg = "exec",
+ executable = True,
+ ),
+ },
+)
+
+def assemble_service_worker_files(outs, **kwargs):
+ _assemble_service_worker_files(
+ outs = outs,
+ outs_as_strings = outs,
+ **kwargs
+ )
diff --git a/scouting/www/index.html b/scouting/www/index.html
index e4e996a..821acf2 100644
--- a/scouting/www/index.html
+++ b/scouting/www/index.html
@@ -4,6 +4,8 @@
<meta charset="utf-8" />
<title>FRC971 Scouting Application</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
+ <meta name="theme-color" content="#000000" />
+ <link rel="manifest" href="/manifest.json" />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css"
@@ -25,5 +27,8 @@
</head>
<body>
<my-app></my-app>
+ <noscript>
+ Please enable JavaScript to continue using this application.
+ </noscript>
</body>
</html>
diff --git a/scouting/www/manifest.json b/scouting/www/manifest.json
new file mode 100644
index 0000000..9366399
--- /dev/null
+++ b/scouting/www/manifest.json
@@ -0,0 +1,17 @@
+{
+ "name": "FRC971 Scouting App",
+ "short_name": "scouting",
+ "theme_color": "#1976d2",
+ "background_color": "#fafafa",
+ "display": "standalone",
+ "scope": "./",
+ "start_url": "./",
+ "icons": [
+ {
+ "src": "assets/971_144.png",
+ "sizes": "144x144",
+ "type": "image/png",
+ "purpose": "maskable any"
+ }
+ ]
+}
diff --git a/scouting/www/ngsw-config.json b/scouting/www/ngsw-config.json
new file mode 100644
index 0000000..78d921d
--- /dev/null
+++ b/scouting/www/ngsw-config.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "./node_modules/@angular/service-worker/config/schema.json",
+ "index": "/index.html",
+ "assetGroups": [
+ {
+ "name": "app",
+ "installMode": "prefetch",
+ "resources": {
+ "files": [
+ "/favicon.ico",
+ "/index.html",
+ "/manifest.json",
+ "/*.css",
+ "/bundle-*/*.js",
+ "/*.js"
+ ]
+ }
+ },
+ {
+ "name": "assets",
+ "installMode": "lazy",
+ "updateMode": "prefetch",
+ "resources": {
+ "files": [
+ "/assets/**",
+ "/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
+ ]
+ }
+ }
+ ]
+ }
+
diff --git a/scouting/www/ngsw_config_wrapper.js b/scouting/www/ngsw_config_wrapper.js
new file mode 100644
index 0000000..0bc7f19
--- /dev/null
+++ b/scouting/www/ngsw_config_wrapper.js
@@ -0,0 +1,37 @@
+const fs = require('fs');
+const path = require('path');
+const {spawnSync} = require('child_process');
+
+const output_dir = path.join(
+ process.env.BAZEL_BINDIR,
+ process.env.BAZEL_PACKAGE
+);
+console.log(output_dir);
+console.log(process.argv[2]);
+console.log(process.cwd());
+const ngsw_config = process.argv[2];
+console.log(`Trying to run ${ngsw_config} ${process.argv.slice(4).join(' ')}`);
+const result = spawnSync(ngsw_config, process.argv.slice(4), {
+ stdout: 'inherit',
+ stderr: 'inherit',
+});
+
+if (result.status || result.error || result.signal) {
+ console.log("Failed to run 'ngsw_config'");
+ console.log(`status: ${result.status}`);
+ console.log(`error: ${result.error}`);
+ console.log(`signal: ${result.signal}`);
+ console.log(`stdout: ${result.stdout}`);
+ console.log(`stderr: ${result.stderr}`);
+ process.exit(1);
+}
+
+const currentDirectory = process.cwd();
+
+// Read the contents of the current directory
+console.log(`Contents of the current directory: ${currentDirectory}`);
+fs.readdirSync(currentDirectory).forEach((file) => {
+ console.log(file);
+});
+
+fs.copyFileSync(path.join(process.argv[4], 'ngsw.json'), process.argv[3]);
diff --git a/scouting/www/rpc/BUILD b/scouting/www/rpc/BUILD
index d1367ea..592735c 100644
--- a/scouting/www/rpc/BUILD
+++ b/scouting/www/rpc/BUILD
@@ -10,6 +10,7 @@
],
generate_public_api = False,
deps = [
+ "//:node_modules/dexie",
"//scouting/webserver/requests/messages:error_response_ts_fbs",
"//scouting/webserver/requests/messages:request_2024_data_scouting_response_ts_fbs",
"//scouting/webserver/requests/messages:request_2024_data_scouting_ts_fbs",
diff --git a/scouting/www/rpc/db.ts b/scouting/www/rpc/db.ts
new file mode 100644
index 0000000..789ac0e
--- /dev/null
+++ b/scouting/www/rpc/db.ts
@@ -0,0 +1,18 @@
+import Dexie, {Table} from 'dexie';
+
+export interface MatchListData {
+ id?: number;
+ data: Uint8Array;
+}
+
+export class AppDB extends Dexie {
+ matchListData!: Table<MatchListData, number>;
+
+ constructor() {
+ super('ngdexieliveQuery');
+ this.version(1).stores({
+ matchListData: 'id,data',
+ });
+ }
+}
+export const db = new AppDB();
diff --git a/scouting/www/rpc/match_list_requestor.ts b/scouting/www/rpc/match_list_requestor.ts
index fa2dcbd..0a812ce 100644
--- a/scouting/www/rpc/match_list_requestor.ts
+++ b/scouting/www/rpc/match_list_requestor.ts
@@ -6,78 +6,82 @@
Match,
RequestAllMatchesResponse,
} from '../../webserver/requests/messages/request_all_matches_response_generated';
-
+import {db, MatchListData} from './db';
const MATCH_TYPE_ORDERING = ['qm', 'ef', 'qf', 'sf', 'f'];
-
@Injectable({providedIn: 'root'})
export class MatchListRequestor {
async fetchMatchList(): Promise<Match[]> {
const builder = new Builder();
RequestAllMatches.startRequestAllMatches(builder);
builder.finish(RequestAllMatches.endRequestAllMatches(builder));
-
const buffer = builder.asUint8Array();
const res = await fetch('/requests/request/all_matches', {
method: 'POST',
body: buffer,
});
-
if (res.ok) {
const resBuffer = await res.arrayBuffer();
- const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
- const parsedResponse =
- RequestAllMatchesResponse.getRootAsRequestAllMatchesResponse(fbBuffer);
-
- // Convert the flatbuffer list into an array. That's more useful.
- const matchList = [];
- for (let i = 0; i < parsedResponse.matchListLength(); i++) {
- matchList.push(parsedResponse.matchList(i));
- }
-
- // Sort the list so it is in chronological order.
- matchList.sort((a, b) => {
- // First sort by match type. E.g. finals are last.
- const aMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(a.compLevel());
- const bMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(b.compLevel());
- if (aMatchTypeIndex < bMatchTypeIndex) {
- return -1;
- }
- if (aMatchTypeIndex > bMatchTypeIndex) {
- return 1;
- }
- // Then sort by match number. E.g. in semi finals, all match 1 rounds
- // are done first. Then come match 2 rounds. And then, if necessary,
- // the match 3 rounds.
- const aMatchNumber = a.matchNumber();
- const bMatchNumber = b.matchNumber();
- if (aMatchNumber < bMatchNumber) {
- return -1;
- }
- if (aMatchNumber > bMatchNumber) {
- return 1;
- }
- // Lastly, sort by set number. I.e. Semi Final 1 Match 1 happens first.
- // Then comes Semi Final 2 Match 1. Then comes Semi Final 1 Match 2. Then
- // Semi Final 2 Match 2.
- const aSetNumber = a.setNumber();
- const bSetNumber = b.setNumber();
- if (aSetNumber < bSetNumber) {
- return -1;
- }
- if (aSetNumber > bSetNumber) {
- return 1;
- }
- return 0;
- });
-
- return matchList;
+ const u8Buffer = new Uint8Array(resBuffer);
+ // Cache the response.
+ await db.matchListData.put({id: 1, data: u8Buffer});
+ return this.parseMatchList(u8Buffer);
} else {
+ const cachedResult = await db.matchListData.where({id: 1}).toArray();
+ if (cachedResult && cachedResult.length == 1) {
+ const u8Buffer = cachedResult[0].data;
+ return this.parseMatchList(u8Buffer);
+ }
const resBuffer = await res.arrayBuffer();
const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
-
const errorMessage = parsedResponse.errorMessage();
throw `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
}
}
+ parseMatchList(u8Buffer: Uint8Array): Match[] {
+ const fbBuffer = new ByteBuffer(u8Buffer);
+ const parsedResponse =
+ RequestAllMatchesResponse.getRootAsRequestAllMatchesResponse(fbBuffer);
+ // Convert the flatbuffer list into an array. That's more useful.
+ const matchList = [];
+ for (let i = 0; i < parsedResponse.matchListLength(); i++) {
+ matchList.push(parsedResponse.matchList(i));
+ }
+ // Sort the list so it is in chronological order.
+ matchList.sort((a, b) => {
+ // First sort by match type. E.g. finals are last.
+ const aMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(a.compLevel());
+ const bMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(b.compLevel());
+ if (aMatchTypeIndex < bMatchTypeIndex) {
+ return -1;
+ }
+ if (aMatchTypeIndex > bMatchTypeIndex) {
+ return 1;
+ }
+ // Then sort by match number. E.g. in semi finals, all match 1 rounds
+ // are done first. Then come match 2 rounds. And then, if necessary,
+ // the match 3 rounds.
+ const aMatchNumber = a.matchNumber();
+ const bMatchNumber = b.matchNumber();
+ if (aMatchNumber < bMatchNumber) {
+ return -1;
+ }
+ if (aMatchNumber > bMatchNumber) {
+ return 1;
+ }
+ // Lastly, sort by set number. I.e. Semi Final 1 Match 1 happens first.
+ // Then comes Semi Final 2 Match 1. Then comes Semi Final 1 Match 2. Then
+ // Semi Final 2 Match 2.
+ const aSetNumber = a.setNumber();
+ const bSetNumber = b.setNumber();
+ if (aSetNumber < bSetNumber) {
+ return -1;
+ }
+ if (aSetNumber > bSetNumber) {
+ return 1;
+ }
+ return 0;
+ });
+ return matchList;
+ }
}
diff --git a/tools/build_rules/js/BUILD b/tools/build_rules/js/BUILD
index 1bbe769..86a0288 100644
--- a/tools/build_rules/js/BUILD
+++ b/tools/build_rules/js/BUILD
@@ -24,3 +24,11 @@
"//:node_modules/@types/node",
],
)
+
+py_binary(
+ name = "assemble_service_worker_files",
+ srcs = [
+ "assemble_service_worker_files.py",
+ ],
+ visibility = ["//visibility:public"],
+)
diff --git a/tools/build_rules/js/assemble_service_worker_files.py b/tools/build_rules/js/assemble_service_worker_files.py
new file mode 100644
index 0000000..d4485d5
--- /dev/null
+++ b/tools/build_rules/js/assemble_service_worker_files.py
@@ -0,0 +1,27 @@
+import argparse
+import shutil
+import sys
+from pathlib import Path
+
+
+def main(argv):
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--input_dir", type=Path, action="append", default=[])
+ parser.add_argument("--output", type=Path, action="append", default=[])
+ parser.add_argument("--relative_output",
+ type=Path,
+ action="append",
+ default=[])
+ args = parser.parse_args(argv[1:])
+
+ for relative_output, output in zip(args.relative_output, args.output):
+ for input_dir in args.input_dir:
+ input_file = input_dir / relative_output
+ if input_file.exists():
+ print(f"Copying {input_file} to {output}")
+ shutil.copy(input_file, output)
+ break
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))