@selvajs/supabase-provider 0.19.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -260
- package/dist/cli/sync-migrations.js +7 -3
- package/dist/cli/sync-migrations.js.map +1 -1
- package/dist/data/SupabaseDataProvider.js +2 -2
- package/dist/data/SupabaseDataProvider.js.map +1 -1
- package/dist/data/migrationHead.d.ts +1 -1
- package/dist/data/migrationHead.js +1 -1
- package/package.json +5 -5
- package/supabase/migrations/20260821120000_list_ordering_indexes.sql +24 -0
package/README.md
CHANGED
|
@@ -1,270 +1,49 @@
|
|
|
1
1
|
# @selvajs/supabase-provider
|
|
2
2
|
|
|
3
|
-
Supabase (Auth + Postgres + Storage) implementation of the `@selvajs/platform` interfaces
|
|
3
|
+
Supabase (Auth + Postgres + Storage) implementation of the `@selvajs/platform` interfaces — `IAuthProvider`, `IDataProvider`, `IStorageProvider`.
|
|
4
4
|
|
|
5
|
-
Runs against **either** a managed Supabase project
|
|
5
|
+
Runs against **either** a managed Supabase project or a local Supabase CLI stack in Docker — same code, different `SUPABASE_URL` and keys.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Table of contents
|
|
10
|
-
|
|
11
|
-
- [Quick start](#quick-start)
|
|
12
|
-
- [Environment variables](#environment-variables)
|
|
13
|
-
- [Switching to the Supabase provider](#switching-to-the-supabase-provider)
|
|
14
|
-
- [Applying the schema](#applying-the-schema)
|
|
15
|
-
- [Development — local Supabase stack](#development--local-supabase-stack)
|
|
16
|
-
- [Production — hosted Supabase project](#production--hosted-supabase-project)
|
|
17
|
-
- [Running the conformance tests](#running-the-conformance-tests)
|
|
18
|
-
- [Architecture notes](#architecture-notes)
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Quick start
|
|
23
|
-
|
|
24
|
-
1. Install the package in the workspace (already listed in `pnpm-workspace.yaml`).
|
|
25
|
-
2. Provision Supabase — either local (`npx supabase start`) or hosted (supabase.com).
|
|
26
|
-
3. Apply the migrations from `packages/providers/supabase/supabase/migrations/` (the local CLI does this automatically on `db reset`).
|
|
27
|
-
4. Copy the three env vars into your selva app `.env` (see below).
|
|
28
|
-
5. Set `SELVA_AUTH_PROVIDER=supabase`, `SELVA_DATA_PROVIDER=supabase`, `SELVA_STORAGE_PROVIDER=supabase` in the same `.env`.
|
|
29
|
-
6. `pnpm dev` — the selva app now reads and writes from Supabase.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Environment variables
|
|
34
|
-
|
|
35
|
-
All env vars are documented in [`packages/selva/.env.example`](../../selva/.env.example) — copy that file to `.env` and edit it. This provider needs `SUPABASE_URL`, `SUPABASE_ANON_KEY`, and `SUPABASE_SERVICE_ROLE_KEY`; the optional bucket / private-URL / signup overrides are also listed there.
|
|
36
|
-
|
|
37
|
-
Both `SELVA_*` keys are still required, but for narrower jobs than under the local provider:
|
|
38
|
-
|
|
39
|
-
- `SELVA_HMAC_KEY` — **not** used for sessions here (those are Supabase JWTs), but still hashes share-link and invite tokens before they're stored.
|
|
40
|
-
- `SELVA_AT_REST_KEY` — encrypts `compute_servers.api_key` before it reaches the database. `SupabaseDataProvider` refuses to construct without it.
|
|
41
|
-
|
|
42
|
-
Rhino.Compute URL + API key are configured in `/admin/compute` and persisted in the `compute_config` table — unchanged by the provider choice.
|
|
43
|
-
|
|
44
|
-
### Finding the keys
|
|
45
|
-
|
|
46
|
-
**Local (`npx supabase status`):**
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
╭──────────────────────────────────────────────────╮
|
|
50
|
-
│ 🔑 Authentication Keys │
|
|
51
|
-
├─────────────┬────────────────────────────────────┤
|
|
52
|
-
│ Publishable │ sb_publishable_... │ ← SUPABASE_ANON_KEY
|
|
53
|
-
│ Secret │ sb_secret_... │ ← SUPABASE_SERVICE_ROLE_KEY
|
|
54
|
-
╰─────────────┴────────────────────────────────────╯
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
**Hosted:** Supabase Dashboard → **Project Settings** → **API**.
|
|
58
|
-
|
|
59
|
-
> **The S3-compat keys under the "Storage (S3)" section are NOT what you want.** Those are for external S3 tooling (rclone, etc.), not for `@supabase/supabase-js`.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Switching to the Supabase provider
|
|
64
|
-
|
|
65
|
-
The selva app picks a provider per interface from env vars, via `createSelvaProviders` (`packages/server/src/providers/create-selva-providers.ts`) and the registry in `packages/selva/src/lib/server/providers.server.ts`:
|
|
66
|
-
|
|
67
|
-
```
|
|
68
|
-
SELVA_AUTH_PROVIDER=supabase # local | supabase | header (default: local)
|
|
69
|
-
SELVA_DATA_PROVIDER=supabase # local | supabase (default: local)
|
|
70
|
-
SELVA_STORAGE_PROVIDER=supabase # local | supabase (default: local)
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
To switch back to the local provider, remove the three vars (or set them to `local`) and restart.
|
|
74
|
-
|
|
75
|
-
For a provider not bundled in the app, point `SELVA_CONFIG_PATH` at an external `selva.config.js`, loaded dynamically at boot in place of the env-driven wiring. It must be an actual `.js` file — there's no TS compiler at runtime.
|
|
76
|
-
|
|
77
|
-
---
|
|
78
|
-
|
|
79
|
-
## Applying the schema
|
|
80
|
-
|
|
81
|
-
> **All Selva tables live in a dedicated `selva` schema, not `public`.** This keeps the `public` namespace entirely free for a consuming app's own tables — `selva.projects` and your `public.projects` can coexist. The initial migration creates the schema, grants the standard Supabase roles access, and tells PostgREST to expose it (see [Exposing the `selva` schema to PostgREST](#exposing-the-selva-schema-to-postgrest) for what that means on a hosted project).
|
|
82
|
-
|
|
83
|
-
The `supabase/migrations/` directory holds timestamp-prefixed files, applied in filename order:
|
|
84
|
-
|
|
85
|
-
| File | What it installs |
|
|
86
|
-
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
87
|
-
| `20260425155514_selva_initial.sql` | The `selva` schema itself, plus `user_profiles` (+ auto-seed trigger), `orgs` / `org_members` / `projects` / `project_members` (+ RLS helpers), `definitions` / `definition_versions` (+ deletion-protection FKs and the atomic `increment_run_count` RPC), `invites` (+ `get_invite_by_token` RPC), `compute_servers` (+ per-org/instance defaults), `share_links` (+ `try_increment_share_link_solve_count` RPC), and the `selva-public` / `selva-private` storage policies. |
|
|
88
|
-
| `20260529202220_selva_definition_version_schema.sql` | Adds `definition_versions.schema` + `schema_extracted_at` (cached compute-extracted UI schema) and the `change_note` column. All `add column if not exists`, safe to re-run. |
|
|
89
|
-
|
|
90
|
-
These two are only the foundation — `supabase/migrations/` ships further timestamp-prefixed files on top (solve metrics, org assets, definition status/audit, erasure functions, …). Filename order in that directory is authoritative, not this table.
|
|
91
|
-
|
|
92
|
-
Future schema changes go in new timestamp-prefixed files: `<UTCtimestamp>_selva_<name>.sql` (e.g. `date -u +%Y%m%d%H%M%S` for the prefix). The `_selva_` infix and timestamp prefix let a consuming app's own migrations interleave by time without ever colliding with Selva's — see [Consuming migrations in an external app](#consuming-migrations-in-an-external-app).
|
|
93
|
-
|
|
94
|
-
The `supabase/seed.sql` file creates the two storage buckets (`selva-public`, `selva-private`).
|
|
95
|
-
|
|
96
|
-
**Local:** `npx supabase db reset` applies everything on a fresh DB.
|
|
97
|
-
|
|
98
|
-
**Hosted:** link the CLI to your project, then push:
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
cd packages/providers/supabase
|
|
102
|
-
|
|
103
|
-
# One-time link. Reads the project-ref from Supabase Dashboard → Project Settings → General.
|
|
104
|
-
npx supabase link --project-ref <your-project-ref>
|
|
105
|
-
|
|
106
|
-
# Review what will be applied.
|
|
107
|
-
npx supabase db diff
|
|
108
|
-
|
|
109
|
-
# Apply migrations + seed.
|
|
110
|
-
npx supabase db push
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
You can also copy each `.sql` file into Supabase Dashboard → **SQL Editor** and run them in order, but prefer the CLI: it tracks which migrations already ran, so re-running it is safe.
|
|
114
|
-
|
|
115
|
-
### Exposing the `selva` schema to PostgREST
|
|
116
|
-
|
|
117
|
-
Because the tables live in `selva` rather than `public`, PostgREST (the REST API supabase-js talks to) must be told to expose that schema, or every query fails with `PGRST106 Invalid schema` / `PGRST205 schema cache`.
|
|
118
|
-
|
|
119
|
-
The initial migration handles this for you: it runs `alter role authenticator set pgrst.db_schemas = '…, selva'` and notifies PostgREST to reload. **You do not list `selva` in `config.toml`'s `[api] schemas`** — PostgREST reads that file at stack boot, before migrations run, so naming a not-yet-created schema there aborts `supabase start`. The migration is the right place because it runs after the schema exists.
|
|
120
|
-
|
|
121
|
-
- **Local dev:** nothing to do — `db reset` / `db push` runs the migration, which exposes `selva` automatically.
|
|
122
|
-
- **Hosted:** the migration still does the work on `db push`. As a belt-and-braces check (or if you applied SQL by hand in the Editor), confirm the schema is exposed under Dashboard → **Project Settings** → **API** → **Exposed schemas** — `selva` should be listed alongside `public` and `graphql_public`. Add it there if it's missing.
|
|
123
|
-
|
|
124
|
-
### Upgrading an existing database
|
|
125
|
-
|
|
126
|
-
`supabase db push` keys on the version prefix recorded in the project's `supabase_migrations.schema_migrations` table. It applies any file whose prefix isn't recorded yet, in filename order, and skips the rest — so upgrading is always just **re-sync (external app) or re-pull (this repo), then `db push`**. Gaps and out-of-order prefixes are fine; only the lexical filename order matters.
|
|
127
|
-
|
|
128
|
-
No backfill step is required for the schema-caching migration: existing definition versions keep working (the app falls back to fetching their schema from Rhino.Compute), and each version's `schema` column is populated lazily the first time it's solved.
|
|
129
|
-
|
|
130
|
-
#### Migrated from the old `000N_` filenames?
|
|
131
|
-
|
|
132
|
-
Earlier releases shipped `0001_initial.sql` / `0002_definition_version_schema.sql`, since renamed to `20260425155514_selva_initial.sql` / `20260529202220_selva_definition_version_schema.sql` (contents unchanged, only the names, so the files slot into a consuming app's migration timeline without collisions).
|
|
133
|
-
|
|
134
|
-
To a database that already applied the `000N_` versions, the renamed files look like brand-new migrations. Both are idempotent (`create or replace`, `add column if not exists`), so a stray re-run is a harmless no-op, but you'd end up with duplicate history rows. To reconcile:
|
|
135
|
-
|
|
136
|
-
- **Local dev stack:** just `npx supabase db reset` — applies the renamed files from scratch.
|
|
137
|
-
- **Hosted project with the old versions applied:** tell the CLI the new versions are already present so it doesn't re-run them:
|
|
138
|
-
```bash
|
|
139
|
-
npx supabase migration repair --status applied 20260425155514 20260529202220
|
|
140
|
-
```
|
|
141
|
-
(Equivalent to inserting those version strings into `supabase_migrations.schema_migrations`.) Then future `db push` runs treat them as done.
|
|
142
|
-
|
|
143
|
-
### Consuming migrations in an external app
|
|
144
|
-
|
|
145
|
-
When you install `@selvajs/supabase-provider` in your own app (rather than running it from this repo), the migration SQL ships inside the package. The clean way to apply it is to **sync the files into your app's own `supabase/migrations/` directory** and let the Supabase CLI drive them alongside your app's own migrations:
|
|
146
|
-
|
|
147
|
-
```bash
|
|
148
|
-
# From your app root, after installing/upgrading the package:
|
|
149
|
-
npx selva-supabase sync-migrations # copies into ./supabase/migrations
|
|
150
|
-
npx supabase db push # applies anything not yet recorded
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
`sync-migrations` copies each packaged migration **verbatim** (same timestamped filename, same bytes). That's deliberate: the filename is the migration's identity in the history table, so copying without renaming keeps every developer's and CI's history identical.
|
|
154
|
-
|
|
155
|
-
- **`--dir <path>`** — target a non-default migrations directory.
|
|
156
|
-
- **`--force`** — overwrite a same-named file whose content differs (otherwise it's reported as a conflict and the command exits non-zero, leaving your copy untouched).
|
|
157
|
-
- Re-running is idempotent: unchanged files are skipped.
|
|
158
|
-
|
|
159
|
-
**Upgrade flow** (running app, you bump the provider and it carries a new migration): `pnpm up @selvajs/supabase-provider`, then `npx selva-supabase sync-migrations` copies in the new file (later timestamp → sorts last), then `npx supabase db push` applies only that file and skips the ones already recorded. No renumbering, even if your app added its own migrations in between.
|
|
160
|
-
|
|
161
|
-
**Notes specific to consuming in an external app:**
|
|
162
|
-
|
|
163
|
-
- **Your tables stay in `public`; Selva's are in `selva`.** Because the engine owns a separate schema, there are no name clashes — your app can have its own `public.projects`, `public.orgs`, `public.definitions`, etc. without touching Selva's. Nothing to check before the first `db push`.
|
|
164
|
-
- **Referencing engine objects from your own migrations.** When your app's tables need to point at the engine (FKs, RLS helpers), qualify them with the `selva` schema: `references selva.orgs(id)`, `selva.is_org_member(org_id)`, `selva.is_instance_admin()`, `selva.set_updated_at()`. The qualified names are self-documenting and won't accidentally resolve to a same-named object in your `public`.
|
|
165
|
-
- **Expose `selva` in your app's PostgREST too.** If your app uses its own Supabase CLI project / `config.toml`, the synced initial migration exposes `selva` via `alter role authenticator …` when it runs — same mechanism as above. On a hosted project, verify it under Project Settings → API → Exposed schemas after the first push.
|
|
166
|
-
- **Storage buckets aren't created by `db push` on a hosted project.** `seed.sql` (which creates `selva-public` / `selva-private`) is dev-only. On a hosted project, run it once by hand in the SQL Editor after the first push — same as the [Production](#production--hosted-supabase-project) flow.
|
|
7
|
+
**Operator setup — provisioning, env vars, migrations, production checklist:** [docs/self-hosting/providers/supabase.md](../../../docs/self-hosting/providers/supabase.md).
|
|
167
8
|
|
|
168
9
|
---
|
|
169
10
|
|
|
170
|
-
##
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
SUPABASE_URL=http://127.0.0.1:54321
|
|
186
|
-
SUPABASE_ANON_KEY=sb_publishable_...
|
|
187
|
-
SUPABASE_SERVICE_ROLE_KEY=sb_secret_...
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { defineConfig } from '@selvajs/platform';
|
|
15
|
+
import {
|
|
16
|
+
SupabaseAuthProvider,
|
|
17
|
+
SupabaseDataProvider,
|
|
18
|
+
SupabaseStorageProvider
|
|
19
|
+
} from '@selvajs/supabase-provider';
|
|
20
|
+
|
|
21
|
+
export default defineConfig((env) => ({
|
|
22
|
+
auth: SupabaseAuthProvider.fromEnv(env),
|
|
23
|
+
data: SupabaseDataProvider.fromEnv(env),
|
|
24
|
+
storage: SupabaseStorageProvider.fromEnv(env)
|
|
25
|
+
}));
|
|
188
26
|
```
|
|
189
27
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
Set the three `SELVA_*_PROVIDER=supabase` env vars (above), then:
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
pnpm dev
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
**Useful commands:**
|
|
199
|
-
|
|
200
|
-
```bash
|
|
201
|
-
npx supabase status # show URL + keys again
|
|
202
|
-
npx supabase db reset # wipe DB + re-run migrations + seed
|
|
203
|
-
npx supabase stop # stop containers (keeps volumes)
|
|
204
|
-
npx supabase stop --no-backup # stop + wipe volumes
|
|
205
|
-
```
|
|
28
|
+
The Selva app already bundles this wiring — it picks a provider per interface from `SELVA_AUTH_PROVIDER` / `SELVA_DATA_PROVIDER` / `SELVA_STORAGE_PROVIDER` via `createSelvaProviders` ([create-selva-providers.ts](../../server/src/providers/create-selva-providers.ts)) over the registry in [providers.server.ts](../../selva/src/lib/server/providers.server.ts). For a provider not bundled in the app, point `SELVA_CONFIG_PATH` at an external `selva.config.js` — an actual `.js` file, since there's no TS compiler at runtime.
|
|
206
29
|
|
|
207
|
-
|
|
30
|
+
`SupabaseDataProvider.fromEnv` refuses to construct without `SELVA_AT_REST_KEY`; it encrypts `compute_servers.api_key` before it reaches the database.
|
|
208
31
|
|
|
209
32
|
---
|
|
210
33
|
|
|
211
|
-
##
|
|
212
|
-
|
|
213
|
-
1. Create a project on [supabase.com](https://supabase.com). Choose a region close to your selva app deployment.
|
|
214
|
-
2. Dashboard → **Project Settings** → **API**. Copy the **Project URL**, **Publishable** key, and **Secret** key.
|
|
215
|
-
3. Apply migrations via the CLI:
|
|
216
|
-
```bash
|
|
217
|
-
cd packages/providers/supabase
|
|
218
|
-
npx supabase link --project-ref <your-project-ref>
|
|
219
|
-
npx supabase db push
|
|
220
|
-
```
|
|
221
|
-
4. Create the two storage buckets by running `supabase/seed.sql` in the Dashboard → **SQL Editor** (the CLI's `db push` doesn't run the seed on a hosted project — it's dev-only).
|
|
222
|
-
5. Set the env vars on your selva app host (Vercel, Fly.io, Docker, etc.):
|
|
223
|
-
```
|
|
224
|
-
SUPABASE_URL=https://<project-ref>.supabase.co
|
|
225
|
-
SUPABASE_ANON_KEY=sb_publishable_...
|
|
226
|
-
SUPABASE_SERVICE_ROLE_KEY=sb_secret_...
|
|
227
|
-
```
|
|
228
|
-
After deploying, register your Rhino.Compute server URL (+ optional API key) via `/admin/compute`.
|
|
229
|
-
6. Deploy the selva app with the three `SELVA_*_PROVIDER=supabase` env vars set.
|
|
230
|
-
7. Bootstrap the first user:
|
|
231
|
-
- Open `/setup` once — creates the first admin with `instance_admin`.
|
|
232
|
-
- Or manually: Dashboard → **Authentication** → **Add user**, then in the SQL Editor: `UPDATE selva.user_profiles SET platform_permissions = ARRAY['instance_admin']::text[] WHERE user_id = '<uuid>';`
|
|
233
|
-
|
|
234
|
-
### Operational checklist
|
|
235
|
-
|
|
236
|
-
- **Backups.** Supabase handles daily backups on paid plans; verify restore works before relying on it.
|
|
237
|
-
- **Rotate keys** if the Secret key is ever logged or committed. Dashboard → **Project Settings** → **API** → **Reset service_role key**.
|
|
238
|
-
- **Row-level security is on for every table.** Disabling it on any table silently breaks the tenant boundary.
|
|
239
|
-
- **Email sender.** Dashboard → **Authentication** → **Email templates** — configure an SMTP provider before going live, otherwise password-reset emails route through Supabase's rate-limited dev sender.
|
|
34
|
+
## Migrations
|
|
240
35
|
|
|
241
|
-
|
|
36
|
+
`supabase/migrations/` holds timestamp-prefixed files applied in filename order. **That directory is authoritative** — don't expect a list here to stay current.
|
|
242
37
|
|
|
243
|
-
|
|
38
|
+
`20260425155514_selva_initial.sql` installs the foundation: the `selva` schema itself, `user_profiles` (+ the `handle_new_auth_user` auto-seed trigger), `orgs` / `org_members` / `projects` / `project_members` (+ RLS helpers), `definitions` / `definition_versions` (+ deletion-protection FKs and the atomic `increment_run_count` RPC), `invites` (+ `get_invite_by_token`), `compute_servers`, `share_links` (+ `try_increment_share_link_solve_count`), and the `selva-public` / `selva-private` storage policies. Everything after it layers on top — cached version schemas, solve metrics, org assets, definition status/audit, erasure functions.
|
|
244
39
|
|
|
245
|
-
|
|
40
|
+
New schema changes go in new files named `<UTCtimestamp>_selva_<name>.sql` (`date -u +%Y%m%d%H%M%S` for the prefix). The `_selva_` infix and timestamp prefix let a consuming app's own migrations interleave by time without ever colliding with Selva's.
|
|
246
41
|
|
|
247
|
-
|
|
248
|
-
cd packages/providers/supabase
|
|
42
|
+
`supabase/seed.sql` creates the two storage buckets. It is dev-only — the CLI does not run it on a hosted project.
|
|
249
43
|
|
|
250
|
-
|
|
251
|
-
npx supabase start
|
|
44
|
+
**No backfill is required for the schema-caching migration.** Existing definition versions keep working (the app falls back to fetching their schema from Rhino.Compute), and each version's `schema` column fills in lazily the first time it's solved.
|
|
252
45
|
|
|
253
|
-
|
|
254
|
-
# (This is separate from your app's .env — it's gitignored and used only by tests.)
|
|
255
|
-
cat > .env.test <<EOF
|
|
256
|
-
SUPABASE_URL=http://127.0.0.1:54321
|
|
257
|
-
SUPABASE_ANON_KEY=<publishable key>
|
|
258
|
-
SUPABASE_SERVICE_ROLE_KEY=<secret key>
|
|
259
|
-
EOF
|
|
260
|
-
|
|
261
|
-
# 3. Run.
|
|
262
|
-
pnpm test
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
When `.env.test` is missing the suite skips with a single explanatory test.
|
|
266
|
-
|
|
267
|
-
The suite wipes every table and every auth user between tests — **do not point it at a production project.**
|
|
46
|
+
The packaged migrations reach a consuming app through the `selva-supabase` bin ([src/cli/sync-migrations.ts](src/cli/sync-migrations.ts)), which copies them verbatim into the app's own `supabase/migrations/`. See the [operator page](../../../docs/self-hosting/providers/supabase.md#3-apply-the-schema) for the flow.
|
|
268
47
|
|
|
269
48
|
---
|
|
270
49
|
|
|
@@ -275,19 +54,19 @@ The suite wipes every table and every auth user between tests — **do not point
|
|
|
275
54
|
Two buckets:
|
|
276
55
|
|
|
277
56
|
- **`selva-public`** (public) — covers, archives. `getPublicUrl` returns the direct CDN URL.
|
|
278
|
-
- **`selva-private`** (private) — `.gh` / `.ghx` files. `getPublicUrl` returns `/api/files/{path}
|
|
57
|
+
- **`selva-private`** (private) — `.gh` / `.ghx` files. `getPublicUrl` returns `/api/files/{path}`, which the selva app's route handler proxies after an auth check.
|
|
279
58
|
|
|
280
59
|
Images are transcoded to WebP (1200px cap, quality 85) via the shared `transcodeImageIfNeeded` helper from `@selvajs/platform/storage`. Same bytes out of both providers.
|
|
281
60
|
|
|
282
61
|
### Auth
|
|
283
62
|
|
|
284
|
-
`SupabaseAuthProvider.verifyLogin` wraps `supabase.auth.signInWithPassword`, returning the JWT directly as `sessionToken`. `verifyToken` calls `supabase.auth.getUser(token)` — GoTrue validates the signature for us. Platform permissions merge in from `user_profiles.platform_permissions
|
|
63
|
+
`SupabaseAuthProvider.verifyLogin` wraps `supabase.auth.signInWithPassword`, returning the JWT directly as `sessionToken`. `verifyToken` calls `supabase.auth.getUser(token)` — GoTrue validates the signature for us. Platform permissions merge in from `user_profiles.platform_permissions`.
|
|
285
64
|
|
|
286
65
|
MFA methods on `IPasswordAuth` are currently undefined — MFA is deferred. If a user enrolls a factor via the Supabase dashboard, `signInWithPassword` returns an AAL1 session and routes that gate on AAL2 would fail; no route does today.
|
|
287
66
|
|
|
288
67
|
### Data + RLS
|
|
289
68
|
|
|
290
|
-
Every client is constructed with `db: { schema: 'selva' }`, so a bare `client.from('orgs')` resolves to `selva.orgs`. Every store goes through `ClientBundle.forRequest(ctx)
|
|
69
|
+
Every client is constructed with `db: { schema: 'selva' }`, so a bare `client.from('orgs')` resolves to `selva.orgs`. Every store goes through `ClientBundle.forRequest(ctx)` ([src/data/client.ts](src/data/client.ts)):
|
|
291
70
|
|
|
292
71
|
- `ctx.system` → service-role client (bypasses RLS)
|
|
293
72
|
- `ctx.adapterContext.sessionToken` → anon client with `Authorization: Bearer <jwt>` (RLS enforces per-user visibility)
|
|
@@ -302,18 +81,31 @@ Helper SQL functions (`selva.is_instance_admin`, `selva.is_org_member`, `selva.v
|
|
|
302
81
|
### Atomic improvements over the local provider
|
|
303
82
|
|
|
304
83
|
- **`incrementRunCount`** uses a SQL function (`UPDATE … SET run_count = run_count + 1`) — atomic. The local provider does read-modify-write and can lose bumps under concurrent solves.
|
|
84
|
+
- **`try_increment_share_link_solve_count`** enforces a share link's cap in one statement, so concurrent solves can't overshoot it.
|
|
85
|
+
|
|
86
|
+
### Erasure
|
|
87
|
+
|
|
88
|
+
`SupabaseDataProvider.onUserDeleted(ctx, userId, { email })` scrubs what FK cascade doesn't reach: deletes `audit_events` the user authored (keyed by plain-text `actor_id`), deletes `invites` addressed to their email, redacts that email from surviving `invite.created` payloads (`redact_audit_event_email`), and tombstones `solve_metrics.actor_id` so capacity aggregates survive while the person doesn't. The caller must capture the email **before** `SupabaseAuthProvider.deleteUser` runs. Credentials live in Supabase `auth.users`, but the operator is still the data controller for everything above — see [data privacy](../../../docs/self-hosting/concepts/data-privacy.md).
|
|
305
89
|
|
|
306
90
|
---
|
|
307
91
|
|
|
308
|
-
##
|
|
92
|
+
## Conformance tests
|
|
93
|
+
|
|
94
|
+
The package ships the `@selvajs/platform` conformance suites running against a **live** Supabase stack. They prove this provider implements every interface the same way the local provider does.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
cd packages/providers/supabase
|
|
98
|
+
npx supabase start
|
|
99
|
+
|
|
100
|
+
cat > .env.test <<EOF
|
|
101
|
+
SUPABASE_URL=http://127.0.0.1:54321
|
|
102
|
+
SUPABASE_ANON_KEY=<publishable key>
|
|
103
|
+
SUPABASE_SERVICE_ROLE_KEY=<secret key>
|
|
104
|
+
EOF
|
|
309
105
|
|
|
310
|
-
|
|
106
|
+
pnpm test
|
|
107
|
+
```
|
|
311
108
|
|
|
312
|
-
|
|
313
|
-
- `SupabaseStorageProvider`
|
|
314
|
-
- `SupabaseOrgStore`, `SupabaseProjectStore`, `SupabaseDefinitionStore` (atomic `incrementRunCount`), `SupabaseInviteStore`, `SupabaseShareLinkStore`, `SupabaseComputeServerStore`
|
|
315
|
-
- `SupabaseUserProfileProvider`
|
|
316
|
-
- `SupabasePlatformPermissionStore`
|
|
317
|
-
- `SupabaseDataProvider` — composition of the data stores
|
|
109
|
+
`.env.test` is gitignored and separate from your app's `.env`. `vitest.config.ts` loads it if present and probes `SUPABASE_URL` before the run; if the file is missing or the stack is unreachable it strips the three vars so every suite takes its no-credentials skip path and prints a warning.
|
|
318
110
|
|
|
319
|
-
|
|
111
|
+
The suites wipe every table and every auth user between tests — **do not point them at a production project.**
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
// copy time would make every machine/CI produce a different history.
|
|
9
9
|
//
|
|
10
10
|
// Usage:
|
|
11
|
-
// npx
|
|
11
|
+
// npx selva-supabase [--dir <path>] [--force]
|
|
12
|
+
//
|
|
13
|
+
// The bin is `selva-supabase` and this script IS the whole command — there is
|
|
14
|
+
// no `sync-migrations` subcommand to type, and parseArgs rejects one as an
|
|
15
|
+
// unknown argument.
|
|
12
16
|
//
|
|
13
17
|
// --dir <path> Target migrations dir (default: ./supabase/migrations)
|
|
14
18
|
// --force Overwrite files that exist with differing content
|
|
@@ -46,10 +50,10 @@ function parseArgs(argv) {
|
|
|
46
50
|
}
|
|
47
51
|
function printHelp() {
|
|
48
52
|
console.log([
|
|
49
|
-
'
|
|
53
|
+
'selva-supabase — copy @selvajs/supabase-provider migrations into your app.',
|
|
50
54
|
'',
|
|
51
55
|
'Usage:',
|
|
52
|
-
' npx
|
|
56
|
+
' npx selva-supabase [--dir <path>] [--force]',
|
|
53
57
|
'',
|
|
54
58
|
' --dir <path> Target migrations dir (default: ./supabase/migrations)',
|
|
55
59
|
' --force Overwrite files that exist with differing content',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-migrations.js","sourceRoot":"","sources":["../../src/cli/sync-migrations.ts"],"names":[],"mappings":";AACA,+EAA+E;AAC/E,6EAA6E;AAC7E,wCAAwC;AACxC,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,qEAAqE;AACrE,EAAE;AACF,SAAS;AACT,
|
|
1
|
+
{"version":3,"file":"sync-migrations.js","sourceRoot":"","sources":["../../src/cli/sync-migrations.ts"],"names":[],"mappings":";AACA,+EAA+E;AAC/E,6EAA6E;AAC7E,wCAAwC;AACxC,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,qEAAqE;AACrE,EAAE;AACF,SAAS;AACT,gDAAgD;AAChD,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,oBAAoB;AACpB,EAAE;AACF,0EAA0E;AAC1E,qEAAqE;AACrE,+EAA+E;AAE/E,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE1F,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAErD,uDAAuD;AACvD,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;AAO9D,SAAS,SAAS,CAAC,IAAc;IAChC,IAAI,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;IACjE,IAAI,KAAK,GAAG,KAAK,CAAC;IAElB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;YACxB,IAAI,CAAC,KAAK;gBAAE,IAAI,CAAC,gCAAgC,CAAC,CAAC;YACnD,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YAC9B,KAAK,GAAG,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YAC7C,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;QAClC,CAAC;IACF,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,SAAS;IACjB,OAAO,CAAC,GAAG,CACV;QACC,4EAA4E;QAC5E,EAAE;QACF,QAAQ;QACR,+CAA+C;QAC/C,EAAE;QACF,yEAAyE;QACzE,oEAAoE;QACpE,EAAE;QACF,iDAAiD;KACjD,CAAC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,OAAe;IAC5B,OAAO,CAAC,KAAK,CAAC,UAAU,OAAO,EAAE,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,SAAS,IAAI;IACZ,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAE9D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC5B,IAAI,CAAC,2CAA2C,SAAS,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC;SAClC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;SACvC,IAAI,EAAE,CAAC;IAET,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,IAAI,CAAC,+BAA+B,SAAS,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACnC,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QACjC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;QAEpC,IAAI,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;YAClC,IAAI,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC/B,OAAO,EAAE,CAAC;gBACV,SAAS;YACV,CAAC;YACD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACZ,OAAO,CAAC,IAAI,CACX,eAAe,IAAI,2DAA2D,CAC9E,CAAC;gBACF,SAAS,EAAE,CAAC;gBACZ,SAAS;YACV,CAAC;QACF,CAAC;QAED,aAAa,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;QACnC,MAAM,EAAE,CAAC;IACV,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,YAAY,OAAO,eAAe,SAAS,mBAAmB,SAAS,EAAE,CAAC,CAAC;IAElG,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;AACzE,CAAC;AAED,IAAI,EAAE,CAAC"}
|
|
@@ -112,8 +112,8 @@ export class SupabaseDataProvider {
|
|
|
112
112
|
*/
|
|
113
113
|
async verifySchemaVersion() {
|
|
114
114
|
const expected = EXPECTED_MIGRATION_HEAD;
|
|
115
|
-
const pushHint = 'Apply the pending Supabase migrations:
|
|
116
|
-
'
|
|
115
|
+
const pushHint = 'Apply the pending Supabase migrations: run `npx selva-supabase` to copy them ' +
|
|
116
|
+
'into your project, then `npx supabase db push`.';
|
|
117
117
|
try {
|
|
118
118
|
const { data, error } = await this.clients.serviceClient.rpc('migration_head');
|
|
119
119
|
if (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SupabaseDataProvider.js","sourceRoot":"","sources":["../../src/data/SupabaseDataProvider.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SupabaseDataProvider.js","sourceRoot":"","sources":["../../src/data/SupabaseDataProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* whenever a migration is added — `migration-head.test.ts` asserts it
|
|
4
4
|
* matches the migrations directory, so CI catches drift.
|
|
5
5
|
*/
|
|
6
|
-
export declare const EXPECTED_MIGRATION_HEAD = "
|
|
6
|
+
export declare const EXPECTED_MIGRATION_HEAD = "20260821120000";
|
|
7
7
|
//# sourceMappingURL=migrationHead.d.ts.map
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
* whenever a migration is added — `migration-head.test.ts` asserts it
|
|
4
4
|
* matches the migrations directory, so CI catches drift.
|
|
5
5
|
*/
|
|
6
|
-
export const EXPECTED_MIGRATION_HEAD = '
|
|
6
|
+
export const EXPECTED_MIGRATION_HEAD = '20260821120000';
|
|
7
7
|
//# sourceMappingURL=migrationHead.js.map
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/supabase-provider",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "Supabase (Auth + Postgres + Storage) implementations of @selvajs/platform interfaces",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "VektorNode",
|
|
7
|
-
"homepage": "https://
|
|
7
|
+
"homepage": "https://selva.dev",
|
|
8
8
|
"bugs": "https://github.com/VektorNode/selva/issues",
|
|
9
9
|
"keywords": [
|
|
10
10
|
"selva",
|
|
@@ -72,11 +72,11 @@
|
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@supabase/supabase-js": "^2.112.2",
|
|
74
74
|
"sharp": "^0.35.3",
|
|
75
|
-
"@selvajs/platform": "^0.19.
|
|
75
|
+
"@selvajs/platform": "^0.19.1"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
|
-
"@types/node": "^
|
|
79
|
-
"supabase": "^2.
|
|
78
|
+
"@types/node": "^26.2.0",
|
|
79
|
+
"supabase": "^2.114.0",
|
|
80
80
|
"typescript": "~6.0.3",
|
|
81
81
|
"vitest": "^4.1.10",
|
|
82
82
|
"@selvajs/config": "0.0.4"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- Two index gaps that only show up once the tables grow.
|
|
2
|
+
--
|
|
3
|
+
-- 1. Definition lists default to `order by created_at` under a `project_id`
|
|
4
|
+
-- filter. `idx_definitions_project` covers the filter but not the sort, so
|
|
5
|
+
-- every project page sorts its whole slice in memory.
|
|
6
|
+
-- 2. Audit pagination is keyset on `(occurred_at desc, id desc)` — see
|
|
7
|
+
-- SupabaseAuditQuery — but the existing indexes stop at `occurred_at`, so
|
|
8
|
+
-- the tie-breaker is a sort, not an index scan.
|
|
9
|
+
|
|
10
|
+
create index if not exists idx_definitions_project_created
|
|
11
|
+
on selva.definitions (project_id, created_at desc)
|
|
12
|
+
where deleted_at is null;
|
|
13
|
+
|
|
14
|
+
drop index if exists selva.audit_events_occurred_at_idx;
|
|
15
|
+
create index if not exists audit_events_occurred_at_id_idx
|
|
16
|
+
on selva.audit_events (occurred_at desc, id desc);
|
|
17
|
+
|
|
18
|
+
drop index if exists selva.audit_events_type_occurred_at_idx;
|
|
19
|
+
create index if not exists audit_events_type_occurred_at_id_idx
|
|
20
|
+
on selva.audit_events (type, occurred_at desc, id desc);
|
|
21
|
+
|
|
22
|
+
drop index if exists selva.audit_events_actor_occurred_at_idx;
|
|
23
|
+
create index if not exists audit_events_actor_occurred_at_id_idx
|
|
24
|
+
on selva.audit_events (actor_id, occurred_at desc, id desc);
|