Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions .agents/skills/effort-graph/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,18 @@ client polling loops. Semantic changes go through `flatbread effort write`.
## 4. Open the explorer (optional)

With a complete `effortGraphContent()` preset in config, Flatbread serves the
content-relation explorer automatically:
content-relation explorer automatically (`@flatbread/explorer` ships with
`flatbread`):

```bash
flatbread start --watch --open
```

- Explorer UI: `http://localhost:5057/`
Mounting and `--open` share the same assets gate: if the packaged SPA assets
are missing, Flatbread skips the explorer mount and `--open` falls back to
`/graphql`.

- Explorer UI (when mounted): `http://localhost:5057/`
- Apollo GraphQL sandbox: `http://localhost:5057/graphql`

No separate app install is required; `@flatbread/explorer` ships with
`flatbread`.
No separate app install is required.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Optional **`pnpm play`** from the repo root is a shortcut for **`cd examples/nex
- Build all packages: `pnpm build`
- **Workspace libraries (watch-only):** `pnpm dev` — runs package `dev` scripts (e.g. `tsup --watch`) for `packages/*`; it does **not** start the Next.js example.
- **Next.js example:** prefer the flow under [Recommended onboarding](#recommended-onboarding-try-flatbread-in-the-nextjs-example); or `pnpm play` as a convenience alias.
- **Effort Graph explorer:** after `pnpm build`, run `pnpm play:efforts` (`flatbread start --watch --open`). When `flatbread.config.js` uses `effortGraphContent()`, Flatbread serves `@flatbread/explorer` at `http://localhost:5057/` (Apollo sandbox at `/graphql`).
- **Effort Graph explorer:** run `pnpm play:efforts` (builds `@flatbread/explorer` via `preplay:efforts`, then `flatbread start --watch --open`). When `flatbread.config.js` uses `effortGraphContent()`, Flatbread serves `@flatbread/explorer` at `http://localhost:5057/` (Apollo sandbox at `/graphql`). For HMR on the SPA shell, run `flatbread start --watch` and `pnpm --filter @flatbread/explorer dev` in parallel (Vite on **5173** proxies API routes to **5057**).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

severity: MED (docs-and-positioning)

Docs claim explorer is always at http://localhost:5057/ for effortGraphContent(), but mount/--open now require assets and HMR UI is on 5173.

Minimal fix: Add the same “when mounted / assets present” qualifier as setup.md, and point HMR UI at 5173.

- Check local CI parity before opening a PR: `pnpm verify`

## Working on a package
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"lint:fix:prettier": "pretty-quick --staged",
"typecheck": "pnpm --filter @flatbread/proof --filter @flatbread/explorer typecheck",
"play": "cd examples/nextjs && pnpm dev",
"preplay:efforts": "pnpm --filter @flatbread/explorer build",
"play:efforts": "pnpm exec flatbread start --watch --open",
"play:build": "pnpm build && cd examples/nextjs && pnpm build",
"prepublish:ci": "pnpm install --frozen-lockfile && pnpm build:types",
Expand Down
12 changes: 8 additions & 4 deletions packages/effort-graph/skills/effort-graph/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,18 @@ client polling loops. Semantic changes go through `flatbread effort write`.
## 4. Open the explorer (optional)

With a complete `effortGraphContent()` preset in config, Flatbread serves the
content-relation explorer automatically:
content-relation explorer automatically (`@flatbread/explorer` ships with
`flatbread`):

```bash
flatbread start --watch --open
```

- Explorer UI: `http://localhost:5057/`
Mounting and `--open` share the same assets gate: if the packaged SPA assets
are missing, Flatbread skips the explorer mount and `--open` falls back to
`/graphql`.

- Explorer UI (when mounted): `http://localhost:5057/`
- Apollo GraphQL sandbox: `http://localhost:5057/graphql`

No separate app install is required; `@flatbread/explorer` ships with
`flatbread`.
No separate app install is required.
17 changes: 14 additions & 3 deletions packages/explorer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ flatbread start --watch --open
# → http://localhost:5057/graphql Apollo sandbox
```

No separate Next app is required.
No separate Next app is required. Requires a built `dist/static` (see
[Develop in the monorepo](#develop-in-the-monorepo)); `pnpm play:efforts` runs
that build automatically.

## Static deploy

Expand All @@ -31,6 +33,7 @@ Same-origin deploys (assets served by Flatbread) need no query param.
| Export | Role |
| ------------------------------ | ------------------------------------------ |
| `getExplorerStaticDir()` | Absolute path to `dist/static` for Express |
| `explorerAssetsPresent()` | Whether prebuilt `index.html` exists |
| `matchExplorerPreset(content)` | Detect Effort Graph (and later presets) |
| `EXPLORER_BOOTSTRAP_PATH` | Bootstrap JSON path Flatbread injects |

Expand All @@ -40,6 +43,14 @@ There is no public React component export in v1.

```bash
pnpm --filter @flatbread/explorer test
pnpm --filter @flatbread/explorer build
pnpm play:efforts # flatbread start --watch --open from repo root
pnpm play:efforts # builds explorer, then flatbread start --watch --open
```

For UI-only iteration with HMR, run Flatbread and Vite in separate terminals
(Vite proxies `/graphql` and `/events` to Flatbread on port **5057**, or
`FLATBREAD_PORT` when set):

```bash
pnpm exec flatbread start --watch # terminal 1 — GraphQL on :5057
pnpm --filter @flatbread/explorer dev # terminal 2 — SPA on :5173
Comment on lines +46 to +55

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

severity: MED (docs-and-positioning, explorer-package)

HMR docs imply FLATBREAD_PORT / -p and which URL to open, but the Vite terminal does not inherit the forked server’s port and readers may hit stale packaged assets on 5057.

Minimal fix: Document exporting FLATBREAD_PORT in the Vite terminal for non-default ports, say open 5173 for HMR (5057 API-only), and note custom graphql/events paths need extra proxy rules without __FLATBREAD_EXPLORER__.

```
7 changes: 6 additions & 1 deletion packages/explorer/src/node/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export { getExplorerStaticDir, EXPLORER_BOOTSTRAP_PATH } from './staticDir.js';
export {
explorerAssetsPresent,
getExplorerStaticDir,
setExplorerStaticDirOverride,
EXPLORER_BOOTSTRAP_PATH,
} from './staticDir.js';
export {
matchExplorerPreset,
type ExplorerPresetId,
Expand Down
51 changes: 49 additions & 2 deletions packages/explorer/src/node/staticDir.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import assert from 'node:assert/strict';
import fs from 'node:fs';
import { mkdtemp, rm } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { describe, it } from 'node:test';
import { getExplorerStaticDir } from './staticDir.js';
import { afterEach, describe, it } from 'node:test';
import {
explorerAssetsPresent,
getExplorerStaticDir,
setExplorerStaticDirOverride,
} from './staticDir.js';

afterEach(() => {
setExplorerStaticDirOverride(undefined);
});

describe('getExplorerStaticDir', () => {
it('resolves to a path ending in dist/static', () => {
Expand All @@ -24,4 +34,41 @@ describe('getExplorerStaticDir', () => {
assert.ok(fs.existsSync(index), `expected ${index} after vite build`);
}
});

it('honors setExplorerStaticDirOverride when set', async () => {
const emptyDir = await mkdtemp(
path.join(os.tmpdir(), 'flatbread-explorer-static-')
);
try {
setExplorerStaticDirOverride(emptyDir);
assert.equal(getExplorerStaticDir(), emptyDir);
assert.equal(explorerAssetsPresent(), false);
} finally {
setExplorerStaticDirOverride(undefined);
await rm(emptyDir, { recursive: true, force: true });
}
});
});

describe('explorerAssetsPresent', () => {
it('reflects whether index.html exists under the static dir', async () => {
const indexPath = path.join(getExplorerStaticDir(), 'index.html');
if (!fs.existsSync(indexPath)) {
assert.equal(explorerAssetsPresent(), false);
return;
}

assert.equal(explorerAssetsPresent(), true);

const emptyDir = await mkdtemp(
path.join(os.tmpdir(), 'flatbread-explorer-absent-')
);
try {
setExplorerStaticDirOverride(emptyDir);
assert.equal(explorerAssetsPresent(), false);
} finally {
setExplorerStaticDirOverride(undefined);
await rm(emptyDir, { recursive: true, force: true });
}
});
});
24 changes: 24 additions & 0 deletions packages/explorer/src/node/staticDir.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

/** HTTP path Flatbread serves for explorer bootstrap JSON. */
export const EXPLORER_BOOTSTRAP_PATH = '/__flatbread/explorer.json';

let staticDirOverride: string | undefined;

/**
* Test-only: force `getExplorerStaticDir()` to `dir`.
* Pass `undefined` to clear. Not for production callers.
*/
export function setExplorerStaticDirOverride(dir: string | undefined): void {
staticDirOverride = dir;
}

Comment on lines +8 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

severity: MED (cli-and-runtime, explorer-package)

Public process-global override has no isolation. Any parallel AVA worker mutating it can corrupt unrelated suites (same root cause as the openPath HIGH).

Minimal fix: Move the override behind a test-only export path, or document it and require serial consumers everywhere it is set/cleared.

/**
* Absolute path to the prebuilt SPA assets shipped in this package.
* Flatbread mounts these with `express.static` when a preset matches.
* Honors `setExplorerStaticDirOverride` when set (tests only).
*/
export function getExplorerStaticDir(): string {
if (staticDirOverride !== undefined) {
return staticDirOverride;
}
const here = path.dirname(fileURLToPath(import.meta.url));
// Works from both `src/node` (tests) and `dist/node` (published).
const packageRoot = path.resolve(here, '../..');
return path.join(packageRoot, 'dist', 'static');
}

/**
* True when prebuilt SPA `index.html` exists under `getExplorerStaticDir()`.
* Flatbread uses this with `matchExplorerPreset` before mounting or advertising
* explorer.
*/
export function explorerAssetsPresent(): boolean {
return fs.existsSync(path.join(getExplorerStaticDir(), 'index.html'));
}
Loading
Loading