Add tool to create foxglove extensions
This tool lets users create new foxglove extensions by running the
equivalent of `npm init foxglove-extension@latest <patch>`.
A future patch will actually create a new extension using this tool.
Change-Id: Ifb3ca868fa72c30477a4943c3a6a805b06d0642d
Signed-off-by: Philipp Schrader <philipp.schrader@gmail.com>
diff --git a/package.json b/package.json
index f37a2b6..5f7b0f2 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"@types/babel__generator": "^7.6.8",
"@types/pako": "2.0.3",
"angularx-qrcode": "^16.0.2",
+ "create-foxglove-extension": "^0.8.6",
"html-insert-assets": "0.14.3",
"cypress": "13.3.1",
"pako": "2.1.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5411319..966570b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -67,6 +67,9 @@
angularx-qrcode:
specifier: ^16.0.2
version: 16.0.2(@angular/core@16.2.12)(@angular/platform-browser@16.2.12)
+ create-foxglove-extension:
+ specifier: ^0.8.6
+ version: 0.8.6(typescript@5.1.6)
cypress:
specifier: 13.3.1
version: 13.3.1
@@ -792,6 +795,13 @@
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
+ /@jridgewell/trace-mapping@0.3.25:
+ resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.0
+ '@jridgewell/sourcemap-codec': 1.4.15
+ dev: true
+
/@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3:
resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==}
requiresBuild: true
@@ -1119,6 +1129,24 @@
dependencies:
'@types/node': 20.11.19
+ /@types/eslint-scope@3.7.7:
+ resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
+ dependencies:
+ '@types/eslint': 8.56.6
+ '@types/estree': 1.0.5
+ dev: true
+
+ /@types/eslint@8.56.6:
+ resolution: {integrity: sha512-ymwc+qb1XkjT/gfoQwxIeHZ6ixH23A+tCT2ADSA/DPVKzAjwYkTXBMCQ/f6fe4wEa85Lhp26VPeUxI7wMhAi7A==}
+ dependencies:
+ '@types/estree': 1.0.5
+ '@types/json-schema': 7.0.15
+ dev: true
+
+ /@types/estree@0.0.51:
+ resolution: {integrity: sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==}
+ dev: true
+
/@types/estree@1.0.5:
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
dev: true
@@ -1127,10 +1155,25 @@
resolution: {integrity: sha512-7btbphLrKvo5yl/5CC2OCxUSMx1wV1wvGT1qDXkSt7yi00/YW7E8k6qzXqJHsp+WU0eoG7r6MTQQXI9lIvd0qA==}
dev: true
+ /@types/glob@7.2.0:
+ resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
+ dependencies:
+ '@types/minimatch': 5.1.2
+ '@types/node': 20.11.19
+ dev: true
+
/@types/jasmine@3.10.3:
resolution: {integrity: sha512-SWyMrjgdAUHNQmutvDcKablrJhkDLy4wunTme8oYLjKp41GnHGxMRXr2MQMvy/qy8H3LdzwQk9gH4hZ6T++H8g==}
dev: true
+ /@types/json-schema@7.0.15:
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ dev: true
+
+ /@types/minimatch@5.1.2:
+ resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
+ dev: true
+
/@types/node@18.19.22:
resolution: {integrity: sha512-p3pDIfuMg/aXBmhkyanPshdfJuX5c5+bQjYLIikPLXAUycEogij/c50n/C+8XOA5L93cU4ZRXtn+dNQGi0IZqQ==}
dependencies:
@@ -1165,6 +1208,120 @@
dev: true
optional: true
+ /@webassemblyjs/ast@1.11.1:
+ resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==}
+ dependencies:
+ '@webassemblyjs/helper-numbers': 1.11.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.1
+ dev: true
+
+ /@webassemblyjs/floating-point-hex-parser@1.11.1:
+ resolution: {integrity: sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==}
+ dev: true
+
+ /@webassemblyjs/helper-api-error@1.11.1:
+ resolution: {integrity: sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==}
+ dev: true
+
+ /@webassemblyjs/helper-buffer@1.11.1:
+ resolution: {integrity: sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==}
+ dev: true
+
+ /@webassemblyjs/helper-numbers@1.11.1:
+ resolution: {integrity: sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==}
+ dependencies:
+ '@webassemblyjs/floating-point-hex-parser': 1.11.1
+ '@webassemblyjs/helper-api-error': 1.11.1
+ '@xtuc/long': 4.2.2
+ dev: true
+
+ /@webassemblyjs/helper-wasm-bytecode@1.11.1:
+ resolution: {integrity: sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==}
+ dev: true
+
+ /@webassemblyjs/helper-wasm-section@1.11.1:
+ resolution: {integrity: sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.1
+ '@webassemblyjs/helper-buffer': 1.11.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.1
+ '@webassemblyjs/wasm-gen': 1.11.1
+ dev: true
+
+ /@webassemblyjs/ieee754@1.11.1:
+ resolution: {integrity: sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==}
+ dependencies:
+ '@xtuc/ieee754': 1.2.0
+ dev: true
+
+ /@webassemblyjs/leb128@1.11.1:
+ resolution: {integrity: sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==}
+ dependencies:
+ '@xtuc/long': 4.2.2
+ dev: true
+
+ /@webassemblyjs/utf8@1.11.1:
+ resolution: {integrity: sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==}
+ dev: true
+
+ /@webassemblyjs/wasm-edit@1.11.1:
+ resolution: {integrity: sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.1
+ '@webassemblyjs/helper-buffer': 1.11.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.1
+ '@webassemblyjs/helper-wasm-section': 1.11.1
+ '@webassemblyjs/wasm-gen': 1.11.1
+ '@webassemblyjs/wasm-opt': 1.11.1
+ '@webassemblyjs/wasm-parser': 1.11.1
+ '@webassemblyjs/wast-printer': 1.11.1
+ dev: true
+
+ /@webassemblyjs/wasm-gen@1.11.1:
+ resolution: {integrity: sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.1
+ '@webassemblyjs/ieee754': 1.11.1
+ '@webassemblyjs/leb128': 1.11.1
+ '@webassemblyjs/utf8': 1.11.1
+ dev: true
+
+ /@webassemblyjs/wasm-opt@1.11.1:
+ resolution: {integrity: sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.1
+ '@webassemblyjs/helper-buffer': 1.11.1
+ '@webassemblyjs/wasm-gen': 1.11.1
+ '@webassemblyjs/wasm-parser': 1.11.1
+ dev: true
+
+ /@webassemblyjs/wasm-parser@1.11.1:
+ resolution: {integrity: sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.1
+ '@webassemblyjs/helper-api-error': 1.11.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.1
+ '@webassemblyjs/ieee754': 1.11.1
+ '@webassemblyjs/leb128': 1.11.1
+ '@webassemblyjs/utf8': 1.11.1
+ dev: true
+
+ /@webassemblyjs/wast-printer@1.11.1:
+ resolution: {integrity: sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.1
+ '@xtuc/long': 4.2.2
+ dev: true
+
+ /@xtuc/ieee754@1.2.0:
+ resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
+ dev: true
+
+ /@xtuc/long@4.2.2:
+ resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
+ dev: true
+
/@yarnpkg/lockfile@1.1.0:
resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
dev: true
@@ -1180,6 +1337,14 @@
mime-types: 2.1.35
negotiator: 0.6.3
+ /acorn-import-assertions@1.9.0(acorn@8.9.0):
+ resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
+ peerDependencies:
+ acorn: ^8
+ dependencies:
+ acorn: 8.9.0
+ dev: true
+
/acorn@8.9.0:
resolution: {integrity: sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==}
engines: {node: '>=0.4.0'}
@@ -1221,6 +1386,23 @@
ajv: 8.12.0
dev: true
+ /ajv-keywords@3.5.2(ajv@6.12.6):
+ resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
+ peerDependencies:
+ ajv: ^6.9.1
+ dependencies:
+ ajv: 6.12.6
+ dev: true
+
+ /ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+ dev: true
+
/ajv@8.12.0:
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
dependencies:
@@ -1304,6 +1486,18 @@
readable-stream: 3.6.2
dev: true
+ /array-union@1.0.2:
+ resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ array-uniq: 1.0.3
+ dev: true
+
+ /array-uniq@1.0.3:
+ resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/asn1@0.2.6:
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
dependencies:
@@ -1569,6 +1763,11 @@
engines: {node: '>=10'}
dev: true
+ /chrome-trace-event@1.0.3:
+ resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
+ engines: {node: '>=6.0'}
+ dev: true
+
/ci-info@3.8.0:
resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
engines: {node: '>=8'}
@@ -1579,6 +1778,16 @@
engines: {node: '>=6'}
dev: true
+ /clean-webpack-plugin@4.0.0(webpack@5.75.0):
+ resolution: {integrity: sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ webpack: '>=4.0.0 <6.0.0'
+ dependencies:
+ del: 4.1.1
+ webpack: 5.75.0
+ dev: true
+
/cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
@@ -1676,6 +1885,11 @@
delayed-stream: 1.0.0
dev: true
+ /commander@10.0.0:
+ resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==}
+ engines: {node: '>=14'}
+ dev: true
+
/commander@2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
@@ -1739,6 +1953,31 @@
object-assign: 4.1.1
vary: 1.1.2
+ /create-foxglove-extension@0.8.6(typescript@5.1.6):
+ resolution: {integrity: sha512-DML7hw3wLcT/5tmIcwl759RYL+to9s8geKJ8TuKQMqhhBeYBx8UwRBweIFA3jFB9+F8zkkqoFq7RlXvz5qpgug==}
+ engines: {node: '>= 14'}
+ hasBin: true
+ dependencies:
+ clean-webpack-plugin: 4.0.0(webpack@5.75.0)
+ commander: 10.0.0
+ jszip: 3.10.1
+ mkdirp: 2.1.5
+ ncp: 2.0.0
+ node-fetch: 2.6.9
+ path-browserify: 1.0.1
+ rimraf: 4.3.1
+ sanitize-filename: 1.6.3
+ ts-loader: 9.4.2(typescript@5.1.6)(webpack@5.75.0)
+ webpack: 5.75.0
+ transitivePeerDependencies:
+ - '@swc/core'
+ - encoding
+ - esbuild
+ - typescript
+ - uglify-js
+ - webpack-cli
+ dev: true
+
/cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@@ -1871,6 +2110,19 @@
engines: {node: '>=8'}
dev: true
+ /del@4.1.1:
+ resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ '@types/glob': 7.2.0
+ globby: 6.1.0
+ is-path-cwd: 2.2.0
+ is-path-in-cwd: 2.1.0
+ p-map: 2.1.0
+ pify: 4.0.1
+ rimraf: 2.7.1
+ dev: true
+
/delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -1979,6 +2231,14 @@
- supports-color
- utf-8-validate
+ /enhanced-resolve@5.16.0:
+ resolution: {integrity: sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==}
+ engines: {node: '>=10.13.0'}
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+ dev: true
+
/enquirer@2.3.6:
resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
engines: {node: '>=8.6'}
@@ -1998,6 +2258,10 @@
resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==}
dev: true
+ /es-module-lexer@0.9.3:
+ resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
+ dev: true
+
/escalade@3.1.1:
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
engines: {node: '>=6'}
@@ -2010,6 +2274,31 @@
engines: {node: '>=0.8.0'}
dev: true
+ /eslint-scope@5.1.1:
+ resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+ engines: {node: '>=8.0.0'}
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 4.3.0
+ dev: true
+
+ /esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+ dependencies:
+ estraverse: 5.3.0
+ dev: true
+
+ /estraverse@4.3.0:
+ resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+ engines: {node: '>=4.0'}
+ dev: true
+
+ /estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+ dev: true
+
/estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
@@ -2021,6 +2310,11 @@
/eventemitter3@4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
+ /events@3.3.0:
+ resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+ engines: {node: '>=0.8.x'}
+ dev: true
+
/execa@4.1.0:
resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
engines: {node: '>=10'}
@@ -2082,6 +2376,10 @@
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
+ /fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+ dev: true
+
/fd-slicer@1.1.0:
resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==}
dependencies:
@@ -2265,6 +2563,10 @@
dependencies:
is-glob: 4.0.3
+ /glob-to-regexp@0.4.1:
+ resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+ dev: true
+
/glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2298,6 +2600,16 @@
once: 1.4.0
dev: true
+ /glob@9.3.5:
+ resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dependencies:
+ fs.realpath: 1.0.0
+ minimatch: 8.0.4
+ minipass: 4.2.8
+ path-scurry: 1.10.1
+ dev: true
+
/global-dirs@3.0.1:
resolution: {integrity: sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==}
engines: {node: '>=10'}
@@ -2310,6 +2622,17 @@
engines: {node: '>=4'}
dev: true
+ /globby@6.1.0:
+ resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ array-union: 1.0.2
+ glob: 7.2.3
+ object-assign: 4.1.1
+ pify: 2.3.0
+ pinkie-promise: 2.0.1
+ dev: true
+
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -2455,7 +2778,6 @@
/immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
- dev: false
/imurmurhash@0.1.4:
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
@@ -2596,6 +2918,25 @@
engines: {node: '>=0.12.0'}
requiresBuild: true
+ /is-path-cwd@2.2.0:
+ resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /is-path-in-cwd@2.1.0:
+ resolution: {integrity: sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ is-path-inside: 2.1.0
+ dev: true
+
+ /is-path-inside@2.1.0:
+ resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==}
+ engines: {node: '>=6'}
+ dependencies:
+ path-is-inside: 1.0.2
+ dev: true
+
/is-path-inside@3.0.3:
resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
engines: {node: '>=8'}
@@ -2624,7 +2965,6 @@
/isarray@1.0.0:
resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
- dev: false
/isbinaryfile@4.0.10:
resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==}
@@ -2647,6 +2987,15 @@
'@pkgjs/parseargs': 0.11.0
dev: true
+ /jest-worker@27.5.1:
+ resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
+ engines: {node: '>= 10.13.0'}
+ dependencies:
+ '@types/node': 20.11.19
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+ dev: true
+
/js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
dev: true
@@ -2661,11 +3010,19 @@
hasBin: true
dev: true
+ /json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+ dev: true
+
/json-parse-even-better-errors@3.0.0:
resolution: {integrity: sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==}
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
dev: true
+ /json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+ dev: true
+
/json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
dev: true
@@ -2723,7 +3080,6 @@
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5
- dev: false
/karma-safari-launcher@1.0.0(karma@6.4.3):
resolution: {integrity: sha512-qmypLWd6F2qrDJfAETvXDfxHvKDk+nyIjpH9xIeI3/hENr0U3nuqkxaftq73PfXZ4aOuOChA6SnLW4m4AxfRjQ==}
@@ -2776,7 +3132,6 @@
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
dependencies:
immediate: 3.0.6
- dev: false
/listr2@3.14.0(enquirer@2.3.6):
resolution: {integrity: sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==}
@@ -2798,6 +3153,11 @@
wrap-ansi: 7.0.0
dev: true
+ /loader-runner@4.3.0:
+ resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
+ engines: {node: '>=6.11.5'}
+ dev: true
+
/locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
@@ -2935,6 +3295,14 @@
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
dev: true
+ /micromatch@4.0.5:
+ resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+ engines: {node: '>=8.6'}
+ dependencies:
+ braces: 3.0.2
+ picomatch: 2.3.1
+ dev: true
+
/mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -2967,6 +3335,13 @@
brace-expansion: 2.0.1
dev: true
+ /minimatch@8.0.4:
+ resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dependencies:
+ brace-expansion: 2.0.1
+ dev: true
+
/minimatch@9.0.3:
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -3041,6 +3416,11 @@
yallist: 4.0.0
dev: true
+ /minipass@4.2.8:
+ resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==}
+ engines: {node: '>=8'}
+ dev: true
+
/minipass@5.0.0:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'}
@@ -3071,6 +3451,12 @@
hasBin: true
dev: true
+ /mkdirp@2.1.5:
+ resolution: {integrity: sha512-jbjfql+shJtAPrFoKxHOXip4xS+kul9W3OzfzzrqueWK2QMGon2bFH2opl6W9EagBThjEz+iysyi/swOoVfB/w==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dev: true
+
/ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
@@ -3085,10 +3471,31 @@
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
dev: true
+ /ncp@2.0.0:
+ resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==}
+ hasBin: true
+ dev: true
+
/negotiator@0.6.3:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
+ /neo-async@2.6.2:
+ resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+ dev: true
+
+ /node-fetch@2.6.9:
+ resolution: {integrity: sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==}
+ engines: {node: 4.x || >=6.0.0}
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+ dependencies:
+ whatwg-url: 5.0.0
+ dev: true
+
/node-gyp@9.4.1:
resolution: {integrity: sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==}
engines: {node: ^12.13 || ^14.13 || >=16}
@@ -3291,6 +3698,11 @@
dependencies:
p-limit: 2.3.0
+ /p-map@2.1.0:
+ resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
+ engines: {node: '>=6'}
+ dev: true
+
/p-map@4.0.0:
resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
engines: {node: '>=10'}
@@ -3332,7 +3744,6 @@
/pako@1.0.11:
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
- dev: false
/pako@2.1.0:
resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==}
@@ -3345,6 +3756,10 @@
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
engines: {node: '>= 0.8'}
+ /path-browserify@1.0.1:
+ resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+ dev: true
+
/path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -3353,6 +3768,10 @@
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
engines: {node: '>=0.10.0'}
+ /path-is-inside@1.0.2:
+ resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==}
+ dev: true
+
/path-key@3.1.1:
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
engines: {node: '>=8'}
@@ -3396,6 +3815,18 @@
engines: {node: '>=6'}
dev: true
+ /pinkie-promise@2.0.1:
+ resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ pinkie: 2.0.4
+ dev: true
+
+ /pinkie@2.0.4:
+ resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
/pngjs@5.0.0:
resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==}
engines: {node: '>=10.13.0'}
@@ -3418,7 +3849,6 @@
/process-nextick-args@2.0.1:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
- dev: false
/process@0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
@@ -3493,6 +3923,12 @@
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
dev: true
+ /randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: true
+
/range-parser@1.2.1:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
@@ -3534,7 +3970,6 @@
safe-buffer: 5.1.2
string_decoder: 1.1.1
util-deprecate: 1.0.2
- dev: false
/readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
@@ -3608,12 +4043,27 @@
/rfdc@1.3.0:
resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
+ /rimraf@2.7.1:
+ resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.3
+ dev: true
+
/rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
hasBin: true
dependencies:
glob: 7.2.3
+ /rimraf@4.3.1:
+ resolution: {integrity: sha512-GfHJHBzFQra23IxDzIdBqhOWfbtdgS1/dCHrDy+yvhpoJY5TdwdT28oWaHWfRpKFDLd3GZnGTx6Mlt4+anbsxQ==}
+ engines: {node: '>=14'}
+ hasBin: true
+ dependencies:
+ glob: 9.3.5
+ dev: true
+
/rollup@4.12.0:
resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@@ -3655,7 +4105,6 @@
/safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
- dev: false
/safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -3664,6 +4113,21 @@
/safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ /sanitize-filename@1.6.3:
+ resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==}
+ dependencies:
+ truncate-utf8-bytes: 1.0.2
+ dev: true
+
+ /schema-utils@3.3.0:
+ resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
+ engines: {node: '>= 10.13.0'}
+ dependencies:
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ ajv-keywords: 3.5.2(ajv@6.12.6)
+ dev: true
+
/selenium-webdriver@4.18.1:
resolution: {integrity: sha512-uP4OJ5wR4+VjdTi5oi/k8oieV2fIhVdVuaOPrklKghgS59w7Zz3nGa5gcG73VcU9EBRv5IZEBRhPr7qFJAj5mQ==}
engines: {node: '>= 14.20.0'}
@@ -3702,12 +4166,17 @@
lru-cache: 6.0.0
dev: true
+ /serialize-javascript@6.0.2:
+ resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
+ dependencies:
+ randombytes: 2.1.0
+ dev: true
+
/set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
/setimmediate@1.0.5:
resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
- dev: false
/setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
@@ -3943,7 +4412,6 @@
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
dependencies:
safe-buffer: 5.1.2
- dev: false
/string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
@@ -3999,6 +4467,11 @@
engines: {node: '>=0.10'}
dev: true
+ /tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+ dev: true
+
/tar@6.1.15:
resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==}
engines: {node: '>=10'}
@@ -4011,6 +4484,30 @@
yallist: 4.0.0
dev: true
+ /terser-webpack-plugin@5.3.10(webpack@5.75.0):
+ resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
+ engines: {node: '>= 10.13.0'}
+ peerDependencies:
+ '@swc/core': '*'
+ esbuild: '*'
+ uglify-js: '*'
+ webpack: ^5.1.0
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+ esbuild:
+ optional: true
+ uglify-js:
+ optional: true
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.25
+ jest-worker: 27.5.1
+ schema-utils: 3.3.0
+ serialize-javascript: 6.0.2
+ terser: 5.30.0
+ webpack: 5.75.0
+ dev: true
+
/terser@5.16.4:
resolution: {integrity: sha512-5yEGuZ3DZradbogeYQ1NaGz7rXVBDWujWlx1PT8efXO6Txn+eWbfKqB2bTDVmFXmePFkoLU6XI8UektMIEA0ug==}
engines: {node: '>=10'}
@@ -4022,6 +4519,17 @@
source-map-support: 0.5.21
dev: true
+ /terser@5.30.0:
+ resolution: {integrity: sha512-Y/SblUl5kEyEFzhMAQdsxVHh+utAxd4IuRNJzKywY/4uzSogh3G219jqbDDxYu4MXO9CzY3tSEqmZvW6AoEDJw==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ '@jridgewell/source-map': 0.3.4
+ acorn: 8.9.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+ dev: true
+
/throttleit@1.0.0:
resolution: {integrity: sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==}
dev: true
@@ -4069,6 +4577,31 @@
url-parse: 1.5.10
dev: true
+ /tr46@0.0.3:
+ resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+ dev: true
+
+ /truncate-utf8-bytes@1.0.2:
+ resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==}
+ dependencies:
+ utf8-byte-length: 1.0.4
+ dev: true
+
+ /ts-loader@9.4.2(typescript@5.1.6)(webpack@5.75.0):
+ resolution: {integrity: sha512-OmlC4WVmFv5I0PpaxYb+qGeGOdm5giHU7HwDDUjw59emP2UYMHy9fFSDcYgSNoH8sXcj4hGCSEhlDZ9ULeDraA==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ typescript: '*'
+ webpack: ^5.0.0
+ dependencies:
+ chalk: 4.1.2
+ enhanced-resolve: 5.16.0
+ micromatch: 4.0.5
+ semver: 7.6.0
+ typescript: 5.1.6
+ webpack: 5.75.0
+ dev: true
+
/tslib@2.6.0:
resolution: {integrity: sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==}
@@ -4192,6 +4725,10 @@
requires-port: 1.0.0
dev: true
+ /utf8-byte-length@1.0.4:
+ resolution: {integrity: sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==}
+ dev: true
+
/util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
@@ -4235,12 +4772,76 @@
resolution: {integrity: sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==}
engines: {node: '>=0.10.0'}
+ /watchpack@2.4.1:
+ resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==}
+ engines: {node: '>=10.13.0'}
+ dependencies:
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+ dev: true
+
/wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
dependencies:
defaults: 1.0.4
dev: true
+ /webidl-conversions@3.0.1:
+ resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+ dev: true
+
+ /webpack-sources@3.2.3:
+ resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
+ engines: {node: '>=10.13.0'}
+ dev: true
+
+ /webpack@5.75.0:
+ resolution: {integrity: sha512-piaIaoVJlqMsPtX/+3KTTO6jfvrSYgauFVdt8cr9LTHKmcq/AMd4mhzsiP7ZF/PGRNPGA8336jldh9l2Kt2ogQ==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+ peerDependencies:
+ webpack-cli: '*'
+ peerDependenciesMeta:
+ webpack-cli:
+ optional: true
+ dependencies:
+ '@types/eslint-scope': 3.7.7
+ '@types/estree': 0.0.51
+ '@webassemblyjs/ast': 1.11.1
+ '@webassemblyjs/wasm-edit': 1.11.1
+ '@webassemblyjs/wasm-parser': 1.11.1
+ acorn: 8.9.0
+ acorn-import-assertions: 1.9.0(acorn@8.9.0)
+ browserslist: 4.23.0
+ chrome-trace-event: 1.0.3
+ enhanced-resolve: 5.16.0
+ es-module-lexer: 0.9.3
+ eslint-scope: 5.1.1
+ events: 3.3.0
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+ json-parse-even-better-errors: 2.3.1
+ loader-runner: 4.3.0
+ mime-types: 2.1.35
+ neo-async: 2.6.2
+ schema-utils: 3.3.0
+ tapable: 2.2.1
+ terser-webpack-plugin: 5.3.10(webpack@5.75.0)
+ watchpack: 2.4.1
+ webpack-sources: 3.2.3
+ transitivePeerDependencies:
+ - '@swc/core'
+ - esbuild
+ - uglify-js
+ dev: true
+
+ /whatwg-url@5.0.0:
+ resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+ dependencies:
+ tr46: 0.0.3
+ webidl-conversions: 3.0.1
+ dev: true
+
/which-module@2.0.1:
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
diff --git a/tools/bazel b/tools/bazel
index d4312b2..4090764 100755
--- a/tools/bazel
+++ b/tools/bazel
@@ -99,7 +99,7 @@
ENVIRONMENT_VARIABLES+=(HOSTNAME="${HOSTNAME}")
ENVIRONMENT_VARIABLES+=(SHELL="${SHELL}")
ENVIRONMENT_VARIABLES+=(USER="${USER}")
-ENVIRONMENT_VARIABLES+=(PATH="/usr/bin:/bin")
+ENVIRONMENT_VARIABLES+=(PATH="${PATH}")
ENVIRONMENT_VARIABLES+=(HOME="${HOME}")
ENVIRONMENT_VARIABLES+=(TERM="${TERM}")
ENVIRONMENT_VARIABLES+=(LANG="${LANG:-C}")
diff --git a/tools/build_rules/foxglove.bzl b/tools/build_rules/foxglove.bzl
new file mode 100644
index 0000000..cfcab92
--- /dev/null
+++ b/tools/build_rules/foxglove.bzl
@@ -0,0 +1,46 @@
+load("@aspect_rules_js//js:defs.bzl", "js_run_binary")
+
+def foxglove_extension(name, **kwargs):
+ """Compiles a foxglove extension into a .foxe file.
+
+ Drag the generated .foxe file onto the foxglove UI in your browser. The
+ extension should then install automatically. If you want to update the
+ extension, drag a new version to the UI.
+
+ Use `tools/foxglove/create-foxglove-extension`. Don't use this rule
+ directly. See `tools/foxglove/README.md` for more details.
+
+ Args:
+ name: The name of the target.
+ **kwargs: The arguments to pass to js_run_binary.
+ """
+
+ # We need to glob all the non-Bazel files because we're going to invoke the
+ # `foxglove-extension` binary directly. That expects to have access to
+ # `package.json` and the like.
+ all_files = native.glob(
+ ["**"],
+ exclude = [
+ "BUILD",
+ "BUILD.bazel",
+ ],
+ )
+
+ # Run the `foxglove-extension` wrapper to create the .foxe file.
+ js_run_binary(
+ name = name,
+ srcs = all_files + [
+ ":node_modules",
+ ],
+ tool = "//tools/foxglove:foxglove_extension_wrapper",
+ outs = ["%s.foxe" % name],
+ args = [
+ "package",
+ "--out",
+ "%s.foxe" % name,
+ ],
+ target_compatible_with = [
+ "@platforms//cpu:x86_64",
+ ],
+ **kwargs
+ )
diff --git a/tools/foxglove/BUILD b/tools/foxglove/BUILD
new file mode 100644
index 0000000..6bbd263
--- /dev/null
+++ b/tools/foxglove/BUILD
@@ -0,0 +1,55 @@
+load("@aspect_rules_js//js:defs.bzl", "js_binary")
+load("@npm//:create-foxglove-extension/package_json.bzl", "bin")
+
+bin.create_foxglove_extension_binary(
+ name = "create_foxglove_extension",
+)
+
+bin.foxglove_extension_binary(
+ name = "foxglove_extension",
+ data = [
+ # This upstream binary needs the dummy npm binary in its runfiles since
+ # it will invoke this dummy npm binary.
+ ":foxglove_extension_wrapper_npm",
+ ],
+)
+
+js_binary(
+ name = "foxglove_extension_wrapper",
+ data = [
+ ":foxglove_extension",
+ # This binary needs the dummy npm binary in its runfiles since it needs
+ # to point the `foxglove_extension` binary above to it.
+ ":foxglove_extension_wrapper_npm",
+ "//:node_modules/create-foxglove-extension",
+ ],
+ entry_point = "foxglove_extension_wrapper.js",
+ visibility = ["//visibility:public"],
+)
+
+js_binary(
+ name = "foxglove_extension_wrapper_npm",
+ entry_point = "foxglove_extension_wrapper_npm.js",
+)
+
+py_binary(
+ name = "creation_wrapper",
+ srcs = ["creation_wrapper.py"],
+ data = [
+ "BUILD.bazel.tmpl",
+ ":creation_wrapper_npm",
+ "@com_github_bazelbuild_buildtools//buildozer",
+ ],
+ target_compatible_with = [
+ "@platforms//cpu:x86_64",
+ ],
+ deps = [
+ "@pip//pyyaml",
+ "@rules_python//python/runfiles",
+ ],
+)
+
+py_binary(
+ name = "creation_wrapper_npm",
+ srcs = ["creation_wrapper_npm.py"],
+)
diff --git a/tools/foxglove/BUILD.bazel.tmpl b/tools/foxglove/BUILD.bazel.tmpl
new file mode 100644
index 0000000..60f03dc
--- /dev/null
+++ b/tools/foxglove/BUILD.bazel.tmpl
@@ -0,0 +1,8 @@
+load("@npm//:defs.bzl", "npm_link_all_packages")
+load("//tools/build_rules:foxglove.bzl", "foxglove_extension")
+
+npm_link_all_packages(name = "node_modules")
+
+foxglove_extension(
+ name = "extension",
+)
diff --git a/tools/foxglove/README.md b/tools/foxglove/README.md
new file mode 100644
index 0000000..7c3a34a
--- /dev/null
+++ b/tools/foxglove/README.md
@@ -0,0 +1,14 @@
+# Creating a new extension
+
+Change directories into the directory of interest.
+
+ $ cd path/to/package
+
+The run the creation script and pass the new directory name as an argument.
+
+ $ ../.../tools/foxglove/create-foxglove-extension <path>
+
+The script will automatically set up all the Bazel hooks so you can compile
+the extension.
+
+ $ bazel build //path/to/package/<path>:extension
diff --git a/tools/foxglove/create-foxglove-extension b/tools/foxglove/create-foxglove-extension
new file mode 100755
index 0000000..ffc3bad
--- /dev/null
+++ b/tools/foxglove/create-foxglove-extension
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -o errexit
+set -o nounset
+set -o pipefail
+
+exec bazel run \
+ --run_under="//tools/foxglove:creation_wrapper" \
+ //tools/foxglove:create_foxglove_extension \
+ -- \
+ "$@"
diff --git a/tools/foxglove/creation_wrapper.py b/tools/foxglove/creation_wrapper.py
new file mode 100644
index 0000000..dd36c5f
--- /dev/null
+++ b/tools/foxglove/creation_wrapper.py
@@ -0,0 +1,126 @@
+import os
+import subprocess
+import sys
+import tempfile
+from pathlib import Path
+
+import yaml
+from python.runfiles import runfiles
+
+RUNFILES = runfiles.Create()
+
+FAKE_NPM_BIN = RUNFILES.Rlocation(
+ "org_frc971/tools/foxglove/creation_wrapper_npm")
+BUILDOZER_BIN = RUNFILES.Rlocation(
+ "com_github_bazelbuild_buildtools/buildozer/buildozer_/buildozer")
+
+WORKSPACE_DIR = Path(os.environ["BUILD_WORKSPACE_DIRECTORY"])
+WORKING_DIR = Path(os.environ["BUILD_WORKING_DIRECTORY"])
+
+
+def create_npm_link(temp_dir: Path, env: dict[str, str]):
+ """Set up the creation_wrapper_npm.py script as the "npm" binary."""
+ bin_dir = temp_dir / "bin"
+ bin_dir.mkdir()
+ npm = bin_dir / "npm"
+ npm.symlink_to(FAKE_NPM_BIN)
+ env["PATH"] = f"{temp_dir / 'bin'}:{env['PATH']}"
+
+
+def run_create_foxglove_extension(argv: list[str], name: str):
+ """Runs the create-foxglove-extension binary.
+
+ Args:
+ argv: The list of command line arguments passed to this wrapper.
+ name: The (directory) name of the new extension to be created.
+ """
+ with tempfile.TemporaryDirectory() as temp_dir:
+ temp_dir = Path(temp_dir)
+ env = os.environ.copy()
+ create_npm_link(temp_dir, env)
+
+ env["BAZEL_BINDIR"] = WORKING_DIR
+ env.pop("RUNFILES_DIR", None)
+ env.pop("RUNFILES_MANIFEST_FILE", None)
+
+ subprocess.run(argv[1:], check=True, env=env, cwd=WORKING_DIR)
+ # For some reason, the `foxglove-extension` binary doesn't set up the
+ # ts-loader dependency. Do it manually here.
+ subprocess.run(["npm", "install", "ts-loader@^9"],
+ check=True,
+ env=env,
+ cwd=WORKING_DIR / name)
+
+
+def add_new_js_project(name: str):
+ """Tell Bazel about the new project."""
+ # The name of the Bazel package for the new extension.
+ package_name = WORKING_DIR.relative_to(WORKSPACE_DIR) / name
+
+ # Add the new "node_modules" directory to the ignore list.
+ bazelignore_file = WORKSPACE_DIR / ".bazelignore"
+ bazelignore = bazelignore_file.read_text()
+ bazelignore_entry = str(package_name / "node_modules")
+ if bazelignore_entry not in bazelignore.splitlines():
+ bazelignore = bazelignore.rstrip("\n") + "\n"
+ bazelignore_file.write_text(bazelignore + bazelignore_entry + "\n")
+
+ # Add the new project to the workspace list. This ensures the lock file
+ # gets updated properly.
+ pnpm_workspace_file = WORKSPACE_DIR / "pnpm-workspace.yaml"
+ pnpm_workspace = yaml.load(pnpm_workspace_file.read_text(),
+ Loader=yaml.CLoader)
+ if str(package_name) not in pnpm_workspace["packages"]:
+ pnpm_workspace["packages"].append(str(package_name))
+ pnpm_workspace_file.write_text(yaml.dump(pnpm_workspace))
+
+ # Add the new project to the workspace. This ensures that all of its
+ # dependencies get downloaded by Bazel.
+ subprocess.check_call([
+ BUILDOZER_BIN,
+ f"add data @//{package_name}:package.json",
+ "WORKSPACE:npm",
+ ],
+ cwd=WORKSPACE_DIR)
+
+ # Regenerate the lock file with the new project's dependencies included.
+ subprocess.check_call([
+ "bazel",
+ "run",
+ "--",
+ "@pnpm//:pnpm",
+ "--dir",
+ WORKSPACE_DIR,
+ "install",
+ "--lockfile-only",
+ ],
+ cwd=WORKSPACE_DIR)
+
+
+def main(argv):
+ """Runs the main logic."""
+
+ # Assume that the only argument the user passed in is the name of the
+ # extension. We can probably do better here, but oh well.
+ create_foxglove_extension_args = argv[2:]
+ name = create_foxglove_extension_args[0]
+
+ run_create_foxglove_extension(argv, name)
+ add_new_js_project(name)
+
+ # Generate a BUILD file.
+ build_file_template = WORKSPACE_DIR / "tools/foxglove/BUILD.bazel.tmpl"
+ build_file = WORKING_DIR / name / "BUILD.bazel"
+ build_file.write_text(build_file_template.read_text())
+
+ # Fix up the tsconfig.json. For some reason the inheritance for the `lib`
+ # field doesn't work out of the box. We're using string manipulation since
+ # we don't have a readily-available "JSON with comments" parser.
+ tsconfig_file = WORKING_DIR / name / "tsconfig.json"
+ tsconfig = tsconfig_file.read_text()
+ tsconfig = tsconfig.replace('"lib": ["dom"]', '"lib": ["dom", "es2022"]')
+ tsconfig_file.write_text(tsconfig)
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/tools/foxglove/creation_wrapper_npm.py b/tools/foxglove/creation_wrapper_npm.py
new file mode 100644
index 0000000..6483536
--- /dev/null
+++ b/tools/foxglove/creation_wrapper_npm.py
@@ -0,0 +1,45 @@
+"""Acts as a dummy `npm` binary for the `create-foxglove-extension` binary.
+
+The `create-foxglove-extension` binary uses `npm` to manipulate the
+`package.json` file instead of doing so directly. Since we don't have access to
+the real `npm` binary here we just emulate the limited functionality we need.
+"""
+
+import argparse
+import json
+import sys
+from pathlib import Path
+
+
+def main(argv: list[str]):
+ """Runs the main logic."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument("command")
+ parser.add_argument("--save-exact", action="store_true")
+ parser.add_argument("--save-dev", action="store_true")
+ args, packages = parser.parse_known_args(argv[1:])
+
+ # Validate the input arguments.
+ if args.command != "install":
+ raise ValueError("Don't know how to simulate anything other "
+ f"than 'install'. Got '{args.command}'.")
+
+ for package in packages:
+ if "@^" not in package:
+ raise ValueError(f"Got unexpected package: {package}")
+
+ # Append the specified packages to the dependencies list.
+ package_version_pairs = list(
+ package.rsplit("@", maxsplit=1) for package in packages)
+ package_json_file = Path.cwd() / "package.json"
+ package_json = json.loads(package_json_file.read_text())
+ package_json.setdefault("dependencies", {}).update(
+ {package: version
+ for package, version in package_version_pairs})
+
+ package_json_file.write_text(
+ json.dumps(package_json, sort_keys=True, indent=4))
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/tools/foxglove/extension.tmpl.BUILD b/tools/foxglove/extension.tmpl.BUILD
new file mode 100644
index 0000000..bfe99aa
--- /dev/null
+++ b/tools/foxglove/extension.tmpl.BUILD
@@ -0,0 +1,5 @@
+load("//tools/build_rules:foxglove.bzl", "foxglove_extension")
+
+foxglove_extension(
+ name = "{NAME}",
+)
diff --git a/tools/foxglove/foxglove_extension_wrapper.js b/tools/foxglove/foxglove_extension_wrapper.js
new file mode 100644
index 0000000..71560f2
--- /dev/null
+++ b/tools/foxglove/foxglove_extension_wrapper.js
@@ -0,0 +1,56 @@
+// This script acts as a wrapper for the `foxglove-extension` binary. We need a
+// wrapper here because `foxglove-extension` wants to invoke `npm` directly.
+// Since we don't support the real npm binary, we force `foxglove-extension` to
+// use our fake npm binary (`foxglove_extension_wrapper_npm.js`) by
+// manipulating the PATH.
+
+const { spawnSync } = require('child_process');
+const path = require('path');
+const process = require('process');
+const fs = require('fs');
+const { tmpdir } = require('os');
+
+// Add a directory to the PATH environment variable.
+function addToPath(directory) {
+ const currentPath = process.env.PATH || '';
+ const newPath = `${directory}${path.delimiter}${currentPath}`;
+ process.env.PATH = newPath;
+}
+
+const fakeNpm = path.join(__dirname, 'foxglove_extension_wrapper_npm.sh');
+
+const tempBinDir = fs.mkdtempSync(path.join(tmpdir(), "foxglove_extension_wrapper-tmp-"));
+fs.symlinkSync(fakeNpm, path.join(tempBinDir, 'npm'));
+
+addToPath(tempBinDir);
+
+// Create a relative path for a specific root-relative directory.
+function getRelativePath(filePath) {
+ // Count the number of directories and construct the relative path.
+ const numDirectories = filePath.split('/').length;
+ return '../'.repeat(numDirectories);
+}
+
+// We need to know the path to the `foxglove-extension` binary from the
+// sub-directory where we're generating code into.
+const relativePath = getRelativePath(process.env.BAZEL_PACKAGE);
+const foxgloveExtensionPath = path.join(relativePath, `tools/foxglove/foxglove_extension.sh`)
+
+// Extract arguments intended for the `foxglove-extension` binary.
+const args = process.argv.slice(2);
+
+// Execute the `foxglove-extension` binary.
+try {
+ const result = spawnSync(foxgloveExtensionPath, args, { stdio: 'inherit', cwd: process.env.BAZEL_PACKAGE });
+ if (result.error) {
+ console.error('Error executing foxglove_extension:', result.error);
+ process.exit(1);
+ }
+ if (result.status !== 0) {
+ console.error(`foxglove_extension exited with status ${result.status}`);
+ process.exit(result.status);
+ }
+} catch (error) {
+ console.error('Error executing foxglove_extension:', error);
+ process.exit(1);
+}
diff --git a/tools/foxglove/foxglove_extension_wrapper_npm.js b/tools/foxglove/foxglove_extension_wrapper_npm.js
new file mode 100644
index 0000000..88dfca5
--- /dev/null
+++ b/tools/foxglove/foxglove_extension_wrapper_npm.js
@@ -0,0 +1,69 @@
+// This script acts as an "npm" binary for the foxglove-extension binary. We
+// don't actually care to do any npm things here. For some reason
+// foxglove-extension defers to npm to execute the various build stages. So
+// all this script does is execute those various build stages. The stages are
+// defined in the package.json file.
+
+const fs = require('fs');
+const { execSync } = require('child_process');
+const path = require('path');
+
+// Read the package.json file.
+function readPackageJson() {
+ try {
+ const packageJson = fs.readFileSync('package.json', 'utf8');
+ return JSON.parse(packageJson);
+ } catch (error) {
+ console.error('Error reading package.json:', error);
+ process.exit(1);
+ }
+}
+
+// Execute the named script specified in package.json.
+function executeScript(scriptName) {
+ const packageJson = readPackageJson();
+ const scripts = packageJson.scripts || {};
+
+ if (!scripts[scriptName]) {
+ console.error(`Script '${scriptName}' not found in package.json`);
+ process.exit(1);
+ }
+
+ // We cannot execute the `foxglove-extension` binary as-is (at least not
+ // without setting up a custom PATH). So we instead point at the
+ // Bazel-generated wrapper script for that binary.
+ const scriptParts = scripts[scriptName].split(' ');
+ const bin = scriptParts[0];
+ if (bin !== 'foxglove-extension') {
+ console.error(`Cannot support commands other than 'foxglove-extension'. Got: ${bin}`);
+ process.exit(1);
+ }
+ scriptParts[0] = path.join(__dirname, 'foxglove_extension.sh');
+
+ // Execute the `foxglove-extension` command specified in the script.
+ try {
+ console.log(`Executing script '${scriptName}'...`);
+ execSync(scriptParts.join(' '), { stdio: 'inherit' });
+ } catch (error) {
+ console.error(`Error executing script '${scriptName}':`, error);
+ process.exit(1);
+ }
+}
+
+function main() {
+ // Validate the input arguments.
+ if (process.argv.length !== 4) {
+ console.error('Usage: node foxglove_extension_wrapper_npm.js <scriptName>');
+ process.exit(1);
+ }
+ if (process.argv[2] !== "run") {
+ console.error(`Cannot support commands other than 'run'. Got: ${process.argv[2]}`);
+ process.exit(1);
+ }
+
+ // Run the script specified in the package.json file.
+ const scriptName = process.argv[3];
+ executeScript(scriptName);
+}
+
+main();
diff --git a/tools/python/requirements.txt b/tools/python/requirements.txt
index ddbaef3..81d306d 100644
--- a/tools/python/requirements.txt
+++ b/tools/python/requirements.txt
@@ -15,6 +15,7 @@
validators
yapf
sympy
+pyyaml
# TODO(phil): Migrate to absl-py. These are abandoned as far as I can tell.
python-gflags