Skip to content

Commit a6959e6

Browse files
chore: add basic tests and readme to laboratory (#8223)
1 parent 2b6d22f commit a6959e6

12 files changed

Lines changed: 1245 additions & 163 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 The Guild
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Hive Laboratory
2+
3+
[Hive](https://the-guild.dev/graphql/hive) is a fully open-source schema registry, analytics,
4+
metrics and gateway for [GraphQL federation](https://the-guild.dev/graphql/hive/federation) and
5+
other GraphQL APIs.
6+
7+
`@graphql-hive/laboratory` is Hive's embeddable, in-browser GraphQL IDE (the "Lab"): an editor and
8+
runner for GraphQL operations, in the spirit of GraphiQL. It powers the Laboratory page inside the
9+
Hive Console and can be embedded into any page that talks to a GraphQL endpoint.
10+
11+
> **Pre-1.0.** The public API (see [`LaboratoryApi`](src/components/laboratory/context.tsx)) is
12+
> still evolving and can change between patch releases.
13+
14+
## Features
15+
16+
- Query builder: click schema fields/arguments to build the operation
17+
- Schema explorer with search (list and tree modes)
18+
- Collections (saved operations) and request history
19+
- Preflight scripts: run JavaScript before a request in a sandboxed Web Worker
20+
- Environment variables with `{{variable}}` interpolation
21+
- Renders a federation query plan when a server includes one in the response `extensions`
22+
(`extensions.queryPlan`), e.g. [Hive Router](https://the-guild.dev/graphql/hive/docs/router) or
23+
Hive Gateway
24+
- Plugin system for adding tabs, command-palette entries and preflight APIs
25+
26+
## Consumers
27+
28+
This package is storage- and transport-agnostic. It exposes state as props and reports changes via
29+
callbacks; the host decides where data lives.
30+
31+
- **Hive Console** (`packages/web/app`) embeds the `<Laboratory>` React component directly in
32+
[`target-laboratory-new.tsx`](../../web/app/src/pages/target-laboratory-new.tsx) and wires the
33+
callbacks to the Hive GraphQL API and `localStorage`.
34+
- **Hive Gateway** serves the Lab as its GraphiQL replacement via
35+
**`@graphql-hive/render-laboratory`** ([`../render-laboratory`](../render-laboratory)), which
36+
wraps this package's UMD bundle plus the Monaco workers into a self-contained HTML page for
37+
`graphql-yoga` servers.
38+
- **Hive Router** embeds this package's UMD bundle (`dist/hive-laboratory.umd.js`) directly at build
39+
time, generating a static page that calls the `HiveLaboratory.renderLaboratory()` global.
40+
41+
## Installation
42+
43+
```bash
44+
pnpm add @graphql-hive/laboratory
45+
```
46+
47+
## Usage
48+
49+
Two entry points are exported from [`src/index.tsx`](src/index.tsx):
50+
51+
**`renderLaboratory(el, props)`** mounts the Lab into a DOM node and wires all state to
52+
`localStorage` for you. This is the quickest way to embed it:
53+
54+
```ts
55+
import { renderLaboratory } from '@graphql-hive/laboratory'
56+
57+
renderLaboratory(document.getElementById('root')!, {
58+
defaultEndpoint: 'https://example.com/graphql'
59+
})
60+
```
61+
62+
**`<Laboratory />`** is the React component when you want to own persistence. Every piece of state
63+
follows the same controlled/uncontrolled contract: a `defaultX` prop seeds the initial value and an
64+
`onXChange` callback fires whenever it changes.
65+
66+
```tsx
67+
import { Laboratory } from '@graphql-hive/laboratory'
68+
69+
function LabPage() {
70+
return (
71+
<Laboratory
72+
theme="dark"
73+
defaultEndpoint={endpoint}
74+
onEndpointChange={setEndpoint}
75+
defaultCollections={collections}
76+
onCollectionsChange={saveCollections}
77+
defaultHistory={history}
78+
onHistoryChange={saveHistory}
79+
// ...tabs, operations, env, preflight, settings, plugins
80+
/>
81+
)
82+
}
83+
```
84+
85+
The full prop surface (endpoint, collections, operations, history, tabs, env, preflight, settings,
86+
tests, plugins, plus granular `onXCreate`/`onXUpdate`/`onXDelete` callbacks and a `permissions`
87+
object) is defined by [`LaboratoryApi`](src/components/laboratory/context.tsx). Treat that interface
88+
as the source of truth rather than this README.
89+
90+
### Permissions
91+
92+
Pass a `permissions` object to gate actions per resource (`preflight`, `collections`,
93+
`collectionsOperations`) with `read`/`create`/`update`/`delete` flags. Gating is applied in the UI
94+
(controls are hidden/disabled) with a backstop in the operations logic; anything unspecified
95+
defaults to allowed.
96+
97+
### Styling and rendering
98+
99+
The Lab bundles its own styles and injects them into its shadow root, so there is no CSS file to
100+
import. It is client-side only (it uses Web Workers, Shadow DOM and Monaco), so mount it in the
101+
browser rather than during server-side rendering.
102+
103+
## Local development
104+
105+
```bash
106+
pnpm dev # Vite dev server on http://localhost:5173
107+
pnpm build # library build (ES + CJS) and UMD build
108+
pnpm lint # eslint
109+
```
110+
111+
`pnpm dev` mounts the Lab via [`src/main.tsx`](src/main.tsx) / [`index.html`](index.html), a thin
112+
harness that persists all state to `localStorage`. No backend is required; set an endpoint in the UI
113+
and go.
114+
115+
Tests run from the monorepo root (this package has no `test` script):
116+
117+
```bash
118+
# from the repo root
119+
pnpm vitest run packages/libraries/laboratory
120+
```
121+
122+
## Architecture
123+
124+
Each feature is a `useX` hook (state + actions) under [`src/lib`](src/lib), paired with a component
125+
under [`src/components/laboratory`](src/components/laboratory). All the hooks are composed into one
126+
context in [`laboratory.tsx`](src/components/laboratory/laboratory.tsx) and consumed via
127+
`useLaboratory()`.
128+
129+
| Feature | Hook (`src/lib`) | UI (`src/components/laboratory`) |
130+
| ------------------------------- | -------------------------------------- | --------------------------------- |
131+
| Endpoint + schema introspection | `endpoint.ts` | (implicit) |
132+
| Operations + run/abort | `operations.ts`, `operations.utils.ts` | `operation.tsx`, `builder.tsx` |
133+
| Collections | `collections.ts` | `collections.tsx` |
134+
| History | `history.ts` | `history.tsx`, `history-item.tsx` |
135+
| Preflight scripts | `preflight.ts` | `preflight.tsx` |
136+
| Environment variables | `env.ts` | `env.tsx` |
137+
| Settings | `settings.ts` | `settings.tsx` |
138+
| Tabs | `tabs.ts` | `tabs.tsx` |
139+
| Query plan | `query-plan/` | `flow.tsx` |
140+
141+
### Style isolation (Shadow DOM)
142+
143+
Because the Lab is embedded into pages it does not control, it renders inside a Shadow DOM
144+
(`ShadowRootContainer` in [`laboratory.tsx`](src/components/laboratory/laboratory.tsx)) and injects
145+
its CSS (Tailwind v4) plus Monaco's CSS inline. This gives two-way isolation from the host page's
146+
styles. A consequence worth knowing: Radix portals (dropdowns, tooltips, dialogs) must target the
147+
Lab's `container` (exposed on the context) rather than `document.body`, or they render unstyled.
148+
149+
The UI is built on shadcn/Radix primitives ([`src/components/ui`](src/components/ui)), Monaco, and
150+
`@tanstack/react-form`.
151+
152+
## Plugins
153+
154+
A plugin can add tabs, command-palette commands, and objects injected into the preflight sandbox.
155+
See [`LaboratoryPlugin`](src/lib/plugins.ts) for the shape and
156+
[`src/plugins/target-env.tsx`](src/plugins/target-env.tsx) for a worked example (the Target
157+
Environment plugin used by Hive Console). Register plugins via the `plugins` prop.
158+
159+
## Releases
160+
161+
This package is published to npm via [Changesets](https://github.com/changesets/changesets) from the
162+
monorepo:
163+
164+
1. Add a changeset from the repo root: `pnpm changeset` (pick `@graphql-hive/laboratory`, choose the
165+
bump, write the summary that becomes the changelog entry).
166+
2. Opening a PR publishes an `alpha` snapshot to npm for testing.
167+
3. Merging to `main` accumulates changes into an "Upcoming Release Changes" PR.
168+
4. Merging that PR versions the package and publishes it to npm.
169+
170+
See [`CHANGELOG.md`](CHANGELOG.md) for release history.
171+
172+
## License
173+
174+
Licensed under the [MIT License](LICENSE).

packages/libraries/laboratory/package.json

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
"scripts": {
2323
"build": "vite build --config vite.lib.config.ts && vite build --config vite.umd.config.ts",
2424
"dev": "vite",
25-
"dev:electron": "VITE_TARGET=electron concurrently \"vite\" \"wait-on http://localhost:5173 && electron .\"",
2625
"lint": "eslint .",
2726
"preview": "vite preview"
2827
},
@@ -77,6 +76,8 @@
7776
"@tanstack/react-router": "^1.154.13",
7877
"@tanstack/react-router-devtools": "^1.154.13",
7978
"@tanstack/router-plugin": "^1.154.13",
79+
"@testing-library/dom": "^10.4.1",
80+
"@testing-library/react": "^16.3.2",
8081
"@types/crypto-js": "^4.2.2",
8182
"@types/lodash": "^4.17.23",
8283
"@types/node": "24.12.2",
@@ -90,7 +91,6 @@
9091
"clsx": "^2.1.1",
9192
"cmdk": "^1.1.1",
9293
"color": "^5.0.3",
93-
"concurrently": "10.0.3",
9494
"crypto-js": "^4.2.0",
9595
"date-fns": "^4.1.0",
9696
"esbuild": "0.28.1",
@@ -99,6 +99,7 @@
9999
"eslint-plugin-react-refresh": "^0.4.26",
100100
"globals": "^16.5.0",
101101
"graphql": "^16.14.0",
102+
"happy-dom": "^20.10.6",
102103
"lodash": "^4.18.1",
103104
"lucide-react": "^0.548.0",
104105
"lz-string": "^1.5.0",
@@ -128,7 +129,6 @@
128129
"vite-plugin-commonjs": "^0.10.4",
129130
"vite-plugin-dts": "^4.5.4",
130131
"vite-plugin-monaco-editor": "^1.1.0",
131-
"wait-on": "9.0.10",
132132
"zod": "^4.3.6"
133133
},
134134
"publishConfig": {
@@ -137,17 +137,5 @@
137137
},
138138
"sideEffects": [
139139
"**/*.css"
140-
],
141-
"build": {
142-
"appId": "com.guild.hive.laboratory",
143-
"productName": "Hive Laboratory",
144-
"artifactName": "HiveLab-${version}-Do_Not_Open_(Seriously_It's_Pre-Release)-${os}-${arch}.${ext}",
145-
"files": [
146-
"dist/**/*",
147-
"electron/**/*"
148-
],
149-
"directories": {
150-
"buildResources": "assets"
151-
}
152-
}
140+
]
153141
}

0 commit comments

Comments
 (0)