better-supabase 0.0.0 → 0.1.0
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/LICENSE +21 -0
- package/README.md +88 -1
- package/bin/better-supabase.js +3 -0
- package/dist/casing-da0uRTqt.js +14 -0
- package/dist/cli/bin.d.ts +1 -0
- package/dist/cli/bin.js +15 -0
- package/dist/cli/index.d.ts +226 -0
- package/dist/cli/index.js +3 -0
- package/dist/cli-CoByWYja.js +6472 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +112 -0
- package/dist/compiler-DL18ukPe.d.ts +14 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +3 -0
- package/dist/config-D-aR2ZoN.js +276 -0
- package/dist/define-CTfWw-SS.d.ts +662 -0
- package/dist/define-QGkq04oe.js +1068 -0
- package/dist/edge/index.d.ts +43 -0
- package/dist/edge/index.js +103 -0
- package/dist/entitlements-BBg61DQZ.d.ts +20 -0
- package/dist/entitlements-BC_khwRy.js +17 -0
- package/dist/env/index.d.ts +2 -0
- package/dist/env/index.js +2 -0
- package/dist/env-BEToab0I.js +204 -0
- package/dist/errors-Bl3rkOzo.js +196 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.js +2 -0
- package/dist/events-2OJsVG_d.d.ts +84 -0
- package/dist/events-BuzTgn2D.js +135 -0
- package/dist/executor-Bazhgsgq.d.ts +195 -0
- package/dist/executor-Bje4suQF.d.ts +25 -0
- package/dist/executor-Ceq60hPM.js +89 -0
- package/dist/hono/index.d.ts +45 -0
- package/dist/hono/index.js +70 -0
- package/dist/index-BD6wKthp.d.ts +597 -0
- package/dist/index-DQVvHzjW.d.ts +74 -0
- package/dist/index-DRid0qIv.d.ts +76 -0
- package/dist/index-Dup-nSe9.d.ts +117 -0
- package/dist/index-Dy6kuF0P.d.ts +71 -0
- package/dist/index-Hf8PP9sP.d.ts +116 -0
- package/dist/index-JXHBuS53.d.ts +144 -0
- package/dist/index-Nxo3TJcm.d.ts +208 -0
- package/dist/index-Sw43J8HG.d.ts +62 -0
- package/dist/index-cEeweTeM.d.ts +38 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +42 -0
- package/dist/invalidate-DbxuL2jL.js +18 -0
- package/dist/jobs/index.d.ts +173 -0
- package/dist/jobs/index.js +477 -0
- package/dist/json-schema-BbwO2mZg.js +181 -0
- package/dist/lint/index.d.ts +75 -0
- package/dist/lint/index.js +167 -0
- package/dist/list/index.d.ts +2 -0
- package/dist/list/index.js +410 -0
- package/dist/live-6b9i72Ux.d.ts +180 -0
- package/dist/live-D8rV1Ldw.js +168 -0
- package/dist/mcp/index.d.ts +105 -0
- package/dist/mcp/index.js +330 -0
- package/dist/mfa-CpogP66z.js +521 -0
- package/dist/mfa-TVia554c.d.ts +44 -0
- package/dist/next/image/index.d.ts +34 -0
- package/dist/next/image/index.js +40 -0
- package/dist/next/index.d.ts +210 -0
- package/dist/next/index.js +426 -0
- package/dist/openapi/index.d.ts +2 -0
- package/dist/openapi/index.js +332 -0
- package/dist/orpc/index.d.ts +46 -0
- package/dist/orpc/index.js +50 -0
- package/dist/otel/index.d.ts +40 -0
- package/dist/otel/index.js +168 -0
- package/dist/path-Cv__-OTj.d.ts +17 -0
- package/dist/plugin-BqR6wKMB.js +23 -0
- package/dist/plugin-HvGqCurB.d.ts +118 -0
- package/dist/plugins/actor/index.d.ts +13 -0
- package/dist/plugins/actor/index.js +43 -0
- package/dist/plugins/rules/index.d.ts +88 -0
- package/dist/plugins/rules/index.js +174 -0
- package/dist/plugins/soft-delete/index.d.ts +38 -0
- package/dist/plugins/soft-delete/index.js +69 -0
- package/dist/plugins/tenant/index.d.ts +43 -0
- package/dist/plugins/tenant/index.js +73 -0
- package/dist/plugins/timestamps/index.d.ts +9 -0
- package/dist/plugins/timestamps/index.js +37 -0
- package/dist/plugins/validation/index.d.ts +24 -0
- package/dist/plugins/validation/index.js +59 -0
- package/dist/pool-BFeqzwQS.d.ts +37 -0
- package/dist/postgres/index.d.ts +28 -0
- package/dist/postgres/index.js +79 -0
- package/dist/postgrest-Dm-vhY6R.d.ts +43 -0
- package/dist/problem-DPu6Fh-t.js +97 -0
- package/dist/problem-DlgB0lYl.d.ts +47 -0
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +3 -0
- package/dist/query-ChvDbsDE.js +186 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.js +5 -0
- package/dist/react/server.d.ts +13 -0
- package/dist/react/server.js +25 -0
- package/dist/react/session.d.ts +2 -0
- package/dist/react/session.js +3 -0
- package/dist/react-CrOvrnIP.js +241 -0
- package/dist/read-set-DhX2d04c.js +216 -0
- package/dist/realtime/index.d.ts +3 -0
- package/dist/realtime/index.js +253 -0
- package/dist/resolve-DW8h9NPm.d.ts +155 -0
- package/dist/resource-CYiu6Ygi.js +175 -0
- package/dist/resource-SkPTRJDD.d.ts +65 -0
- package/dist/respond-4-dhoV7x.js +380 -0
- package/dist/respond-PoTpy5DT.d.ts +169 -0
- package/dist/result-CTkCZ6JA.d.ts +198 -0
- package/dist/result-DWXatkd6.js +103 -0
- package/dist/scope-CDHcS7dE.js +90 -0
- package/dist/seed-CzecODcJ.js +101 -0
- package/dist/server/index.d.ts +42 -0
- package/dist/server/index.js +68 -0
- package/dist/session-C3osL21k.d.ts +21 -0
- package/dist/session-CD-3orO_.js +19 -0
- package/dist/shared-_6-uPL7E.js +57 -0
- package/dist/simplify-oHiDKRVQ.js +665 -0
- package/dist/spec-pins-CJPVghHy.d.ts +20 -0
- package/dist/spec-pins-t6sPaHvl.js +20 -0
- package/dist/sql-M8l3fnS0.js +260 -0
- package/dist/ssr/index.d.ts +5 -0
- package/dist/ssr/index.js +4 -0
- package/dist/standard-CyhRzctb.d.ts +7 -0
- package/dist/standard-FUi3Z3-7.js +21 -0
- package/dist/stats-3iy8u3_P.js +566 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage-BWXVUXGk.js +461 -0
- package/dist/tables-rZxRxNPW.js +59 -0
- package/dist/template-BeOGnynM.js +93 -0
- package/dist/template-mbvlysOo.d.ts +6 -0
- package/dist/testing/index.d.ts +294 -0
- package/dist/testing/index.js +751 -0
- package/dist/types-6cYK9pbJ.js +29 -0
- package/dist/types-C2fslP1z.d.ts +195 -0
- package/dist/version-Cqq1m3UY.js +4 -0
- package/dist/view-B0ltUmTi.d.ts +43 -0
- package/dist/view-B6F9QbG-.js +35 -0
- package/dist/webhooks/index.d.ts +152 -0
- package/dist/webhooks/index.js +2 -0
- package/dist/webhooks-DbMTJJX2.js +139 -0
- package/package.json +246 -17
- package/schemas/config-v1.json +328 -0
- package/schemas/doctor-report-v1.json +105 -0
- package/schemas/snapshot-v2.json +1002 -0
- package/skills/better-supabase/SKILL.md +84 -0
- package/skills/better-supabase/references/plugins.md +56 -0
- package/skills/better-supabase/references/troubleshooting.md +39 -0
- package/skills/better-supabase-api/SKILL.md +67 -0
- package/skills/better-supabase-api/references/adapters.md +108 -0
- package/skills/better-supabase-testing/SKILL.md +65 -0
- package/index.js +0 -1
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { d as GeneratorInput, u as Generator, v as ResolvedConfig } from "../index-BD6wKthp.js";
|
|
2
|
+
import { E as SchemaMeta, P as Update, h as Insert, k as TableKey, n as AnyModels, t as AnyFunctions } from "../types-C2fslP1z.js";
|
|
3
|
+
import { r as Executor } from "../executor-Bazhgsgq.js";
|
|
4
|
+
import { P as DbStats, l as CacheAdapter, t as BetterSupabase, y as Db } from "../define-CTfWw-SS.js";
|
|
5
|
+
import { h as RequestContext, n as AnyPlugin } from "../plugin-HvGqCurB.js";
|
|
6
|
+
import { t as SqlClient } from "../executor-Bje4suQF.js";
|
|
7
|
+
import { t as Postgres } from "../pool-BFeqzwQS.js";
|
|
8
|
+
import { n as AuthResolver } from "../resolve-DW8h9NPm.js";
|
|
9
|
+
import { r as EventSink, t as CloudEvent } from "../index-DQVvHzjW.js";
|
|
10
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
11
|
+
//#region src/testing/jwt.d.ts
|
|
12
|
+
interface TestJwtClaims {
|
|
13
|
+
readonly sub: string;
|
|
14
|
+
readonly role?: string;
|
|
15
|
+
readonly email?: string;
|
|
16
|
+
readonly aal?: "aal1" | "aal2";
|
|
17
|
+
readonly expiresIn?: number;
|
|
18
|
+
readonly [claim: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Signs an HS256 access token the local Supabase stack accepts. Test-only:
|
|
22
|
+
* production tokens come from Supabase Auth.
|
|
23
|
+
*/
|
|
24
|
+
export declare function signTestJwt(secret: string, claims: TestJwtClaims): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Accepts `signTestJwt` tokens as users, for API servers tested against the
|
|
27
|
+
* local stack: `createServer(sb, { auth: { resolvers: [localAuth(secret)] } })`.
|
|
28
|
+
* Tokens that are not HS256-signed with `secret` fall through. Test-only.
|
|
29
|
+
*/
|
|
30
|
+
export declare function localAuth(secret: string): AuthResolver;
|
|
31
|
+
interface TestSigner {
|
|
32
|
+
/** Public JWKS; pass as `jwks` to `resolveAuth` / `createServer({ auth: { jwks } })`. */
|
|
33
|
+
readonly jwks: {
|
|
34
|
+
readonly keys: readonly JsonWebKey[];
|
|
35
|
+
};
|
|
36
|
+
sign(claims: TestJwtClaims): Promise<string>;
|
|
37
|
+
}
|
|
38
|
+
/** An in-memory ES256 key pair that signs tokens like Supabase Auth's asymmetric keys. */
|
|
39
|
+
export declare function createTestSigner(): Promise<TestSigner>;
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/testing/as-user.d.ts
|
|
42
|
+
/** The local CLI stack's default JWT secret. */
|
|
43
|
+
export declare const LOCAL_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long";
|
|
44
|
+
interface LocalStack {
|
|
45
|
+
/** Defaults to `$SUPABASE_URL`, then `http://127.0.0.1:54321`. */
|
|
46
|
+
readonly url?: string;
|
|
47
|
+
/** Defaults to `$SUPABASE_PUBLISHABLE_KEY`, then `$SUPABASE_ANON_KEY`. */
|
|
48
|
+
readonly publishableKey?: string;
|
|
49
|
+
/** Defaults to `$SUPABASE_JWT_SECRET`, then the CLI default. */
|
|
50
|
+
readonly jwtSecret?: string;
|
|
51
|
+
/** Service-role key for `expectTenantIsolation`; defaults to `$SUPABASE_SECRET_KEY`. */
|
|
52
|
+
readonly secretKey?: string;
|
|
53
|
+
/** Enables `sql`: the same user over direct Postgres. */
|
|
54
|
+
readonly postgres?: Postgres;
|
|
55
|
+
}
|
|
56
|
+
interface TestUser<M extends AnyModels, F extends AnyFunctions, E> {
|
|
57
|
+
readonly token: string;
|
|
58
|
+
readonly claims: TestJwtClaims;
|
|
59
|
+
/** supabase-js signed in as the user (storage, realtime, functions). */
|
|
60
|
+
readonly supabase: SupabaseClient;
|
|
61
|
+
/** Repositories over PostgREST as the user; RLS applies. */
|
|
62
|
+
readonly db: Db<M, F, E, SupabaseClient>;
|
|
63
|
+
/** Repositories over direct Postgres as the user, when `postgres` is given. */
|
|
64
|
+
readonly sql: Db<M, F, E, unknown> | undefined;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A test user against the local stack: a signed token and repositories that
|
|
68
|
+
* run as that user, for live RLS tests.
|
|
69
|
+
*
|
|
70
|
+
* ```ts
|
|
71
|
+
* const alice = await asUser(sb, { sub: aliceId, org_id: acme });
|
|
72
|
+
* expect(await alice.db.customers.count().orThrow()).toBe(3);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function asUser<M extends AnyModels, D, F extends AnyFunctions, E>(sb: BetterSupabase<M, D, F, E>, claims: TestJwtClaims, stack?: LocalStack): Promise<TestUser<M, F, E>>;
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/testing/budget.d.ts
|
|
78
|
+
/** The part of a Playwright `Response` the budget check reads. */
|
|
79
|
+
interface BudgetResponse {
|
|
80
|
+
url(): string;
|
|
81
|
+
headers(): Record<string, string>;
|
|
82
|
+
finished(): Promise<unknown>;
|
|
83
|
+
request(): {
|
|
84
|
+
headers(): Record<string, string>;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** The part of a Playwright `Page` the budget check uses. */
|
|
88
|
+
interface BudgetPage {
|
|
89
|
+
on(event: "response", listener: (response: BudgetResponse) => void): unknown;
|
|
90
|
+
off(event: "response", listener: (response: BudgetResponse) => void): unknown;
|
|
91
|
+
reload(): Promise<unknown>;
|
|
92
|
+
readonly request: {
|
|
93
|
+
get(url: string): Promise<{
|
|
94
|
+
ok(): boolean;
|
|
95
|
+
status(): number;
|
|
96
|
+
json(): Promise<unknown>;
|
|
97
|
+
}>;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
interface DbBudgetExpectation {
|
|
101
|
+
readonly maxCalls?: number;
|
|
102
|
+
readonly maxWaves?: number;
|
|
103
|
+
/** The navigation to measure. Defaults to reloading the page. */
|
|
104
|
+
readonly during?: () => Promise<unknown>;
|
|
105
|
+
/** Also measure router prefetches. Defaults to false. */
|
|
106
|
+
readonly prefetches?: boolean;
|
|
107
|
+
/** How long to wait for a streamed response to finish. Defaults to 10 seconds. */
|
|
108
|
+
readonly timeoutMs?: number;
|
|
109
|
+
}
|
|
110
|
+
interface MeasuredRender {
|
|
111
|
+
readonly url: string;
|
|
112
|
+
readonly requestId: string;
|
|
113
|
+
readonly stats: DbStats;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Measures every document and RSC navigation response during `during` and
|
|
117
|
+
* fails when one render makes more database calls or waves than allowed.
|
|
118
|
+
* Needs `createNext(sb, { debug })` and `next.debugRoute()` in the app.
|
|
119
|
+
*/
|
|
120
|
+
export declare function expectDbBudget(page: BudgetPage, expectation: DbBudgetExpectation): Promise<readonly MeasuredRender[]>;
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/testing/conformance.d.ts
|
|
123
|
+
interface ConformanceCheck {
|
|
124
|
+
readonly name: string;
|
|
125
|
+
readonly ok: boolean;
|
|
126
|
+
readonly message?: string;
|
|
127
|
+
}
|
|
128
|
+
interface ConformanceReport {
|
|
129
|
+
/** `Executor "postgrest"`, `CacheAdapter "next"`, ... */
|
|
130
|
+
readonly subject: string;
|
|
131
|
+
readonly checks: readonly ConformanceCheck[];
|
|
132
|
+
}
|
|
133
|
+
/** Thrown by a conformance kit when any check fails. Lists every failure, not just the first. */
|
|
134
|
+
export declare class ConformanceError extends Error {
|
|
135
|
+
readonly report: ConformanceReport;
|
|
136
|
+
constructor(report: ConformanceReport);
|
|
137
|
+
}
|
|
138
|
+
type AnySupabase = BetterSupabase<any, any, any, any>;
|
|
139
|
+
interface TestExecutorOptions {
|
|
140
|
+
/** A definition whose schema matches the executor's database. Install no plugins. */
|
|
141
|
+
readonly sb: AnySupabase;
|
|
142
|
+
/** App key of a table with rows the executor can read. Defaults to the first table. */
|
|
143
|
+
readonly table?: string;
|
|
144
|
+
/** A row to insert and delete again, to check writes. Skips write checks when omitted. */
|
|
145
|
+
readonly create?: Readonly<Record<string, unknown>>;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Proves an `Executor` meets the contract: rows keyed by the selection's
|
|
149
|
+
* aliases, counts, `aborted` errors, failures returned as `Result`s rather
|
|
150
|
+
* than thrown, and (with `create`) a write round trip.
|
|
151
|
+
*
|
|
152
|
+
* ```ts
|
|
153
|
+
* it('conforms', () => testExecutor(myExecutor, { sb, table: 'tags', create: { name: 'x' } }));
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export declare function testExecutor(executor: Executor, options: TestExecutorOptions): Promise<ConformanceReport>;
|
|
157
|
+
/**
|
|
158
|
+
* Proves a `CacheAdapter` accepts table and row targets, repeated and
|
|
159
|
+
* concurrent calls, and never mutates its input.
|
|
160
|
+
*/
|
|
161
|
+
export declare function testCacheAdapter(adapter: CacheAdapter, options?: {
|
|
162
|
+
readonly table?: string;
|
|
163
|
+
}): Promise<ConformanceReport>;
|
|
164
|
+
interface TestEventSinkOptions {
|
|
165
|
+
/** Events the sink delivered, for sinks that can be read back (an outbox, a test queue). */
|
|
166
|
+
readonly received?: () => readonly CloudEvent[] | Promise<readonly CloudEvent[]>;
|
|
167
|
+
}
|
|
168
|
+
/** Proves an `EventSink` accepts empty and full batches without mutating them, and delivers them when readable. */
|
|
169
|
+
export declare function testEventSink(sink: EventSink, options?: TestEventSinkOptions): Promise<ConformanceReport>;
|
|
170
|
+
interface TestAuthResolverOptions {
|
|
171
|
+
/** Requests with credentials this resolver owns but that must not verify. */
|
|
172
|
+
readonly invalid: readonly Request[];
|
|
173
|
+
/** Requests that must resolve to a user or service, optionally with the expected user id. */
|
|
174
|
+
readonly valid?: readonly {
|
|
175
|
+
readonly request: Request;
|
|
176
|
+
readonly userId?: string;
|
|
177
|
+
}[];
|
|
178
|
+
/** A request without this resolver's credentials. Defaults to a bare GET. */
|
|
179
|
+
readonly unrelated?: Request;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Proves an `AuthResolver` passes on requests it doesn't understand, fails
|
|
183
|
+
* closed (`invalid`, never anon) on bad credentials, and never throws.
|
|
184
|
+
*/
|
|
185
|
+
export declare function testAuthResolver(resolver: AuthResolver, options: TestAuthResolverOptions): Promise<ConformanceReport>;
|
|
186
|
+
interface TestGeneratorOptions {
|
|
187
|
+
/** Schema metadata to generate from: `schema.meta` from your generated module. */
|
|
188
|
+
readonly meta: SchemaMeta;
|
|
189
|
+
/** Typegen metadata; defaults to an empty database. */
|
|
190
|
+
readonly introspection?: GeneratorInput["introspection"];
|
|
191
|
+
readonly extras?: GeneratorInput["extras"];
|
|
192
|
+
readonly config?: ResolvedConfig;
|
|
193
|
+
}
|
|
194
|
+
/** Proves a `Generator` writes relative, unique paths, is deterministic and doesn't mutate its input. */
|
|
195
|
+
export declare function testGenerator(generator: Generator, options: TestGeneratorOptions): Promise<ConformanceReport>;
|
|
196
|
+
interface TestPluginOptions {
|
|
197
|
+
/** The definition without this plugin. */
|
|
198
|
+
readonly sb: AnySupabase;
|
|
199
|
+
/** Table to exercise. Defaults to the first table. */
|
|
200
|
+
readonly table?: string;
|
|
201
|
+
/** Context the plugin needs, such as `{ tenant }` for `tenant()`. */
|
|
202
|
+
readonly context?: RequestContext;
|
|
203
|
+
/** Row for `create`, to exercise `beforeMutation`. */
|
|
204
|
+
readonly create?: Readonly<Record<string, unknown>>;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Proves a `Plugin` targets API v1, installs cleanly, keeps `transformQuery`
|
|
208
|
+
* and `beforeMutation` pure and deterministic, and keeps executors'
|
|
209
|
+
* results intact when it wraps them.
|
|
210
|
+
*/
|
|
211
|
+
export declare function testPlugin(plugin: AnyPlugin, options: TestPluginOptions): Promise<ConformanceReport>;
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/testing/isolation.d.ts
|
|
214
|
+
interface IsolationTenant {
|
|
215
|
+
/** Handed to each table's `row`, usually the tenant's id. */
|
|
216
|
+
readonly id: string;
|
|
217
|
+
/** The claims of a user in this tenant, as for `asUser`. */
|
|
218
|
+
readonly claims: TestJwtClaims;
|
|
219
|
+
/** Used in failure messages; defaults to `id`. */
|
|
220
|
+
readonly name?: string;
|
|
221
|
+
}
|
|
222
|
+
interface IsolationTable<M extends AnyModels, K extends keyof M> {
|
|
223
|
+
/**
|
|
224
|
+
* A row owned by `tenant`. `n` is 0 for the row seeded through the service
|
|
225
|
+
* role and 1 for the other tenant's insert attempt: put it in unique columns.
|
|
226
|
+
*/
|
|
227
|
+
readonly row: (tenant: IsolationTenant, n: 0 | 1) => Insert<M, K>;
|
|
228
|
+
/** A change the other tenant tries to make. It must differ from `row`. */
|
|
229
|
+
readonly update: Update<M, K>;
|
|
230
|
+
}
|
|
231
|
+
type IsolationTables<M extends AnyModels> = { readonly [K in TableKey<M>]?: IsolationTable<M, K>; };
|
|
232
|
+
interface TenantIsolationOptions<M extends AnyModels> {
|
|
233
|
+
readonly tenants: readonly [IsolationTenant, IsolationTenant];
|
|
234
|
+
readonly tables: IsolationTables<M>;
|
|
235
|
+
/** Defaults to `$SUPABASE_URL`, `$SUPABASE_PUBLISHABLE_KEY` and `$SUPABASE_SECRET_KEY`. */
|
|
236
|
+
readonly stack?: LocalStack;
|
|
237
|
+
/** Runs before any row is created, e.g. `() => seed.insert(sql)` for the tenants and their memberships. */
|
|
238
|
+
readonly seed?: () => Promise<void>;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Checks row-level security between two tenants against a running stack: for
|
|
242
|
+
* each table, a user of one tenant must not select, insert, update or delete
|
|
243
|
+
* the other tenant's rows. Runs without `sb`'s plugins, so the `tenant()`
|
|
244
|
+
* plugin can't hide a missing policy. Throws a `ConformanceError` naming each
|
|
245
|
+
* table and command that leaks; removes every row it created.
|
|
246
|
+
*
|
|
247
|
+
* ```ts
|
|
248
|
+
* await expectTenantIsolation(sb, {
|
|
249
|
+
* tenants: [
|
|
250
|
+
* { id: ACME, claims: { sub: alice, org_id: ACME } },
|
|
251
|
+
* { id: GLOBEX, claims: { sub: bob, org_id: GLOBEX } },
|
|
252
|
+
* ],
|
|
253
|
+
* tables: {
|
|
254
|
+
* tags: {
|
|
255
|
+
* row: (tenant, n) => ({ organizationId: tenant.id, name: `iso-${n}` }),
|
|
256
|
+
* update: { name: 'changed' },
|
|
257
|
+
* },
|
|
258
|
+
* },
|
|
259
|
+
* });
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
export declare function expectTenantIsolation<M extends AnyModels, D, F extends AnyFunctions, E>(sb: BetterSupabase<M, D, F, E>, options: TenantIsolationOptions<M>): Promise<ConformanceReport>;
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region src/testing/seed.d.ts
|
|
265
|
+
/** Named rows per table, in app casing: `{ customers: { acme: { ... } } }`. */
|
|
266
|
+
type SeedFixtures<M extends AnyModels> = { readonly [K in keyof M]?: Readonly<Record<string, M[K]["Insert"]>>; };
|
|
267
|
+
/** Rejects columns that are not in the table's Insert type. */
|
|
268
|
+
type ExactSeed<M extends AnyModels, S> = { readonly [K in keyof S]: K extends keyof M ? { readonly [N in keyof S[K]]: { readonly [C in keyof S[K][N]]: C extends keyof M[K]["Insert"] ? S[K][N][C] : never; }; } : never; };
|
|
269
|
+
declare const SEED: unique symbol;
|
|
270
|
+
interface Seed<S> {
|
|
271
|
+
readonly [SEED]: true;
|
|
272
|
+
/** The fixtures as given, so tests can reference `seed.rows.customers.acme.id`. */
|
|
273
|
+
readonly rows: S;
|
|
274
|
+
/** One `insert ... on conflict do nothing` per table, parents before children. */
|
|
275
|
+
statements(): string[];
|
|
276
|
+
sql(): string;
|
|
277
|
+
/** Inserts the fixtures, e.g. in a test's `beforeAll`. Needs a service-role client. */
|
|
278
|
+
insert(sql: SqlClient): Promise<void>;
|
|
279
|
+
}
|
|
280
|
+
export declare function isSeed(value: unknown): value is Seed<unknown>;
|
|
281
|
+
/**
|
|
282
|
+
* Typed fixtures shared by `supabase/seeds` and your tests. Rows use app
|
|
283
|
+
* casing and Insert types; `better-supabase seed` renders them to SQL.
|
|
284
|
+
*
|
|
285
|
+
* ```ts
|
|
286
|
+
* export const seed = defineSeed(sb, {
|
|
287
|
+
* organizations: { acme: { id: ACME, name: 'Acme' } },
|
|
288
|
+
* customers: { first: { organizationId: ACME, name: 'First customer' } },
|
|
289
|
+
* });
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
export declare function defineSeed<M extends AnyModels, D, F extends AnyFunctions, E, const S extends SeedFixtures<M>>(sb: BetterSupabase<M, D, F, E>, fixtures: S & ExactSeed<M, S>): Seed<S>;
|
|
293
|
+
//#endregion
|
|
294
|
+
export type { BudgetPage, BudgetResponse, ConformanceCheck, ConformanceReport, DbBudgetExpectation, ExactSeed, IsolationTable, IsolationTables, IsolationTenant, LocalStack, MeasuredRender, Seed, SeedFixtures, TenantIsolationOptions, TestAuthResolverOptions, TestEventSinkOptions, TestExecutorOptions, TestGeneratorOptions, TestJwtClaims, TestPluginOptions, TestSigner, TestUser };
|