sixty

Lovable で作ったアプリ向け

サーバーはありません。それでもバグはあります。

あなたのアプリがやることは、普通の監視ツールには見えない2か所で起きています。ブラウザと Supabase です。Sixty はその両方をページの内側から測ります — クエリと、それが返すもの、リアルタイムのチャンネル、人が何を押してそれが効いたかどうか、そしてそのどれもがどれだけ速かったか。そして公開ごとに1つ前と比較し、見つけたものをコードを書いたエージェントに渡します。

何を測るか

ページが出すすべての Supabase クエリ
1回の操作あたり何本走り、それぞれ何行返し、レスポンスがどれだけ大きく、どれだけ時間がかかったか。「遅くなった」の正体は、ほぼ必ずここにあります。
ページの速さを、ルートごとに
読み込んだ全員にわたる実際の p75 での LCP、INP、CLS、TTFB — 1台のマシンによるラボの点数ではありません。国別に分解します。あなたのところでは問題ないページが、遅延の大きい場所では使い物にならないことがあるからです。
人が何を押し、それが効いたかどうか
何も起きなかったクリック、同じ操作部品への繰り返しのクリック、そしてページを何百回も描き直す操作。
認証、ストレージ、Edge Function、リアルタイム
Supabase のアプリは1つではなく4つのサービスです。エージェントは各エンドポイントが何をするかを理解していて、いくらかかったかだけを見ているのではありません — だから拒否されたサインイン、存在しないカラム、却下された書き込み、購読に失敗したチャンネルが、それぞれ匿名の「失敗したリクエスト」ではなく、そのものとして届きます。
動く前のコード
ハンドラのないボタン、依存関係のせいで永遠に回り続けることが確定している副作用、後片付けされない購読。これらは実行時に何も出しません — 一度も取り付けられなかったハンドラは測れません — ので、ビルド時にソースから読み取り、そのファイルが実際に受けているトラフィック順に並べます。

何を見つけるか

どれも1つ前のリリースと比較されます。だから検出結果は、日付ではなく変更を名指しします。

すべてを返し始めたクエリ

書き直しの途中でフィルタが消え、30行を返していたクエリが30,000行を返す。あなたのプロジェクトの数行が相手なら一瞬なので、本物のデータが後ろにつくまで何もおかしく見えません。

レンダーの内側で生まれた N+1

取得処理がリストの中に移り、1本のクエリが40本になる。どれも速いので、1つのリクエストとして見ればおかしなところはありません — おかしいのは回数だけで、その回数こそ、他に導入できるどんなものも見せてくれないものです。

ユーザーが他人の行を見ている — あるいは誰の行も見ていない

行レベルセキュリティのポリシーが抜けていたり間違っていたりしても、Postgres はエラーを出しません。あるはずのない行を返すか、1行も返さないかで、ページは空のまま描画されます。Sixty はその両側を見ます。ポリシーの下でデータベースが拒否した文と、許可されて、以前は中身があったのに空で返ってくる文の両方です。

いつまでも読み込みが終わらないページ

表示から12秒たってもまだ画面にある読み込み表示 — たいていは catch の中で失敗したリクエストか、true にしたまま戻されなかった読み込みフラグです。

コードの下でずれていくスキーマ

コードが期待していて、データベースにはもうないカラム・テーブル・リレーション。実行時に、ブラウザで、そのページを開いた人のところで失敗します。

誰も報告しなかったエラー

コードのどこから出たかでまとめます。何もクラッシュさせない React の失敗も含めて — エラーバウンダリが捕まえ、空の領域を出し、記録します。

同じリアルタイムチャンネルを、三重に購読

副作用が購読を開き、一度も後片付けしないので、レンダーのたびに1つ増えます。何も throw されず、何も遅くなりません。ただ、新しいメッセージが3回ずつ表示されるのを本人が見ている間に、接続数は機能全体を止める上限へ向かって増えていきます。

測ってみると何もしていないボタン

押されたのに、リクエストは出ず、データも変わらず、ページも動かない — たいていは一度も配線されなかったハンドラです。そのあとに続く連打も一緒に。最初のクリックが何も起きなかったように見えたとき、人はそうするからです。

1回のクリックでページを何百回も描き直す

レンダーのループです。エラーにもならず、リクエストも失敗しません。ただ、その電話を持っている人のバッテリーを空にします。

拒否されているセッション

あるエンドポイントへの呼び出しのうち一定の割合が 401 か 403 で返る、あるいは必要な場所でトークンが期限切れになっている。サーバーのないアプリでは、拒否が目に見える場所はここだけです。

フィードに何が届くか

読むためのダッシュボードではありません。検出1件につきカード1枚。数値と、それを変えたリリースと、コーディングエージェントが修正を書くのに必要な証拠が付いています。

再構成したものです。これらは公開済みの Supabase アプリから検出器が実際に出す不具合ですが、Node の例と違って、あなたが走らせられるスクリプトの出力ではありません。だから引用ではなく作図です。

行数select:orders
30行30,000行1000×

このクエリはテーブルの全注文を返し、ブラウザ側で絞り込んでいます。40行しかないプロジェクトに対して書かれたもので、そこでは一瞬で終わっていました。

/orders で、前々回の公開以降

何も返らないselect:invoices
12行0行

昨日は請求書を返していた同じクエリが、今日は1件も返しません。何も失敗していません。あるポリシーがすべてを絞り込み始め、ページは空のまま描画されています。

/invoices で、前回の公開以降

導入方法

これは手作業では入れません。Lovable のチャットにプロンプトを1つ貼れば、エージェントが組み込まれます — Vite プラグイン、init の呼び出し、そしてテレメトリを転送するルート。おかげでキーがバンドルに乗ることは一度もありません。ログインすれば、そのプロンプトはあなたのキーを入れた状態で書かれます。

npm install @sixty-sh/supabase

// vite.config.ts
import drift from '@sixty-sh/supabase/vite'
export default defineConfig({ plugins: [react(), drift()] })

// src/integrations/supabase/client.ts — the file every Lovable app has
import { withSixty } from '@sixty-sh/supabase'

export const supabase = withSixty(createClient(URL, ANON_KEY), {
  key: 'sixty_pk_…',
  service: 'my-app',
  endpoint: 'https://ingest.sixty.sh',
})

そのあと2回公開してください。比較は時計ではなく公開に紐づいているので、最初の検出は次の公開のあとに届きます — バージョンが1つだけでは比べる相手がありません。

できないこと

  • 公開ビルドからの検出は、クエリとルートを指します。ソースの行は指しません。本番のバンドルは圧縮されていて、それを元に戻すソースマップはまだ読んでいないので、「このファイルの40行目」ではなく「このページのこのクエリ」になります。
  • Lovable のプレビューでは足りません。指紋付きのアセットを持たない開発サーバーで動くため、比較を固定できるバージョンが存在しません — 比較には本物の公開が2回必要です。
  • ここではサーバーを見張っていません。あなたにサーバーがないからです。あとから自前の Edge Function を足す場合、それが見えるようになるには Node のエージェントが要ります。

これが理由でここに来たのなら

最後の変更が何をしたのか、確かめてください。

試してみる

別のものを作っていますか? Node のサービス、React Native のアプリ のページもあります — 見えるものが違うので、内容も違います。

Lovable と Supabase のアプリ向けパフォーマンス監視 — Sixty