Stacked on #48305.
## What
PR 3 of the stacked SQL-editor query-source series (Database vs Logs).
Stacked on the PR 2 branch `charislam/log-sql-content-shape`.
Turns `SnippetWithContent` into a discriminated union on `type` and
makes all snippet writes source-aware:
- `data/content/sql-folders-query.ts`: `SnippetWithContent` is now `{
type: 'sql'; content?: SqlSnippets.Content } | { type: 'log_sql';
content?: LogSqlSnippets.Content } | { type: 'report'; content?: never
}`. `report` is kept (the content endpoints' wire type carries it) but
has no SQL content — its body is `Dashboards.Content`, loaded through
the separate `Content` union.
- `setSql` brands per type (`untrustedLogSql` vs `untrustedSql`).
- `buildUpsertPayload` persists `snippet.type` (no longer hardcoded
`'sql'`).
- `createSqlSnippetSkeletonV2({ source })` emits the matching type +
content shape with the `as any` cast removed.
- New `components/interfaces/SQLEditor/querySource.ts`:
`SqlSnippetSource` + `getSnippetSource`.
- `seedSnippet` test helper gains a `source` arg.
- New `remapWireSnippet` boundary helper in `content-remap.ts`
concentrates the single wire->domain assertion, so `content-id-query` /
`content-upsert-mutation` call sites are cast-free (no `as unknown as`).
- Collateral: query result types aligned to the union; `updateSnippet`
no longer accepts `type` (source is immutable); db-only editor read
paths narrow away `log_sql`.
## Why
Impossible-states-impossible typing: a snippet's brand follows its
content type, so logs SQL and database SQL can never cross execution
paths. No behavior change for existing database snippets.
## Testing
- \`pnpm typecheck\` — clean
- \`pnpm --filter studio run lint:ratchet\` — no new warnings
- \`pnpm test:studio\` (data/content, SQLEditor, state/sql-editor) —
passing, including new tests for \`getSnippetSource\`, source-aware
\`setSql\`, type-aware \`buildUpsertPayload\`, and both skeleton shapes.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added source-aware creation for SQL editor snippets, including
log-based SQL snippets.
* Introduced backend source mapping so log snippets are treated as
log_sql.
* **Bug Fixes**
* Improved SQL retrieval/prettification so log snippets no longer use
the wrong fallback content.
* Ensured log snippets are sanitized and preserve correct type, content,
identifiers, and statuses during save/upsert flows.
* **Tests**
* Expanded unit and integration coverage for log snippet creation,
source mapping, editing, prettification, and upsert payloads.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.
YES
## What kind of change does this PR introduce?
Bug fix
## What is the current behavior?
Fixes#48110
In the SQL editor Valtio store, `removeFavorite` guards against a
missing snippet with `if (storeSnippet.snippet)`, which reads `.snippet`
off `undefined` and throws `TypeError: Cannot read properties of
undefined (reading 'snippet')` whenever the id is not loaded in
`sqlEditorState.snippets`. Its counterpart `addFavorite` guards
correctly with `if (storeSnippet)` and no-ops on the same input.
## What is the new behavior?
`removeFavorite` now uses the same `if (storeSnippet)` guard as
`addFavorite`, so un-favoriting an id that is not in the store is a safe
no-op instead of a crash. Behavior for loaded snippets is unchanged.
Since `StateSnippet.snippet` is a required field, the old check was
always true whenever `storeSnippet` existed, so the only real world
difference between the two guards was the crash on the missing case.
I also added a small vitest file covering both methods (favorite set
plus needsSaving queued for loaded snippets, no-op for missing ids). The
missing-id test for `removeFavorite` fails with the exact TypeError
above when run against the old guard, and passes with this fix.
## Additional context
Root cause: `apps/studio/state/sql-editor/sql-editor-state.ts` line 260
(compare `removeFavorite` at lines 258 to 264 with `addFavorite` at
lines 250 to 256).
Gates run locally on top of current master (45ba40eff9): `pnpm
test:prettier`, `pnpm typecheck` (8/8 packages), `pnpm lint
--filter=studio` (0 errors), `pnpm test:studio` (only failure is
`lib/local-storage.test.ts`, which fails identically on clean master),
and `pnpm build --filter=studio`.
quick disclosure: I traced this one down and built the fix and test with
help from Claude Code, then verified everything locally myself. still a
college freshman finding my way around this codebase, so if the minimal
guard fix is not the direction you want (for example collapsing both
methods into one setFavorite), happy to rework it :)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed favorite removal so it works correctly for saved SQL snippets.
* Prevented favorite and unfavorite actions from causing errors when the
specified snippet cannot be found.
* **Tests**
* Added coverage for favoriting, unfavoriting, saving state updates, and
missing snippets.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->