@supabase/lite 0.3.1-next.1 → 0.3.1-next.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/LIMITATIONS.md ADDED
@@ -0,0 +1,49 @@
1
+ # Limitations (quick reference)
2
+
3
+ Token-efficient cheat sheet for agents and humans. Read this before authoring schema or client code. Detailed semantics, status tables, and workarounds live in [STATUS.md](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md).
4
+
5
+ Anchors below point to the corresponding STATUS.md section. If a limitation here is fixed, delete the bullet (do not strike-through). If you ship a feature change in STATUS.md, update this file in the same commit (see [AGENTS.md](https://github.com/supabase-community/lite/blob/HEAD/AGENTS.md)).
6
+
7
+ ## SQL / DDL (SQLite path)
8
+
9
+ - `DEFAULT auth.uid()` (and `auth.role()`, `auth.email()`, `auth.jwt()`) on columns → not supported. Drop the default, pass `user_id` from the client, rely on RLS `WITH CHECK`. See [Column Defaults](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#column-defaults).
10
+ - Subquery `WITH CHECK` on `INSERT` (`user_id IN (SELECT …)`, `EXISTS (…)`) → throws. Denormalise the owning column. See [RLS known limitations](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#row-level-security-rls).
11
+ - Scalar functions outside the allow-list in `DEFAULT` or `CHECK` (`trim`, `btrim`, `length`, `lower`, `upper`, …) → `Function call "<name>" not supported`. Use literals or move the check to the app layer. See [Column Defaults](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#column-defaults) and [CHECK constraint functions](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#check-constraint-functions).
12
+ - `nextval` / `currval`, `clock_timestamp`, `txid_current`, user-defined functions → not supported. See [Column Defaults](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#column-defaults).
13
+ - `DROP POLICY` / `ALTER POLICY`, `FORCE ROW LEVEL SECURITY` → not translated. See [RLS known limitations](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#row-level-security-rls).
14
+ - PL/pgSQL `DECLARE`, `IF`, `LOOP`, `RAISE`, variables → not supported in trigger bodies. See [PL/pgSQL Trigger Functions](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#plpgsql-trigger-functions).
15
+
16
+ ## supabase-js (SQLite path)
17
+
18
+ - `rpc()` → not supported. Use a regular HTTP endpoint for custom logic. See [Control & Specialized](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#control--specialized).
19
+ - Embedded-table dotted-path filters (`eq('rel.col', v)`) → not supported. Filter on a FK column or restructure the query. See [Embedded filters](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#embedded-filters).
20
+ - `contains` / `containedBy` / `overlaps` → partial. Arrays of scalars and shallow objects work; arrays of objects and nested objects do not. See [Array & JSON Filters](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#array--json-filters).
21
+ - `textSearch` (fts/plfts/phfts/wfts) → not implemented on SQLite. See [Full-Text Search](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#full-text-search).
22
+ - `regexMatch` / `regexIMatch` → not implemented on SQLite. See [Regex](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#regex).
23
+ - Range operators (`rangeGt`, …) and quantified comparisons (`eq(any)`, …) → not implemented on SQLite. See [Range Operators](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#range-operators) and [Quantified Comparison Operators](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#quantified-comparison-operators).
24
+ - `schema()` → SQLite is single-schema. See [Control & Specialized](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#control--specialized).
25
+
26
+ ## Auth (planned, not yet shipped)
27
+
28
+ - OAuth, anonymous sign-in, identity linking, admin API, MFA → planned. See [Auth API: Planned](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#-planned).
29
+
30
+ ## Runtime / dev
31
+
32
+ - `vite preview` does **not** mount the API. Plugin runs in `vite` / `vite dev` only. See [Vite plugin scope](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#vite-plugin-scope).
33
+ - Do not run `lite dev` (or `lite start`) alongside the Vite plugin — both bind the API and collide. See [When to use what](https://github.com/supabase-community/lite/blob/HEAD/README.md#when-to-use-what).
34
+
35
+ ## Postgres backends (pglite, postgres)
36
+
37
+ Most SQLite-only limitations above do not apply. `rpc()`, ranges, regex, quantified comparisons, full-text search, and native `DEFAULT auth.uid()` all work on the Postgres path. See per-section status tables in [STATUS.md](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md).
38
+
39
+ ## Anti-patterns
40
+
41
+ Common ways code goes wrong against supalite. The fix for each is the corresponding bullet above.
42
+
43
+ - Don't put `DEFAULT auth.uid()` on a column. Drop the default; pass `user_id` from the client; let RLS `WITH CHECK` enforce ownership.
44
+ - Don't call `rpc()` on the SQLite path. Run a regular HTTP endpoint, or switch the driver to `pglite` / `postgres` in `config.toml`.
45
+ - Don't use embedded dotted-path filters (`.eq('rel.col', v)`) on SQLite. Filter the FK column on the parent, or fetch matching ids first.
46
+ - Don't run `lite dev` or `lite start` next to the Vite plugin — port collision.
47
+ - Don't assume `vite preview` mounts the API. It doesn't. Use `lite start` or a real backend for non-dev environments.
48
+ - Don't write `.env` files with `VITE_SUPABASE_URL` for an in-Vite plugin app until [#27](https://github.com/supabase-community/lite/issues/27) ships — use `window.location.origin` + any non-empty anon string.
49
+ - Don't reach for `trim` / `length` / `lower` inside `CHECK` constraints on SQLite — use literal/operator comparisons.
package/PATTERNS.md ADDED
@@ -0,0 +1,119 @@
1
+ # Patterns
2
+
3
+ Canonical recipes for apps built on `@supabase/lite`. Pair with [LIMITATIONS.md](https://github.com/supabase-community/lite/blob/HEAD/LIMITATIONS.md) (what to avoid) and [STATUS.md](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md) (full reference).
4
+
5
+ This file is the authoritative source — the bundled [`supalite` skill](https://github.com/supabase-community/lite/blob/HEAD/skills/supalite/SKILL.md) points agents here, so updates land for every consumer on the next `npm install`.
6
+
7
+ ## Per-user multi-tenant ("each user sees only their own X")
8
+
9
+ Most-common shape. Works the same on SQLite, PGlite, and Postgres.
10
+
11
+ Schema:
12
+
13
+ ```sql
14
+ create table <thing> (
15
+ id uuid primary key default gen_random_uuid(),
16
+ user_id uuid not null references auth.users(id) on delete cascade,
17
+ -- domain columns
18
+ created_at timestamptz not null default now()
19
+ );
20
+
21
+ alter table <thing> enable row level security;
22
+
23
+ create policy "select own" on <thing> for select to authenticated using (auth.uid() = user_id);
24
+ create policy "insert own" on <thing> for insert to authenticated with check (auth.uid() = user_id);
25
+ create policy "update own" on <thing> for update to authenticated using (auth.uid() = user_id) with check (auth.uid() = user_id);
26
+ create policy "delete own" on <thing> for delete to authenticated using (auth.uid() = user_id);
27
+ ```
28
+
29
+ The client supplies `user_id` on insert (SQLite RLS evaluates `WITH CHECK` against supplied values; there is no `DEFAULT auth.uid()` on SQLite — see [LIMITATIONS.md](https://github.com/supabase-community/lite/blob/HEAD/LIMITATIONS.md#sql--ddl-sqlite-path)):
30
+
31
+ ```ts
32
+ const { data: { session } } = await supabase.auth.getSession();
33
+ await supabase.from("<thing>").insert({
34
+ user_id: session.user.id,
35
+ // ...
36
+ });
37
+ ```
38
+
39
+ ## Filtering an embedded resource (SQLite path)
40
+
41
+ Dotted-path filters (`.eq('rel.col', v)`) are not supported on SQLite. Two options:
42
+
43
+ ```ts
44
+ // Not supported on SQLite:
45
+ // supabase.from("trips").select("*, days(*)").eq("days.city", "Paris")
46
+
47
+ // Option A — filter on FK column after pre-resolving ids:
48
+ const { data: dayMatches } = await supabase.from("days").select("trip_id").eq("city", "Paris");
49
+ const tripIds = dayMatches.map(d => d.trip_id);
50
+ const { data } = await supabase.from("trips").select("*, days(*)").in("id", tripIds);
51
+
52
+ // Option B — flatten the query and group in JS.
53
+ ```
54
+
55
+ Native on Postgres / PGlite.
56
+
57
+ ## Custom server logic without `rpc()`
58
+
59
+ `rpc()` is not implemented on the SQLite path. Three options, in order of preference:
60
+
61
+ 1. Express the logic as a SQL view or trigger in `schemas/schema.sql`.
62
+ 2. Switch the driver to `pglite` or `postgres` in `supabase/config.toml`. `rpc()` works there.
63
+ 3. Run a regular server endpoint (Hono / Express / Next route handler / Vite middleware) and call it directly from the client.
64
+
65
+ ## Vite + supalite cold start
66
+
67
+ The canonical Vite recipe:
68
+
69
+ 1. `bun add @supabase/lite @supabase/supabase-js`
70
+ 2. `vite.config.ts`:
71
+ ```ts
72
+ import { defineConfig } from "vite";
73
+ import { supalite } from "@supabase/lite/vite";
74
+ export default defineConfig({ plugins: [supalite()] });
75
+ ```
76
+ 3. `bunx lite init` to scaffold `supabase/`.
77
+ 4. Write Postgres DDL in `supabase/schemas/schema.sql` (RLS enabled).
78
+ 5. `src/lib/supabase.ts`:
79
+ ```ts
80
+ import { createClient } from "@supabase/supabase-js";
81
+ export const supabase = createClient(window.location.origin, "any-string-works-for-now");
82
+ ```
83
+ 6. `bun run dev`.
84
+
85
+ Same-process, same origin, hot-reload on schema changes. Do **not** run `lite dev` alongside.
86
+
87
+ ## `updated_at` timestamps via trigger
88
+
89
+ ```sql
90
+ create or replace function set_updated_at() returns trigger language plpgsql as $$
91
+ begin
92
+ new.updated_at = now();
93
+ return new;
94
+ end;
95
+ $$;
96
+
97
+ create trigger set_<thing>_updated_at
98
+ before update on <thing>
99
+ for each row execute function set_updated_at();
100
+ ```
101
+
102
+ The translator inlines `NEW.updated_at = now()` into a SQLite `CREATE TRIGGER` body. See [STATUS.md#plpgsql-trigger-functions](https://github.com/supabase-community/lite/blob/HEAD/STATUS.md#plpgsql-trigger-functions) for the supported subset.
103
+
104
+ ## Profiles row on signup (`handle_new_user`)
105
+
106
+ ```sql
107
+ create or replace function handle_new_user() returns trigger language plpgsql as $$
108
+ begin
109
+ insert into public.profiles (id, email) values (new.id, new.email);
110
+ return new;
111
+ end;
112
+ $$;
113
+
114
+ create trigger on_auth_user_created
115
+ after insert on auth.users
116
+ for each row execute function handle_new_user();
117
+ ```
118
+
119
+ Supported on all backends.
package/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
 
11
11
  Lightweight TypeScript-native Supabase implementation. SQLite as the primary database, with PGlite and Postgres support. Ships a PostgREST-compatible REST API and a GoTrue-compatible Auth API, so `@supabase/supabase-js` works as-is.
12
12
 
13
- Supalite targets AI builders who want quick, cheap prototypes today with a clear path to upgrade later. It supplements Supabase rather than replacing it. The project stays lightweight by implementing only what fits on top of non-UDF SQLite: roughly 60% of the most-used Supabase features, focused on the subset most useful for fast iteration.
13
+ Supalite targets AI builders who want quick, cheap prototypes today with a clear path to upgrade later. It supplements Supabase rather than replacing it. The project stays lightweight by implementing only what fits on top of stock SQLite: roughly 60% of the most-used Supabase features, focused on the subset most useful for fast iteration.
14
14
 
15
15
  **Scope:** Both declarative schema (`supabase/schemas/*.sql`) and imperative Postgres migrations (`supabase/migrations/*.sql`, Supabase-CLI compatible) are supported. See [Migrations](#migrations). Advanced Postgres-specific column types (ranges, arrays of composites, and similar) are not available.
16
16
 
@@ -18,6 +18,27 @@ Validated against the upstream PostgREST and GoTrue test suites: 1,803 tests pas
18
18
 
19
19
  ---
20
20
 
21
+ ## Agents & Skill
22
+
23
+ If you're an agent working on a supalite project, read these first:
24
+
25
+ - [`LIMITATIONS.md`](https://github.com/supabase-community/lite/blob/HEAD/LIMITATIONS.md) — what's unsupported / partial, plus anti-patterns. Token-efficient cheat sheet.
26
+ - [`PATTERNS.md`](https://github.com/supabase-community/lite/blob/HEAD/PATTERNS.md) — canonical recipes (per-user RLS, embedded filter workarounds, custom server logic, Vite cold start, triggers).
27
+
28
+ The npm package ships a [`supalite` skill](https://github.com/supabase-community/lite/blob/HEAD/skills/supalite/SKILL.md). After `npm install`, link it into your agent's skills dir so the cold-start checklist, routing rule, and limitation pointers trigger automatically:
29
+
30
+ ```bash
31
+ # Agent standard: project-scoped install
32
+ mkdir -p .agents/skills && ln -s ../../node_modules/@supabase/lite/skills/supalite .agents/skills/supalite
33
+
34
+ # Claude Code: project-scoped install
35
+ mkdir -p .claude/skills && ln -s ../../node_modules/@supabase/lite/skills/supalite .claude/skills/supalite
36
+ ```
37
+
38
+ The skill itself points at the installed docs (not their content), so updates land for every consumer on the next `npm install`.
39
+
40
+ ---
41
+
21
42
  ## Feature overview
22
43
 
23
44
  Compatibility is measured from the `@supabase/supabase-js` surface. The goal is that code written against Supabase keeps working when pointed at @supabase/lite. Direct database access (raw SQL clients, Postgres wire protocol, `psql`) is **not** a target; everything below is scoped to what supabase-js exercises.
@@ -65,6 +86,20 @@ Edit `supabase/schemas/schema.sql` and the dev server re-applies the schema auto
65
86
 
66
87
  ---
67
88
 
89
+ ## When to use what
90
+
91
+ | Scenario | Use | Notes |
92
+ |---|---|---|
93
+ | Vite frontend (React/Vue/Svelte/…) | [`@supabase/lite/vite` plugin](#vite-plugin) | Same-process. No separate CLI. API mounted on the Vite dev server. |
94
+ | Non-Vite app, want auto schema-reload | `lite dev` | Separate process. Watches `schemas/*.sql`, re-applies on change. |
95
+ | Non-Vite app, manual control / CI / prod-like | `lite start` | Separate process. No watch, no auto-migrate. |
96
+
97
+ > Do not run `lite dev` or `lite start` alongside the Vite plugin — both bind the API and will collide.
98
+
99
+ Known limitations across all paths: see [LIMITATIONS.md](https://github.com/supabase-community/lite/blob/HEAD/LIMITATIONS.md). Canonical recipes: see [PATTERNS.md](https://github.com/supabase-community/lite/blob/HEAD/PATTERNS.md).
100
+
101
+ ---
102
+
68
103
  ## CLI
69
104
 
70
105
  ```
@@ -136,20 +171,25 @@ export default defineConfig({
136
171
  });
137
172
  ```
138
173
 
139
- Then from your frontend:
174
+ Then from your frontend. Because the plugin mounts the API on the Vite dev
175
+ server itself, the simplest dev setup is to point supabase-js at the current
176
+ origin and pass any non-empty string as the anon key:
140
177
 
141
178
  ```ts
142
179
  import { createClient } from "@supabase/supabase-js";
143
180
 
144
- const client = createClient(
145
- import.meta.env.VITE_SUPABASE_URL,
146
- import.meta.env.VITE_SUPABASE_ANON_KEY,
147
- );
181
+ const client = createClient(window.location.origin, "any-string-works-for-now");
148
182
  ```
149
183
 
184
+ The plugin does **not** currently inject `VITE_SUPABASE_URL` or
185
+ `VITE_SUPABASE_ANON_KEY` — env injection is tracked in [#27](https://github.com/supabase-community/lite/issues/27). Until that lands, prefer `window.location.origin` over an env-driven URL for the in-Vite path.
186
+
150
187
  The plugin auto-resolves `./supabase/config.toml`, applies the schema on boot,
151
188
  watches `schemas/*.sql` for hot-reload, and mounts `/auth/v1`, `/rest/v1`, and
152
- `/_system` on the Vite server. Only active during `vite` / `vite dev`.
189
+ `/_system` on the Vite server. Only active during `vite` / `vite dev`;
190
+ `vite preview` does not mount the API.
191
+
192
+ > Do not run `lite dev` or `lite start` next to the plugin — both bind the API and will collide. See [When to use what](#when-to-use-what).
153
193
 
154
194
  See [`examples/todo`](https://github.com/supabase-community/lite/tree/HEAD/examples/todo) for a full Vite + React + Tailwind + RLS example, or [`examples/next-todo`](https://github.com/supabase-community/lite/tree/HEAD/examples/next-todo) for the same pattern using Next.js App Router catch-all route handlers.
155
195
 
package/STATUS.md CHANGED
@@ -156,6 +156,56 @@ CREATE TABLE orders
156
156
 
157
157
  Unsupported PostgreSQL data types currently include `oid`, `xid`, `xid8`, `cid`, `money`, `citext`, `cidr`, `macaddr`, `macaddr8`, `bit`, `bit varying`, `varbit`, geometric types (`point`, `line`, `lseg`, `box`, `path`, `polygon`, `circle`), `xml`, text-search types (`tsvector`, `tsquery`), range and multirange types, `reg*` catalog reference types, internal types (`tid`, `pg_lsn`, `internal`), pseudo-types, and handler types.
158
158
 
159
+ ### Column Defaults
160
+
161
+ Column `DEFAULT` expressions in Postgres DDL are evaluated by the SQLite translator at CREATE TABLE time. Only constant expressions and a small allow-list of functions are honored.
162
+
163
+ **Honored:**
164
+
165
+ | Default expression | SQLite emission | Notes |
166
+ |---------------------------------------------|-------------------------------------------|-----------------------------|
167
+ | Literals (`'pending'`, `true`, `0`, `NULL`) | as-is | Booleans map to `0`/`1` |
168
+ | `gen_random_uuid()`, `uuid_generate_v4()` | inline `randomblob`-based UUID expression | RFC 4122 v4 shape |
169
+ | `now()`, `current_timestamp` | `datetime('now')` | |
170
+ | `current_date` | `date('now')` | |
171
+ | `current_time` | `time('now')` | |
172
+ | `random()` | `random()` | |
173
+
174
+ **Not honored — fails with `Function call "<name>" not supported`:**
175
+
176
+ - `auth.uid()`, `auth.role()`, `auth.email()`, `auth.jwt()` — these read JWT claims and have no SQLite equivalent. They only work inside RLS `USING` / `WITH CHECK` (rewritten on the AST), not as column defaults.
177
+ - `nextval(...)`, `currval(...)` — sequences are not modeled.
178
+ - `clock_timestamp()`, `statement_timestamp()`, `txid_current()`, and other volatile catalog functions.
179
+ - User-defined functions and any function not in the allow-list above.
180
+
181
+ **Workaround for `default auth.uid()`:**
182
+
183
+ Drop the default; pass `user_id` from the client on insert (sourced from the authenticated session). RLS `WITH CHECK (user_id = auth.uid())` already enforces ownership server-side.
184
+
185
+ ```sql
186
+ -- Instead of:
187
+ user_id uuid not null default auth.uid() references auth.users (id),
188
+
189
+ -- Use:
190
+ user_id uuid not null references auth.users (id),
191
+ -- and rely on a WITH CHECK policy to bind the row to the caller.
192
+ ```
193
+
194
+ This limitation applies only to the SQLite path. On PGlite/Postgres backends, `auth.uid()` works as a column default because the `auth.uid()` SQL function is bootstrapped natively.
195
+
196
+ ### CHECK constraint functions
197
+
198
+ `CHECK (…)` constraints in Postgres DDL are passed to SQLite as-is and evaluated by SQLite at write time. Only functions SQLite itself knows are valid. Postgres-only scalars that are commonly reached for in `CHECK` constraints are rejected by the translator with `Function call "<name>" not supported`:
199
+
200
+ - `trim`, `btrim`, `ltrim`, `rtrim` → use literal comparisons or move the validation to the app layer.
201
+ - `length` (on text) → SQLite has `length()`, but the translator currently rejects it inside `CHECK`. Track in [#27 / friction logs].
202
+ - `lower`, `upper` → same — work in `WHERE` but not currently in `CHECK`.
203
+ - Any custom function or extension scalar (`btrim`, `regexp_replace`, etc.).
204
+
205
+ **Workaround:** keep CHECK constraints to literal/operator comparisons (`length(col) > 0` is unsafe; `col <> ''` is fine), and validate richer rules in the app or via RLS `WITH CHECK` predicates.
206
+
207
+ On the Postgres / PGlite path these functions work as in upstream Postgres.
208
+
159
209
  ### Row Level Security (RLS)
160
210
 
161
211
  RLS is supported on all database backends. The enforcement strategy differs by dialect:
@@ -182,7 +232,7 @@ RLS is supported on all database backends. The enforcement strategy differs by d
182
232
 
183
233
  | Limitation | Details |
184
234
  |-----------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
185
- | Subquery `WITH CHECK` on `INSERT` | `WITH CHECK` expressions containing subqueries (e.g. `user_id IN (SELECT ...)`) cannot be evaluated in-memory. Currently throws an error. |
235
+ | Subquery `WITH CHECK` on `INSERT` | `WITH CHECK` expressions containing subqueries (e.g. `user_id IN (SELECT ...)`, `EXISTS (...)`) cannot be evaluated in-memory. Currently throws an error. **Workaround:** denormalise the authorising column (e.g. `user_id` onto the child table) and use `auth.uid() = user_id`. See [WITH CHECK subqueries](https://github.com/supabase-community/lite/blob/HEAD/internal/docs/limitations/rls-with-check-subqueries.md). |
186
236
  | `UPSERT` applies `INSERT` policies only | Postgres applies `UPDATE` policies on conflict; supalite applies `INSERT WITH CHECK` to all upsert rows since conflict resolution is unknown pre-execution. |
187
237
  | `FORCE ROW LEVEL SECURITY` | Not differentiated from `ENABLE ROW LEVEL SECURITY`; table owner is not exempt. |
188
238
  | `RETURNING` + `SELECT` policy | Postgres errors if `RETURNING` references rows not visible to `SELECT` policy. Not checked. |
@@ -349,6 +399,23 @@ All operators are parsed into the AST. The "Status" column reflects whether a wo
349
399
  | Nested embedding | ✅ | ✅ | Multi-level joins |
350
400
  | Aggregates | ✅ | ✅ | count, sum, avg, min, max; spread aggregates return PGRST127 |
351
401
 
402
+ ### Embedded filters
403
+
404
+ Filtering an embedded resource by one of its own columns (the dotted-path syntax `.eq('rel.col', value)` from supabase-js) is **not supported on SQLite**. Such filters are not rewritten into the embedded subquery; results are unfiltered or error.
405
+
406
+ **Workaround:** filter on the foreign-key column on the parent table, or fetch the embedded set and filter in JS. Example:
407
+
408
+ ```ts
409
+ // Not supported on SQLite:
410
+ await client.from("trips").select("*, days(*)").eq("days.city", "Paris");
411
+
412
+ // Use instead:
413
+ const { data: days } = await client.from("days").select("trip_id").eq("city", "Paris");
414
+ await client.from("trips").select("*, days(*)").in("id", days.map(d => d.trip_id));
415
+ ```
416
+
417
+ Supported natively on the Postgres / PGlite path.
418
+
352
419
  ### Response Shaping & Utilities
353
420
 
354
421
  | Method | SQLite | Postgres | Notes |
@@ -732,7 +799,13 @@ Mirrors upstream behavior documented in [`internal/docs/cli/environment.md`](htt
732
799
  | **Drivers** | ✅ | Minimal email, SMS, and cache driver interfaces. Configured via `options.drivers`, exposed at `app.drivers`. |
733
800
  | **Realtime** | 🔄 | Config schema defined (`app/src/config/realtime.ts`). |
734
801
  | **Edge Functions** | 🔄 | Config schema defined (`app/src/config/functions.ts`). Per-function JWT verification, entrypoints. |
735
- | **Vite plugin** | ✅ | `@supabase/lite/vite` subpath export mounts supalite as middleware in a Vite dev server (`app/src/vite/`). |
802
+ | **Vite plugin** | ✅ | `@supabase/lite/vite` subpath export mounts supalite as middleware in a Vite dev server (`app/src/vite/`). See [Vite plugin scope](#vite-plugin-scope). |
803
+
804
+ ### Vite plugin scope
805
+
806
+ - **Active only during `vite` / `vite dev`.** `vite preview`, `vite build`, and any production server do **not** mount the API. Use a real backend (`lite start`, hosted Supabase, or equivalent) for non-dev environments.
807
+ - **Same-process by design.** The plugin mounts `/auth/v1`, `/rest/v1`, and `/_system` on the Vite dev server. Do not run `lite dev` or `lite start` alongside — both bind the API and will collide on port.
808
+ - **No env-var injection (yet).** The plugin does not currently set `VITE_SUPABASE_URL` / `VITE_SUPABASE_ANON_KEY`. Tracked in [#27](https://github.com/supabase-community/lite/issues/27). For now pass `window.location.origin` + any non-empty anon string to `createClient`.
736
809
 
737
810
  ---
738
811