Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
09b1296
pin env, response and dock-switch state before the redux refactor
arpit-bruno Oct 14, 2026
b400217
dropping the unread collection copy
arpit-bruno Oct 15, 2026
9c0e5fd
add tests for theme toggle functionality in components
arpit-bruno Oct 16, 2026
6e8b735
adds theme obj
arpit-bruno Oct 24, 2026
8344812
pin standalone bundle lifecycle in e2e
arpit-bruno Nov 12, 2026
a56612e
pin what updateCollection does to playground edits, env and navigation
arpit-bruno Nov 12, 2026
89d734a
pin docs nav and playground when a different collection is passed whi…
arpit-bruno Nov 12, 2026
fdf60bf
pin string, file and url collection inputs with loading and error states
arpit-bruno Nov 12, 2026
6b6fd5e
pin theme, hideThemeSwitcher and gitCollectionUrl changed through upd…
arpit-bruno Nov 12, 2026
b89342d
pin selected environment surviving a reload in the dev app
arpit-bruno Nov 12, 2026
c8f7dcc
pin how two standalone instances on one page share theme, env, url an…
arpit-bruno Nov 12, 2026
0bd111a
run standalone e2e under its own config so other specs skip the bundl…
arpit-bruno Nov 12, 2026
f9a01cd
move theme state out of redux into a per-instance theme object
arpit-bruno Nov 12, 2026
afb393e
move the active environment out of redux into the per-instance sessio…
arpit-bruno Nov 12, 2026
0eeedab
move show vars into the session env and delete the env slice and the …
arpit-bruno Nov 12, 2026
1167b6e
theme toggle flips from the mode the button showed, as before
arpit-bruno Nov 12, 2026
daef57d
move collection load status, error and source text out of redux into …
arpit-bruno Nov 12, 2026
d10efe2
keep docs folder collapse in appshell instead of the collection items…
arpit-bruno Nov 12, 2026
5164efe
move the collection parse, fetch and hydrate pipeline into a loader h…
arpit-bruno Nov 12, 2026
08fc553
hydrate the collection once in the loader and provide it read-only wi…
arpit-bruno Nov 12, 2026
393cd41
expanding folders that are already open no longer re-renders the docs…
arpit-bruno Nov 12, 2026
ad3238c
pin playground selection, response layout, format, preview and folder…
arpit-bruno Nov 12, 2026
3be5a0e
move playground selection out of redux into a per-instance session ob…
arpit-bruno Nov 12, 2026
701316b
split playgroundbody into a selection hook, a view switch and the lay…
arpit-bruno Nov 12, 2026
2d10590
move response layout, format and preview choices out of redux into th…
arpit-bruno Nov 12, 2026
aa988ac
keep playground folder collapse in a per-uuid map on the per-instance…
arpit-bruno Nov 12, 2026
735196e
clearing the collection keeps the environments and collection setting…
arpit-bruno Nov 12, 2026
712aa47
port the playground slice reducers to pure workspace updaters with th…
arpit-bruno Nov 12, 2026
db2a9f3
playground views, the run lifecycle and the resolver write-back take …
arpit-bruno Nov 12, 2026
0445c79
the per-instance session workspace owns the playground working copy, …
arpit-bruno Nov 12, 2026
84a7066
freeze the playground working copy and the loaded collection again, a…
arpit-bruno Nov 12, 2026
df6de0f
envswitcher and the variable resolver read the playground working cop…
arpit-bruno Nov 12, 2026
5b66ae9
remove redux toolkit and react-redux from the package, the @slices al…
arpit-bruno Nov 12, 2026
26e4f36
docs environments page reads the playground working copy, so edited e…
arpit-bruno Nov 12, 2026
167644c
test:e2e runs the standalone suite even when the main suite fails, an…
arpit-bruno Nov 12, 2026
9e275ee
two-instances spec reaches each instance through page objects scoped …
arpit-bruno Nov 12, 2026
c388800
workspace keeps one replaceCollection, drops the setCollection forwar…
arpit-bruno Nov 12, 2026
db1aabe
url edit spec reopens the playground and checks the working copy stil…
arpit-bruno Nov 12, 2026
c65bd30
drop the duplicate state-without-redux changeset
arpit-bruno Nov 12, 2026
63b6865
docs pages read the playground working copy so edits show until reload
arpit-bruno Nov 12, 2026
17b7d03
observable drops persist and onchange; env saves and theme applies it…
arpit-bruno Nov 12, 2026
76640d6
workspace drops the actions interface, without and clearcollection fo…
arpit-bruno Nov 12, 2026
ab6e7d6
resolver writes when given onwritevariable, pagerouter uses the itemu…
arpit-bruno Nov 12, 2026
f0c1e31
sessionprovider is the context provider, usenavmodel imported from do…
arpit-bruno Nov 12, 2026
feaa9b5
add coding standards and architecture docs for people and agents
arpit-bruno Nov 12, 2026
8ef6aeb
add agents.md, a tracked root claude.md that imports it, and a codera…
arpit-bruno Nov 12, 2026
c64ffbf
no-explicit-any is an error on changed lines, contributing points at …
arpit-bruno Nov 12, 2026
a90fd74
file naming rule: pascalcase components, camelcase for the rest of sr…
arpit-bruno Nov 12, 2026
28a26e1
delete dead overview environments, docs method, request vars, code vi…
arpit-bruno Nov 12, 2026
efd607b
add exported html regression spec with old and new generator fixtures…
arpit-bruno Nov 12, 2026
33f5968
move faker functions, run types, select option and playground runs ou…
arpit-bruno Nov 12, 2026
56ace58
add bruno-shared and bruno-api-playground workspaces with tsconfig, v…
arpit-bruno Nov 12, 2026
92878b7
move codeeditor monaco colours into theme tokens and drop hex literal…
arpit-bruno Nov 12, 2026
ed55525
type the runner, scripting sandbox and playground views that used any…
arpit-bruno Nov 12, 2026
e090f36
type the shared schema helpers, workspace session and their specs tha…
arpit-bruno Nov 12, 2026
ba30f78
type the file utils and their spec that used any and drop unused catc…
arpit-bruno Nov 12, 2026
ac7ba85
move shared files, styles and gen:theme into bruno-shared and import …
arpit-bruno Nov 12, 2026
6d4c7d3
move the playground body, runner and scripting into bruno-api-playgro…
arpit-bruno Nov 12, 2026
3400319
add eslint boundary rules so shared imports neither docs nor playgrou…
arpit-bruno Nov 12, 2026
700d5b0
build the playground as a classic-script api-playground-<hash>.js tha…
arpit-bruno Nov 12, 2026
52d2070
load the playground in the standalone bundle from the emitted api-pla…
arpit-bruno Nov 12, 2026
82cfe51
add standalone e2e that the playground file loads only on try or a de…
arpit-bruno Nov 12, 2026
d0e9d46
document the docs, playground and shared packages, the import boundar…
arpit-bruno Nov 12, 2026
67fe634
highlight shared code through the one prism instance so the playgroun…
arpit-bruno Nov 12, 2026
68a370b
retry the playground file on the next open after a failed load instea…
arpit-bruno Nov 12, 2026
f3d3d9e
give the injected playground script the nonce of the api-docs.js tag …
arpit-bruno Nov 12, 2026
44c1c52
document the playground load limits: bundler-imported esm location, t…
arpit-bruno Nov 12, 2026
8596820
declare each package's dependencies in the package that imports them …
arpit-bruno Nov 12, 2026
129ac1b
keep the cdn externals in one shared list and test that the docs runt…
arpit-bruno Nov 12, 2026
fe64918
treat a playground file that loads without defining the playground as…
arpit-bruno Nov 12, 2026
6190978
build the libraries bundle before the playground cdn build so build:c…
arpit-bruno Nov 12, 2026
25a2cd1
build the libraries bundle before the playground typecheck so a fresh…
arpit-bruno Nov 13, 2026
5b4b710
pin that hideplayground never loads the playground file, even for a d…
arpit-bruno Nov 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 5 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
"bumpVersionsWithWorkspaceProtocolOnly": true,
"ignore": [
"@usebruno/api-shared",
"@usebruno/api-playground"
]
}
5 changes: 5 additions & 0 deletions .changeset/playground-edits-reach-docs-variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@usebruno/api-docs": minor
---

Docs pages now show edits made in the playground until the page is reloaded.
5 changes: 5 additions & 0 deletions .changeset/playground-loads-only-when-opened.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@usebruno/api-docs": patch
---

The playground now loads only when opened, so docs pages load less code.
160 changes: 160 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

language: 'en-US'
early_access: false
tone_instructions: 'Expert reviewer in TypeScript, React, Emotion, Vite, Vitest and Playwright. Be concise. Favour the simplest code that works; flag abstractions without a second user. Explain more only when asked.'

knowledge_base:
opt_out: false
code_guidelines:
enabled: true
filePatterns:
- '**/CODING_STANDARDS.md'
learnings:
scope: 'local'
jira:
usage: 'disabled'

reviews:
profile: 'chill'
high_level_summary: true
poem: false
review_status: true
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
base_branches: ['main', 'release/.*']
path_filters:
- '!**/dist/**'
- '!**/dist-standalone/**'
- '!**/dist-cdn/**'
- '!**/playwright-report/**'
- '!**/test-results/**'
- '!**/package-lock.json'
- '!packages/bruno-shared/src/styles/theme.generated.css'
path_instructions:
- path: '**/*'
instructions: |
This repo renders a Bruno API collection as docs with a playground that sends real
requests. Reason as an API client would (requests, responses, auth, headers, bodies,
environments), not as a document viewer. CODING_STANDARDS.md is the source of truth
and ARCHITECTURE.md says who owns what. Its core rule: the simplest code that works,
abstractions only when required. Don't restate ESLint style; the pre-commit hook fixes
it. Focus on correctness, root cause over symptom, the simplicity smells, missing
tests, dead code left behind and comments that narrate the change.
Three packages: bruno-shared, bruno-api-playground, bruno-api-docs. Boundary: shared
imports neither of the others; playground imports only shared; docs only type-imports
the playground, apart from the one lazy import() in
packages/bruno-api-docs/src/components/Playground/load.ts. Cross-package imports use
package names; `@/` exists only in docs. Flag code placed in the wrong package: if
both need it, shared; anything that runs requests or scripts, playground.
- path: 'packages/bruno-shared/**'
instructions: |
Shared code used by docs and the playground (session, document, routing, theme, ui,
shared components, test utils). Flag:
- Any import from @usebruno/api-docs or @usebruno/api-playground, or a relative path
into their folders.
- An `@/` import; shared uses relative imports or its own package name.
- Code only one package uses; it belongs in that package.
- A new React context or module-level state the playground reads, without adding it to
the externals in packages/bruno-api-playground/vite.config.cdn.ts and Bruno.__rt in
packages/bruno-api-docs/src/components/Playground/loadCdn.ts. Without that the CDN
build gets a second copy and the playground reads a different context.
- A single barrel that re-exports everything; it makes Monaco load eagerly.
- path: 'packages/bruno-api-playground/**'
instructions: |
The playground: PlaygroundBody and below, the runner and QuickJS scripting. It is
loaded lazily, as an ES module in dev and as the classic script
api-playground-<hash>.js in the CDN build. Flag:
- Any import from @usebruno/api-docs or a relative path into packages/bruno-api-docs.
- An `@/` import; the playground uses relative imports or package names.
- A change to the externals in vite.config.cdn.ts without the matching Bruno.__rt
entry in packages/bruno-api-docs/src/components/Playground/loadCdn.ts, or the other
way round.
- State that must outlive the playground kept inside it instead of in the shared
session or workspace.
- path: 'packages/bruno-api-docs/**'
instructions: |
Docs view, the playground dock shell, the standalone CDN entry and all e2e. Flag:
- A value import of @usebruno/api-playground. Only types may be imported; the one
value import is the lazy import() in src/components/Playground/load.ts.
- Anything that makes the playground load before Try or a deep link.
- Changes to the public Bruno.apiDocs API or the bundle file names.
- Docs code that runs requests or scripts; that belongs in the playground.
- path: 'packages/bruno-shared/src/{session,observable,document}/**'
instructions: |
State rules from CODING_STANDARDS.md §State. Flag:
- A fact with two owners, or a copy of state that could be derived during render.
- An updater that mutates, rebuilds untouched branches, or returns a new object on a
no-op. Updaters are pure `(state, payload) => state`.
- A storage key written outside the owner that persists it, or a renamed key.
- Anything that makes the document writable after load.
- A new store library, generic store layer or option nobody passes.
- path: 'packages/*/src/components/**'
instructions: |
React rules from CODING_STANDARDS.md §React and §Styling. Flag:
- A leaf reading the session or workspace directly instead of taking props and callbacks.
- An effect that computes something derivable during render.
- A handler reading live state at click time instead of a value from render.
- A context hook that doesn't throw outside its provider.
- A component doing two jobs, or a near-duplicate of something in a ui or components
folder of any package.
- Compound components where there are no real slots.
- Colours or fonts not from `--oc-*` CSS variables.
- path: 'packages/bruno-api-docs/e2e/**'
instructions: |
Follow packages/bruno-api-docs/e2e/README.md and CODING_STANDARDS.md §E2E. Flag:
- Locators not by data-testid, or selectors inlined in specs instead of page objects.
- `new` in specs; expects inside page objects or components.
- `page.waitForTimeout`, `page.pause()`, `test.only`.
- Serial mode without a stated reason.
- Assertions that are conditional or check a substring the fixture already has.
- path: 'packages/bruno-api-playground/src/{runner,scripting}/**'
instructions: |
This code builds real HTTP requests and runs user scripts in a QuickJS sandbox. Check
it against what the Bruno desktop app does for the same request. Remember:
- Interpolation and auth are separate stages; a change to one doesn't cover the other.
- Only basic, bearer, apikey and digest reach the wire. awsv4, ntlm and wsse are
silently dropped. Flag code or tests that assume otherwise.
- HTTP is plain browser fetch; every network failure is one TypeError.
- Treat interpolated values, headers, URLs and script output as untrusted: header or
URL injection, new host capabilities exposed to scripts, secrets in logs or errors.
- `x || default` where an empty token, header or param is a real state.
pre_merge_checks:
custom_checks:
- name: 'No abstraction without a second user'
mode: 'warning'
instructions: |
Fail if the PR adds an interface or factory with one implementation, a wrapper that
only forwards, a helper with one caller, an option no caller passes, or an export
nothing imports. Pass if each new abstraction has at least two real users in the
code after this PR.
- name: 'Package change has a changeset'
mode: 'warning'
instructions: |
If the PR changes files under packages/*/src, it must add a markdown file under
.changeset/ for @usebruno/api-docs. Fail if it doesn't. Pass for PRs that only touch docs, tests,
CI or tooling.
- name: 'Behaviour change has a test'
mode: 'warning'
instructions: |
If the PR changes what the app does (new branch, default, UI state, bug fix), it must
add or change a unit spec or an e2e spec that would fail without the change. Fail if
a behaviour change has no test. Pass for pure refactors with existing coverage.
- name: 'No renamed storage keys or public names'
mode: 'warning'
instructions: |
Fail if the PR renames or removes the storage keys oc-docs.theme, oc-docs.env,
oc-docs:docsSidebarWidth, oc-docs:playgroundSidebarWidth or any other oc-docs key,
the .oc-playground class, the dist bundle file names, or the Bruno.apiDocs API
(updateCollection, updateOptions, destroy).
- name: 'E2E conventions'
mode: 'warning'
instructions: |
For changed files under packages/bruno-api-docs/e2e: fail on page.waitForTimeout,
test.only, page.pause, locators not by data-testid, expects outside specs, or serial
mode without a comment saying why.

chat:
auto_reply: true
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ jobs:

- run: npm run lint

- run: npm run test:run -w packages/bruno-api-docs
- run: npm run typecheck --workspaces --if-present

- run: npm run test:run --workspaces --if-present

# `draft` is null on push events, so that arm is required.
- name: Build
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ node_modules
dist
.DS_Store
dist-standalone
dist-cdn
test.js

# Claude Code
.claude/
CLAUDE.md
!/CLAUDE.md
test-results
!integrations/contract-tests/fixtures/walk-safety/node_modules/
integrations/dist-tarballs
Expand Down
55 changes: 55 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# AGENTS.md

`@usebruno/api-docs` renders a Bruno OpenCollection as API docs with a playground that sends
real requests. It ships as a CDN bundle (`Bruno.apiDocs(target, { content })`), not on npm.
The code lives in three workspaces under `packages/`; the repo root holds lint, changesets and CI.

**Read `ARCHITECTURE.md` before changing code. Follow `CODING_STANDARDS.md`.** Its core rule:
the simplest code that works, abstractions only when required.

## Where code goes

- `packages/bruno-api-docs` (`@usebruno/api-docs`): docs pages, sidebar, search, the playground
dock shell, the standalone and dev entries, all e2e.
- `packages/bruno-api-playground` (`@usebruno/api-playground`, private): `PlaygroundBody` and below,
the runner, QuickJS scripting. Anything that runs requests or scripts goes here.
- `packages/bruno-shared` (`@usebruno/api-shared`, private): session, document, routing, theme,
styles, ui primitives, shared components, test utils. If docs and playground both need it, it goes here.

Boundary (lint): shared imports neither; playground imports only shared; docs only type-imports
the playground, apart from the one lazy `import()` in
`packages/bruno-api-docs/src/components/Playground/load.ts`. Cross-package imports use package
names. `@/` exists only in docs.

## Commands

From the repo root:

| Task | Command |
|---|---|
| Unit tests, all packages | `npm run test:run` |
| Unit tests, one package | `npm run test:run -w @usebruno/api-shared` (or `@usebruno/api-playground`, `@usebruno/api-docs`) |
| Lint changed lines | `npm run lint` (whole tree: `npm run lint:all`) |
| Changeset | `npm run changeset` |

From `packages/bruno-api-docs`:

| Task | Command |
|---|---|
| Dev server | `npm run dev` |
| E2E, main + standalone | `npm run test:e2e` |
| E2E, standalone only | `npm run test:e2e:standalone` |
| One e2e spec | `npx playwright test e2e/tests/<area>/<name>.spec.ts` |
| Build | `npm run build`, `npm run build:standalone` (builds the playground file first) |

## Rules agents break most

1. No new abstraction (interface, factory, wrapper, option, helper) without a second real use.
2. PRs target the active `release/vX.Y.0` branch, never `main`.
3. A change to any package needs a changeset (`npm run changeset` at the root).
4. Locally run only the e2e specs your change touches. CI runs the full suite.
5. Never rename storage keys (`oc-docs.theme`, `oc-docs.env`, `oc-docs:*`), the
`.oc-playground` class or bundle names.
6. Zero comments by default. Only what the code cannot say.
7. New files: components `PascalCase.tsx`, everything else in `src` `camelCase.ts`, e2e `kebab-case`.
Don't copy a neighbouring file's naming.
Loading
Loading