better-supabase 0.0.0 → 0.2.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.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -1
  3. package/bin/better-supabase.js +4 -0
  4. package/dist/casing-da0uRTqt.js +14 -0
  5. package/dist/claims-CoTobNC-.js +25 -0
  6. package/dist/cli/bin.d.ts +1 -0
  7. package/dist/cli/bin.js +15 -0
  8. package/dist/cli/index.d.ts +226 -0
  9. package/dist/cli/index.js +3 -0
  10. package/dist/cli-DrSgDOQC.js +6646 -0
  11. package/dist/client/index.d.ts +2 -0
  12. package/dist/client/index.js +112 -0
  13. package/dist/compiler-CZ9nIuqy.d.ts +14 -0
  14. package/dist/config/index.d.ts +3 -0
  15. package/dist/config/index.js +3 -0
  16. package/dist/config-ClDXmVtY.js +277 -0
  17. package/dist/define-BXMYD9AY.js +1071 -0
  18. package/dist/define-e3zI4_4g.d.ts +679 -0
  19. package/dist/edge/index.d.ts +43 -0
  20. package/dist/edge/index.js +103 -0
  21. package/dist/entitlements-CTEBm7IB.js +20 -0
  22. package/dist/entitlements-Dng-ctuT.d.ts +42 -0
  23. package/dist/env/index.d.ts +2 -0
  24. package/dist/env/index.js +2 -0
  25. package/dist/env-BEToab0I.js +204 -0
  26. package/dist/errors-Bl3rkOzo.js +196 -0
  27. package/dist/events/index.d.ts +2 -0
  28. package/dist/events/index.js +2 -0
  29. package/dist/events-BuzTgn2D.js +135 -0
  30. package/dist/events-DOt3nSO-.d.ts +84 -0
  31. package/dist/executor-BYXQ9AGn.d.ts +25 -0
  32. package/dist/executor-CGdNQ27g.d.ts +195 -0
  33. package/dist/executor-DeppvirK.js +89 -0
  34. package/dist/guards-BnzDvFrz.js +10 -0
  35. package/dist/hono/index.d.ts +45 -0
  36. package/dist/hono/index.js +70 -0
  37. package/dist/index-Bw1bwtC0.d.ts +144 -0
  38. package/dist/index-CBDufH1M.d.ts +120 -0
  39. package/dist/index-CG69s15v.d.ts +38 -0
  40. package/dist/index-Ciu5Xlml.d.ts +74 -0
  41. package/dist/index-D4v7sncu.d.ts +62 -0
  42. package/dist/index-DOsjFvMf.d.ts +618 -0
  43. package/dist/index-DbxFr7XG.d.ts +76 -0
  44. package/dist/index-DuP14VHo.d.ts +216 -0
  45. package/dist/index-Dy6kuF0P.d.ts +71 -0
  46. package/dist/index-K-3WM2Hp.d.ts +117 -0
  47. package/dist/index.d.ts +110 -0
  48. package/dist/index.js +43 -0
  49. package/dist/invalidate-DbxuL2jL.js +18 -0
  50. package/dist/jobs/index.d.ts +173 -0
  51. package/dist/jobs/index.js +478 -0
  52. package/dist/json-schema-BpYqkCKf.js +182 -0
  53. package/dist/lint/index.d.ts +75 -0
  54. package/dist/lint/index.js +167 -0
  55. package/dist/list/index.d.ts +2 -0
  56. package/dist/list/index.js +410 -0
  57. package/dist/live-D8rV1Ldw.js +168 -0
  58. package/dist/live-DJyZMD6w.d.ts +191 -0
  59. package/dist/logger-glM28NVN.js +21 -0
  60. package/dist/mcp/index.d.ts +142 -0
  61. package/dist/mcp/index.js +463 -0
  62. package/dist/mfa-CpLfJSde.js +541 -0
  63. package/dist/mfa-TVia554c.d.ts +44 -0
  64. package/dist/next/image/index.d.ts +34 -0
  65. package/dist/next/image/index.js +40 -0
  66. package/dist/next/index.d.ts +210 -0
  67. package/dist/next/index.js +427 -0
  68. package/dist/openapi/index.d.ts +2 -0
  69. package/dist/openapi/index.js +332 -0
  70. package/dist/orpc/index.d.ts +46 -0
  71. package/dist/orpc/index.js +50 -0
  72. package/dist/otel/index.d.ts +40 -0
  73. package/dist/otel/index.js +168 -0
  74. package/dist/path-Cv__-OTj.d.ts +17 -0
  75. package/dist/permdock-sql-CghlHPuH.js +117 -0
  76. package/dist/plugin-BqR6wKMB.js +23 -0
  77. package/dist/plugin-C4TR9J0Z.d.ts +118 -0
  78. package/dist/plugins/actor/index.d.ts +13 -0
  79. package/dist/plugins/actor/index.js +43 -0
  80. package/dist/plugins/rules/index.d.ts +89 -0
  81. package/dist/plugins/rules/index.js +167 -0
  82. package/dist/plugins/soft-delete/index.d.ts +38 -0
  83. package/dist/plugins/soft-delete/index.js +69 -0
  84. package/dist/plugins/tenant/index.d.ts +49 -0
  85. package/dist/plugins/tenant/index.js +75 -0
  86. package/dist/plugins/timestamps/index.d.ts +9 -0
  87. package/dist/plugins/timestamps/index.js +37 -0
  88. package/dist/plugins/validation/index.d.ts +24 -0
  89. package/dist/plugins/validation/index.js +59 -0
  90. package/dist/pool-R2m_DkXi.d.ts +37 -0
  91. package/dist/postgres/index.d.ts +28 -0
  92. package/dist/postgres/index.js +79 -0
  93. package/dist/postgrest-cAEQ_IQN.d.ts +43 -0
  94. package/dist/problem-BTkTtRV9.js +97 -0
  95. package/dist/problem-DlgB0lYl.d.ts +47 -0
  96. package/dist/query/index.d.ts +2 -0
  97. package/dist/query/index.js +3 -0
  98. package/dist/query-ChvDbsDE.js +186 -0
  99. package/dist/react/index.d.ts +7 -0
  100. package/dist/react/index.js +5 -0
  101. package/dist/react/server.d.ts +13 -0
  102. package/dist/react/server.js +25 -0
  103. package/dist/react/session.d.ts +2 -0
  104. package/dist/react/session.js +3 -0
  105. package/dist/react-C83BdBGt.js +242 -0
  106. package/dist/read-set-4lI3W_gZ.js +216 -0
  107. package/dist/realtime/index.d.ts +4 -0
  108. package/dist/realtime/index.js +268 -0
  109. package/dist/resolve-CjHILk2w.d.ts +170 -0
  110. package/dist/resource-BHEWmQJK.js +175 -0
  111. package/dist/resource-CgZa3YG_.d.ts +65 -0
  112. package/dist/respond-DMcbQwfN.js +383 -0
  113. package/dist/respond-Dne1ARSa.d.ts +169 -0
  114. package/dist/result-CTkCZ6JA.d.ts +198 -0
  115. package/dist/result-DWXatkd6.js +103 -0
  116. package/dist/scope-CDHcS7dE.js +90 -0
  117. package/dist/seed-CzecODcJ.js +101 -0
  118. package/dist/server/index.d.ts +42 -0
  119. package/dist/server/index.js +68 -0
  120. package/dist/session-C3LxnQ0e.d.ts +22 -0
  121. package/dist/session-CqEwzVup.js +20 -0
  122. package/dist/shared-_6-uPL7E.js +57 -0
  123. package/dist/simplify-bwJpLnaS.js +665 -0
  124. package/dist/spec-pins-C3oc-Tdp.js +20 -0
  125. package/dist/spec-pins-CqorWDnO.d.ts +20 -0
  126. package/dist/sql-DTjA1LPj.js +260 -0
  127. package/dist/ssr/index.d.ts +5 -0
  128. package/dist/ssr/index.js +4 -0
  129. package/dist/standard-CyhRzctb.d.ts +7 -0
  130. package/dist/standard-FUi3Z3-7.js +21 -0
  131. package/dist/stats-C5W-rBfR.js +566 -0
  132. package/dist/storage/index.d.ts +5 -0
  133. package/dist/storage/index.js +2 -0
  134. package/dist/storage-DuGn38fr.js +527 -0
  135. package/dist/tables-rZxRxNPW.js +59 -0
  136. package/dist/template-mbvlysOo.d.ts +6 -0
  137. package/dist/testing/index.d.ts +325 -0
  138. package/dist/testing/index.js +819 -0
  139. package/dist/types-6cYK9pbJ.js +29 -0
  140. package/dist/types-C83v5SQz.d.ts +248 -0
  141. package/dist/version-Cqq1m3UY.js +4 -0
  142. package/dist/view-D1s0wa0T.d.ts +49 -0
  143. package/dist/view-aOYizTeE.js +36 -0
  144. package/dist/webhooks/index.d.ts +152 -0
  145. package/dist/webhooks/index.js +2 -0
  146. package/dist/webhooks-DMipjhSP.js +139 -0
  147. package/package.json +250 -17
  148. package/schemas/config-v1.json +373 -0
  149. package/schemas/doctor-report-v1.json +106 -0
  150. package/schemas/snapshot-v2.json +1006 -0
  151. package/skills/better-supabase/SKILL.md +84 -0
  152. package/skills/better-supabase/references/plugins.md +65 -0
  153. package/skills/better-supabase/references/troubleshooting.md +39 -0
  154. package/skills/better-supabase-api/SKILL.md +67 -0
  155. package/skills/better-supabase-api/references/adapters.md +108 -0
  156. package/skills/better-supabase-testing/SKILL.md +66 -0
  157. package/index.js +0 -1
@@ -0,0 +1,169 @@
1
+ import { n as AnyModels, t as AnyFunctions } from "./types-C83v5SQz.js";
2
+ import { p as DbError, t as AsyncResult } from "./result-CTkCZ6JA.js";
3
+ import { I as StatsRecorder, P as DbStats, t as BetterSupabase, y as Db } from "./define-e3zI4_4g.js";
4
+ import { h as RequestContext } from "./plugin-C4TR9J0Z.js";
5
+ import { t as BetterSupabaseEnv } from "./index-Dy6kuF0P.js";
6
+ import { r as SqlClaims, t as Postgres } from "./pool-R2m_DkXi.js";
7
+ import { o as ImpersonationOptions, t as Aal } from "./mfa-TVia554c.js";
8
+ import { a as ResolveAuthOptions, r as AuthState, t as AuthResolution } from "./resolve-CjHILk2w.js";
9
+ import { n as Bucket } from "./index-DuP14VHo.js";
10
+ import { SupabaseClient } from "@supabase/supabase-js";
11
+ //#region src/server/delete-account.d.ts
12
+ interface DeleteAccountOptions {
13
+ /**
14
+ * Buckets to clear first. Objects whose `owner` placeholder (`{userId}`,
15
+ * or `owner.param` for `owner` buckets) is the user are removed; buckets
16
+ * without one are skipped.
17
+ */
18
+ readonly buckets?: readonly Bucket<string>[];
19
+ /**
20
+ * App keys of tables whose rows the database deletes or updates with the
21
+ * user (`on delete cascade` or `set null` to `auth.users`). They get a
22
+ * table-wide `mutation` notice, so caches drop their reads.
23
+ */
24
+ readonly cascades?: readonly string[];
25
+ readonly context?: RequestContext;
26
+ readonly signal?: AbortSignal;
27
+ }
28
+ interface DeleteAccountResult {
29
+ readonly userId: string;
30
+ /** Removed objects per bucket id. */
31
+ readonly removed: Readonly<Record<string, number>>;
32
+ }
33
+ //#endregion
34
+ //#region src/server/replicas.d.ts
35
+ /** Cookie holding the epoch ms until which reads stay on the primary. */
36
+ declare const PRIMARY_COOKIE = "bs-primary-until";
37
+ /** Where a request context sends its reads. */
38
+ interface ReplicaState {
39
+ /** Reads go to the primary: this context wrote, or a recent request did. */
40
+ readonly pinned: boolean;
41
+ /** This context wrote through its repositories (or called `pin()`). */
42
+ readonly wrote: boolean;
43
+ /** Sends later reads to the primary, e.g. after a write through `$client` or `$sql`. */
44
+ pin(): void;
45
+ }
46
+ //#endregion
47
+ //#region src/server/server.d.ts
48
+ interface ServerOptions {
49
+ /** Defaults to `loadEnv()`. */
50
+ readonly env?: BetterSupabaseEnv;
51
+ /** Enables `ctx.sql` and `actingAs()`. */
52
+ readonly postgres?: Postgres;
53
+ readonly auth?: Omit<ResolveAuthOptions, "env">;
54
+ /**
55
+ * Extra headers on the caller's PostgREST requests, readable in Postgres as
56
+ * `current_setting('request.headers')` (channel, request id, client IP for audit).
57
+ */
58
+ readonly headers?: (request: Request) => Readonly<Record<string, string>>;
59
+ /**
60
+ * PostgREST URL for the caller's reads: a read replica or the `<ref>-all`
61
+ * load balancer. Defaults to `SUPABASE_READ_URL`; `false` reads from the
62
+ * primary.
63
+ */
64
+ readonly readUrl?: string | false;
65
+ readonly replicas?: {
66
+ /** How long the next requests read from the primary after a write. Defaults to 5 s. */
67
+ readonly pinMs?: number;
68
+ };
69
+ }
70
+ interface ServerContext<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> {
71
+ readonly auth: AuthState<C, P>;
72
+ readonly resolution: AuthResolution<C, P>;
73
+ /** supabase-js client scoped to the caller (RLS applies). */
74
+ readonly supabase: SupabaseClient;
75
+ /** Repositories over PostgREST as the caller. */
76
+ readonly db: Db<M, F, E, SupabaseClient>;
77
+ /** Repositories over direct Postgres as the caller, when `postgres` is configured. */
78
+ readonly sql: Db<M, F, E, undefined> | undefined;
79
+ /** Calls, waves, tables and time for `db` and `sql` together. */
80
+ stats(): DbStats;
81
+ /** Where `db` reads go, when a read URL is configured. */
82
+ readonly replica: ReplicaState | undefined;
83
+ }
84
+ interface ContextOptions {
85
+ readonly refresh?: boolean;
86
+ /** Epoch ms until which `db` reads from the primary (the `bs-primary-until` cookie). */
87
+ readonly pinnedUntil?: number;
88
+ /** Also records this context's calls into a request-wide recorder. */
89
+ readonly stats?: StatsRecorder;
90
+ }
91
+ interface BetterServer<M extends AnyModels, F extends AnyFunctions, E, C = unknown, P = unknown> {
92
+ readonly env: BetterSupabaseEnv;
93
+ resolve(request: Request, options?: {
94
+ readonly refresh?: boolean;
95
+ }): Promise<AuthResolution<C, P>>;
96
+ /**
97
+ * Resolves auth and binds repositories to the caller. Refreshes an expired
98
+ * cookie session only with `refresh: true`; send `ctx.resolution.apply(response)`.
99
+ */
100
+ context(request: Request, options?: ContextOptions): Promise<ServerContext<M, F, E, C, P>>;
101
+ /** The context for an auth state resolved elsewhere (the proxy, a queue message). */
102
+ contextFor(auth: AuthState<C, P>, options?: ContextOptions): ServerContext<M, F, E, C, P>;
103
+ /** A stateless supabase-js client for an auth state, optionally with extra request headers. */
104
+ supabaseFor(auth: AuthState, headers?: Readonly<Record<string, string>>): SupabaseClient;
105
+ /** Repositories for an auth state (e.g. one resolved by the proxy). */
106
+ dbFor(auth: AuthState, context?: RequestContext): Db<M, F, E, SupabaseClient>;
107
+ /** Service-role repositories. Bypasses RLS; the actor is `service`. */
108
+ admin(context?: RequestContext): Db<M, F, E, SupabaseClient>;
109
+ /**
110
+ * Deletes a user: their objects in `buckets`, then the Auth user, then a
111
+ * `mutation` notice for `auth.users`. Needs the secret key. Access tokens
112
+ * already issued stay valid until they expire.
113
+ */
114
+ deleteAccount(userId: string, options?: DeleteAccountOptions): AsyncResult<DeleteAccountResult>;
115
+ /**
116
+ * Repositories running as a user, with RLS, over direct Postgres. With
117
+ * `impersonation`, the claims carry `act: { sub: actor, reason }`, which
118
+ * the `audit` and `actor` SQL kit modules record.
119
+ */
120
+ actingAs(userId: string, claims?: Omit<SqlClaims, "sub" | "act">, impersonation?: ImpersonationOptions): Db<M, F, E, undefined>;
121
+ }
122
+ /**
123
+ * Server-side entry point for APIs, jobs, MCP servers and framework
124
+ * adapters. Holds the secrets; `sb` stays isomorphic.
125
+ */
126
+ declare function createServer<M extends AnyModels, D, F extends AnyFunctions, E, C = unknown, P = unknown>(sb: BetterSupabase<M, D, F, E, C, P>, options?: ServerOptions): BetterServer<M, F, E, C, P>;
127
+ //#endregion
128
+ //#region src/server/respond.d.ts
129
+ type AuthKind = AuthState["kind"];
130
+ interface GuardOptions {
131
+ /** Auth kinds allowed through. Defaults to `['user']`. */
132
+ readonly allow?: readonly Exclude<AuthKind, "invalid">[];
133
+ /**
134
+ * Assurance level user sessions need. `aal2` answers 403 with
135
+ * `required: 'aal2'` until the user verifies a second factor.
136
+ */
137
+ readonly aal?: Aal;
138
+ }
139
+ /** `undefined` when `auth` may pass; otherwise the 401/403 error to send. */
140
+ declare function guard(auth: AuthState, allow?: GuardOptions["allow"], aal?: Aal): DbError | undefined;
141
+ declare function isResult(value: unknown): value is {
142
+ ok: boolean;
143
+ data: unknown;
144
+ error: unknown;
145
+ };
146
+ type Settled = {
147
+ readonly ok: true;
148
+ readonly data: unknown;
149
+ } | {
150
+ readonly ok: false;
151
+ readonly error: DbError;
152
+ };
153
+ /**
154
+ * Settles a handler's return value: `Result`s and `AsyncResult`s unwrap,
155
+ * thrown `DbException`s (or errors whose `cause` is a `DbError`) become
156
+ * errors, anything else is data. Other thrown
157
+ * errors propagate.
158
+ */
159
+ declare function settle(run: () => unknown): Promise<Settled>;
160
+ interface RespondOptions {
161
+ readonly instance?: string;
162
+ readonly expose?: boolean;
163
+ /** Status for data responses. Defaults to 200 (204 without data). */
164
+ readonly status?: number;
165
+ }
166
+ /** A handler's value as a response: `Response` as is, data as JSON, errors as Problem Details. */
167
+ declare function respond(run: () => unknown, options?: RespondOptions): Promise<Response>;
168
+ //#endregion
169
+ export { DeleteAccountResult as _, guard as a, settle as c, ServerContext as d, ServerOptions as f, DeleteAccountOptions as g, ReplicaState as h, Settled as i, BetterServer as l, PRIMARY_COOKIE as m, GuardOptions as n, isResult as o, createServer as p, RespondOptions as r, respond as s, AuthKind as t, ContextOptions as u };
@@ -0,0 +1,198 @@
1
+ //#region src/core/errors.d.ts
2
+ /**
3
+ * Extra fields carried by each error kind. Plugins can add kinds through the
4
+ * `errors` extension point; apps that want the new kinds in the union can add
5
+ * them to this interface.
6
+ */
7
+ interface DbErrorKinds {
8
+ not_found: Record<never, never>;
9
+ unauthorized: Record<never, never>;
10
+ /** `required`: the assurance level the route needs, when a second factor is missing. */
11
+ forbidden: {
12
+ required?: "aal1" | "aal2";
13
+ };
14
+ conflict: {
15
+ constraint?: string;
16
+ columns?: readonly string[];
17
+ };
18
+ foreign_key: {
19
+ constraint?: string;
20
+ columns?: readonly string[];
21
+ };
22
+ check: {
23
+ constraint?: string;
24
+ };
25
+ not_null: {
26
+ column?: string;
27
+ };
28
+ exclusion: {
29
+ constraint?: string;
30
+ };
31
+ invalid_input: Record<never, never>;
32
+ raised: Record<never, never>;
33
+ timeout: Record<never, never>;
34
+ serialization: Record<never, never>;
35
+ network: Record<never, never>;
36
+ aborted: Record<never, never>;
37
+ invalid_request: Record<never, never>;
38
+ validation: {
39
+ issues: readonly ValidationIssue[];
40
+ };
41
+ multiple_rows: Record<never, never>;
42
+ stale: Record<never, never>;
43
+ /** `retryAfter`: seconds until the window resets (the `Retry-After` header). */
44
+ rate_limited: {
45
+ retryAfter?: number;
46
+ };
47
+ unexpected: Record<never, never>;
48
+ }
49
+ type DbErrorKind = keyof DbErrorKinds;
50
+ interface ValidationIssue {
51
+ readonly message: string;
52
+ readonly path?: readonly PropertyKey[];
53
+ }
54
+ interface DbErrorBase<K extends DbErrorKind> {
55
+ readonly kind: K;
56
+ readonly message: string;
57
+ /** SQLSTATE or PostgREST code (`23505`, `PGRST116`), when known. */
58
+ readonly code?: string;
59
+ /** Suggested HTTP status, used by the RFC 9457 mapping. */
60
+ readonly status: number;
61
+ readonly details?: string;
62
+ /** Postgres `HINT`. `raised` errors use it as an app-level error code. */
63
+ readonly hint?: string;
64
+ /** App-cased table key the operation ran against. */
65
+ readonly table?: string;
66
+ }
67
+ /**
68
+ * A database error as plain, serializable data. Safe to send across server
69
+ * action and RPC boundaries.
70
+ */
71
+ type DbError = { [K in DbErrorKind]: DbErrorBase<K> & DbErrorKinds[K]; }[DbErrorKind];
72
+ type DbErrorOf<K extends DbErrorKind> = Extract<DbError, {
73
+ kind: K;
74
+ }>;
75
+ declare function statusOf(kind: DbErrorKind): number;
76
+ type ExtraOf<K extends DbErrorKind> = Record<never, never> extends DbErrorKinds[K] ? [extra?: DbErrorKinds[K] & Partial<Omit<DbErrorBase<K>, "kind">>] : [extra: DbErrorKinds[K] & Partial<Omit<DbErrorBase<K>, "kind">>];
77
+ /** Creates a `DbError` with the default status for its kind. */
78
+ declare function dbError<K extends DbErrorKind>(kind: K, message: string, ...[extra]: ExtraOf<K>): DbErrorOf<K>;
79
+ declare function isDbError(value: unknown): value is DbError;
80
+ /**
81
+ * A unique violation, optionally for one constraint. Accepts a `DbError` or a
82
+ * `DbException`. Pass the generated `UniqueConstraint` type to check names:
83
+ * `isConflict<UniqueConstraint>(error, 'customers_organization_id_kvk_key')`.
84
+ */
85
+ declare const isConflict: <C extends string = string>(error: unknown, constraint?: NoInfer<C>) => error is DbErrorOf<"conflict"> & {
86
+ readonly constraint: C;
87
+ };
88
+ /** A CHECK violation, optionally for one constraint (`CheckConstraint`). */
89
+ declare const isCheck: <C extends string = string>(error: unknown, constraint?: NoInfer<C>) => error is DbErrorOf<"check"> & {
90
+ readonly constraint: C;
91
+ };
92
+ /** A foreign key violation, optionally for one constraint (`ForeignKeyConstraint`). */
93
+ declare const isForeignKey: <C extends string = string>(error: unknown, constraint?: NoInfer<C>) => error is DbErrorOf<"foreign_key"> & {
94
+ readonly constraint: C;
95
+ };
96
+ /** Thrown by `.orThrow()`. Carries the plain `DbError` as `error`. */
97
+ declare class DbException extends Error {
98
+ override readonly name = "DbException";
99
+ readonly error: DbError;
100
+ constructor(error: DbError);
101
+ get kind(): DbErrorKind;
102
+ }
103
+ /** Raw error shape shared by PostgREST responses and `pg` errors. */
104
+ interface RawDbError {
105
+ readonly message?: string;
106
+ readonly code?: string;
107
+ readonly details?: string | null;
108
+ readonly hint?: string | null;
109
+ readonly constraint?: string;
110
+ readonly column?: string;
111
+ readonly table?: string;
112
+ readonly name?: string;
113
+ }
114
+ type ErrorMapper = (raw: RawDbError, fallback: DbError) => DbError | undefined;
115
+ /**
116
+ * Maps a PostgREST or Postgres error to a `DbError`. Custom mappers run
117
+ * first; the first one that returns an error wins.
118
+ */
119
+ declare function mapDbError(raw: RawDbError, mappers?: readonly ErrorMapper[]): DbError;
120
+ //#endregion
121
+ //#region src/core/result.d.ts
122
+ /** Turns a `DbError` into the error `.orThrow()` throws; set with `sb.mapError()`. */
123
+ type ThrowMapper = (error: DbError) => unknown;
124
+ type Ok<T> = {
125
+ readonly ok: true;
126
+ readonly data: T;
127
+ readonly error: null;
128
+ };
129
+ type Err<E = DbError> = {
130
+ readonly ok: false;
131
+ readonly data: null;
132
+ readonly error: E;
133
+ };
134
+ type Result<T, E = DbError> = Ok<T> | Err<E>;
135
+ declare function ok<T>(data: T): Ok<T>;
136
+ declare function err<E = DbError>(error: E): Err<E>;
137
+ /**
138
+ * A lazy-free `PromiseLike<Result<T>>` with helpers. `await` gives the
139
+ * `Result`; `.orThrow()` gives the data or throws a `DbException`.
140
+ */
141
+ declare class AsyncResult<T> implements PromiseLike<Result<T>> {
142
+ #private;
143
+ constructor(promise: Promise<Result<T>>);
144
+ static from<T>(run: () => Promise<Result<T>>): AsyncResult<T>;
145
+ static ok<T>(data: T): AsyncResult<T>;
146
+ static err<T = never>(error: DbError): AsyncResult<T>;
147
+ then<R1 = Result<T>, R2 = never>(onfulfilled?: ((value: Result<T>) => R1 | PromiseLike<R1>) | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null): Promise<R1 | R2>;
148
+ /**
149
+ * Resolves with the data, or rejects with a `DbException`. Pass `factory`
150
+ * to throw your own error instead, e.g. an HTTP error for your framework;
151
+ * without it, the mapper from `sb.mapError()` applies when one is set.
152
+ */
153
+ orThrow(factory?: (error: DbError) => unknown): Promise<T>;
154
+ /** Resolves with the data, or `fallback` when the result is an error. */
155
+ unwrapOr<F>(fallback: F): Promise<T | F>;
156
+ map<U>(fn: (data: T) => U): AsyncResult<U>;
157
+ mapError(fn: (error: DbError) => DbError): AsyncResult<T>;
158
+ andThen<U>(fn: (data: T) => PromiseLike<Result<U>>): AsyncResult<U>;
159
+ }
160
+ /** What `toBetterResult` returns: the `status`/`value`/`error` fields every better-result value has. */
161
+ type BetterResultShape<T, E> = {
162
+ readonly status: "ok";
163
+ readonly value: T;
164
+ } | {
165
+ readonly status: "error";
166
+ readonly error: E;
167
+ };
168
+ /** The part of better-result's `Result` namespace `toBetterResult` calls. */
169
+ interface BetterResultApi<T, E> {
170
+ ok(value: NoInfer<T>): unknown;
171
+ err(error: NoInfer<E>): unknown;
172
+ }
173
+ /**
174
+ * Builds a better-result value with the `Result` namespace you pass, so
175
+ * better-result stays your dependency. Errors go through `mapError`, or, for a
176
+ * result from a `db` of `sb.mapError(fn)`, through `fn` (typed `unknown`).
177
+ * The value is a real `Ok`/`Err`: cast it (`as Result<T, E>`) for its
178
+ * methods in types; the cast itself is not checked against `T`.
179
+ *
180
+ * ```ts
181
+ * import { Result } from 'better-result';
182
+ * const customer = toBetterResult(await db.customers.findById(id), Result, toAppError);
183
+ * ```
184
+ */
185
+ declare function toBetterResult<T, E>(result: Result<T>, api: BetterResultApi<T, E>, mapError: (error: DbError) => E): BetterResultShape<T, E>;
186
+ declare function toBetterResult<T>(result: Result<T>, api: BetterResultApi<T, DbError>): BetterResultShape<T, DbError>;
187
+ declare function toBetterResult<T, E>(result: AsyncResult<T>, api: BetterResultApi<T, E>, mapError: (error: DbError) => E): Promise<BetterResultShape<T, E>>;
188
+ declare function toBetterResult<T>(result: AsyncResult<T>, api: BetterResultApi<T, unknown>): Promise<BetterResultShape<T, unknown>>;
189
+ /**
190
+ * Converts a better-result value back into a `Result`, by its `status`
191
+ * field, so it works across better-result versions. A `DbError`, or an
192
+ * error whose `cause` is one, is kept; others go through `mapError`
193
+ * (default `toDbError`).
194
+ */
195
+ declare function fromBetterResult<T>(result: BetterResultShape<T, unknown>, mapError?: (error: unknown) => DbError): Result<T>;
196
+ declare function toDbError(cause: unknown): DbError;
197
+ //#endregion
198
+ export { isConflict as C, statusOf as D, mapDbError as E, isCheck as S, isForeignKey as T, DbException as _, Ok as a, ValidationIssue as b, err as c, toBetterResult as d, toDbError as f, DbErrorOf as g, DbErrorKinds as h, Err as i, fromBetterResult as l, DbErrorKind as m, BetterResultApi as n, Result as o, DbError as p, BetterResultShape as r, ThrowMapper as s, AsyncResult as t, ok as u, ErrorMapper as v, isDbError as w, dbError as x, RawDbError as y };
@@ -0,0 +1,103 @@
1
+ import { n as dbError, r as dbErrorOf, t as DbException } from "./errors-Bl3rkOzo.js";
2
+ //#region src/core/result.ts
3
+ const throwMappers = /* @__PURE__ */ new WeakMap();
4
+ function inherit(from, next) {
5
+ const mapper = throwMappers.get(from);
6
+ if (mapper) throwMappers.set(next, mapper);
7
+ return next;
8
+ }
9
+ /** Marks `result` so its `.orThrow()` throws `mapper(error)`; returns it. */
10
+ function withErrorMapper(result, mapper) {
11
+ throwMappers.set(result, mapper);
12
+ return result;
13
+ }
14
+ function ok(data) {
15
+ return {
16
+ ok: true,
17
+ data,
18
+ error: null
19
+ };
20
+ }
21
+ function err(error) {
22
+ return {
23
+ ok: false,
24
+ data: null,
25
+ error
26
+ };
27
+ }
28
+ /**
29
+ * A lazy-free `PromiseLike<Result<T>>` with helpers. `await` gives the
30
+ * `Result`; `.orThrow()` gives the data or throws a `DbException`.
31
+ */
32
+ var AsyncResult = class AsyncResult {
33
+ #promise;
34
+ constructor(promise) {
35
+ this.#promise = promise.catch((cause) => err(toDbError(cause)));
36
+ }
37
+ static from(run) {
38
+ return new AsyncResult(Promise.resolve().then(run));
39
+ }
40
+ static ok(data) {
41
+ return new AsyncResult(Promise.resolve(ok(data)));
42
+ }
43
+ static err(error) {
44
+ return new AsyncResult(Promise.resolve(err(error)));
45
+ }
46
+ then(onfulfilled, onrejected) {
47
+ return this.#promise.then(onfulfilled, onrejected);
48
+ }
49
+ /**
50
+ * Resolves with the data, or rejects with a `DbException`. Pass `factory`
51
+ * to throw your own error instead, e.g. an HTTP error for your framework;
52
+ * without it, the mapper from `sb.mapError()` applies when one is set.
53
+ */
54
+ async orThrow(factory) {
55
+ const result = await this.#promise;
56
+ if (result.ok) return result.data;
57
+ const mapper = factory ?? throwMappers.get(this);
58
+ throw mapper ? mapper(result.error) : new DbException(result.error);
59
+ }
60
+ /** Resolves with the data, or `fallback` when the result is an error. */
61
+ async unwrapOr(fallback) {
62
+ const result = await this.#promise;
63
+ return result.ok ? result.data : fallback;
64
+ }
65
+ map(fn) {
66
+ return inherit(this, new AsyncResult(this.#promise.then((result) => result.ok ? ok(fn(result.data)) : result)));
67
+ }
68
+ mapError(fn) {
69
+ return inherit(this, new AsyncResult(this.#promise.then((result) => result.ok ? result : err(fn(result.error)))));
70
+ }
71
+ andThen(fn) {
72
+ return inherit(this, new AsyncResult(this.#promise.then((result) => result.ok ? fn(result.data) : result)));
73
+ }
74
+ };
75
+ function toBetterResult(result, api, mapError) {
76
+ const build = (settled, mapper) => settled.ok ? api.ok(settled.data) : api.err(mapper ? mapper(settled.error) : settled.error);
77
+ if (result instanceof AsyncResult) {
78
+ const mapper = mapError ?? throwMappers.get(result);
79
+ return result.then((settled) => build(settled, mapper));
80
+ }
81
+ return build(result, mapError);
82
+ }
83
+ /**
84
+ * Converts a better-result value back into a `Result`, by its `status`
85
+ * field, so it works across better-result versions. A `DbError`, or an
86
+ * error whose `cause` is one, is kept; others go through `mapError`
87
+ * (default `toDbError`).
88
+ */
89
+ function fromBetterResult(result, mapError = toDbError) {
90
+ switch (result.status) {
91
+ case "ok": return ok(result.value);
92
+ case "error": return err(dbErrorOf(result.error) ?? mapError(result.error));
93
+ default: throw new TypeError(`better-supabase: fromBetterResult() expects a better-result value, got ${String(result)}`);
94
+ }
95
+ }
96
+ function toDbError(cause) {
97
+ if (cause instanceof DbException) return cause.error;
98
+ if (cause instanceof Error && cause.name === "AbortError") return dbError("aborted", cause.message || "The operation was aborted");
99
+ const message = cause instanceof Error ? cause.message : String(cause);
100
+ return dbError("unexpected", message);
101
+ }
102
+ //#endregion
103
+ export { toBetterResult as a, ok as i, err as n, toDbError as o, fromBetterResult as r, withErrorMapper as s, AsyncResult as t };
@@ -0,0 +1,90 @@
1
+ import { r as not, t as and } from "./types-6cYK9pbJ.js";
2
+ //#region src/ir/scope.ts
3
+ /**
4
+ * Applies `scopeFor` to every relation filter in a condition. `every(W)`
5
+ * becomes `none(S and not W)` so out-of-scope rows (soft-deleted, other
6
+ * tenants) neither satisfy nor fail it.
7
+ */
8
+ function scopeCondition(condition, scopeFor) {
9
+ if (!condition) return void 0;
10
+ switch (condition.kind) {
11
+ case "and":
12
+ case "or": return {
13
+ kind: condition.kind,
14
+ items: condition.items.map((item) => scopeCondition(item, scopeFor) ?? item)
15
+ };
16
+ case "not": return {
17
+ kind: "not",
18
+ item: scopeCondition(condition.item, scopeFor) ?? condition.item
19
+ };
20
+ case "column": return condition;
21
+ case "relation": {
22
+ const inner = scopeCondition(condition.where, scopeFor);
23
+ const scope = scopeFor(condition.target);
24
+ if (!scope) return {
25
+ ...condition,
26
+ where: inner
27
+ };
28
+ switch (condition.quantifier) {
29
+ case "some":
30
+ case "none": return {
31
+ ...condition,
32
+ where: and(inner, scope)
33
+ };
34
+ case "every": return inner ? {
35
+ ...condition,
36
+ quantifier: "none",
37
+ where: and(scope, not(inner))
38
+ } : {
39
+ ...condition,
40
+ where: void 0
41
+ };
42
+ default: return condition.quantifier;
43
+ }
44
+ }
45
+ default: return condition;
46
+ }
47
+ }
48
+ function scopeInclude(include, scopeFor) {
49
+ return {
50
+ ...include,
51
+ selection: scopeSelection(include.selection, scopeFor),
52
+ where: and(scopeCondition(include.where, scopeFor), scopeFor(include.target))
53
+ };
54
+ }
55
+ /** Applies `scopeFor` to every include and to filters inside includes. */
56
+ function scopeSelection(selection, scopeFor) {
57
+ if (selection.includes.length === 0) return selection;
58
+ return {
59
+ ...selection,
60
+ includes: selection.includes.map((include) => scopeInclude(include, scopeFor))
61
+ };
62
+ }
63
+ /**
64
+ * Scopes a whole operation: the root table's rows (reads, updates, deletes),
65
+ * relation filters, includes and returned embeds. Inserts only get their
66
+ * returning selection scoped.
67
+ */
68
+ function scopeOperation(op, scopeFor, root = true) {
69
+ const own = root ? scopeFor(op.table) : void 0;
70
+ switch (op.kind) {
71
+ case "select": return {
72
+ ...op,
73
+ selection: scopeSelection(op.selection, scopeFor),
74
+ where: and(scopeCondition(op.where, scopeFor), own)
75
+ };
76
+ case "update":
77
+ case "delete": return {
78
+ ...op,
79
+ where: and(scopeCondition(op.where, scopeFor), own),
80
+ returning: op.returning ? scopeSelection(op.returning, scopeFor) : void 0
81
+ };
82
+ case "insert": return {
83
+ ...op,
84
+ returning: op.returning ? scopeSelection(op.returning, scopeFor) : void 0
85
+ };
86
+ default: return op;
87
+ }
88
+ }
89
+ //#endregion
90
+ export { scopeOperation as n, scopeSelection as r, scopeCondition as t };
@@ -0,0 +1,101 @@
1
+ //#region src/testing/seed.ts
2
+ const SEED = Symbol.for("better-supabase.seed");
3
+ function isSeed(value) {
4
+ return typeof value === "object" && value !== null && SEED in value;
5
+ }
6
+ const quote = (text) => `'${text.replaceAll("'", "''")}'`;
7
+ function arrayLiteral(values) {
8
+ return `{${values.map((value) => {
9
+ if (value === null) return "NULL";
10
+ if (Array.isArray(value)) return arrayLiteral(value);
11
+ return `"${(value instanceof Date ? value.toISOString() : String(value)).replace(/[\\"]/g, "\\$&")}"`;
12
+ }).join(",")}}`;
13
+ }
14
+ function literal(value, column, where) {
15
+ if (value === null) return "null";
16
+ if (column.json) return quote(JSON.stringify(value));
17
+ if (Array.isArray(value)) return quote(arrayLiteral(value));
18
+ if (value instanceof Date) return quote(value.toISOString());
19
+ const kind = typeof value;
20
+ switch (kind) {
21
+ case "string": return quote(value);
22
+ case "boolean":
23
+ case "bigint": return String(value);
24
+ case "number":
25
+ if (!Number.isFinite(value)) throw new TypeError(`${where}: ${String(value)} is not a finite number`);
26
+ return String(value);
27
+ case "object":
28
+ case "function":
29
+ case "symbol":
30
+ case "undefined": throw new TypeError(`${where}: unsupported value of type ${kind}`);
31
+ default: throw new TypeError(`${where}: unsupported value of type ${String(kind)}`);
32
+ }
33
+ }
34
+ const ident = (name) => `"${name.replaceAll("\"", "\"\"")}"`;
35
+ /** Parents first (by forward relations), otherwise in the order given. */
36
+ function order(meta, keys) {
37
+ const sorted = [];
38
+ const visiting = /* @__PURE__ */ new Set();
39
+ const visit = (key) => {
40
+ if (sorted.includes(key) || visiting.has(key)) return;
41
+ visiting.add(key);
42
+ for (const relation of Object.values(meta.tables[key]?.relations ?? {})) if (relation.direction === "forward" && relation.table !== key && keys.includes(relation.table)) visit(relation.table);
43
+ visiting.delete(key);
44
+ sorted.push(key);
45
+ };
46
+ for (const key of keys) visit(key);
47
+ return sorted;
48
+ }
49
+ function statement(table, rows) {
50
+ const entries = Object.entries(rows);
51
+ if (entries.length === 0) return void 0;
52
+ const columns = [];
53
+ for (const [, row] of entries) for (const [key, value] of Object.entries(row)) {
54
+ if (value === void 0 || columns.includes(key)) continue;
55
+ const column = table.columns[key];
56
+ if (!column) throw new TypeError(`seed ${table.key}: unknown column "${key}"`);
57
+ if (column.generated) throw new TypeError(`seed ${table.key}: "${key}" is a generated column`);
58
+ columns.push(key);
59
+ }
60
+ const values = entries.map(([name, row]) => {
61
+ return ` (${columns.map((key) => row[key] === void 0 ? "default" : literal(row[key], table.columns[key], `seed ${table.key}.${name}.${key}`)).join(", ")})`;
62
+ });
63
+ return `insert into ${`${ident(table.schema)}.${ident(table.name)}`} (${columns.map((key) => ident(table.columns[key].db)).join(", ")}) values\n${values.join(",\n")}\non conflict do nothing;`;
64
+ }
65
+ /**
66
+ * Typed fixtures shared by `supabase/seeds` and your tests. Rows use app
67
+ * casing and Insert types; `better-supabase seed` renders them to SQL.
68
+ *
69
+ * ```ts
70
+ * export const seed = defineSeed(sb, {
71
+ * organizations: { acme: { id: ACME, name: 'Acme' } },
72
+ * customers: { first: { organizationId: ACME, name: 'First customer' } },
73
+ * });
74
+ * ```
75
+ */
76
+ function defineSeed(sb, fixtures) {
77
+ const meta = sb.meta;
78
+ const statements = () => {
79
+ const keys = Object.keys(fixtures).filter((key) => fixtures[key] !== void 0);
80
+ for (const key of keys) {
81
+ const table = meta.tables[key];
82
+ if (!table) throw new TypeError(`seed: unknown table "${key}"`);
83
+ if (table.kind === "view") throw new TypeError(`seed: "${key}" is a view`);
84
+ }
85
+ return order(meta, keys).flatMap((key) => {
86
+ const sql = statement(meta.tables[key], fixtures[key]);
87
+ return sql ? [sql] : [];
88
+ });
89
+ };
90
+ return {
91
+ [SEED]: true,
92
+ rows: fixtures,
93
+ statements,
94
+ sql: () => statements().join("\n\n"),
95
+ async insert(sql) {
96
+ for (const text of statements()) await sql.queryRaw(text);
97
+ }
98
+ };
99
+ }
100
+ //#endregion
101
+ export { isSeed as n, defineSeed as t };