@supabase/lite 0.3.1-next.1 → 0.3.1-next.3
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 +49 -0
- package/PATTERNS.md +119 -0
- package/README.md +48 -7
- package/STATUS.md +75 -2
- package/dist/cli/index.js +1 -1
- package/dist/cli/lib.js +2 -2
- package/dist/index.js +42 -37
- package/dist/vite/index.js +2 -2
- package/package.json +4 -1
- package/skills/supalite/SKILL.md +38 -0
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
|
|
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,26 @@ 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.
|
|
189
|
+
`/_system` on the Vite server. Active in both `vite` / `vite dev` and `vite
|
|
190
|
+
preview` (preview mounts the API and runs boot migrations, but does not watch
|
|
191
|
+
schemas — it simulates production).
|
|
192
|
+
|
|
193
|
+
> 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
194
|
|
|
154
195
|
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
196
|
|
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
|
|