diff --git a/.changeset/sour-trees-build.md b/.changeset/sour-trees-build.md new file mode 100644 index 0000000..4d3846f --- /dev/null +++ b/.changeset/sour-trees-build.md @@ -0,0 +1,5 @@ +--- +'@envyjs/webui': minor +--- + +Added support for self-hosting and customization of the Envy viewer diff --git a/.gitignore b/.gitignore index fb051db..5246998 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,6 @@ bin # examples/next.js **/.next/ **/next-env.d.ts + +# other +.DS_Store diff --git a/README.md b/README.md index 637bc00..95869f4 100644 --- a/README.md +++ b/README.md @@ -28,12 +28,13 @@ Envy will trace the network calls from every application in your stack and allow _Note: Envy is intended for development usage only, and is not a replacement for optimized production telemetry_
- Envy + Envy
## Contents - [Getting Started](#getting-started) +- [Customizing](#customizing) - [Production Bundles](#production-bundles) - [Contributing](#contributing) @@ -125,11 +126,17 @@ enableTracing({ serviceName: 'your-website-name' }).then(() => { _Browsers prevent full timing data from being accessed from cross-origin requests unless the server responds with the [Timing-Allow-Origin](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Timing-Allow-Origin) header_. -### Production Bundles +## Customizing + +Whilst Envy will run as a zero-config standalone viewer, it is also possible to run the Envy viewer locally from your application and to define your own systems to customize how traces are presented. + +See the [customization docs](docs/customizing.md) for more information. + +## Production Bundles Envy is designed to enhance your developer experience and is not intended for production usage. Depending on your application, there are various ways to exclude it from your bundle in production. -#### Dynamic Imports (Typescript) +### Dynamic Imports (Typescript) ```ts if (process.env.NODE_ENV !== 'production') { @@ -139,7 +146,7 @@ if (process.env.NODE_ENV !== 'production') { } ``` -#### Dynamic Require (Javascript) +### Dynamic Require (Javascript) ```ts if (process.env.NODE_ENV !== 'production') { @@ -148,7 +155,7 @@ if (process.env.NODE_ENV !== 'production') { } ``` -#### Disabling Tracing +### Disabling Tracing This option is the simplest, but will leave the code in your output bundle. Depending on your application and its deployment and packaging method, this may be acceptable in your usage. diff --git a/docs/customizing.md b/docs/customizing.md new file mode 100644 index 0000000..184e390 --- /dev/null +++ b/docs/customizing.md @@ -0,0 +1,107 @@ +# Customizing Envy + +## Creating your own systems + +A system is a `class` which defines the following: + +- What identifies the trace as belonging to the system, e.g., the hostname, path, etc. +- What icon to display for the system +- What data to show in the list view for the trace +- What data to show in the detail for the trace + +**Let's start by example:** + +In the application you are sending traces from, you can create a new `class` like the following: + +```tsx +// ./src/systems/CatFactsSystem.tsx + +import { System, Trace } from '@envyjs/webui'; + +export default class CatFactsSystem implements System { + name = 'Cat Facts API'; + + isMatch(trace: Trace) { + // this system applies to all traces which are requests to the `cat-fact.herokuapp.com` host + return trace.http?.host === 'cat-fact.herokuapp.com'; + } + + getIconUri() { + // to avoid the need for external resources, icons can be defined as base64 data + return ''; + } + + getTraceRowData() { + // this is the text which will be displayed below the host and path in the list view + return { + data: 'This is a cat fact', + }; + } +} +``` + +Once you have that system, we need to register it with the Envy viewer. The only way to do this currently is to host the envy viewer yourself as a react component, and pass the systems to register in the props. + +For example: + +```tsx +// ./src/MyEnvyViewer.tsx + +import EnvyViewer from '@envyjs/webui'; +import { createRoot } from 'react-dom/client'; + +import CatFactsSystem from './systems/CatFactsSystem'; + +function MyEnvyViewer() { + return ; +} +``` + +Then, you would serve this component up either on a new route in your application, or as a separate application. For example, using parcel you might have the following: + +```tsx +// src/myEnvyViewer.html + + + + + My custome Envy viewer + + +
+ + + + + +// src/myEnvyViewer.js +import EnvyViewer from '@envyjs/webui'; +import { createRoot } from 'react-dom/client'; + +import MyEnvyViewer from './MyEnvyViewer'; + +const container = document.getElementById('root'); +const root = createRoot(container); + +root.render(); +``` + +Finally, in your `package.json`, you would have to start the `@envyjs/webui` collector, opting out of launching the default viewer UI, and load your UI instead: + +``` +// package.json + +{ + "scripts": { + "start:envy": "concurrently \"yarn start:collector\" \"yarn start:viewer\"", + "start:collector": "npx @envyjs/webui --noUi", + "start:viewer": "parcel ./src/myEnvyViewer.html --port 4002 --no-cache" + } +} +``` + +Then, running `yarn start:envy` in your application would start the collector process and launch your customized viewer: + +
+ An example of a custom system defining the presentation of a trace +
diff --git a/envy-custom-system.png b/envy-custom-system.png new file mode 100644 index 0000000..cbaf541 Binary files /dev/null and b/envy-custom-system.png differ diff --git a/examples/apollo-client/package.json b/examples/apollo-client/package.json index f677006..9c8fcd8 100644 --- a/examples/apollo-client/package.json +++ b/examples/apollo-client/package.json @@ -5,10 +5,15 @@ "license": "MIT", "private": true, "scripts": { - "start": "parcel ./src/index.html --port 4001 --no-cache" + "start": "yarn start:web", + "start:custom-viewer": "concurrently \"yarn start:envy\" \"yarn start:web\" \"yarn start:viewer\"", + "start:web": "parcel ./src/index.html --port 4001 --no-cache", + "start:envy": "npx @envyjs/webui --noUi", + "start:viewer": "parcel ./src/viewer/viewer.html --port 4002 --no-cache" }, "dependencies": { "@envyjs/web": "*", + "@envyjs/webui": "*", "react": "^18.2.0", "react-dom": "^18.2.0", "urql": "^4.0.5" diff --git a/examples/apollo-client/src/index.html b/examples/apollo-client/src/index.html index 88b8027..22e9301 100644 --- a/examples/apollo-client/src/index.html +++ b/examples/apollo-client/src/index.html @@ -6,7 +6,7 @@ -
+
diff --git a/examples/apollo-client/src/viewer/systems/CatFacts.tsx b/examples/apollo-client/src/viewer/systems/CatFacts.tsx new file mode 100644 index 0000000..3f992b3 --- /dev/null +++ b/examples/apollo-client/src/viewer/systems/CatFacts.tsx @@ -0,0 +1,19 @@ +import { System, Trace } from '@envyjs/webui'; + +export default class CatFactsSystem implements System { + name = 'Cat Facts API'; + + isMatch(trace: Trace) { + return trace.http?.host === 'cat-fact.herokuapp.com'; + } + + getIconUri() { + return 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pjxzdmcgdmlld0JveD0iMCAwIDUxMiA1MTIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgaWQ9IkJsYWNrX2NhdCI+PHBhdGggZD0iTTM3MC4wOTE4LDUyLjE0NmMtNS4xMzEyLTUuMTAzNS0xMy40NS00LjczLTE4LjU2MzguMzg1Ni0yMi44NzQ4LDIyLjg1NzYtNDIuNDkzOSw1MC4zNTYzLTU3LjkwNDcsODEuMThhMjYyLjg1NDQsMjYyLjg1NDQsMCwwLDAtNzUuMzM2MywwLDMxMi45NTEyLDMxMi45NTEyLDAsMCwwLTU3LjgyMzUtODEuMTYwN2MtNS4xMS01LjEyMzgtMTMuNDMyNi01LjUwNTEtMTguNTY4MS0uNDAyN0M4My4zOTI2LDExMC4yODE0LDQ2LDE5OC4zMzc4LDQ2LDI5Ny4yNDg1YzAsOTEuODc1LDkzLjk3NzEsMTY2LjI1LDIxMCwxNjYuMjUsMTE1LjkzNzUsMCwyMTAtNzQuMzc1LDIxMC0xNjYuMjVDNDY2LDE5OC4zMzY4LDQyOC41MjYyLDExMC4yNzgxLDM3MC4wOTE4LDUyLjE0NlpNMTQ2LjYyNSwzMzAuODQ5M2MtMjQuMzI3NC00LjcyNTQtNDQuOTc2Mi0yMi4zMTI5LTU2Ljg3NS00Ni43MjU4LDExLjg5ODgtMjQuNDEyOCwzMi41NDc2LTQyLDU2Ljg3NS00Ni43MjQ3Wm0yNi4yNSwwVjIzNy4zOTg4YzI0LjIzNzcsNC43MjQzLDQ0Ljk3NjIsMjIuMzExOSw1Ni44NzUsNDYuNzI0N0MyMTcuODUxMiwzMDguNTM2NCwxOTcuMTEyNywzMjYuMTIzOSwxNzIuODc1LDMzMC44NDkzWm0xNjYuMjUsMGMtMjQuMzI3NC00LjcyNTQtNDQuOTc2Mi0yMi4zMTI5LTU2Ljg3NS00Ni43MjU4LDExLjg5ODgtMjQuNDEyOCwzMi41NDc2LTQyLDU2Ljg3NS00Ni43MjQ3Wm0yNi4yNSwwVjIzNy4zOTg4YzI0LjIzNzcsNC43MjQzLDQ0Ljk3NjIsMjIuMzExOSw1Ni44NzUsNDYuNzI0N0M0MTAuMzUxMiwzMDguNTM2NCwzODkuNjEyNywzMjYuMTIzOSwzNjUuMzc1LDMzMC44NDkzWiIvPjwvZz48L3N2Zz4='; + } + + getTraceRowData() { + return { + data: 'Cat fact', + }; + } +} diff --git a/examples/apollo-client/src/viewer/systems/CocktailDb.tsx b/examples/apollo-client/src/viewer/systems/CocktailDb.tsx new file mode 100644 index 0000000..ae89533 --- /dev/null +++ b/examples/apollo-client/src/viewer/systems/CocktailDb.tsx @@ -0,0 +1,32 @@ +import { System, Trace } from '@envyjs/webui'; + +type CocktailDbData = { + name: string; +}; + +export default class CocktailDbSystem implements System { + name = 'Cocktail Database'; + + isMatch(trace: Trace) { + return trace.http?.host === 'www.thecocktaildb.com'; + } + + getData(trace: Trace) { + const data = trace.http?.responseBody ? JSON.parse(trace.http?.responseBody) : null; + + return { + name: data?.drinks[0].strDrink ?? '', + }; + } + + getIconUri() { + return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiBpbWFnZS1yZW5kZXJpbmc9Im9wdGltaXplUXVhbGl0eSIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIHZpZXdCb3g9IjAgMCA1MDUgNTEyLjQ0Ij48cGF0aCBmaWxsPSIjRkRDNDU0IiBkPSJNNDU5LjIzNSAxNDUuNDQ1YzIwLjU3NCA4MS45NDggMTcuMDU0IDE1OS43MjItNTAuMTI0IDE4MS41ODctMzcuNTcyIDEyLjE0OS02Ny45NzkgNy43MDItOTEuNjI5LTEyLjM4OS0zMi44NzQtMjcuOTI5LTQ2LjYzOC03Ny41ODUtNTIuNTk2LTEzMC4xMDUgOTMuNzg4LTQyLjAzOSAxOTYuMDc2LTkyLjU3IDE5NC4zNDktMzkuMDkzeiIvPjxwYXRoIGZpbGw9IiM2ODM4MDAiIGZpbGwtcnVsZT0ibm9uemVybyIgZD0iTTM1Mi45NTEgNTA0LjE4M2MtNy41NTMgMS40NDMtMTQuODUyLTMuNTEyLTE2LjI5Ni0xMS4wNjQtMS40NDMtNy41NTMgMy41MTEtMTQuODUzIDExLjA2NC0xNi4yOTZsNTguNjc0LTExLjM0NS0yNS43MTUtMTI1Ljc2OWMtMS41MzMtNy41MjQgMy4zMjQtMTQuODczIDEwLjg0OS0xNi40MDYgNy41MjUtMS41MzQgMTQuODczIDMuMzIzIDE2LjQwNiAxMC44NDlsMjUuNzc1IDEyNi4wNDIgNTQuNzQ2LTEwLjU4NWM3LjU1My0xLjQ0MyAxNC44NTMgMy41MTEgMTYuMjk2IDExLjA2NCAxLjQ0MyA3LjU1My0zLjUxMSAxNC44NTItMTEuMDY0IDE2LjI5NmwtMTQwLjczNSAyNy4yMTR6Ii8+PHBhdGggZmlsbD0iI0ZEQzQ1NCIgZD0iTTQ5Ljc2OCAxNDUuNDQ1Yy0yMC41NzQgODEuOTQ4LTE3LjA1NCAxNTkuNzIyIDUwLjEyNCAxODEuNTg3IDM3LjU3MiAxMi4xNDkgNjcuOTc5IDcuNzAyIDkxLjYyOS0xMi4zODkgMzIuODc0LTI3LjkyOSA0Ni42MzgtNzcuNTg1IDUyLjU5Ni0xMzAuMTA1LTQ2Ljc2OS01NS40MTUtMTI5LjU2Ni0yNi4wNjItMTk0LjM0OS0zOS4wOTN6Ii8+PHBhdGggZmlsbD0iIzY4MzgwMCIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNMjM5Ljk2NyA1NS44MzhjLS42NDktMy44NjItMS45NDYtNi45ODQtMy45MzQtOS4yOTgtMS45NzEtMi4yOTctNC44NjQtNC4wMTEtOC43MTktNS4wOTdsLTc2Ljk3NS0xNS4xNzhjLTQuODc0LS45NjEtOS42OTctMS45OTktMTMuOTQ3LTIuOTExLTIzLjEwMi00Ljk2NS0yMy4yMDctNC45ODUtMzUuNjQxIDI1Ljk0NC0yNy41NjggNjguNTc2LTUwLjE0NiAxMzAuMDAxLTUzLjQ3MyAxNzcuNTg3LTMuMDk1IDQ0LjIzOCAxMS4xNzEgNzYuNDA2IDU1LjYyOCA5MC44NzRsLjQwNi4xNDJjMTcuMTg3IDUuNTAzIDMyLjYwNSA3LjI1NCA0Ni4yODEgNS4zN2wuMjc4LS4wMzRjMTMuMjM5LTEuODY3IDI1LjAxNC03LjI0OCAzNS4zNTMtMTYuMDM2IDUxLjk2Ny00NC4xNTEgNTMuMTMtMTM5LjU1MiA1NC4wNDItMjE0LjYyMi4xNi0xMy4wMjguMzEyLTI1LjQ3OS43MDEtMzYuNzQxek0xMjcuNjY2IDM0Mi45NDNMMTAwLjg4IDQ3My45MjJsNTYuNDAxIDEwLjkwOGM3LjU1MyAxLjQ0NCAxMi41MDcgOC43NDMgMTEuMDY0IDE2LjI5Ni0xLjQ0MyA3LjU1My04Ljc0MyAxMi41MDctMTYuMjk2IDExLjA2NEwxMS4zMTQgNDg0Ljk3NkMzLjc2MSA0ODMuNTMzLTEuMTkzIDQ3Ni4yMzMuMjUgNDY4LjY4YzEuNDQ0LTcuNTUzIDguNzQzLTEyLjUwNyAxNi4yOTYtMTEuMDYzbDU3LjAyMiAxMS4wMjUgMjYuODQtMTMxLjI0N2MtMS4wMS0uMzAyLTIuMDE5LS42MTctMy4wMzYtLjk0M2wtLjQ5Ni0uMTQ2Yy01NS4wOS0xNy45MjYtNzIuODEyLTU3LjA3MS02OS4wNjItMTEwLjcxOCAzLjUxOC01MC4yOTYgMjYuNjM1LTExMy4zNjYgNTQuODQ4LTE4My41NDFDMTAxLjIzMy00LjE1NSAxMDEuNC00LjExNiAxNDAuNDM4IDQuMjcyYzMuODE5LjgxOSA4LjE0MiAxLjc1MyAxMy42NDEgMi44MzVsNzcuNzY5IDE1LjM2OWM4LjAyNSAyLjEyIDE0LjMyMiA1Ljk2NCAxOC45OTIgMTEuMzk0IDQuNjA4IDUuMzU2IDcuNDE0IDEyLjAzOSA4LjU0OCAxOS45MzUuMDY3LjU2Mi4wOTggMS4xMzQuMDc3IDEuNzE3LS40MjMgMTEuNzY1LS41NzYgMjQuMjI3LS43MzUgMzcuMjg1LS45NTggNzguOTk4LTIuMTgzIDE3OS4zODUtNjAuOTEyIDIyOS4yNzgtMTMuMTQzIDExLjE2OC0yOC4yMDMgMTguMDMxLTQ1LjIxMyAyMC40NDJsLS4zNC4wNTVjLTcuODIgMS4wNzktMTYuMDE5IDEuMjA0LTI0LjU5OS4zNjF6Ii8+PHBhdGggZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNMjAyLjk5NyAyMjMuMTI4YTUuNTcyIDUuNTcyIDAgMDExMS4wMzMgMS41NjhjLTMuMTE2IDIxLjQ0NC0xMC41MyAzOC41NjItMjEuNzA4IDUxLjg2NC0xMS4yMTMgMTMuMzQzLTI2LjA5MyAyMi43MjEtNDQuMTI0IDI4LjY2NGE1LjU4IDUuNTggMCAwMS03LjA0Mi0zLjU1MyA1LjU4IDUuNTggMCAwMTMuNTUyLTcuMDQzYzE2LjExNi01LjMxMSAyOS4zMTMtMTMuNTY5IDM5LjEwNy0yNS4yMjIgOS44MjUtMTEuNjkyIDE2LjM3Ni0yNi45NjEgMTkuMTgyLTQ2LjI3OHpNMzA5LjAxMSAyNDguMjY0YTUuNTczIDUuNTczIDAgMDExMC4zOC00LjA2YzcuMTQ3IDE4LjE0MiAxNy4wMjUgMzEuNTAyIDI5LjI4OSA0MC42NDggMTIuMjA5IDkuMTA3IDI2LjkzNiAxNC4xMjQgNDMuODE4IDE1LjYwMmE1LjU4MiA1LjU4MiAwIDAxLS45NTcgMTEuMTIyYy0xOC45MzktMS42NTUtMzUuNTgyLTcuMzc2LTQ5LjUzNi0xNy43ODctMTMuOTEyLTEwLjM3Ny0yNS4wNDItMjUuMzQ0LTMyLjk5NC00NS41MjV6Ii8+PHBhdGggZmlsbD0iIzY4MzgwMCIgZmlsbC1ydWxlPSJub256ZXJvIiBkPSJNMjQ5LjYyMiA1My44MDVjMS4xMjQtNy44OTYgMy45MzUtMTQuNTc1IDguNTM5LTE5LjkzNSA0LjY2OS01LjQzIDEwLjk2Ni05LjI3NCAxOC45OTQtMTEuMzk0bDc3Ljc3LTE1LjM2OWM1LjUwNi0xLjA4NiA5LjgxOC0yLjAxNiAxMy42NDEtMi44MzUgMzkuMDM1LTguMzg4IDM5LjIwNC04LjQyMyA1Ny43NzYgMzcuNzc1IDI4LjIxMyA3MC4xNzUgNTEuMzI5IDEzMy4yNDUgNTQuODQ3IDE4My41NDEgMy43NTEgNTMuNjQ3LTEzLjk3NCA5Mi43OTItNjkuMDY1IDExMC43MThsLS40OTYuMTQ2Yy0yMC4wMjEgNi40MTQtMzguMzA1IDguNDE2LTU0Ljg4OCA2LjEzbC0uMzQxLS4wNTVjLTE3LjAxLTIuNDExLTMyLjA3MS05LjI3NC00NS4yMTItMjAuNDQyLTU4LjczNy00OS44OTctNTkuOTU4LTE1MC4yOTQtNjAuOTE2LTIyOS4yOTUtLjE1Ni0xMy4wNTUtLjMwOS0yNS41MTctLjczMi0zNy4yNjgtLjAyMS0uNTgzLjAxLTEuMTU1LjA4My0xLjcxN3ptMjMuMzQ2LTcuMjY1Yy0xLjk4NSAyLjMxLTMuMjg2IDUuNDMzLTMuOTMxIDkuMjk0LjM4OCAxMS4yNjIuNTQxIDIzLjcwNi42OTcgMzYuNzI3LjkxMyA3NS4wNzggMi4wNzUgMTcwLjQ4OSA1NC4wNDYgMjE0LjY0IDEwLjMzOSA4Ljc4OCAyMi4xMTQgMTQuMTY5IDM1LjM1MyAxNi4wMzZsLjI3OC4wMzRjMTMuNjggMS44ODggMjkuMDk0LjEzMyA0Ni4yNzgtNS4zN2wuNDA2LS4xNDJjNDQuNDU2LTE0LjQ2OCA1OC43MjYtNDYuNjM2IDU1LjYzMS05MC44NzQtMy4zMjctNDcuNTg2LTI1LjkwNi0xMDkuMDExLTUzLjQ3My0xNzcuNTg3LTEyLjQzMS0zMC45MjYtMTIuNTM4LTMwLjkwOS0zNS42NDEtMjUuOTQ0LTQuMjU0LjkxMi05LjA4MyAxLjk1My0xMy45NDcgMi45MTFMMjgxLjY5IDQxLjQ0N2MtMy44NTggMS4wODItNi43NDggMi43OTktOC43MjIgNS4wOTN6Ii8+PC9zdmc+'; + } + + getTraceRowData(trace: Trace) { + const data = this.getData(trace); + return { + data: data.name, + }; + } +} diff --git a/examples/apollo-client/src/viewer/viewer.html b/examples/apollo-client/src/viewer/viewer.html new file mode 100644 index 0000000..ef179e1 --- /dev/null +++ b/examples/apollo-client/src/viewer/viewer.html @@ -0,0 +1,11 @@ + + + + + Envy - Custom viewer + + +
+ + + diff --git a/examples/apollo-client/src/viewer/viewer.js b/examples/apollo-client/src/viewer/viewer.js new file mode 100644 index 0000000..85c2e54 --- /dev/null +++ b/examples/apollo-client/src/viewer/viewer.js @@ -0,0 +1,10 @@ +import EnvyViewer from '@envyjs/webui'; +import { createRoot } from 'react-dom/client'; + +import CatFactsSystem from './systems/CatFacts'; +import CocktailDbSystem from './systems/CocktailDb'; + +const container = document.getElementById('root'); +const root = createRoot(container); + +root.render(); diff --git a/jest.config.ts b/jest.config.ts index d5b83db..e2e5eb7 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,22 +1,4 @@ export default { preset: 'ts-jest', - transform: { - '^.+\\.tsx?$': [ - 'ts-jest', - { - diagnostics: { - ignoreCodes: [1343], - }, - astTransformers: { - before: [ - { - path: 'ts-jest-mock-import-meta', - options: { metaObjectReplacement: { url: 'https://www.url.com/' } }, - }, - ], - }, - }, - ], - }, testMatch: ['**/__tests__/**/*.[jt]s?(x)', '**/src/**/?(*.)+(spec|test).[jt]s?(x)'], }; diff --git a/package.json b/package.json index 6770982..5b759b0 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test", - "example:apollo": "concurrently \"cd examples/apollo && yarn start\" \"wait-on tcp:4000 && (cd examples/apollo-client && yarn start)\"", - "example:express": "concurrently \"cd examples/express && yarn start\" \"wait-on tcp:4000 && (cd examples/express-client && yarn dev)\"", + "example:apollo": "concurrently \"cd examples/apollo && yarn start\" \"cd examples/apollo-client && yarn start:custom-viewer\"", + "example:express": "concurrently \"cd examples/express && yarn start\" \"cd examples/express-client && yarn dev\"", "example:next": "cd examples/next && yarn && yarn dev", "changeset": "changeset" }, diff --git a/packages/webui/package.json b/packages/webui/package.json index 9bb6856..452e308 100644 --- a/packages/webui/package.json +++ b/packages/webui/package.json @@ -2,9 +2,16 @@ "name": "@envyjs/webui", "version": "0.3.2", "description": "Envy Web UI", - "source": [ - "src/index.html" - ], + "targets": { + "main": false, + "viewer": { + "source": "src/index.html", + "distDir": "dist" + } + }, + "main": "dist/integration.cjs.js", + "module": "dist/integration.esm.js", + "types": "dist/integration.d.ts", "bin": { "envy": "bin/start.cjs" }, @@ -25,9 +32,11 @@ "test:watch": "jest --watch --coverage", "test:coverage": "jest --coverage && open ./coverage/lcov-report/index.html", "prebuild": "rimraf dist && rimraf bin", - "build": "yarn build:parcel && yarn build:scripts", - "build:parcel": "cross-env NODE_ENV=production parcel build --no-cache", - "build:scripts": "copyfiles --flat ./src/scripts/start.cjs ./src/scripts/startCollector.cjs ./src/scripts/startViewer.cjs ./bin", + "build": "yarn build:app && yarn build:integration && yarn build:typedefs && yarn build:bin", + "build:app": "cross-env NODE_ENV=production parcel build --no-cache", + "build:integration": "tailwindcss -i ./src/styles/base.css -o ./dist/viewer.css && node ./src/scripts/buildIntegration.cjs", + "build:typedefs": "tsc --project ./tsconfig.types.json", + "build:bin": "copyfiles --flat ./src/scripts/start.cjs ./src/scripts/startCollector.cjs ./src/scripts/startViewer.cjs ./bin", "lint": "tsc --noEmit && eslint ./src --ext .ts,.tsx" }, "dependencies": { @@ -59,6 +68,8 @@ "copyfiles": "^2.4.1", "cross-env": "^7.0.3", "crypto-browserify": "^3.12.0", + "esbuild": "^0.19.3", + "esbuild-plugin-inline-import": "^1.0.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "events": "^3.1.0", diff --git a/packages/webui/src/components/ui/FiltersAndActions.tsx b/packages/webui/src/components/ui/FiltersAndActions.tsx index b879581..38841d1 100644 --- a/packages/webui/src/components/ui/FiltersAndActions.tsx +++ b/packages/webui/src/components/ui/FiltersAndActions.tsx @@ -40,7 +40,7 @@ export default function FiltersAndActions() { label="Systems:" multiSelect items={systems.map(x => ({ - icon: x.getIconPath?.(null) ?? defaultSystem.getIconPath(), + icon: x.getIconUri?.(null) ?? defaultSystem.getIconUri(), value: x.name, }))} onChange={handleSystemsChange} diff --git a/packages/webui/src/components/ui/TraceDetail.test.tsx b/packages/webui/src/components/ui/TraceDetail.test.tsx index 939d386..1753b55 100644 --- a/packages/webui/src/components/ui/TraceDetail.test.tsx +++ b/packages/webui/src/components/ui/TraceDetail.test.tsx @@ -2,9 +2,9 @@ import { act, cleanup, render, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { - SystemRequestDetailsComponent, - SystemResponseDetailsComponent, - getIconPath, + RequestDetailsComponent, + ResponseDetailsComponent, + getIconUri, getRequestBody, getResponseBody, } from '@/systems'; @@ -88,15 +88,15 @@ describe('TraceDetail', () => { clearSelectedTraceFn = jest.fn(); // having to do this here, like this, so that we can override some of the mock return values later - jest.mocked(SystemRequestDetailsComponent).mockImplementation(({ trace, ...props }: any) => { + jest.mocked(RequestDetailsComponent).mockImplementation(({ trace, ...props }: any) => { return
Mock SystemRequestDetailsComponent component: {trace.id}
; }); - jest.mocked(SystemResponseDetailsComponent).mockImplementation(({ trace, ...props }: any) => { + jest.mocked(ResponseDetailsComponent).mockImplementation(({ trace, ...props }: any) => { return
Mock SystemResponseDetailsComponent component: {trace.id}
; }); jest.mocked(getRequestBody).mockReturnValue('mock_request_body'); jest.mocked(getResponseBody).mockReturnValue('mock_response_body'); - jest.mocked(getIconPath).mockReturnValue('mock_icon.jpg'); + jest.mocked(getIconUri).mockReturnValue('mock_icon.jpg'); setUseApplicationData({ getSelectedTrace: getSelectedTraceFn as () => Trace, diff --git a/packages/webui/src/components/ui/TraceDetail.tsx b/packages/webui/src/components/ui/TraceDetail.tsx index 476791b..8442a18 100644 --- a/packages/webui/src/components/ui/TraceDetail.tsx +++ b/packages/webui/src/components/ui/TraceDetail.tsx @@ -3,9 +3,9 @@ import { useCallback, useEffect, useRef } from 'react'; import { Code, DateTime, Field, Fields, JsonDisplay, Loading, Section, XmlDisplay } from '@/components'; import useApplication from '@/hooks/useApplication'; import { - SystemRequestDetailsComponent, - SystemResponseDetailsComponent, - getIconPath, + RequestDetailsComponent, + ResponseDetailsComponent, + getIconUri, getRequestBody, getResponseBody, } from '@/systems'; @@ -103,7 +103,7 @@ export default function TraceDetail({ className }: DetailProps) {
- +
@@ -146,7 +146,7 @@ export default function TraceDetail({ className }: DetailProps) { - +
@@ -181,7 +181,7 @@ export default function TraceDetail({ className }: DetailProps) { )} - + ) : ( diff --git a/packages/webui/src/integration.tsx b/packages/webui/src/integration.tsx new file mode 100644 index 0000000..1aaf983 --- /dev/null +++ b/packages/webui/src/integration.tsx @@ -0,0 +1,29 @@ +/* istanbul ignore file */ + +/* eslint-disable import/no-unresolved */ +// @ts-expect-error +import css from 'inline:../dist/viewer.css'; + +import App from './App'; +import { registerSystem } from './systems/registration'; +import { System } from './types'; + +export type { System, TraceRowData, Trace } from './types'; +export * from './components'; + +export type EnvyViewerProps = { + systems?: System[]; +}; + +export default function EnvyViewer({ systems }: EnvyViewerProps) { + for (const system of systems ?? []) { + registerSystem(system); + } + + return ( + <> + + + + ); +} diff --git a/packages/webui/src/scripts/buildIntegration.cjs b/packages/webui/src/scripts/buildIntegration.cjs new file mode 100644 index 0000000..e1ce528 --- /dev/null +++ b/packages/webui/src/scripts/buildIntegration.cjs @@ -0,0 +1,30 @@ +const { build } = require('esbuild'); +const inlineImportPlugin = require('esbuild-plugin-inline-import'); + +const { dependencies } = require('../../package.json'); + +const integrationFile = 'src/integration.tsx'; + +const shared = { + bundle: true, + entryPoints: [integrationFile], + external: Object.keys(dependencies), + logLevel: 'info', + plugins: [inlineImportPlugin()], + minify: true, + sourcemap: true, +}; + +build({ + ...shared, + format: 'esm', + outfile: './dist/integration.esm.js', + target: ['es2022', 'node16'], +}); + +build({ + ...shared, + format: 'cjs', + outfile: './dist/integration.cjs.js', + target: ['es2022', 'node16'], +}); diff --git a/packages/webui/src/scripts/start.cjs b/packages/webui/src/scripts/start.cjs index 1b4e9ef..e041612 100644 --- a/packages/webui/src/scripts/start.cjs +++ b/packages/webui/src/scripts/start.cjs @@ -2,9 +2,12 @@ const argv = require('yargs-parser')(process.argv.slice(2)); const devMode = argv.dev ?? false; +const noUi = argv.noUi ?? argv.noui ?? false; require('./startCollector.cjs'); global.collectorStarted = () => { - require(devMode ? './startViewerDev.cjs' : './startViewer.cjs'); + if (noUi === false) { + require(devMode ? './startViewerDev.cjs' : './startViewer.cjs'); + } }; diff --git a/packages/webui/src/systems/Default.test.tsx b/packages/webui/src/systems/Default.test.tsx index 4115b24..cba54e4 100644 --- a/packages/webui/src/systems/Default.test.tsx +++ b/packages/webui/src/systems/Default.test.tsx @@ -15,7 +15,7 @@ describe('DefaultSystem', () => { it('should return the expected icon', () => { const instance = new DefaultSystem(); - expect(instance.getIconPath()).toEqual('/Default.svg'); + expect(instance.getIconUri()).toEqual(expect.any(String)); }); it('should return `null` for `getData`', () => { diff --git a/packages/webui/src/systems/Default.tsx b/packages/webui/src/systems/Default.tsx index fb7f6a2..3bda142 100644 --- a/packages/webui/src/systems/Default.tsx +++ b/packages/webui/src/systems/Default.tsx @@ -1,7 +1,5 @@ import { System, Trace } from '@/types'; -const icon = new URL('Default.svg', import.meta.url); - export default class DefaultSystem implements System { name = 'Default'; @@ -9,8 +7,8 @@ export default class DefaultSystem implements System { return true; } - getIconPath() { - return icon.pathname; + getIconUri() { + return 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0MjAiCmhlaWdodD0iNDIwIiBzdHJva2U9IiMyMjIyMjIiIGZpbGw9Im5vbmUiPgo8cGF0aCBzdHJva2Utd2lkdGg9IjI2IgpkPSJNMjA5LDE1YTE5NSwxOTUgMCAxLDAgMiwweiIvPgo8cGF0aCBzdHJva2Utd2lkdGg9IjE4IgpkPSJtMjEwLDE1djM5MG0xOTUtMTk1SDE1TTU5LDkwYTI2MCwyNjAgMCAwLDAgMzAyLDAgbTAsMjQwIGEyNjAsMjYwIDAgMCwwLTMwMiwwTTE5NSwyMGEyNTAsMjUwIDAgMCwwIDAsMzgyIG0zMCwwIGEyNTAsMjUwIDAgMCwwIDAtMzgyIi8+Cjwvc3ZnPg=='; } getData() { diff --git a/packages/webui/src/systems/GraphQL.test.tsx b/packages/webui/src/systems/GraphQL.test.tsx index fb1011b..a6407e0 100644 --- a/packages/webui/src/systems/GraphQL.test.tsx +++ b/packages/webui/src/systems/GraphQL.test.tsx @@ -90,7 +90,7 @@ describe('GraphQLSystem', () => { it('should return the expected icon', () => { const instance = new GraphQLSystem(); - expect(instance.getIconPath()).toEqual('/GraphQL.svg'); + expect(instance.getIconUri()).toEqual(expect.any(String)); }); it('should expected data for `getData` when trace represents a query', () => { diff --git a/packages/webui/src/systems/GraphQL.tsx b/packages/webui/src/systems/GraphQL.tsx index 259541c..9f95b9e 100644 --- a/packages/webui/src/systems/GraphQL.tsx +++ b/packages/webui/src/systems/GraphQL.tsx @@ -16,8 +16,6 @@ type GraphQLData = { response: string | null; }; -const icon = new URL('GraphQL.svg', import.meta.url); - export default class GraphQLSystem implements System { name = 'GraphQL'; @@ -25,8 +23,8 @@ export default class GraphQLSystem implements System { return trace.http?.path?.endsWith('/graphql') ?? false; } - getIconPath() { - return icon.pathname; + getIconUri() { + return 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAgMTIwIiBmaWxsPSIjZTEwMDk4Ij4KICA8c3R5bGU+CiAgICBzdmcgewogICAgICBmaWxsOiAjRkZGRkZGOwogICAgfQogIDwvc3R5bGU+CiAgPGRlZnM+CiAgICA8ZyBpZD0ibG9nbyI+CiAgICAgIDxwYXRoCiAgICAgICAgZmlsbC1ydWxlPSJldmVub2RkIgogICAgICAgIGNsaXAtcnVsZT0iZXZlbm9kZCIKICAgICAgICBkPSJNNTAgNi45MDMwOEw4Ny4zMjMgMjguNDUxNVY3MS41NDg0TDUwIDkzLjA5NjhMMTIuNjc3IDcxLjU0ODRWMjguNDUxNUw1MCA2LjkwMzA4Wk0xNi44NjQ3IDMwLjg2OTNWNjIuNTI1MUw0NC4yNzk1IDE1LjA0MTRMMTYuODY0NyAzMC44NjkzWk01MCAxMy41MDg2TDE4LjM5NzUgNjguMjQ1N0g4MS42MDI1TDUwIDEzLjUwODZaTTc3LjQxNDggNzIuNDMzNEgyMi41ODUyTDUwIDg4LjI2MTNMNzcuNDE0OCA3Mi40MzM0Wk04My4xMzUzIDYyLjUyNTFMNTUuNzIwNSAxNS4wNDE0TDgzLjEzNTMgMzAuODY5M1Y2Mi41MjUxWiIKICAgICAgLz4KICAgICAgPGNpcmNsZSBjeD0iNTAiIGN5PSI5LjMyMDkiIHI9IjguODIiIC8+CiAgICAgIDxjaXJjbGUgY3g9Ijg1LjIyOTIiIGN5PSIyOS42NjA1IiByPSI4LjgyIiAvPgogICAgICA8Y2lyY2xlIGN4PSI4NS4yMjkyIiBjeT0iNzAuMzM5NiIgcj0iOC44MiIgLz4KICAgICAgPGNpcmNsZSBjeD0iNTAiIGN5PSI5MC42NzkxIiByPSI4LjgyIiAvPgogICAgICA8Y2lyY2xlIGN4PSIxNC43NjU5IiBjeT0iNzAuMzM5NiIgcj0iOC44MiIgLz4KICAgICAgPGNpcmNsZSBjeD0iMTQuNzY1OSIgY3k9IjI5LjY2MDUiIHI9IjguODIiIC8+CiAgICA8L2c+CiAgPC9kZWZzPgogIDxyZWN0IHdpZHRoPSIxMjAiIGhlaWdodD0iMTIwIiByeD0iMjAiIGZpbGw9IiNFMTAwOTgiIC8+CiAgPHVzZSBocmVmPSIjbG9nbyIgeD0iMTAiIHk9IjEwIiAvPgo8L3N2Zz4K'; } getData(trace: Trace) { diff --git a/packages/webui/src/systems/Sanity.test.tsx b/packages/webui/src/systems/Sanity.test.tsx index f974c46..2d18c7d 100644 --- a/packages/webui/src/systems/Sanity.test.tsx +++ b/packages/webui/src/systems/Sanity.test.tsx @@ -51,7 +51,7 @@ describe('SanitySystem', () => { it('should return the expected icon', () => { const instance = new SanitySystem(); - expect(instance.getIconPath()).toEqual('/Sanity.svg'); + expect(instance.getIconUri()).toEqual(expect.any(String)); }); it('should expected data for `getData`', () => { diff --git a/packages/webui/src/systems/Sanity.tsx b/packages/webui/src/systems/Sanity.tsx index f35175f..72306a8 100644 --- a/packages/webui/src/systems/Sanity.tsx +++ b/packages/webui/src/systems/Sanity.tsx @@ -11,8 +11,6 @@ type SanityData = { query?: string | null; }; -const icon = new URL('Sanity.svg', import.meta.url); - export default class SanitySystem implements System { name = 'Sanity'; @@ -20,8 +18,8 @@ export default class SanitySystem implements System { return !!trace.sanity; } - getIconPath() { - return icon.pathname; + getIconUri() { + return 'data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjggMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPHJlY3Qgd2lkdGg9IjI4IiBoZWlnaHQ9IjI4IiByeD0iNiIgZmlsbD0iI0YwM0UyRiI+PC9yZWN0PgogIDxwYXRoCiAgICBkPSJNOC42MiA3LjI1YzAgMi40MSAxLjUyIDMuODQgNC41NCA0LjZsMy4yMS43M2MyLjg3LjY0IDQuNjEgMi4yNSA0LjYxIDQuODdhNC45MSA0LjkxIDAgMDEtMS4wNyAzLjE1YzAtMi42MS0xLjM3LTQuMDItNC42OS00Ljg3bC0zLjE1LS43Yy0yLjUyLS41Ny00LjQ3LTEuODktNC40Ny00LjczYTQuODkgNC44OSAwIDAxMS4wMi0zLjA1eiIKICAgIGZpbGw9IiNmZmYiCiAgPjwvcGF0aD4KICA8cGF0aAogICAgZD0iTTE3Ljk0IDE2LjhjMS4zNy44NyAxLjk3IDIuMDcgMS45NyAzLjgtMS4xMyAxLjQyLTMuMTIgMi4yMi01LjQ2IDIuMjItMy45NCAwLTYuNy0xLjktNy4zLTUuMjFoMy43OGMuNDggMS41MiAxLjc3IDIuMjIgMy41IDIuMjIgMi4xIDAgMy40OS0xLjEgMy41Mi0zLjAzIgogICAgZmlsbD0iI0Y5QjFBQiIKICA+PC9wYXRoPgogIDxwYXRoCiAgICBkPSJNMTAuNTkgMTAuODJhMy45OSAzLjk5IDAgMDEtMS45Ny0zLjU3YzEuMS0xLjQgMy0yLjI3IDUuMzItMi4yNyA0IDAgNi4zMyAyLjA4IDYuOSA1SDE3LjJjLS40LTEuMTUtMS40LTIuMDUtMy4yMy0yLjA1LTEuOTYgMC0zLjMgMS4xMi0zLjM3IDIuOSIKICAgIGZpbGw9IiNGOUIxQUIiCiAgPjwvcGF0aD4KPC9zdmc+Cg=='; } getData(trace: Trace) { diff --git a/packages/webui/src/systems/index.tsx b/packages/webui/src/systems/index.tsx index ffecaf5..ba37dd6 100644 --- a/packages/webui/src/systems/index.tsx +++ b/packages/webui/src/systems/index.tsx @@ -19,8 +19,8 @@ type SystemDetailProps = { trace: Trace; }; -export function getIconPath(trace: Trace | null): string { - return callOrFallback(trace as Trace, 'getIconPath'); +export function getIconUri(trace: Trace | null): string { + return callOrFallback(trace as Trace, 'getIconUri'); } export function getRequestBody(trace: Trace): any { @@ -37,7 +37,7 @@ export function ListDataComponent({ trace }: SystemDetailProps): React.ReactNode const [path, qs] = pathAndQuery(trace); return ( (trace, 'requestDetailComponent'); return Component ? ( <> @@ -55,7 +55,7 @@ export function SystemRequestDetailsComponent({ trace }: SystemDetailProps): Rea ) : null; } -export function SystemResponseDetailsComponent({ trace }: SystemDetailProps): React.ReactNode { +export function ResponseDetailsComponent({ trace }: SystemDetailProps): React.ReactNode { const Component = callOrFallback(trace, 'responseDetailComponent'); return Component ? ( <> diff --git a/packages/webui/src/systems/systems.test.tsx b/packages/webui/src/systems/systems.test.tsx index 9090eeb..e51e28c 100644 --- a/packages/webui/src/systems/systems.test.tsx +++ b/packages/webui/src/systems/systems.test.tsx @@ -8,9 +8,9 @@ import { Trace } from '@/types'; import { ListDataComponent, - SystemRequestDetailsComponent, - SystemResponseDetailsComponent, - getIconPath, + RequestDetailsComponent, + ResponseDetailsComponent, + getIconUri, getRequestBody, getResponseBody, } from '.'; @@ -54,8 +54,8 @@ describe('Systems', () => { } as Trace; it('should return correct value for `getSystemIconPath`', () => { - const result = getIconPath(trace); - expect(result).toEqual('foo_foo_id.jpg'); + const result = getIconUri(trace); + expect(result).toEqual('foo_foo_id_base64'); }); it('should return correct value for `getRequestBody`', () => { @@ -71,17 +71,17 @@ describe('Systems', () => { it('should return correct data in the `ListDataComponent`', () => { const component = ListDataComponent({ trace }); const { container } = render(component as ReactElement); - expect(container).toHaveTextContent('foo_foo_id.jpg | www.foo.com | /foo | Foo data: foo_id'); + expect(container).toHaveTextContent('foo_foo_id_base64 | www.foo.com | /foo | Foo data: foo_id'); }); it('should return correct `SystemRequestDetailsComponent`', () => { - const component = SystemRequestDetailsComponent({ trace }); + const component = RequestDetailsComponent({ trace }); const { container } = render(component as ReactElement); expect(container).toHaveTextContent('SystemRequestDetailsComponent: Foo foo_id'); }); it('should return correct `SystemResponseDetailsComponent`', () => { - const component = SystemResponseDetailsComponent({ trace }); + const component = ResponseDetailsComponent({ trace }); const { container } = render(component as ReactElement); expect(container).toHaveTextContent('SystemResponseDetailsComponent: Foo foo_id'); }); @@ -101,9 +101,9 @@ describe('Systems', () => { }, } as Trace; - it('should return correct value for `getSystemIconPath`', () => { - const result = getIconPath(trace); - expect(result).toEqual('bar_bar_id.jpg'); + it('should return correct value for `getIconUri`', () => { + const result = getIconUri(trace); + expect(result).toEqual('bar_bar_id_base64'); }); it('should return correct value for `getRequestBody`', () => { @@ -119,17 +119,17 @@ describe('Systems', () => { it('should return correct data in the `ListDataComponent`', () => { const component = ListDataComponent({ trace }); const { container } = render(component as ReactElement); - expect(container).toHaveTextContent('bar_bar_id.jpg | www.bar.com | /bar | Bar data: bar_id'); + expect(container).toHaveTextContent('bar_bar_id_base64 | www.bar.com | /bar | Bar data: bar_id'); }); it('should return correct `SystemRequestDetailsComponent`', () => { - const component = SystemRequestDetailsComponent({ trace }); + const component = RequestDetailsComponent({ trace }); const { container } = render(component as ReactElement); expect(container).toHaveTextContent('SystemRequestDetailsComponent: Bar bar_id'); }); it('should return correct `SystemResponseDetailsComponent`', () => { - const component = SystemResponseDetailsComponent({ trace }); + const component = ResponseDetailsComponent({ trace }); const { container } = render(component as ReactElement); expect(container).toHaveTextContent('SystemResponseDetailsComponent: Bar bar_id'); }); @@ -150,8 +150,8 @@ describe('Systems', () => { } as Trace; it('should return correct value for `getSystemIconPath`', () => { - const result = getIconPath(trace); - expect(result).toEqual('default.jpg'); + const result = getIconUri(trace); + expect(result).toEqual('default_base64'); }); it('should return correct value for `getRequestBody`', () => { @@ -167,16 +167,16 @@ describe('Systems', () => { it('should return correct data in the `ListDataComponent`', () => { const component = ListDataComponent({ trace }); const { container } = render(component as ReactElement); - expect(container).toHaveTextContent('default.jpg | www.fallback.com | /fallback | query=fallback'); + expect(container).toHaveTextContent('default_base64 | www.fallback.com | /fallback | query=fallback'); }); it('should return correct `SystemRequestDetailsComponent`', () => { - const component = SystemRequestDetailsComponent({ trace }); + const component = RequestDetailsComponent({ trace }); expect(component).toBeNull(); }); it('should return correct `SystemResponseDetailsComponent`', () => { - const component = SystemResponseDetailsComponent({ trace }); + const component = ResponseDetailsComponent({ trace }); expect(component).toBeNull(); }); }); @@ -196,8 +196,8 @@ describe('Systems', () => { } as Trace; it('should return correct value for `getSystemIconPath`', () => { - const result = getIconPath(trace); - expect(result).toEqual('default.jpg'); + const result = getIconUri(trace); + expect(result).toEqual('default_base64'); }); it('should return correct value for `getRequestBody`', () => { @@ -213,16 +213,16 @@ describe('Systems', () => { it('should return correct data in the `ListDataComponent`', () => { const component = ListDataComponent({ trace }); const { container } = render(component as ReactElement); - expect(container).toHaveTextContent('default.jpg | www.other.com | /other | query=other'); + expect(container).toHaveTextContent('default_base64 | www.other.com | /other | query=other'); }); it('should return correct `SystemRequestDetailsComponent`', () => { - const component = SystemRequestDetailsComponent({ trace }); + const component = RequestDetailsComponent({ trace }); expect(component).toBeNull(); }); it('should return correct `SystemResponseDetailsComponent`', () => { - const component = SystemResponseDetailsComponent({ trace }); + const component = ResponseDetailsComponent({ trace }); expect(component).toBeNull(); }); }); diff --git a/packages/webui/src/testing/mockSystems.ts b/packages/webui/src/testing/mockSystems.ts index e487bcd..1db5b1d 100644 --- a/packages/webui/src/testing/mockSystems.ts +++ b/packages/webui/src/testing/mockSystems.ts @@ -11,8 +11,8 @@ const mockSystems: System[] = [ isMatch(trace: Trace) { return trace.http?.host === 'www.foo.com'; } - getIconPath(trace: Trace | null) { - return `foo_${trace?.id}.jpg`; + getIconUri(trace: Trace | null) { + return `foo_${trace?.id}_base64`; } getData(trace: Trace) { return { @@ -43,8 +43,8 @@ const mockSystems: System[] = [ isMatch(trace: Trace) { return trace.http?.host === 'www.bar.com'; } - getIconPath(trace: Trace | null) { - return `bar_${trace?.id}.jpg`; + getIconUri(trace: Trace | null) { + return `bar_${trace?.id}_base64`; } getData(trace: Trace) { return { @@ -75,7 +75,7 @@ const mockSystems: System[] = [ isMatch(trace: Trace) { return trace.http?.host === 'www.fallback.com'; } - getIconPath() { + getIconUri() { return null; } getData() { @@ -110,8 +110,8 @@ const mockDefaultSystem = new (class implements System { isMatch() { return true; } - getIconPath() { - return 'default.jpg'; + getIconUri() { + return 'default_base64'; } getData() { return null; diff --git a/packages/webui/src/types/index.ts b/packages/webui/src/types/index.ts index 1e5a8f4..68dd1aa 100644 --- a/packages/webui/src/types/index.ts +++ b/packages/webui/src/types/index.ts @@ -10,7 +10,7 @@ export type TraceRowData = { export interface System { name: string; isMatch(trace: Trace): boolean; - getIconPath?(trace: Trace | null): string | null; + getIconUri?(trace: Trace | null): string | null; getData?(trace: Trace): T; getTraceRowData?(trace: Trace): TraceRowData | null; requestDetailComponent?(trace: Trace): React.ReactNode; diff --git a/packages/webui/tsconfig.types.json b/packages/webui/tsconfig.types.json new file mode 100644 index 0000000..a4032d3 --- /dev/null +++ b/packages/webui/tsconfig.types.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "declaration": true, + "downlevelIteration": true, + "emitDeclarationOnly": true, + "esModuleInterop": true, + "module": "ES2022", + "moduleResolution": "node", + "jsx": "react-jsx", + "outDir": "dist", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/integration.tsx", "src/types/css.d.ts"] +} diff --git a/yarn.lock b/yarn.lock index f12711f..64cd53b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -759,111 +759,221 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== +"@esbuild/android-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz#91a3b1b4a68c01ffdd5d8ffffb0a83178a366ae0" + integrity sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw== + "@esbuild/android-arm@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== +"@esbuild/android-arm@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.3.tgz#08bd09f2ebc312422f4e94ae954821f9cf37b39e" + integrity sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA== + "@esbuild/android-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== +"@esbuild/android-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.3.tgz#b1dffec99ed5505fc57561e8758b449dba4924fe" + integrity sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ== + "@esbuild/darwin-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== +"@esbuild/darwin-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz#2e0db5ad26313c7f420f2cd76d9d263fc49cb549" + integrity sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw== + "@esbuild/darwin-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== +"@esbuild/darwin-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz#ebe99f35049180023bb37999bddbe306b076a484" + integrity sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw== + "@esbuild/freebsd-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== +"@esbuild/freebsd-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz#cf8b58ba5173440ea6124a3d0278bfe4ce181c20" + integrity sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg== + "@esbuild/freebsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== +"@esbuild/freebsd-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz#3f283099810ef1b8468cd1a9400c042e3f12e2a7" + integrity sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA== + "@esbuild/linux-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== +"@esbuild/linux-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz#a8b3aa69653ac504a51aa73739fb06de3a04d1ff" + integrity sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ== + "@esbuild/linux-arm@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== +"@esbuild/linux-arm@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz#ff6a2f68d4fc3ab46f614bca667a1a81ed6eea26" + integrity sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ== + "@esbuild/linux-ia32@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== +"@esbuild/linux-ia32@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz#5813baf70e406304e8931b200e39d0293b488073" + integrity sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA== + "@esbuild/linux-loong64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== +"@esbuild/linux-loong64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz#21110f29b5e31dc865c7253fde8a2003f7e8b6fd" + integrity sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A== + "@esbuild/linux-mips64el@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== +"@esbuild/linux-mips64el@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz#4530fc416651eadeb1acc27003c00eac769eb8fd" + integrity sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A== + "@esbuild/linux-ppc64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== +"@esbuild/linux-ppc64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz#facf910b0d397e391b37b01a1b4f6e363b04e56b" + integrity sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g== + "@esbuild/linux-riscv64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== +"@esbuild/linux-riscv64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz#4a67abe97a495430d5867340982f5424a64f2aac" + integrity sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA== + "@esbuild/linux-s390x@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== +"@esbuild/linux-s390x@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz#c5fb47474b9f816d81876c119dbccadf671cc5f6" + integrity sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA== + "@esbuild/linux-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz#c7690b3417af318a9b6f96df3031a8865176d338" integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== +"@esbuild/linux-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz#f22d659969ab78dc422f1df8d9a79bc1e7b12ee3" + integrity sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ== + "@esbuild/netbsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== +"@esbuild/netbsd-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz#e9b046934996991f46b8c1cadac815aa45f84fd4" + integrity sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw== + "@esbuild/openbsd-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== +"@esbuild/openbsd-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz#b287ef4841fc1067bbbd9a60549e8f9cf1b7ee3a" + integrity sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q== + "@esbuild/sunos-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== +"@esbuild/sunos-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz#b2b8ba7d27907c7245f6e57dc62f3b88693f84b0" + integrity sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw== + "@esbuild/win32-arm64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== +"@esbuild/win32-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz#1974c8c180c9add4962235662c569fcc4c8f43dd" + integrity sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A== + "@esbuild/win32-ia32@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== +"@esbuild/win32-ia32@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz#b02cc2dd8b6aed042069680f01f45fdfd3de5bc4" + integrity sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q== + "@esbuild/win32-x64@0.18.20": version "0.18.20" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== +"@esbuild/win32-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz#e5036be529f757e58d9a7771f2f1b14782986a74" + integrity sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -4495,6 +4605,11 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +esbuild-plugin-inline-import@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esbuild-plugin-inline-import/-/esbuild-plugin-inline-import-1.0.1.tgz#6f1c73a4a6606d74861e6e5342485e6b32917249" + integrity sha512-QcBbsf7nJnD1GW2SOxgE0lLJ2GgqhGZCoPujxFaWatooNXpYwDCYuOadThKPLWcB9Sx6a7i3GU7RNBEB1dcYOQ== + esbuild@^0.18.10: version "0.18.20" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.18.20.tgz#4709f5a34801b43b799ab7d6d82f7284a9b7a7a6" @@ -4523,6 +4638,34 @@ esbuild@^0.18.10: "@esbuild/win32-ia32" "0.18.20" "@esbuild/win32-x64" "0.18.20" +esbuild@^0.19.3: + version "0.19.3" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.3.tgz#d9268cd23358eef9d76146f184e0c55ff8da7bb6" + integrity sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw== + optionalDependencies: + "@esbuild/android-arm" "0.19.3" + "@esbuild/android-arm64" "0.19.3" + "@esbuild/android-x64" "0.19.3" + "@esbuild/darwin-arm64" "0.19.3" + "@esbuild/darwin-x64" "0.19.3" + "@esbuild/freebsd-arm64" "0.19.3" + "@esbuild/freebsd-x64" "0.19.3" + "@esbuild/linux-arm" "0.19.3" + "@esbuild/linux-arm64" "0.19.3" + "@esbuild/linux-ia32" "0.19.3" + "@esbuild/linux-loong64" "0.19.3" + "@esbuild/linux-mips64el" "0.19.3" + "@esbuild/linux-ppc64" "0.19.3" + "@esbuild/linux-riscv64" "0.19.3" + "@esbuild/linux-s390x" "0.19.3" + "@esbuild/linux-x64" "0.19.3" + "@esbuild/netbsd-x64" "0.19.3" + "@esbuild/openbsd-x64" "0.19.3" + "@esbuild/sunos-x64" "0.19.3" + "@esbuild/win32-arm64" "0.19.3" + "@esbuild/win32-ia32" "0.19.3" + "@esbuild/win32-x64" "0.19.3" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"