Skip to content

Commit 0e1cd4e

Browse files
authored
Add local Streams development support and stream browser improvements (#1478)
* Add local Streams development wiring * Add stream aggregation rollup panel * Expand stream diagnostics, search, and demo runtime support * Stabilize stream polling and search progress * Add resizable Studio sidebar * Unify sidebar stream filtering and refresh * Add routing key browsing to stream view * fix(table): preserve page index when staging edits Avoid resetting pagination when row-search apply receives the current term. Adds regression tests for unchanged and changed row-search applies. * feat: improve WAL history and stream diagnostics * chore: update Prisma dev runtime to latest published build * chore: add release changeset
1 parent b10686a commit 0e1cd4e

80 files changed

Lines changed: 24433 additions & 1092 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/green-rabbits-obey.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@prisma/studio-core": minor
3+
---
4+
5+
Expand Prisma Streams support across Studio with a dedicated stream browser, live stream aggregations, stream diagnostics, routing-key browsing, WAL history handoff from tables, and a more flexible demo/runtime setup for local and external Streams servers.

.pnpmfile.cjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const {
2+
patchStudioDevPackages,
3+
} = require("./scripts/dev/local-streams-override.cjs");
4+
5+
module.exports = {
6+
hooks: {
7+
readPackage(pkg) {
8+
return patchStudioDevPackages(pkg, {
9+
env: process.env,
10+
logger: console,
11+
rootDir: __dirname,
12+
});
13+
},
14+
},
15+
};

Architecture/demo-compute-bundling.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ The `demo/ppg-dev` server has two runtime modes:
66

77
- local development mode, where it rebuilds browser assets from source and watches the repo
88
- deploy mode, where it serves prebuilt browser assets from a bundled artifact
9+
- external data-source mode, where the demo keeps serving the Studio shell locally but proxies Streams to a caller-provided upstream server and runs direct TCP queries against a caller-provided PostgreSQL connection string instead of starting local Prisma Dev
910

1011
The deploy path exists because the demo server is not just a Bun server entrypoint. In development it expects the Studio repo checkout so it can rebuild `client.tsx` and `ui/index.css` at runtime.
1112

13+
Bundled deploy mode uses the embedded local Prisma Streams runtime exactly as published by `@prisma/streams-local`, so Studio does not carry a second demo-local memory autotune layer on top of Streams' own defaults.
14+
1215
## Build Responsibilities
1316

1417
`demo/ppg-dev/build-compute.ts` is the production packager for the demo.
@@ -55,3 +58,54 @@ This is an explicit exception to the "no manual runtime asset copying" rule abov
5558
- If the import fails, the server falls back to local development mode.
5659

5760
That keeps one server implementation for both workflows without adding a separate production-only server entrypoint.
61+
62+
## Local Development Shutdown
63+
64+
In local development mode, `demo/ppg-dev/server.ts` also owns the lifecycle of the Prisma Dev child runtime, including the local Prisma Streams server.
65+
66+
- The first shutdown signal MUST start orderly cleanup for the Bun HTTP server, Prisma Dev runtime, Postgres client, and file watchers.
67+
- If cleanup stalls, a repeated shutdown signal MUST force the demo process to exit instead of being ignored.
68+
- The demo process SHOULD also force-exit after a short timeout if cleanup never finishes, so orphaned Prisma Dev and Streams listeners do not block the next `pnpm demo:ppg` run.
69+
70+
## External Demo Mode
71+
72+
The `pnpm demo:ppg` entrypoint MAY also be launched against external data sources:
73+
74+
- `pnpm demo:ppg -- --streams-server-url <streams-url>`
75+
- `pnpm demo:ppg -- --database-url <postgres-url> --streams-server-url <streams-url>`
76+
77+
When `--streams-server-url` is provided, Studio MUST treat the run as external mode.
78+
79+
- It MUST NOT start local Prisma Dev.
80+
- It MUST NOT start a local Prisma Streams server.
81+
- It MUST NOT set up local `prisma-wal` wiring, because that is part of the colocated Prisma Dev + Streams startup path.
82+
- If `--database-url` is also provided, it MUST connect the BFF executor directly to that PostgreSQL URL.
83+
- If `--database-url` is omitted, the demo MUST run in streams-only mode and MUST NOT expose database-driven Studio navigation or views.
84+
- The browser config MUST continue to expose Streams through Studio's own `/api/streams` proxy path so the UI contract stays unchanged.
85+
- The demo shell MUST NOT claim the database was locally seeded when external mode is active.
86+
87+
## Local Streams Development Override
88+
89+
Studio keeps `@prisma/dev` as the only runtime dependency in source, but local
90+
development MAY temporarily override both the root `@prisma/dev` dependency and
91+
that package's transitive `@prisma/streams-local` dependency through the
92+
repo-level `.pnpmfile.cjs` hook.
93+
94+
- The override MUST remain opt-in through `STUDIO_USE_LOCAL_STREAMS=1`.
95+
- `pnpm streams:use-local` MUST point Studio's root `@prisma/dev` dependency at
96+
the sibling `../team-expansion/dev/server` package (or a caller-provided
97+
`STUDIO_LOCAL_PRISMA_DEV_PACKAGE_DIR`) so local `@prisma/dev` source changes
98+
are exercised directly from this checkout.
99+
- Default installs MUST continue to resolve the published npm package.
100+
- `pnpm streams:use-local` MUST build `../streams/dist/npm/streams-local` (or a
101+
caller-provided override path) and reinstall dependencies with `--no-lockfile`
102+
so the repo can switch implementations without persisting a machine-local
103+
path to `pnpm-lock.yaml`.
104+
- `pnpm streams:use-local` MUST also build or otherwise validate the linked
105+
local `@prisma/dev` package before reinstalling Studio, because the linked
106+
package's published entrypoints resolve from its `dist/` directory.
107+
- `pnpm streams:use-npm` MUST restore the published npm dependencies with the
108+
same `--no-lockfile` behavior.
109+
- Because `build-compute.ts` resolves `@prisma/streams-local` from the installed
110+
`@prisma/dev` dependency tree, bundled demo artifacts MUST follow whichever
111+
local or published streams package is currently installed.

Architecture/navigation-url-state.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ This architecture governs:
1010

1111
- active Studio view (`table`, `schema`, `console`, `sql`, `stream`)
1212
- active schema/table/stream
13+
- active stream follow mode
14+
- active stream aggregation-panel visibility
15+
- active stream aggregation range while the aggregation panel is open
1316
- pagination URL state
1417
- sorting URL state
1518
- column pinning URL state
@@ -38,6 +41,9 @@ Only keys declared in [`ui/hooks/nuqs.ts`](../ui/hooks/nuqs.ts) are allowed:
3841
- `schema`
3942
- `table`
4043
- `stream`
44+
- `streamFollow`
45+
- `aggregations`
46+
- `streamAggregationRange`
4147
- `filter`
4248
- `sort`
4349
- `pin`
@@ -48,12 +54,15 @@ Only keys declared in [`ui/hooks/nuqs.ts`](../ui/hooks/nuqs.ts) are allowed:
4854

4955
Notes:
5056

51-
- `search` is row-search term state for active table view.
57+
- `search` is shared search term state for the active data view. In table view it drives row search, and in stream view it drives stream-event search when the selected stream advertises search capability.
5258
- `searchScope` is legacy URL state and is not used for table-name navigation filtering.
5359
- `pin` stores left-pinned data columns for the grid as a comma-separated list (for example `pin=id,bigint_col`).
5460
- `pin` order is authoritative and MUST be updated when users drag-reorder pinned columns.
5561
- `pageIndex` remains URL-backed for table navigation.
5662
- `pageSize` remains a supported hash key for compatibility, but table rendering now takes its authoritative rows-per-page preference from `studioUiCollection.tablePageSize` in [`Architecture/ui-state.md`](ui-state.md).
63+
- `streamFollow` stores the active stream follow mode (`paused`, `live`, or `tail`).
64+
- `aggregations` is an open-only flag for the active stream aggregation strip; when present it MUST be serialized as a bare key with no explicit value.
65+
- `streamAggregationRange` stores the active stream aggregation range, but MUST only be serialized while `aggregations` is present.
5766

5867
Adding a new URL key requires updating `StateKey` in `nuqs.ts` first.
5968

@@ -70,6 +79,14 @@ Adding a new URL key requires updating `StateKey` in `nuqs.ts` first.
7079
- `searchScope`: `"table"` (legacy default)
7180
- `view`: `"table"`
7281
- `stream`: no default; only meaningful when `view=stream`
82+
- `streamFollow`: no global default in `useNavigation`; the active stream view MUST resolve an absent value to `tail` and materialize that into the hash
83+
- `aggregations`: no global default in `useNavigation`; the active stream view MUST treat an absent flag as closed and MUST NOT materialize that closed state into the hash
84+
- `streamAggregationRange`: no standalone default; the active stream view MUST clear it whenever `aggregations` is absent, and MUST materialize its default range only after the aggregation panel is opened
85+
86+
When Studio is running without a database connection but with Streams enabled:
87+
88+
- the resolved default `view` MUST become `"stream"` instead of `"table"`
89+
- stale database-oriented views such as `table`, `schema`, `console`, and `sql` MUST resolve back to the stream view instead of trying to render database-only UI against a disabled database session
7390

7491
When URL params are stale from a previous DB, invalid `schema`/`table` values MUST be resolved to valid current defaults.
7592
Shared table page size and infinite-scroll mode are not derived from URL defaults; they are restored through Studio UI state and then mirrored into query behavior by `usePagination`.

Architecture/non-standard-ui.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,74 @@ It deliberately excludes:
7777
- `Skeleton`
7878
- Why it stays non-standard:
7979
- The stream view needs a dense multi-column summary row with inline expansion, single-open-row behavior, clipped preview text, and infinite-scroll loading inside one scroll container.
80+
- The same custom row composite also carries the short-lived highlight animation for newly revealed events, which needs to live on the exact row shell that preserves stream-scroll anchoring.
81+
- The surrounding stream chrome now also mixes a control-only header, a fixed footer summary cluster, follow-mode-specific scroll behavior, and a search-only footer progress fill with a scroll-trigger loading pulse that standard ShadCN layout primitives do not model as one reusable component.
8082
- No stock ShadCN component provides that event-log interaction model, so Studio keeps a custom composite while still building it from standard ShadCN primitives.
8183

84+
### Inline Stream Search Validation Message
85+
86+
- Canonical component:
87+
- [`ui/studio/input/ExpandableSearchControl.tsx`](ui/studio/input/ExpandableSearchControl.tsx)
88+
- Closest standard ShadCN alternatives:
89+
- `Command`
90+
- `Popover`
91+
- `Alert`
92+
- Why it stays non-standard:
93+
- The stream header search needs syntax feedback and context-aware suggestions that stay visually attached to the expanding inline field without introducing a portal-backed overlay or a full alert block inside the header chrome.
94+
- Studio therefore keeps a custom anchored assist panel directly under the input, while still building the suggestion list from standard ShadCN `Command` primitives.
95+
- Keeping the feedback and suggestions inline avoids the layering and focus issues of a separate popover in this compact header layout, and it lets the suggestion list open immediately with starter field suggestions, stay content-sized above the sticky header row, hold partial field prefixes locally, preserve a stable keyboard selection during background refreshes, and still draw value candidates from remembered rows even when the currently visible filtered result set is empty.
96+
97+
### Stream Routing Key Selector
98+
99+
- Canonical components:
100+
- [`ui/studio/views/stream/StreamRoutingKeySelector.tsx`](ui/studio/views/stream/StreamRoutingKeySelector.tsx)
101+
- [`ui/hooks/use-stream-routing-keys.ts`](../ui/hooks/use-stream-routing-keys.ts)
102+
- Closest standard ShadCN alternatives:
103+
- `Popover`
104+
- `Command`
105+
- `Input`
106+
- Why it stays non-standard:
107+
- The stream header needs a compact routing-key picker that can sit beside the expanding search field, page through a potentially massive lexicographically sorted keyspace, and still support keyboard-first selection without rendering every key at once.
108+
- The API only exposes cursor-based routing-key pages, and the selector now also owns a clearable selected-key state that must work even when the stream has no search schema.
109+
- When a key is selected, the closed trigger also needs to expand into a compact inline pill that keeps the chosen routing key visible without stealing the full search-field slot.
110+
- Studio therefore keeps a custom popover composite with a prefix input, a virtualized infinite list, and a hover-only clear affordance on the trigger itself instead of trying to force that behavior into a stock `Command` list.
111+
112+
### Stream Aggregation Strip
113+
114+
- Canonical components:
115+
- [`ui/studio/views/stream/StreamAggregationsPanel.tsx`](ui/studio/views/stream/StreamAggregationsPanel.tsx)
116+
- [`ui/studio/views/stream/StreamView.tsx`](ui/studio/views/stream/StreamView.tsx)
117+
- Closest standard ShadCN alternatives:
118+
- `Card`
119+
- `Button`
120+
- `Popover`
121+
- Why it stays non-standard:
122+
- The stream view needs a compact, single-band aggregation surface that mixes horizontally scrollable metric cards, inline sparkline backgrounds, quick time-range toggles, follow-mode-driven refresh behavior, and a small custom-range popover directly above an independently scrollable event log.
123+
- No stock ShadCN component covers that event-log-adjacent observability layout, especially once each metric column has to support fixed-width horizontal scrolling, stacked percentile cards with plain-text secondary labels, auto-scaled unit display, TanStack DB-backed per-series preferences that survive range switches and stream navigation, hover-revealed dropdown controls without reflowing the card chrome, and a tighter split date/time absolute-range editor instead of the browser's native `datetime-local` chrome.
124+
- Required internals:
125+
- `Badge`
126+
- `Button`
127+
- `Card`
128+
- `DropdownMenu`
129+
- `Input`
130+
- `Label`
131+
- `Popover`
132+
- `Skeleton`
133+
134+
### Stream Footer Diagnostics Popover
135+
136+
- Canonical components:
137+
- [`ui/studio/views/stream/StreamDiagnosticsPopover.tsx`](ui/studio/views/stream/StreamDiagnosticsPopover.tsx)
138+
- [`ui/studio/views/stream/StreamView.tsx`](ui/studio/views/stream/StreamView.tsx)
139+
- Closest standard ShadCN alternatives:
140+
- `Popover`
141+
- `Card`
142+
- `Badge`
143+
- Why it stays non-standard:
144+
- Studio needs a compact, stream-specific diagnostics surface anchored to the footer summary itself, mixing logical payload size, explicit object-storage and local-storage buckets, node-local request accounting, search-family coverage, and state-aware run-accelerator status in one dense popover.
145+
- The storage breakdowns also need collapsible ledger-style accounting boxes whose headers surface the section totals when folded shut, plus faint shared-cap annotations that sit beside right-aligned byte values and one shared cap marker spanning both Routing and Exact cache rows, which is not a stock ShadCN pattern.
146+
- No stock ShadCN pattern covers that descriptor-driven observability layout, especially when the UI must distinguish logical bytes from physical storage signals, separate search coverage from historical run indexes, hide unconfigured routing rows, and keep the remaining cost caveats explicit instead of inventing unavailable totals.
147+
82148
## Standardization Candidates
83149

84150
These are the current high-signal places where Studio is bypassing a plausible standard ShadCN component or composition pattern.
@@ -122,7 +188,7 @@ These are the current high-signal places where Studio is bypassing a plausible s
122188
- Files:
123189
- [`ui/studio/Navigation.tsx`](ui/studio/Navigation.tsx)
124190
- Current UI:
125-
- Custom sidebar sections, custom table search disclosure, custom sidebar item primitive.
191+
- Custom sidebar sections, shared inline search-and-refresh disclosure for both tables and streams, custom sidebar item primitive, and a draggable resize separator on the sidebar edge.
126192
- Plausible standard ShadCN alternative:
127193
- `Sidebar`
128194
- standard sidebar/menu composition

0 commit comments

Comments
 (0)