@supabase/lite 0.2.0 → 0.2.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/UPGRADE.md ADDED
@@ -0,0 +1,221 @@
1
+ # Upgrade to Supabase
2
+
3
+ This document describes the current `lite upgrade` behavior. It is intended for both users and LLM agents working on the upgrade path.
4
+
5
+ Supalite is designed as a lightweight starting point with a path to full Supabase. The upgrade command exports the local Supalite project, rehearses it against Postgres-compatible execution, and applies it to a Supabase target.
6
+
7
+ ## Quick Start
8
+
9
+ Hosted Supabase is the default target:
10
+
11
+ ```bash
12
+ lite upgrade
13
+ lite upgrade --target hosted
14
+ ```
15
+
16
+ Use `--dry-run` first to check readiness and rehearse the generated SQL without creating or changing a Supabase target:
17
+
18
+ ```bash
19
+ lite upgrade --dry-run
20
+ ```
21
+
22
+ Use `--force` to skip the interactive confirmation:
23
+
24
+ ```bash
25
+ lite upgrade --force
26
+ ```
27
+
28
+ Write generated credentials to a JSON file:
29
+
30
+ ```bash
31
+ lite upgrade --force --dump-credentials ./supabase-credentials.json
32
+ ```
33
+
34
+ ## Targets
35
+
36
+ ### Hosted Supabase
37
+
38
+ `--target hosted` is the default. It creates a new hosted Supabase project through the Supabase Management API, waits for it to become healthy, applies schema/auth/data, fetches API keys, and prints the new project details.
39
+
40
+ Relevant options:
41
+
42
+ ```bash
43
+ lite upgrade \
44
+ --target hosted \
45
+ --org-id <organization-id-or-slug> \
46
+ --region <region-key> \
47
+ --project-name <name> \
48
+ --supabase-token <personal-access-token>
49
+ ```
50
+
51
+ If `--supabase-token` is omitted, `SUPABASE_ACCESS_TOKEN` is used. If required hosted values are omitted in an interactive shell, the CLI prompts for them.
52
+
53
+ Only `--mode user` is currently supported. `--mode platform` is reserved for future work.
54
+
55
+ ### Local Supabase
56
+
57
+ `--target local` upgrades into a local Supabase CLI workdir. By default, the workdir is the current directory, so the command initializes or updates `./supabase/config.toml` in the project being upgraded:
58
+
59
+ ```bash
60
+ lite upgrade --target local --force --no-migrate-sessions
61
+ ```
62
+
63
+ Use `--local-dir` to put the Supabase CLI workdir somewhere else:
64
+
65
+ ```bash
66
+ lite upgrade \
67
+ --target local \
68
+ --local-dir ../my-local-supabase \
69
+ --force \
70
+ --no-migrate-sessions
71
+ ```
72
+
73
+ The local target uses the Supabase CLI through `bunx supabase@2.98.1` by default. Override the executable with `LITE_SUPABASE_CLI` if needed.
74
+
75
+ Local target behavior:
76
+
77
+ - Starts a disposable-style local Supabase stack with deterministic generated config.
78
+ - Removes Supalite-only database config keys such as `[db].driver` and `[db].url` before starting Supabase CLI.
79
+ - Pins `db.major_version = 15`.
80
+ - Enables Studio for inspecting the upgraded local project.
81
+ - Disables services that are not needed for upgrade verification, including mailpit, realtime, storage, imgproxy, edge runtime, analytics/logflare/vector, and supavisor.
82
+ - Parses `supabase status -o json` for API URL, DB URL, anon key, service role key, and JWT secret.
83
+ - Applies schema/auth/data directly to the local Postgres database.
84
+ - Leaves the local Supabase stack running after a successful CLI upgrade.
85
+
86
+ Stop a local target manually with:
87
+
88
+ ```bash
89
+ bunx supabase@2.98.1 stop --workdir <local-dir> --no-backup
90
+ ```
91
+
92
+ To rerun a local upgrade cleanly in the same workdir, stop the stack and remove Supabase CLI runtime state before running the upgrade again:
93
+
94
+ ```bash
95
+ cd <project-dir>
96
+ bunx supabase@2.98.1 stop --workdir . --no-backup
97
+ rm -rf supabase/.branches supabase/.temp
98
+ lite upgrade --target local --force --no-migrate-sessions
99
+ ```
100
+
101
+ If the local target uses a separate directory, clean that directory instead:
102
+
103
+ ```bash
104
+ bunx supabase@2.98.1 stop --workdir <local-dir> --no-backup
105
+ rm -rf <local-dir>/supabase/.branches <local-dir>/supabase/.temp
106
+ lite upgrade --target local --local-dir <local-dir> --force --no-migrate-sessions
107
+ ```
108
+
109
+ After a successful rerun, verify the database directly before relying on Studio:
110
+
111
+ ```bash
112
+ bunx supabase@2.98.1 status --workdir <local-dir-or-project-dir> -o json
113
+ psql '<DB URL from status>' -c '\dt public.*'
114
+ psql '<DB URL from status>' -c 'select * from public.<table>;'
115
+ ```
116
+
117
+ ## What Gets Migrated
118
+
119
+ The shared upgrade runner applies statements in this order:
120
+
121
+ 1. Project schema from local schema files.
122
+ 2. `auth.users`.
123
+ 3. `auth.identities`.
124
+ 4. Optional hosted session rows, when session migration is enabled.
125
+ 5. Optional hosted refresh token rows, when session migration is enabled.
126
+ 6. User table data, in foreign-key-safe order.
127
+ 7. Sequence resets after all migrated user data has been inserted.
128
+ 8. Hosted auth config sync, when supported by the target.
129
+
130
+ User data migration emits inserts for application tables and resets serial or bigserial sequences after explicit migrated IDs. This avoids the common post-upgrade problem where the next insert collides with migrated primary keys.
131
+
132
+ ## Session Migration
133
+
134
+ Hosted upgrades can preserve existing Supalite sessions by importing `auth.jwt_secret` as a Supabase HS256 signing key and migrating session and refresh token rows:
135
+
136
+ ```bash
137
+ lite upgrade --migrate-sessions
138
+ ```
139
+
140
+ If `auth.jwt_secret` is missing, hosted session migration fails and the CLI asks you to rerun with `--no-migrate-sessions`.
141
+
142
+ Weak JWT secrets are blocked non-interactively unless explicitly allowed:
143
+
144
+ ```bash
145
+ lite upgrade --migrate-sessions --allow-weak-jwt-secret
146
+ ```
147
+
148
+ Use this only when preserving sessions is more important than rotating a weak development secret. If sessions are not migrated, existing tokens become invalid and users must sign in again.
149
+
150
+ Local Supabase target does not support preserving sessions yet. Run local upgrades with:
151
+
152
+ ```bash
153
+ lite upgrade --target local --no-migrate-sessions
154
+ ```
155
+
156
+ ## Auth Config
157
+
158
+ Hosted upgrades map supported Supalite auth settings to Supabase Management API auth config and apply them after schema/auth/data migration.
159
+
160
+ Local upgrades do not call Management API config endpoints. Instead, supported local Supabase CLI config values are written before the local stack starts. This currently covers settings such as API max rows, auth site URL, redirect URLs, JWT expiry, signup flags, anonymous sign-ins, minimum password length, and email confirmation behavior.
161
+
162
+ ## Rehearsal and Readiness
163
+
164
+ Every non-dry-run upgrade performs:
165
+
166
+ 1. Readiness checks.
167
+ 2. An in-memory PGlite rehearsal.
168
+ 3. The real target upgrade only if rehearsal succeeds.
169
+
170
+ `--dry-run` runs readiness and rehearsal only:
171
+
172
+ ```bash
173
+ lite upgrade --dry-run
174
+ ```
175
+
176
+ Rehearsal creates Supabase-compatible roles before applying grants, then exercises the generated schema/auth/data SQL against a Postgres-compatible engine.
177
+
178
+ ## Known Gaps
179
+
180
+ Storage migration is not implemented. If storage is enabled, the command warns but continues with database and auth migration.
181
+
182
+ Realtime config migration is not implemented. If realtime is enabled, the command warns but continues.
183
+
184
+ Local target does not preserve sessions or JWT secret. Users must re-authenticate after local target migration.
185
+
186
+ Local Supabase is not a valid `SupabaseManagementApi` endpoint. A local CLI stack exposes API URL, DB URL, keys, and JWT secret through `supabase status`; it does not expose hosted Management API project routes like `/v1/projects/{ref}/database/query`.
187
+
188
+ Supabox is a better future target for high-fidelity hosted-flow tests, because it runs the local Supabase platform/control-plane and project lifecycle. It should augment, not replace, the faster local Supabase CLI harness.
189
+
190
+ ## Testing the Upgrade Path
191
+
192
+ Fast focused tests:
193
+
194
+ ```bash
195
+ cd app
196
+ bun test src/cli/upgrade/*.spec.ts src/cli/commands/cloud/upgrade.cmd.spec.ts test/cli/upgrade/local-supabase-status.test.ts
197
+ ```
198
+
199
+ Opt-in Docker test against local Supabase CLI:
200
+
201
+ ```bash
202
+ cd app
203
+ LITE_LOCAL_SUPABASE_TESTS=1 bun test test/cli/upgrade/local-supabase.test.ts
204
+ ```
205
+
206
+ Full default verification:
207
+
208
+ ```bash
209
+ cd app && bun test
210
+ bun test --recursive
211
+ ```
212
+
213
+ The intended next testing layer is a fixture-based upgrade suite:
214
+
215
+ 1. Copy a full Supalite fixture project to a temp directory.
216
+ 2. Run that fixture's app/e2e tests against Supalite.
217
+ 3. Run `lite upgrade --target local --local-dir <temp-target> --force --no-migrate-sessions`.
218
+ 4. Point the same app/e2e tests at the upgraded Supabase API URL and anon key.
219
+ 5. Assert the same user-visible behavior before and after upgrade.
220
+
221
+ For hosted-flow fidelity, add a separate opt-in Supabox lane later. That suite should validate the default hosted code path through a local Management API/project lifecycle rather than through the single-project Supabase CLI stack.
@@ -1,5 +1,3 @@
1
- import { ParseResult } from 'libpg-query';
2
- import { DeparserOptions } from 'pgsql-deparser';
3
1
  import { SelectQueryBuilder, InsertQueryBuilder, UpdateQueryBuilder, DeleteQueryBuilder, KyselyConfig, Kysely } from 'kysely';
4
2
 
5
3
  /**
@@ -256,128 +254,6 @@ type TranslatorConfig = {
256
254
  basePath?: string;
257
255
  };
258
256
 
259
- type TUnwrappedConst = string | number | boolean | null | undefined;
260
-
261
- type PolicyCommand = "SELECT" | "INSERT" | "UPDATE" | "DELETE" | "ALL";
262
- type PolicyRole = "anon" | "authenticated" | string;
263
- interface PolicyData {
264
- name: string;
265
- table: string;
266
- schema?: string;
267
- command: PolicyCommand;
268
- permissive: boolean;
269
- roles: PolicyRole[];
270
- using?: Where;
271
- withCheck?: Where;
272
- }
273
- declare class Policy {
274
- readonly data: PolicyData;
275
- constructor(data: PolicyData);
276
- appliesTo(cmd: "SELECT" | "INSERT" | "UPDATE" | "DELETE"): boolean;
277
- appliesToRole(role: string): boolean;
278
- toJSON(): PolicyData;
279
- static fromJSON(data: PolicyData): Policy;
280
- }
281
-
282
- declare class CheckConstraintError extends Error {
283
- readonly table: string;
284
- readonly column: string;
285
- readonly constraint: string;
286
- readonly value: unknown;
287
- constructor(table: string, column: string, constraint: string, value: unknown);
288
- }
289
-
290
- type SqliteType = "INTEGER" | "REAL" | "TEXT" | "BLOB" | "ANY";
291
- type DefaultFn = () => unknown;
292
- interface ValidationResult {
293
- status: "pass" | "warn" | "fail";
294
- message: string | null;
295
- action?: string;
296
- }
297
- interface FieldContext {
298
- schema: string;
299
- table: string;
300
- column: string;
301
- pgTypeName: string;
302
- nullable: boolean;
303
- defaultValue: string | null;
304
- defaultFn?: DefaultFn | null;
305
- isPrimaryKey: boolean;
306
- isUnique: boolean;
307
- isSerial: boolean;
308
- isGenerated?: boolean;
309
- fkRef?: {
310
- refSchema?: string;
311
- refTable: string;
312
- refColumn: string;
313
- constraintName?: string;
314
- };
315
- hasCheck?: boolean;
316
- checkConstraintName?: string;
317
- uniqueConstraintName?: string;
318
- }
319
- interface ColumnDDLOptions {
320
- includeNullable?: boolean;
321
- }
322
- declare abstract class Field {
323
- readonly context: FieldContext;
324
- constructor(context: FieldContext);
325
- abstract get sqliteType(): SqliteType;
326
- get isShimBacked(): boolean;
327
- checkConstraint(): string | null;
328
- serialize(value: unknown): unknown;
329
- deserialize(value: unknown): unknown;
330
- validateStorage(_rawSqliteValue: unknown): ValidationResult;
331
- protected validationFail(message: string, action?: string): ValidationResult;
332
- protected validationPass(): ValidationResult;
333
- protected isNullish(value: unknown): value is null | undefined;
334
- toColumnDDL(options?: ColumnDDLOptions): string;
335
- protected checkError(constraint: string, value: unknown): CheckConstraintError;
336
- protected quoteIfNeeded(name: string): string;
337
- }
338
-
339
- type FunctionResolutionMode = "synthetic" | "translate" | "auto";
340
-
341
- declare class TableSchema {
342
- readonly table: string;
343
- readonly schema: string;
344
- private fields;
345
- constructor(table: string, schema?: string, fields?: Map<string, Field>);
346
- get(col: string): Field | undefined;
347
- has(col: string): boolean;
348
- set(col: string, field: Field): void;
349
- columns(): string[];
350
- all(): Field[];
351
- serializeRow(row: Record<string, unknown>): Record<string, unknown>;
352
- deserializeRow(row: Record<string, unknown>): Record<string, unknown>;
353
- applyDefaults(row: Record<string, unknown>): Record<string, unknown>;
354
- }
355
-
356
- type CollectedEnums = Map<string, string[]>;
357
- type CollectedVariable = {
358
- local?: boolean;
359
- value: TUnwrappedConst;
360
- };
361
- type CollectedVariables = Map<string, CollectedVariable>;
362
- type CollectedRlsTables = Set<string>;
363
- type CollectedSchema = Map<string, TableSchema>;
364
- type CollectedTableConstraint = {
365
- schema: string;
366
- table: string;
367
- kind: "unique" | "check" | "foreign_key";
368
- name?: string;
369
- columns: string[];
370
- refSchema?: string;
371
- refTable?: string;
372
- refColumns?: string[];
373
- };
374
- type CollectedComment = {
375
- schema: string;
376
- table: string;
377
- column?: string;
378
- text: string;
379
- };
380
-
381
257
  interface TableInfo {
382
258
  name: string;
383
259
  sql: string;
@@ -598,33 +474,6 @@ interface ConnectionMigrator {
598
474
  safeSortPlanSteps(steps: PlanStep[]): PlanStep[];
599
475
  }
600
476
 
601
- type SchemaHandling = "default" | "prefix";
602
- interface DeparseOptions extends DeparserOptions {
603
- schemaHandling?: SchemaHandling;
604
- forceDefaultSchema?: string | false;
605
- enums?: CollectedEnums;
606
- /** Current DB introspection. Required for ALTER TABLE ops that need 12-step rebuild. */
607
- introspection?: IntrospectResult;
608
- functionResolution?: FunctionResolutionMode;
609
- }
610
-
611
- declare function translatePostgresDdl(pgSql: string, options?: DeparseOptions): Promise<string>;
612
- declare function deparsePostgresDdl(pgSql: string, options?: DeparseOptions & {
613
- strict?: boolean;
614
- }): Promise<{
615
- ddl: string;
616
- enums: CollectedEnums;
617
- rls: {
618
- tables: CollectedRlsTables;
619
- policies: Policy[];
620
- };
621
- schema: CollectedSchema;
622
- vars: CollectedVariables;
623
- tableConstraints: CollectedTableConstraint[];
624
- comments: CollectedComment[];
625
- ast: ParseResult;
626
- }>;
627
-
628
477
  interface CacheSetOptions {
629
478
  ttl?: number;
630
479
  }
@@ -707,4 +556,4 @@ declare abstract class Connection<Driver = unknown, DB = any, Config extends ICo
707
556
  withContext<T>(_vars: VarsContext | undefined, fn: (db: Kysely<any>) => Promise<T>, _opts?: ConnectionContextOptions): Promise<T>;
708
557
  }
709
558
 
710
- export { type QbDelete as $, type AnyAST as A, type BaseAST as B, Connection as C, DataLossError as D, type EmbedDef as E, type FiltersResult as F, type ForeignKeyInfo as G, type HeadersResult as H, type IntrospectResult as I, type IndexDiff as J, type IndexInfo as K, type InsertAST as L, type IntrospectOptions as M, type JoinDef as N, type JoinMap as O, type PolicyCommand as P, type Meta as Q, MigrationError as R, type OrderEntry as S, type TransactionOptions as T, type PlanResult as U, type VarsContext as V, type PlanStep as W, PlanStepType as X, type PreferToken as Y, type PrimaryKeyInfo as Z, type Qb as _, type IConnectionConfig as a, type QbInsert as a0, type QbSelect as a1, type QbUpdate as a2, type QueryAST as a3, type QueryParamsResult as a4, RelationNotFoundError as a5, type RouteResult as a6, type RpcAST as a7, type RpcResult as a8, type SchemaDiffResult as a9, type SelectEntry as aa, type SelectResult as ab, type TableDiff as ac, type TableInfo as ad, type TextSearchValue as ae, type TransformsResult as af, type TranslatorConfig as ag, type TriggerInfo as ah, type UniqueConstraintInfo as ai, type UpdateAST as aj, type UpsertAST as ak, type UpsertResult as al, type ViewInfo as am, type Where as an, type WhereValue as ao, isRef as ap, translatePostgresDdl as aq, type CacheDriver as b, type CacheSetOptions as c, deparsePostgresDdl as d, type PolicyRole as e, Policy as f, type AST as g, type ASTType as h, type AggregateFunction as i, type BodyResult as j, type CheckConstraintInfo as k, type ColumnDef as l, type ColumnDiff as m, type ColumnInfo as n, type ColumnRef as o, type CommentInfo as p, type ConnectionContextOptions as q, type ConnectionMigrator as r, type CustomTypesInfo as s, type DataLossWarning as t, type DeleteAST as u, type Dialect as v, type DiffResult as w, type EmbedTransform as x, type ExplainOptions as y, type ForeignKeyDiff as z };
559
+ export { type QbUpdate as $, type AnyAST as A, type BaseAST as B, Connection as C, DataLossError as D, type EmbedDef as E, type FiltersResult as F, type InsertAST as G, type HeadersResult as H, type IConnectionConfig as I, type IntrospectOptions as J, type JoinDef as K, type JoinMap as L, type Meta as M, MigrationError as N, type OrderEntry as O, type PlanResult as P, type PlanStep as Q, PlanStepType as R, type PreferToken as S, type TransactionOptions as T, type PrimaryKeyInfo as U, type VarsContext as V, type Where as W, type Qb as X, type QbDelete as Y, type QbInsert as Z, type QbSelect as _, type IntrospectResult as a, type QueryAST as a0, type QueryParamsResult as a1, RelationNotFoundError as a2, type RouteResult as a3, type RpcAST as a4, type RpcResult as a5, type SchemaDiffResult as a6, type SelectEntry as a7, type SelectResult as a8, type TableDiff as a9, type TableInfo as aa, type TextSearchValue as ab, type TransformsResult as ac, type TranslatorConfig as ad, type TriggerInfo as ae, type UniqueConstraintInfo as af, type UpdateAST as ag, type UpsertAST as ah, type UpsertResult as ai, type ViewInfo as aj, type WhereValue as ak, isRef as al, type CacheDriver as b, type CacheSetOptions as c, type AST as d, type ASTType as e, type AggregateFunction as f, type BodyResult as g, type CheckConstraintInfo as h, type ColumnDef as i, type ColumnDiff as j, type ColumnInfo as k, type ColumnRef as l, type CommentInfo as m, type ConnectionContextOptions as n, type ConnectionMigrator as o, type CustomTypesInfo as p, type DataLossWarning as q, type DeleteAST as r, type Dialect as s, type DiffResult as t, type EmbedTransform as u, type ExplainOptions as v, type ForeignKeyDiff as w, type ForeignKeyInfo as x, type IndexDiff as y, type IndexInfo as z };