Skip to content

feat: add inspector SQLite shell and property bindings#4410

Merged
NathanFlurry merged 7 commits intomainfrom
inspector-sqlite-shell
Mar 31, 2026
Merged

feat: add inspector SQLite shell and property bindings#4410
NathanFlurry merged 7 commits intomainfrom
inspector-sqlite-shell

Conversation

@NathanFlurry
Copy link
Copy Markdown
Member

Description

This adds a manual SQLite shell to the actor Inspector UI, including query execution, result rendering, and editable named-property inputs that re-run the preview. It also adds POST /inspector/database/execute support for both positional args and named properties, updates the raw SQLite binding helpers, and documents the new flow.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Ran pnpm exec vitest run tests/driver-memory.test.ts -t "POST /inspector/database/execute|GET /inspector/state returns actor state" in rivetkit-typescript/packages/rivetkit.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 12, 2026 19:56 Destroyed
@railway-app
Copy link
Copy Markdown

railway-app bot commented Mar 12, 2026

🚅 Deployed to the rivet-pr-4410 environment in rivet-frontend

Service Status Web Updated (UTC)
frontend-cloud 😴 Sleeping (View Logs) Web Mar 31, 2026 at 12:45 am
frontend-inspector 😴 Sleeping (View Logs) Web Mar 30, 2026 at 9:16 pm
website 😴 Sleeping (View Logs) Web Mar 30, 2026 at 9:15 pm
mcp-hub ✅ Success (View Logs) Web Mar 30, 2026 at 9:05 pm
ladle ❌ Build Failed (View Logs) Web Mar 30, 2026 at 9:05 pm

@NathanFlurry NathanFlurry requested a review from jog1t March 12, 2026 20:01
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 13, 2026 02:51 Destroyed
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 13, 2026

More templates

@rivetkit/cloudflare-workers

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/cloudflare-workers@4410

@rivetkit/framework-base

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/framework-base@4410

@rivetkit/next-js

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/next-js@4410

@rivetkit/react

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/react@4410

rivetkit

pnpm add https://pkg.pr.new/rivet-dev/rivet/rivetkit@4410

@rivetkit/sql-loader

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sql-loader@4410

@rivetkit/sqlite-vfs

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/sqlite-vfs@4410

@rivetkit/traces

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/traces@4410

@rivetkit/workflow-engine

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/workflow-engine@4410

@rivetkit/virtual-websocket

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/virtual-websocket@4410

@rivetkit/engine-runner

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner@4410

@rivetkit/engine-runner-protocol

pnpm add https://pkg.pr.new/rivet-dev/rivet/@rivetkit/engine-runner-protocol@4410

commit: 339c3bc

@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 13, 2026 03:06 Destroyed
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 13, 2026 03:09 Destroyed
@claude
Copy link
Copy Markdown

claude bot commented Mar 13, 2026

PR Review: Add inspector SQLite shell and property bindings

This is a solid feature addition. The backend refactoring (PRAGMA-based schema introspection, named bindings, #withDatabase lock helper) is a genuine improvement over the previous sample-based approach. A few issues to address before merge.


Bugs

1. hasPositionalBindings false-positives on ? in string literals and comments (actor-database.tsx)

The current sql.includes("?") check warns for valid SQL like SELECT '?' AS q, -- is it nullable?, and json_extract(col, '$.key'). The server executes these correctly but the UI shows a spurious warning. A simple fix is to strip single-quoted literals and -- comments before checking.

2. DEFAULT_SQL hardcodes a test_data table (actor-database.tsx)

Any actor without test_data gets an error the first time they open the SQL shell. A universally valid default like SELECT name FROM sqlite_master WHERE type = 'table' would be less confusing.

3. createStagedEditId can produce collisions (actor-database.tsx)

rowKey is JSON.stringify-ed and already contains : characters. Row [["id",1]] with column col produces the same staged-edit ID as row [["id","1:col"]] with column "". Use a non-ambiguous separator (e.g., \0) or a structured hash.


Correctness

4. #withDatabase relies on a fragile result as T cast (actor-inspector.ts)

The let result: T | undefined / return result as T pattern works today because errors propagate through the lock. But if the lock callback ever swallows an exception, undefined is silently returned as T. Returning the value directly from lock (if the type supports it) would be cleaner and type-safe.

5. normalizeSqlitePropertyBindings triple-expands every key (actor-inspector.ts)

Each unadorned property like value becomes three entries: :value, @value, and $value. Only one is ever matched. Some SQLite drivers reject extra unbound names. A comment explaining the intent (and confirming the driver ignores extras) would help future readers.

6. isSqliteBindingValue accepts bigint on the server but the UI does not (shared.ts vs actor-database.tsx)

The UI error message should explicitly say bigint is unsupported via this HTTP endpoint (it is a JSON transport limitation, not a SQLite limitation).


Minor / Style

  • applyEdits is not transactional: Staged cell edits are applied as individual sequential UPDATEs. If one fails midway, earlier updates are committed while the UI still shows the batch as pending. Wrapping in BEGIN/COMMIT/ROLLBACK would make multi-cell edits atomic.
  • Inline IIFE in table cell render (database-table.tsx): The ~50-line IIFE inside .map() that computes cell context would be easier to follow as a named renderTableCell function.
  • Header duplication in actor-inspector-context.tsx: Several HTTP methods build identical headers inline. A small buildInspectorHeaders helper would reduce repetition.

Test Coverage

Current happy-path tests for the new endpoint are good. Gaps worth adding:

  • Both args and properties supplied simultaneously (should return 400).
  • Invalid binding type in args or properties (400 path).
  • Empty sql field.

Positive Highlights

  • PRAGMA-based schema introspection is a meaningful improvement over sample-based column detection and correctly surfaces primary key metadata needed for the cell editor.
  • The bindingChangeToken 250 ms debounce for auto-re-running named-property queries is a good UX touch.
  • parseBindingDraft gracefully falls back to treating raw input as a string when it is not valid JSON — the correct default for simple string values.
  • Documentation additions in debugging.mdx, sqlite.mdx, and skill-base-rivetkit.md are clear and accurate.

@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 13, 2026 03:22 Destroyed
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 13, 2026 03:31 Destroyed
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 13, 2026 03:41 Destroyed
Copy link
Copy Markdown
Contributor

@jog1t jog1t left a comment

Choose a reason for hiding this comment

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

some stuff related to the react code, which llm did you use? i think we need to install react skills in the repo for better results.

}, [stagedEdits]);
const stagedEditCount = stagedEditList.length;

useEffect(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this should happen on callback, no need for useEffect here, common llm mistake

just call those cleanup functions when the setTable i called, in transition for batched update.

setTableEditError(null);
}, []);

const applyEdits = useCallback(async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i wonder if this could be moved to the mutation, instead of doing everything here

const hasNextPage = page < totalPages - 1;
const hasPrevPage = page > 0;

const beginCellEdit = useCallback(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there's a lot of logic that can be encapsulated in the separate hook so the row editing is a separate hook.

Record<string, string>
>({});
const [bindingChangeToken, setBindingChangeToken] = useState(0);
const [result, setResult] = useState<DatabaseExecuteResult | null>(null);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there's no need to have this here, you can use the mutation state for that.

const hasMixedBindings = hasNamedBindings && hasPositionalBindings;

useEffect(() => {
setPropertyDrafts((current) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thats create a race condition that can overwrite user interaction (seen below), this can be derived on a first render.

};
}, [namedBindings, propertyDrafts]);

const resultColumns = useMemo(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

no need for useMemos, they obstruct the code and are added by the react compiler

bindingError === null &&
propertiesError === null;

useEffect(() => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

what this do?

Comment on lines +761 to +781
<ScrollArea className="h-full w-full">
{result === null ? null : result.rows.length === 0 ? (
<div className="px-3 py-4 text-sm text-muted-foreground">
Statement executed successfully. No rows were
returned.
</div>
) : resultRowsAreTable && resultColumns.length > 0 ? (
<DatabaseTable
className="overflow-hidden"
columns={resultColumns}
enableColumnResizing={false}
enableRowSelection={false}
enableSorting={false}
data={result.rows}
/>
) : (
<pre className="overflow-auto px-3 py-4 text-xs">
{JSON.stringify(result.rows, null, 2)}
</pre>
)}
</ScrollArea>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i think this view should be rethinked -> inline editing is not possible when using db shell, is that correct?

}

const response = await fetch(
`${computeActorUrl({ ...credentials, actorId })}/inspector/database/execute`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why does this not follow the BARE protocol?

@NathanFlurry NathanFlurry changed the title Add inspector SQLite shell and property bindings feat: add inspector SQLite shell and property bindings Mar 20, 2026
@NathanFlurry NathanFlurry force-pushed the inspector-sqlite-shell branch from 1025bc5 to 339c3bc Compare March 30, 2026 21:04
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4410 March 30, 2026 21:04 Destroyed
Copy link
Copy Markdown
Member Author

NathanFlurry commented Mar 30, 2026

Copy link
Copy Markdown
Member Author

NathanFlurry commented Mar 31, 2026

Merge activity

  • Mar 31, 1:11 AM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Mar 31, 1:11 AM UTC: @NathanFlurry merged this pull request with Graphite.

@NathanFlurry NathanFlurry merged commit 7b7c06e into main Mar 31, 2026
16 of 21 checks passed
@NathanFlurry NathanFlurry deleted the inspector-sqlite-shell branch March 31, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants