diff --git a/package-lock.json b/package-lock.json index 3891f87..25aab97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,9 @@ "@fluentui/react-components": "^9.58.2", "@fluentui/react-icons": "^2.0.274", "@reduxjs/toolkit": "^2.5.1", + "@tanstack/react-query": "^5.64.2", + "date-fns": "^4.1.0", + "gtfs-realtime-bindings": "^1.1.1", "i18next": "^23.16.8", "i18next-browser-languagedetector": "^8.0.2", "idb": "^8.0.1", @@ -1042,7 +1045,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1052,7 +1054,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1178,7 +1179,6 @@ "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.26.3" @@ -1194,7 +1194,6 @@ "version": "7.26.3", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -5197,6 +5196,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdoc/salty": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.9.tgz", + "integrity": "sha512-yYxMVH7Dqw6nO0d5NIV8OQWnitU8k6vXH8NtgqAfIa/IUqRMxRv/NUJJ08VEKbAakwxlgBl5PJdrU0dMPStsnw==", + "license": "Apache-2.0", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5248,6 +5259,70 @@ "dev": true, "license": "MIT" }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@reduxjs/toolkit": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.1.tgz", @@ -5677,6 +5752,32 @@ "tslib": "^2.8.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.64.2.tgz", + "integrity": "sha512-hdO8SZpWXoADNTWXV9We8CwTkXU88OVWRBcsiFrk7xJQnhm6WRlweDzMD+uH+GnuieTBVSML6xFa17C2cNV8+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.64.2.tgz", + "integrity": "sha512-3pakNscZNm8KJkxmovvtZ4RaXLyiYYobwleTMvpIGUoKRa8j8VlrQKNl5W8VUEfVfZKkikvXVddLuWMbcSCA1Q==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.64.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -5929,11 +6030,32 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "license": "MIT" + }, + "node_modules/@types/markdown-it": { + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "22.10.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", - "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -6359,7 +6481,6 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -6372,7 +6493,6 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -6432,7 +6552,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -6455,7 +6574,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/aria-query": { @@ -6789,14 +6907,18 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "license": "MIT" + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "license": "MIT" }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -6969,6 +7091,18 @@ ], "license": "CC-BY-4.0" }, + "node_modules/catharsis": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", + "integrity": "sha512-prMTQVpcns/tzFgFVkVp6ak6RykZyWb3gu8ckUpd6YkTlacOd3DXGJjIpD4Q6zJirizvaiAjSSHlOsA+6sNh2A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/chai": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", @@ -6990,7 +7124,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -7017,7 +7150,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -7030,7 +7162,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/color-parse": { @@ -7322,6 +7453,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -7361,7 +7502,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, "license": "MIT" }, "node_modules/deepmerge": { @@ -7553,7 +7693,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -7821,6 +7960,97 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=4.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "license": "MIT", + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "license": "MIT", + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", @@ -8317,7 +8547,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -8383,7 +8612,6 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", @@ -8397,6 +8625,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -8427,7 +8668,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -8447,7 +8687,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -8511,7 +8750,6 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, "license": "MIT" }, "node_modules/fast-uri": { @@ -8695,7 +8933,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -9018,7 +9255,6 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -9028,6 +9264,15 @@ "dev": true, "license": "MIT" }, + "node_modules/gtfs-realtime-bindings": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gtfs-realtime-bindings/-/gtfs-realtime-bindings-1.1.1.tgz", + "integrity": "sha512-+k8+/MmiBmUUWlASs4CeTkV+Qyz/FgbZxXdg9rDU62XRfJOpRaRe+nKWCGKse965jffVZ0tIu1K+R7hRvjSLfQ==", + "dependencies": { + "protobufjs": "^7.1.2", + "protobufjs-cli": "^1.0.2" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -9045,7 +9290,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9298,7 +9542,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -9309,7 +9552,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -9906,6 +10148,53 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/js2xmlparser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz", + "integrity": "sha512-6n4D8gLlLf1n5mNLQPRfViYzu9RATblzPEtm1SthMX1Pjao0r9YI9nw7ZIfRxQMERS87mcswrg+r/OYrPRX6jA==", + "license": "Apache-2.0", + "dependencies": { + "xmlcreate": "^2.0.4" + } + }, + "node_modules/jsdoc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", + "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", + "license": "Apache-2.0", + "dependencies": { + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", + "@types/markdown-it": "^14.1.1", + "bluebird": "^3.7.2", + "catharsis": "^0.9.0", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.2", + "klaw": "^3.0.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^8.6.7", + "marked": "^4.0.10", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "underscore": "~1.13.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jsdom": { "version": "25.0.1", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", @@ -10073,6 +10362,15 @@ "node": ">=0.10.0" } }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, "node_modules/known-css-properties": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", @@ -10117,6 +10415,15 @@ "dev": true, "license": "MIT" }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10137,7 +10444,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.debounce": { @@ -10168,6 +10474,12 @@ "dev": true, "license": "MIT" }, + "node_modules/long": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz", + "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==", + "license": "Apache-2.0" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -10225,6 +10537,45 @@ "dev": true, "license": "ISC" }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -10253,6 +10604,12 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" + }, "node_modules/meow": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", @@ -10343,12 +10700,23 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mrmime": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", @@ -10571,7 +10939,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -11016,6 +11383,90 @@ "dev": true, "license": "MIT" }, + "node_modules/protobufjs": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/protobufjs-cli": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/protobufjs-cli/-/protobufjs-cli-1.1.3.tgz", + "integrity": "sha512-MqD10lqF+FMsOayFiNOdOGNlXc4iKDCf0ZQPkPR+gizYh9gqUeGTWulABUCdI+N67w5RfJ6xhgX4J8pa8qmMXQ==", + "license": "BSD-3-Clause", + "dependencies": { + "chalk": "^4.0.0", + "escodegen": "^1.13.0", + "espree": "^9.0.0", + "estraverse": "^5.1.0", + "glob": "^8.0.0", + "jsdoc": "^4.0.0", + "minimist": "^1.2.0", + "semver": "^7.1.2", + "tmp": "^0.2.1", + "uglify-js": "^3.7.7" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "protobufjs": "^7.0.0" + } + }, + "node_modules/protobufjs-cli/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/protobufjs-cli/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", @@ -11032,6 +11483,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -11431,6 +11891,15 @@ "node": ">=0.10.0" } }, + "node_modules/requizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -11727,7 +12196,6 @@ "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -12190,7 +12658,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -12412,7 +12879,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -12676,6 +13142,15 @@ "dev": true, "license": "MIT" }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -12941,6 +13416,24 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -12960,11 +13453,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/underscore": { + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", + "license": "MIT" + }, "node_modules/undici-types": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -13523,7 +14021,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13947,7 +14444,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -14009,6 +14505,12 @@ "dev": true, "license": "MIT" }, + "node_modules/xmlcreate": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", + "integrity": "sha512-nquOebG4sngPmGPICTS5EnxqhKbCmz5Ox5hsszI2T6U5qdrJizBc+0ilYSEjTSzU0yZcmvppztXe/5Al5fUwdg==", + "license": "Apache-2.0" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index a46c38f..cdd6e78 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "@fluentui/react-components": "^9.58.2", "@fluentui/react-icons": "^2.0.274", "@reduxjs/toolkit": "^2.5.1", + "@tanstack/react-query": "^5.64.2", + "date-fns": "^4.1.0", + "gtfs-realtime-bindings": "^1.1.1", "i18next": "^23.16.8", "i18next-browser-languagedetector": "^8.0.2", "idb": "^8.0.1", diff --git a/src/App.tsx b/src/App.tsx index d7ec7b9..c71fbec 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,16 @@ import { Link as LinkFluent, Title1 } from "@fluentui/react-components"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; // eslint-disable-next-line no-unused-vars import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, Outlet } from "react-router-dom"; import "./App.css"; -import { NavBar } from "./components/nav/NavBar"; +import { NavBar } from "./components/nav/NavBar.js"; function App() { + const queryClient = new QueryClient(); + const [width, setWidth] = useState(window.innerWidth); const { t } = useTranslation(); @@ -21,24 +24,26 @@ function App() { }, []); return ( -
-
- - - - {t("home.title.name")} - - - - {width >= 800 && } -
- - {width < 800 && } -
+ +
+
+ + + + {t("home.title.name")} + + + + {width >= 800 && } +
+ + {width < 800 && } +
+
); } diff --git a/src/components/alerts/AlertUtils.tsx b/src/components/alerts/AlertUtils.tsx new file mode 100644 index 0000000..09e104c --- /dev/null +++ b/src/components/alerts/AlertUtils.tsx @@ -0,0 +1,27 @@ +import { TtcBadge } from "../badges.js"; + +export const ParsedTtcAlertText = ( + badge: { highlightAll?: boolean; line?: string }, + feedText: string, + id: string +) => { + const lineNum = parseInt(`${badge.line}`); + + const lineFilter = badge.line + ? lineNum < 6 + ? `Line ${lineNum}` + : `${lineNum}` + : badge.highlightAll + ? (feedText.match(/\d+/)?.[0] ?? "") + : ""; + + return badge.line || badge.highlightAll + ? feedText + .split(lineFilter) + .flatMap((item) => [ + item, + , + ]) + .slice(0, -1) + : feedText; +}; diff --git a/src/components/alerts/AlertsPage.tsx b/src/components/alerts/AlertsPage.tsx new file mode 100644 index 0000000..fd09f09 --- /dev/null +++ b/src/components/alerts/AlertsPage.tsx @@ -0,0 +1,40 @@ +import { useQuery } from "@tanstack/react-query"; + +import { gtfsAlerts, ttcAlerts } from "../fetch/queries.js"; +import { ParsedTtcAlertText } from "./AlertUtils.js"; +import { SkeetList } from "./SkeetList.js"; + +export default function TtcAlertList() { + const socialMediaQuery = useQuery(ttcAlerts); + const gtfsAlertsResp = useQuery(gtfsAlerts); + + return ( +
+

Recent Service Alerts

+

Current alerts

+ {Array.isArray(gtfsAlertsResp.data?.entity) && + gtfsAlertsResp.data.entity.map((item, index) => ( +

+ {ParsedTtcAlertText( + { highlightAll: true }, + item.alert.descriptionText.translation[0].text, + item.alert.headerText.translation[0].text + )} +

+ ))} +

Recent alerts ({socialMediaQuery.data?.feed.length})

+

+ Source:{" "} + + https://bsky.app/profile/ttcalerts.bsky.social + +

+ {socialMediaQuery.data && ( + + )} +
+ ); +} diff --git a/src/components/alerts/Skeet.tsx b/src/components/alerts/Skeet.tsx new file mode 100644 index 0000000..fe8e965 --- /dev/null +++ b/src/components/alerts/Skeet.tsx @@ -0,0 +1,31 @@ +import { formatDistanceStrict } from "date-fns"; + +import { ParsedTtcAlertText } from "./AlertUtils.js"; + +export const SkeetElement = ({ + skeet, + badge, +}: { + skeet: { + post: { + cid: string; + record: { text: string; createdAt: string }; + }; + }; + badge: { highlightAll?: boolean; line?: string }; +}) => { + const cid = skeet.post.cid; + const feedText = skeet.post.record.text; + + const parsedText = ParsedTtcAlertText(badge, feedText, cid); + return ( +
  • +

    + {formatDistanceStrict(skeet.post.record.createdAt, new Date(), { + addSuffix: true, + })} +

    + {parsedText} +
  • + ); +}; diff --git a/src/components/alerts/SkeetList.module.css b/src/components/alerts/SkeetList.module.css new file mode 100644 index 0000000..4e9c2b3 --- /dev/null +++ b/src/components/alerts/SkeetList.module.css @@ -0,0 +1,5 @@ +.skeet-list { + li { + list-style-type: none; + } +} diff --git a/src/components/alerts/SkeetList.tsx b/src/components/alerts/SkeetList.tsx new file mode 100644 index 0000000..2d942b5 --- /dev/null +++ b/src/components/alerts/SkeetList.tsx @@ -0,0 +1,17 @@ +import { SkeetElement } from "./Skeet.js"; +import style from "./SkeetList.module.css"; + +export const SkeetList = ({ + skeetList, + line, +}: { + skeetList: any[]; + line?: string; +}) => { + const badgeArg = line === "all" ? { highlightAll: true } : { line }; + const dataArray = skeetList.map((skeet) => ( + + )); + + return ; +}; diff --git a/src/components/alerts/TtcAlertList.tsx b/src/components/alerts/TtcAlertList.tsx new file mode 100644 index 0000000..9d5418a --- /dev/null +++ b/src/components/alerts/TtcAlertList.tsx @@ -0,0 +1,28 @@ +import { useQuery } from "@tanstack/react-query"; + +import { ttcAlerts } from "../fetch/queries.js"; +import { SkeetList } from "./SkeetList.js"; + +export function TtcAlertList({ lineNum }: { lineNum: number[] }) { + const bskyAlerts = useQuery(ttcAlerts); + + const filteredBskyAlerts = + bskyAlerts.data?.feed.filter( + (skeet: { post: { record: { text: string } } }) => + lineNum.some((line) => { + return line < 6 + ? skeet.post.record.text.match(`Line ${line}`) + : skeet.post.record.text.startsWith(`${line}`); + }) + ) ?? []; + return ( + <> + {filteredBskyAlerts.length > 0 && ( + 1 ? "all" : `${lineNum[0]}`} + /> + )} + + ); +} diff --git a/src/components/etaCard/EtaCard.tsx b/src/components/etaCard/EtaCard.tsx index 6d604a6..f569b55 100644 --- a/src/components/etaCard/EtaCard.tsx +++ b/src/components/etaCard/EtaCard.tsx @@ -32,6 +32,8 @@ export function EtaCard(props: { enabled?: string[]; direction?: string; }) { + const uniqueLines = [...new Set(props.lines)]; + const directionArray = props.direction?.split(", ") ?? []; return (
  • 6 + uniqueLines.length > 6 ? [style["badge-group"], style.overflow].join(" ") : style["badge-group"] } > - {props.lines.map((line: string) => { - return ; + {uniqueLines.map((line: string) => { + return ( + + ); })}
    - {props.direction && ( - - )} - {props.name} + {directionArray.length > 0 && + directionArray.map((direction) => ( + + ))} + {props.name} } @@ -98,7 +106,7 @@ export function EtaCard(props: { Choose which bus(es) to show @@ -115,6 +123,8 @@ function FavouriteEditor(props: { enabled?: string[]; onDelete?: () => void; }) { + const uniqueLines = [...new Set(props.lines)]; + const { t } = useTranslation(); const dispatch = useAppDispatch(); @@ -122,7 +132,7 @@ function FavouriteEditor(props: { const onChangeFunction = useCallback( (line: string) => { if (!props.enabled) { - const cutOffEnabled = [...props.lines]; + const cutOffEnabled = [...uniqueLines]; const cutOffIndex = cutOffEnabled.indexOf(line); cutOffEnabled.splice(cutOffIndex, 1); dispatch( @@ -161,13 +171,13 @@ function FavouriteEditor(props: { } } }, - [props.lines, props.enabled] + [uniqueLines, props.enabled] ); return (
    - {props.lines.map((line) => ( + {uniqueLines.map((line) => ( { const routeTitleRegex = /\d+-/; @@ -17,39 +20,18 @@ const parseRouteTitle = (input: string) => { }; export function RoutesInfo() { - const [routeJsonData, setRouteJsonData] = useState(); const [routesDb, setRoutesDb] = useState<{ tag: number; title: string }[]>( [] ); + const lineData: UseQueryResult = + useQuery(ttcLines); useEffect(() => { - const controller = new AbortController(); - - const fetchEtaData = async () => { - const { data, Error } = await FetchJSONWithCancelToken( - "https://webservices.umoiq.com/service/publicJSONFeed?command=routeList&a=ttc", - { signal: controller.signal } - ); - - return { data, Error }; - }; - - fetchEtaData().then(({ data, Error }) => { - if (Error || !data) { - return; - } - - setRouteJsonData(data); - if (data.route.length > 0) { - setRoutesDb(data.route); - } - }); - - // when useEffect is called, the following clean-up fn will run first - return () => { - controller.abort(); - }; - }, []); + if (lineData.data?.route && (lineData.data?.route.length ?? 0) > 0) { + setRoutesDb(lineData.data.route); + // addRoutes(lineData.data.route); + } + }, [lineData]); const routesCards = routesDb.map((routeItem) => { return ( @@ -83,18 +65,12 @@ export function RoutesInfo() { {subwayCards()} {routesCards} - {routeJsonData && } + {lineData.data && } ); } function subwayCards() { - const subwayLines = [ - { line: 1, name: "Yonge-University" }, - { line: 2, name: "Bloor-Danforth" }, - { line: 4, name: "Sheppard" }, - ]; - const result = subwayLines.map((subwayLine) => { return (
  • diff --git a/src/components/fetch/FetchStop.tsx b/src/components/fetch/FetchStop.tsx index 399d12b..cfd8f1c 100644 --- a/src/components/fetch/FetchStop.tsx +++ b/src/components/fetch/FetchStop.tsx @@ -1,5 +1,6 @@ import { Button, Title1 } from "@fluentui/react-components"; import { ArrowClockwise24Regular } from "@fluentui/react-icons"; +import { useQuery } from "@tanstack/react-query"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -7,6 +8,7 @@ import { EtaPredictionJson } from "../../models/etaJson.js"; import { EtaBusWithID, LineStopEta } from "../../models/etaObjects.js"; import { store } from "../../store/index.js"; import { settingsSelectors } from "../../store/settings/slice.js"; +import { TtcAlertList } from "../alerts/TtcAlertList.js"; import { DirectionBadge } from "../badges.js"; import { BookmarkButton } from "../bookmarks/BookmarkButton.js"; import CountdownGroup from "../countdown/CountdownGroup.js"; @@ -15,15 +17,32 @@ import SMSButton from "../eta/SMSButton.js"; import { etaParser } from "../parser/etaParser.js"; import RawDisplay from "../rawDisplay/RawDisplay.js"; import style from "./FetchStop.module.css"; -import { getStopPredictions } from "./fetchUtils.js"; +import { ttcStops } from "./queries.js"; +function RefreshButton({ + handleRefreshClick, +}: { + handleRefreshClick: () => void; +}) { + const { t } = useTranslation(); + + return ( + + ); +} function StopPredictionInfo(props: { stopId: number }): JSX.Element { - const [data, setData] = useState(); - const [stopId] = useState(props.stopId); + const stopId = props.stopId; const [etaDb, setEtaDb] = useState([]); const [lastUpdatedAt, setLastUpdatedAt] = useState(Date.now()); const { t } = useTranslation(); const [unifiedEta, setUnifiedEta] = useState([]); + const [lineList, setLineList] = useState([]); + const ttcStopPrediction = useQuery({ + ...ttcStops(stopId), + queryKey: [`ttc-stop-${stopId}`, lastUpdatedAt.toString()], + }); const handleRefreshClick = useCallback(() => { setLastUpdatedAt(Date.now()); @@ -33,40 +52,28 @@ function StopPredictionInfo(props: { stopId: number }): JSX.Element { settingsSelectors.selectById(store.getState().settings, "unifiedEta") ?.value === "true"; - function RefreshButton() { - return ( - - ); - } - useEffect(() => { - const controller = new AbortController(); + if (ttcStopPrediction.data) { + setEtaDb(etaParser(ttcStopPrediction.data)); + } + }, [ttcStopPrediction]); - getStopPredictions(stopId, { signal: controller.signal }).then((data) => { - if (data) { - setData(data); - setEtaDb(etaParser(data)); + useEffect(() => { + setLineList([ + ...new Set(etaDb.map((item) => parseInt(item.line.toString()))), + ]); + if (unifiedEtaValue) { + let templist: EtaBusWithID[] = []; + for (const list of etaDb) { + templist = templist.concat(list.etas ?? []); } - }); - // when useEffect is called, the following clean-up fn will run first - return () => { - controller.abort(); - }; - }, [lastUpdatedAt]); - - useEffect(() => { - let templist: EtaBusWithID[] = []; - for (const list of etaDb) { - templist = templist.concat(list.etas ?? []); + setUnifiedEta(templist.sort((a, b) => a.epochTime - b.epochTime)); } - setUnifiedEta(templist.sort((a, b) => a.epochTime - b.epochTime)); - }, [etaDb]); + }, [etaDb, unifiedEtaValue]); - if (data) { - if (data.Error === undefined) { + if (ttcStopPrediction.data) { + if (ttcStopPrediction.data.Error === undefined) { let listContent: JSX.Element[] = []; if (unifiedEtaValue) { listContent = unifiedEta.map((element, index) => { @@ -96,6 +103,7 @@ function StopPredictionInfo(props: { stopId: number }): JSX.Element { }); return (
    + {etaDb[0] && ( <> @@ -114,7 +122,7 @@ function StopPredictionInfo(props: { stopId: number }): JSX.Element { )}
    - + {etaDb[0] && ( )} - +
    ); } else { @@ -146,18 +154,18 @@ function StopPredictionInfo(props: { stopId: number }): JSX.Element {
    {t("reminder.failToLocate")}
    - +
    - {data.Error["#text"]} - + {ttcStopPrediction.data.Error["#text"]} +
    ); } } else { return (
    - {navigator.onLine ? ( + {ttcStopPrediction.status === "pending" ? ( {t("reminder.loading")} ) : ( <> @@ -167,7 +175,7 @@ function StopPredictionInfo(props: { stopId: number }): JSX.Element { )}
    - +
    diff --git a/src/components/fetch/FetchSubwayStop.tsx b/src/components/fetch/FetchSubwayStop.tsx index 348d978..d0c37b9 100644 --- a/src/components/fetch/FetchSubwayStop.tsx +++ b/src/components/fetch/FetchSubwayStop.tsx @@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next"; import { SubwayStop } from "../../models/ttc.js"; import { store } from "../../store/index.js"; import { subwayDbSelectors } from "../../store/suwbayDb/slice.js"; +import { TtcAlertList } from "../alerts/TtcAlertList.js"; import { BookmarkButton } from "../bookmarks/BookmarkButton.js"; import { CountdownSec } from "../countdown/CountdownSec.js"; import RawDisplay from "../rawDisplay/RawDisplay.js"; @@ -75,6 +76,7 @@ function SubwayStopPredictionInfo(props: { )} {data.directionText} +
    { + const response = await fetch( + "https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=ttcalerts.bsky.social" + ); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + + return response.json(); + }, + staleTime: 60 * 1000, + refetchInterval: 60 * 1000, +}); + +export const ttcStops = (stopId: number) => { + return { + queryKey: [`ttc-stop-${stopId}`], + queryFn: async () => { + const response = await fetch( + `https://webservices.umoiq.com/service/publicJSONFeed?command=predictions&a=ttc&stopId=${stopId}` + ); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + + return response.json(); + }, + placeholderData: (prev: any) => prev, + }; +}; + +export const ttcLines = queryOptions({ + queryKey: ["ttc-lines"], + queryFn: async () => { + const response = await fetch( + "https://webservices.umoiq.com/service/publicJSONFeed?command=routeList&a=ttc" + ); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + + return response.json(); + }, + staleTime: 24 * 60 * 60 * 1000, + refetchInterval: 60 * 1000, +}); + +export const gtfsAlerts = queryOptions({ + queryKey: ["gtfs-alerts"], + queryFn: async () => { + const response = await fetch("https://bustime.ttc.ca/gtfsrt/alerts"); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + + const buffer = await response.arrayBuffer(); + const feed = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode( + new Uint8Array(buffer) + ); + + return feed.toJSON(); + }, + staleTime: 60 * 1000, + refetchInterval: 60 * 1000, +}); + +// inaccessible; CORS Missing Allow Origin +// export const ttcGtfsAlerts = { +// queryKey: ["ttc-gtfs"], +// queryFn: async () => { +// const response = await fetch("https://bustime.ttc.ca/gtfsrt/alerts"); +// if (!response.ok) { +// throw new Error("Network response was not ok"); +// } + +// const buffer = await response.arrayBuffer(); +// const feed = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode( +// new Uint8Array(buffer) +// ); +// return feed; +// }, +// staleTime: 60 * 1000, +// refetchInterval: 60 * 1000, +// }; diff --git a/src/components/nearby/Nearby.tsx b/src/components/nearby/Nearby.tsx index ec39879..a4e02d9 100644 --- a/src/components/nearby/Nearby.tsx +++ b/src/components/nearby/Nearby.tsx @@ -8,7 +8,7 @@ import { changeSettings, settingsSelectors, } from "../../store/settings/slice.js"; -import { addStops, getSize } from "../../store/ttcRouteDb.js"; +import { addStops, getSize } from "../../store/ttcStopsDb.js"; import style from "./Nearby.module.css"; import NearbyList from "./NearbyList.js"; diff --git a/src/components/nearby/NearbyList.tsx b/src/components/nearby/NearbyList.tsx index 156d105..14f2092 100644 --- a/src/components/nearby/NearbyList.tsx +++ b/src/components/nearby/NearbyList.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { StopWithDistance } from "../../models/db.js"; -import { getStopsWithinRange } from "../../store/ttcRouteDb.js"; +import { getStopsWithinRange } from "../../store/ttcStopsDb.js"; import RawDisplay from "../rawDisplay/RawDisplay.js"; import style from "./NearbyList.module.css"; import NearbyStopCard from "./NearbyStopCard.js"; diff --git a/src/data/ttc.ts b/src/data/ttc.ts new file mode 100644 index 0000000..ade65b8 --- /dev/null +++ b/src/data/ttc.ts @@ -0,0 +1,5 @@ +export const subwayLines = [ + { line: 1, name: "Yonge-University" }, + { line: 2, name: "Bloor-Danforth" }, + { line: 4, name: "Sheppard" }, +]; diff --git a/src/routes/Home.tsx b/src/routes/Home.tsx index 68841a9..d9b742d 100644 --- a/src/routes/Home.tsx +++ b/src/routes/Home.tsx @@ -11,6 +11,7 @@ import { useCallback, useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; +import TtcAlertList from "../components/alerts/AlertsPage.js"; import FavouriteEta from "../components/eta/FavouriteEta.js"; import Nearby from "../components/nearby/Nearby.js"; import { stopBookmarksSelectors } from "../store/bookmarks/slice.js"; @@ -52,11 +53,11 @@ export default function Home() { className="directon-buttons" onTabSelect={handleTabClick} > - - {t("nav.label.nearby")} - Beta - + {t("nav.label.nearby")} {t("nav.label.bookmarks")} + + Service AlertsBeta +
    @@ -64,6 +65,9 @@ export default function Home() {
    +
    + +
    ); } diff --git a/src/routes/Line.tsx b/src/routes/Line.tsx index b6ce678..114dc49 100644 --- a/src/routes/Line.tsx +++ b/src/routes/Line.tsx @@ -3,19 +3,23 @@ import { useEffect } from "react"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; -import RouteInfo from "../components/fetch/FetchRoute"; -import SubwayRouteInfo from "../components/fetch/FetchSubwayRoute"; +import { TtcAlertList } from "../components/alerts/TtcAlertList.js"; +import RouteInfo from "../components/fetch/FetchRoute.js"; +import SubwayRouteInfo from "../components/fetch/FetchSubwayRoute.js"; export default function Line() { const params = useParams(); const { t } = useTranslation(); const lineNum = parseInt(`${params.lineId}`); + useEffect(() => { document.title = t("lines.browserTitle", { lineNum }); }); + return (
    + {t("lines.number", { lineNum })} {lineNum < 6 && } diff --git a/src/store/ttcRoutesDb.ts b/src/store/ttcRoutesDb.ts new file mode 100644 index 0000000..8c712f0 --- /dev/null +++ b/src/store/ttcRoutesDb.ts @@ -0,0 +1,39 @@ +import { openDB } from "idb"; + +const dbPromise = openDB("TTCStops", 1, { + upgrade(db) { + const store = db.createObjectStore("routes", { + keyPath: "tag", + }); + store.createIndex("tag", "tag"); + store.createIndex("title", "title"); + }, +}); + +export async function getRoute(key) { + return (await dbPromise).get("routes", key); +} +export async function addRoute(val) { + return (await dbPromise).put("routes", val); +} +export async function addRoutes(vals) { + await clear(); + const store = (await dbPromise) + .transaction("routes", "readwrite") + .objectStore("routes"); + vals.forEach((item) => { + store.put(item); + }); +} +export async function del(key) { + return (await dbPromise).delete("routes", key); +} +export async function clear() { + return (await dbPromise).clear("routes"); +} +export async function keys() { + return (await dbPromise).getAllKeys("routes"); +} +export async function getSize() { + return (await dbPromise).count("routes"); +} diff --git a/src/store/ttcRouteDb.ts b/src/store/ttcStopsDb.ts similarity index 100% rename from src/store/ttcRouteDb.ts rename to src/store/ttcStopsDb.ts