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.
- package/LICENSE +21 -0
- package/README.md +88 -1
- package/bin/better-supabase.js +4 -0
- package/dist/casing-da0uRTqt.js +14 -0
- package/dist/claims-CoTobNC-.js +25 -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-DrSgDOQC.js +6646 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +112 -0
- package/dist/compiler-CZ9nIuqy.d.ts +14 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.js +3 -0
- package/dist/config-ClDXmVtY.js +277 -0
- package/dist/define-BXMYD9AY.js +1071 -0
- package/dist/define-e3zI4_4g.d.ts +679 -0
- package/dist/edge/index.d.ts +43 -0
- package/dist/edge/index.js +103 -0
- package/dist/entitlements-CTEBm7IB.js +20 -0
- package/dist/entitlements-Dng-ctuT.d.ts +42 -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-BuzTgn2D.js +135 -0
- package/dist/events-DOt3nSO-.d.ts +84 -0
- package/dist/executor-BYXQ9AGn.d.ts +25 -0
- package/dist/executor-CGdNQ27g.d.ts +195 -0
- package/dist/executor-DeppvirK.js +89 -0
- package/dist/guards-BnzDvFrz.js +10 -0
- package/dist/hono/index.d.ts +45 -0
- package/dist/hono/index.js +70 -0
- package/dist/index-Bw1bwtC0.d.ts +144 -0
- package/dist/index-CBDufH1M.d.ts +120 -0
- package/dist/index-CG69s15v.d.ts +38 -0
- package/dist/index-Ciu5Xlml.d.ts +74 -0
- package/dist/index-D4v7sncu.d.ts +62 -0
- package/dist/index-DOsjFvMf.d.ts +618 -0
- package/dist/index-DbxFr7XG.d.ts +76 -0
- package/dist/index-DuP14VHo.d.ts +216 -0
- package/dist/index-Dy6kuF0P.d.ts +71 -0
- package/dist/index-K-3WM2Hp.d.ts +117 -0
- package/dist/index.d.ts +110 -0
- package/dist/index.js +43 -0
- package/dist/invalidate-DbxuL2jL.js +18 -0
- package/dist/jobs/index.d.ts +173 -0
- package/dist/jobs/index.js +478 -0
- package/dist/json-schema-BpYqkCKf.js +182 -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-D8rV1Ldw.js +168 -0
- package/dist/live-DJyZMD6w.d.ts +191 -0
- package/dist/logger-glM28NVN.js +21 -0
- package/dist/mcp/index.d.ts +142 -0
- package/dist/mcp/index.js +463 -0
- package/dist/mfa-CpLfJSde.js +541 -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 +427 -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/permdock-sql-CghlHPuH.js +117 -0
- package/dist/plugin-BqR6wKMB.js +23 -0
- package/dist/plugin-C4TR9J0Z.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 +89 -0
- package/dist/plugins/rules/index.js +167 -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 +49 -0
- package/dist/plugins/tenant/index.js +75 -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-R2m_DkXi.d.ts +37 -0
- package/dist/postgres/index.d.ts +28 -0
- package/dist/postgres/index.js +79 -0
- package/dist/postgrest-cAEQ_IQN.d.ts +43 -0
- package/dist/problem-BTkTtRV9.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-C83BdBGt.js +242 -0
- package/dist/read-set-4lI3W_gZ.js +216 -0
- package/dist/realtime/index.d.ts +4 -0
- package/dist/realtime/index.js +268 -0
- package/dist/resolve-CjHILk2w.d.ts +170 -0
- package/dist/resource-BHEWmQJK.js +175 -0
- package/dist/resource-CgZa3YG_.d.ts +65 -0
- package/dist/respond-DMcbQwfN.js +383 -0
- package/dist/respond-Dne1ARSa.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-C3LxnQ0e.d.ts +22 -0
- package/dist/session-CqEwzVup.js +20 -0
- package/dist/shared-_6-uPL7E.js +57 -0
- package/dist/simplify-bwJpLnaS.js +665 -0
- package/dist/spec-pins-C3oc-Tdp.js +20 -0
- package/dist/spec-pins-CqorWDnO.d.ts +20 -0
- package/dist/sql-DTjA1LPj.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-C5W-rBfR.js +566 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage-DuGn38fr.js +527 -0
- package/dist/tables-rZxRxNPW.js +59 -0
- package/dist/template-mbvlysOo.d.ts +6 -0
- package/dist/testing/index.d.ts +325 -0
- package/dist/testing/index.js +819 -0
- package/dist/types-6cYK9pbJ.js +29 -0
- package/dist/types-C83v5SQz.d.ts +248 -0
- package/dist/version-Cqq1m3UY.js +4 -0
- package/dist/view-D1s0wa0T.d.ts +49 -0
- package/dist/view-aOYizTeE.js +36 -0
- package/dist/webhooks/index.d.ts +152 -0
- package/dist/webhooks/index.js +2 -0
- package/dist/webhooks-DMipjhSP.js +139 -0
- package/package.json +250 -17
- package/schemas/config-v1.json +373 -0
- package/schemas/doctor-report-v1.json +106 -0
- package/schemas/snapshot-v2.json +1006 -0
- package/skills/better-supabase/SKILL.md +84 -0
- package/skills/better-supabase/references/plugins.md +65 -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 +66 -0
- package/index.js +0 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { t as DbException } from "./errors-Bl3rkOzo.js";
|
|
2
|
+
import { t as invalidationTargets } from "./tables-rZxRxNPW.js";
|
|
3
|
+
import { t as invalidateTables } from "./invalidate-DbxuL2jL.js";
|
|
4
|
+
//#region src/query/index.ts
|
|
5
|
+
/** Query errors stay `DbException`s, even for an `sb` with `mapError()`. */
|
|
6
|
+
const asException = (error) => new DbException(error);
|
|
7
|
+
function isSkip(value) {
|
|
8
|
+
return typeof value === "symbol";
|
|
9
|
+
}
|
|
10
|
+
function withoutSignal(value) {
|
|
11
|
+
if (typeof value !== "object" || value === null) return value;
|
|
12
|
+
const { signal: _signal, ...rest } = value;
|
|
13
|
+
return rest;
|
|
14
|
+
}
|
|
15
|
+
function specQuery(runtime, spec, key) {
|
|
16
|
+
const stale = runtime.staleTime === void 0 ? {} : { staleTime: runtime.staleTime };
|
|
17
|
+
if (isSkip(spec)) return {
|
|
18
|
+
queryKey: key ?? ["bs", "$skip"],
|
|
19
|
+
queryFn: spec,
|
|
20
|
+
meta: { bsTables: [] },
|
|
21
|
+
...stale
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
queryKey: key ?? [
|
|
25
|
+
"bs",
|
|
26
|
+
spec.table,
|
|
27
|
+
spec.method,
|
|
28
|
+
...spec.args
|
|
29
|
+
],
|
|
30
|
+
queryFn: ({ signal }) => runtime.db().$run(spec, { signal }).orThrow(asException),
|
|
31
|
+
meta: { bsTables: runtime.sb.tablesOf(spec) },
|
|
32
|
+
...stale
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function tableQueries(runtime, table) {
|
|
36
|
+
const key = ["bs", table];
|
|
37
|
+
const specs = runtime.sb.spec[table];
|
|
38
|
+
const stale = runtime.staleTime === void 0 ? {} : { staleTime: runtime.staleTime };
|
|
39
|
+
const read = (method) => (args) => isSkip(args) ? specQuery(runtime, args, [
|
|
40
|
+
...key,
|
|
41
|
+
method,
|
|
42
|
+
"$skip"
|
|
43
|
+
]) : specQuery(runtime, specs[method](withoutSignal(args)));
|
|
44
|
+
const repo = () => {
|
|
45
|
+
const found = runtime.db()[table];
|
|
46
|
+
if (!found) throw new TypeError(`better-supabase: unknown table "${table}"`);
|
|
47
|
+
return found;
|
|
48
|
+
};
|
|
49
|
+
const call = (method, ...args) => {
|
|
50
|
+
const fn = repo()[method];
|
|
51
|
+
if (!fn) throw new TypeError(`better-supabase: ${table}.${method} is not available`);
|
|
52
|
+
return fn(...args);
|
|
53
|
+
};
|
|
54
|
+
const mutation = (op, run, tables) => ({
|
|
55
|
+
mutationKey: [...key, op],
|
|
56
|
+
mutationFn: (variables) => run(variables).orThrow(asException),
|
|
57
|
+
onSuccess: (_data, _variables, _result, context) => invalidateTables(context.client, tables)
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
key,
|
|
61
|
+
findMany: read("findMany"),
|
|
62
|
+
findFirst: read("findFirst"),
|
|
63
|
+
findUnique: read("findUnique"),
|
|
64
|
+
count: read("count"),
|
|
65
|
+
aggregate: read("aggregate"),
|
|
66
|
+
exists: read("exists"),
|
|
67
|
+
paginate: read("paginate"),
|
|
68
|
+
findById: (id, args) => isSkip(id) ? specQuery(runtime, id, [
|
|
69
|
+
...key,
|
|
70
|
+
"findById",
|
|
71
|
+
"$skip"
|
|
72
|
+
]) : specQuery(runtime, specs["findById"](id, withoutSignal(args))),
|
|
73
|
+
infinite: (args) => {
|
|
74
|
+
const base = withoutSignal(args);
|
|
75
|
+
return {
|
|
76
|
+
queryKey: [
|
|
77
|
+
...key,
|
|
78
|
+
"infinite",
|
|
79
|
+
base
|
|
80
|
+
],
|
|
81
|
+
queryFn: ({ signal, pageParam }) => runtime.db().$run(specs["paginate"]({
|
|
82
|
+
...base,
|
|
83
|
+
after: pageParam
|
|
84
|
+
}), { signal }).orThrow(asException),
|
|
85
|
+
initialPageParam: null,
|
|
86
|
+
getNextPageParam: (last) => last.nextCursor ?? void 0,
|
|
87
|
+
meta: { bsTables: runtime.sb.tablesOf(specs["paginate"]({
|
|
88
|
+
...base,
|
|
89
|
+
after: null
|
|
90
|
+
})) },
|
|
91
|
+
...stale
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
infinitePages: (args) => {
|
|
95
|
+
const base = withoutSignal(args);
|
|
96
|
+
return {
|
|
97
|
+
queryKey: [
|
|
98
|
+
...key,
|
|
99
|
+
"infinitePages",
|
|
100
|
+
base
|
|
101
|
+
],
|
|
102
|
+
queryFn: ({ signal, pageParam }) => runtime.db().$run(specs["paginate"]({
|
|
103
|
+
...base,
|
|
104
|
+
page: pageParam
|
|
105
|
+
}), { signal }).orThrow(asException),
|
|
106
|
+
initialPageParam: base.page ?? 1,
|
|
107
|
+
getNextPageParam: (last) => last.page.hasMore ? last.page.number + 1 : void 0,
|
|
108
|
+
meta: { bsTables: runtime.sb.tablesOf(specs["paginate"](base)) },
|
|
109
|
+
...stale
|
|
110
|
+
};
|
|
111
|
+
},
|
|
112
|
+
create: (args) => mutation("create", (data) => call("create", data, args), [table]),
|
|
113
|
+
update: (args) => mutation("update", (variables) => call("update", variables.id, variables.patch, args), [table]),
|
|
114
|
+
upsert: (args) => mutation("upsert", (data) => call("upsert", data, args), [table]),
|
|
115
|
+
delete: (args) => mutation("delete", (id) => call("delete", id, args), invalidationTargets(runtime.sb.meta, table))
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* TanStack Query option factories for every table, framework-neutral: pass
|
|
120
|
+
* them to `useQuery` (React), `createQuery` (Svelte, Solid), `useQuery`
|
|
121
|
+
* (Vue) or `injectQuery` (Angular). Payload types flow into the cache, errors
|
|
122
|
+
* are `DbException`s, and mutations invalidate every query that read a
|
|
123
|
+
* changed table.
|
|
124
|
+
*
|
|
125
|
+
* ```ts
|
|
126
|
+
* const q = createQueries(sb, () => browser.db);
|
|
127
|
+
* const { data } = useQuery(q.customers.findMany({ select: ['id', 'name'] }));
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
function createQueries(sb, db, options = {}) {
|
|
131
|
+
const runtime = {
|
|
132
|
+
sb,
|
|
133
|
+
db: typeof db === "function" ? db : () => db,
|
|
134
|
+
staleTime: options.staleTime
|
|
135
|
+
};
|
|
136
|
+
const stale = options.staleTime === void 0 ? {} : { staleTime: options.staleTime };
|
|
137
|
+
const queries = {
|
|
138
|
+
key: ["bs"],
|
|
139
|
+
$spec: (spec) => specQuery(runtime, spec),
|
|
140
|
+
$prefetch: (client, spec) => client.prefetchQuery(specQuery(runtime, spec)),
|
|
141
|
+
$rpc: (name, args, rpcOptions = {}) => ({
|
|
142
|
+
queryKey: [
|
|
143
|
+
"bs",
|
|
144
|
+
"$rpc",
|
|
145
|
+
name,
|
|
146
|
+
isSkip(args) ? "$skip" : args ?? {}
|
|
147
|
+
],
|
|
148
|
+
queryFn: isSkip(args) ? args : ({ signal }) => runtime.db().$rpc(name, args ?? {}, { signal }).orThrow(asException),
|
|
149
|
+
meta: { bsTables: rpcOptions.tables ?? [] },
|
|
150
|
+
...stale
|
|
151
|
+
}),
|
|
152
|
+
$rpcMutation: (name) => ({
|
|
153
|
+
mutationKey: [
|
|
154
|
+
"bs",
|
|
155
|
+
"$rpc",
|
|
156
|
+
name
|
|
157
|
+
],
|
|
158
|
+
mutationFn: (args) => runtime.db().$rpc(name, args ?? {}).orThrow(asException),
|
|
159
|
+
onSuccess: (_data, _variables, _result, context) => invalidateTables(context.client, (sb.options.rpc?.[name]?.invalidates ?? []).flatMap((table) => invalidationTargets(sb.meta, table)))
|
|
160
|
+
})
|
|
161
|
+
};
|
|
162
|
+
for (const table of Object.keys(sb.meta.tables)) {
|
|
163
|
+
let built;
|
|
164
|
+
Object.defineProperty(queries, table, {
|
|
165
|
+
enumerable: true,
|
|
166
|
+
get: () => {
|
|
167
|
+
built ??= tableQueries(runtime, table);
|
|
168
|
+
return built;
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return queries;
|
|
173
|
+
}
|
|
174
|
+
/** Invalidates every query that read a table in the target. */
|
|
175
|
+
function queryCache(client) {
|
|
176
|
+
return {
|
|
177
|
+
name: "tanstack-query",
|
|
178
|
+
invalidate: (target) => invalidateTables(client, target.tables)
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Invalidates affected queries for every mutation and declared RPC `sb` performs in this runtime. */
|
|
182
|
+
function invalidateOnMutation(sb, client) {
|
|
183
|
+
return sb.cache(queryCache(client));
|
|
184
|
+
}
|
|
185
|
+
//#endregion
|
|
186
|
+
export { invalidateOnMutation as n, queryCache as r, createQueries as t };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { s as Impersonator } from "../mfa-TVia554c.js";
|
|
2
|
+
import { t as AuthSession } from "../view-D1s0wa0T.js";
|
|
3
|
+
import { r as LiveCountSeed } from "../live-DJyZMD6w.js";
|
|
4
|
+
import { n as MembershipClaim, r as hasEntitlement, t as EntitlementKey } from "../entitlements-Dng-ctuT.js";
|
|
5
|
+
import { n as SessionProviderProps, r as useSession, t as SessionProvider } from "../session-C3LxnQ0e.js";
|
|
6
|
+
import { a as BrowserLike, c as LiveCountHookOptions, d as createHooks, f as useAuth, g as useSupabase, h as useLiveQuery, i as BroadcastOptions, l as LiveQueryHookOptions, m as useLiveCount, n as BetterSupabaseProvider, o as ClaimsOf, p as useBroadcast, r as BetterSupabaseProviderProps, s as LiveCount, t as BetterHooks, u as ProfileOf } from "../index-CBDufH1M.js";
|
|
7
|
+
export { type AuthSession, BetterHooks, BetterSupabaseProvider, BetterSupabaseProviderProps, BroadcastOptions, BrowserLike, ClaimsOf, type EntitlementKey, type Impersonator, LiveCount, LiveCountHookOptions, type LiveCountSeed, LiveQueryHookOptions, type MembershipClaim, ProfileOf, SessionProvider, type SessionProviderProps, createHooks, hasEntitlement, useAuth, useBroadcast, useLiveCount, useLiveQuery, useSession, useSupabase };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { n as useSession, t as SessionProvider } from "../session-CqEwzVup.js";
|
|
3
|
+
import { t as hasEntitlement } from "../entitlements-CTEBm7IB.js";
|
|
4
|
+
import { a as useLiveCount, i as useBroadcast, n as createHooks, o as useLiveQuery, r as useAuth, s as useSupabase, t as BetterSupabaseProvider } from "../react-C83BdBGt.js";
|
|
5
|
+
export { BetterSupabaseProvider, SessionProvider, createHooks, hasEntitlement, useAuth, useBroadcast, useLiveCount, useLiveQuery, useSession, useSupabase };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { t as AuthSession } from "../view-D1s0wa0T.js";
|
|
2
|
+
import { n as SessionProviderProps, t as SessionProvider } from "../session-C3LxnQ0e.js";
|
|
3
|
+
import { a as BrowserLike, f as useAuth, g as useSupabase, h as useLiveQuery, i as BroadcastOptions, l as LiveQueryHookOptions, n as BetterSupabaseProvider, o as ClaimsOf, p as useBroadcast, r as BetterSupabaseProviderProps, t as BetterHooks, u as ProfileOf } from "../index-CBDufH1M.js";
|
|
4
|
+
//#region src/react/server.d.ts
|
|
5
|
+
/** The `react-server` build of `useSession`: await `next.session()` instead. */
|
|
6
|
+
export declare const useSession: <C = unknown, P = unknown>() => AuthSession<C, P>;
|
|
7
|
+
/**
|
|
8
|
+
* The `react-server` build of `createHooks`: importing a module that creates
|
|
9
|
+
* hooks is safe on the server, calling a hook there throws.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createHooks<B extends BrowserLike>(): BetterHooks<B>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { type AuthSession, type BetterHooks, BetterSupabaseProvider, type BetterSupabaseProviderProps, type BroadcastOptions, type BrowserLike, type ClaimsOf, type LiveQueryHookOptions, type ProfileOf, SessionProvider, type SessionProviderProps, useAuth, useBroadcast, useLiveQuery, useSupabase };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { i as useBroadcast, o as useLiveQuery, r as useAuth, s as useSupabase, t as BetterSupabaseProvider } from "../react-C83BdBGt.js";
|
|
2
|
+
import { SessionProvider } from "./session.js";
|
|
3
|
+
//#region src/react/server.ts
|
|
4
|
+
function clientOnly(name) {
|
|
5
|
+
return () => {
|
|
6
|
+
throw new Error(`better-supabase: ${name}() runs in Client Components only. In Server Components use \`next.server()\` or \`db.$run(spec)\`.`);
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
/** The `react-server` build of `useSession`: await `next.session()` instead. */
|
|
10
|
+
const useSession = clientOnly("useSession");
|
|
11
|
+
/**
|
|
12
|
+
* The `react-server` build of `createHooks`: importing a module that creates
|
|
13
|
+
* hooks is safe on the server, calling a hook there throws.
|
|
14
|
+
*/
|
|
15
|
+
function createHooks() {
|
|
16
|
+
return {
|
|
17
|
+
useDb: clientOnly("useDb"),
|
|
18
|
+
useQueries: clientOnly("useQueries"),
|
|
19
|
+
useSupabase: clientOnly("useSupabase"),
|
|
20
|
+
useAuth: clientOnly("useAuth"),
|
|
21
|
+
useSession: clientOnly("useSession")
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { BetterSupabaseProvider, SessionProvider, createHooks, useAuth, useBroadcast, useLiveQuery, useSession, useSupabase };
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { i as tenantClaimPaths, n as claimAt, r as claimsOf } from "./claims-CoTobNC-.js";
|
|
2
|
+
import { t as invalidateTables } from "./invalidate-DbxuL2jL.js";
|
|
3
|
+
import { n as liveQuery, t as liveCount } from "./live-D8rV1Ldw.js";
|
|
4
|
+
import { n as useSession } from "./session-CqEwzVup.js";
|
|
5
|
+
import { createContext, createElement, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
6
|
+
//#region src/react/index.ts
|
|
7
|
+
const BrowserContext = createContext(null);
|
|
8
|
+
const LOADING = {
|
|
9
|
+
status: "loading",
|
|
10
|
+
user: null,
|
|
11
|
+
claims: null
|
|
12
|
+
};
|
|
13
|
+
/** Provides the browser client to the hooks. */
|
|
14
|
+
function BetterSupabaseProvider(props) {
|
|
15
|
+
const { browser, queryClient } = props;
|
|
16
|
+
const user = useRef(void 0);
|
|
17
|
+
useEffect(() => browser.auth.subscribe(() => {
|
|
18
|
+
const snapshot = browser.auth.current();
|
|
19
|
+
if (snapshot.status === "loading") return;
|
|
20
|
+
const id = snapshot.user?.id ?? null;
|
|
21
|
+
if (user.current !== void 0 && user.current !== id) queryClient?.removeQueries({ queryKey: ["bs"] });
|
|
22
|
+
user.current = id;
|
|
23
|
+
}), [browser, queryClient]);
|
|
24
|
+
const value = useMemo(() => ({
|
|
25
|
+
browser,
|
|
26
|
+
queryClient
|
|
27
|
+
}), [browser, queryClient]);
|
|
28
|
+
return createElement(BrowserContext.Provider, { value }, props.children);
|
|
29
|
+
}
|
|
30
|
+
function useBrowserContext() {
|
|
31
|
+
const value = useContext(BrowserContext);
|
|
32
|
+
if (!value) throw new Error("better-supabase: wrap your app in <BetterSupabaseProvider browser={browser}>");
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function useBrowser() {
|
|
36
|
+
return useBrowserContext().browser;
|
|
37
|
+
}
|
|
38
|
+
/** The session state for UI: `loading`, `signed-out` or `signed-in` with the user and claims. */
|
|
39
|
+
function useAuth() {
|
|
40
|
+
const browser = useBrowser();
|
|
41
|
+
return useSyncExternalStore(browser.auth.subscribe, browser.auth.current, () => LOADING);
|
|
42
|
+
}
|
|
43
|
+
function useSupabase() {
|
|
44
|
+
return useBrowser().supabase;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Hooks typed for your schema.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* export const { useDb, useQueries, useAuth } = createHooks<typeof browser>();
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function createHooks() {
|
|
54
|
+
return {
|
|
55
|
+
useDb() {
|
|
56
|
+
const browser = useBrowser();
|
|
57
|
+
useAuth();
|
|
58
|
+
return browser.db;
|
|
59
|
+
},
|
|
60
|
+
useQueries() {
|
|
61
|
+
const browser = useBrowser();
|
|
62
|
+
useAuth();
|
|
63
|
+
return browser.queries;
|
|
64
|
+
},
|
|
65
|
+
useSupabase,
|
|
66
|
+
useAuth,
|
|
67
|
+
useSession
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Subscribes to a topic while mounted. Pass `null` values to pause. It
|
|
72
|
+
* resubscribes when the topic or the signed-in user changes.
|
|
73
|
+
*
|
|
74
|
+
* ```ts
|
|
75
|
+
* useBroadcast(customersTopic, orgId ? { orgId } : null, {}, { invalidate: ['customers'] });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
function useBroadcast(topic, values, handlers, options) {
|
|
79
|
+
const { browser, queryClient } = useBrowserContext();
|
|
80
|
+
const auth = useAuth();
|
|
81
|
+
const [status, setStatus] = useState("closed");
|
|
82
|
+
const latest = useRef({
|
|
83
|
+
handlers,
|
|
84
|
+
options
|
|
85
|
+
});
|
|
86
|
+
latest.current = {
|
|
87
|
+
handlers,
|
|
88
|
+
options
|
|
89
|
+
};
|
|
90
|
+
const name = values ? topic.topic(values) : null;
|
|
91
|
+
const userId = auth.user?.id ?? null;
|
|
92
|
+
if (options?.invalidate && !queryClient) throw new Error("better-supabase: useBroadcast({ invalidate }) needs <BetterSupabaseProvider queryClient={...}>");
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (!name || auth.status === "loading") return void 0;
|
|
95
|
+
const matched = topic.match(name);
|
|
96
|
+
if (!matched) return void 0;
|
|
97
|
+
const forward = (payload, message) => {
|
|
98
|
+
const current = latest.current;
|
|
99
|
+
const table = current.handlers;
|
|
100
|
+
(table?.[message.event] ?? table?.["*"])?.(payload, message);
|
|
101
|
+
const invalidate = current.options?.invalidate;
|
|
102
|
+
if (!invalidate || !queryClient) return;
|
|
103
|
+
if (typeof invalidate !== "function") {
|
|
104
|
+
invalidateTables(queryClient, invalidate);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const queryKey of invalidate(message)) queryClient.invalidateQueries({ queryKey });
|
|
108
|
+
};
|
|
109
|
+
const subscription = topic.subscribe(browser.supabase, matched, { "*": forward }, {
|
|
110
|
+
...latest.current.options?.self === void 0 ? {} : { self: latest.current.options.self },
|
|
111
|
+
onStatus: setStatus,
|
|
112
|
+
onInvalid: (message, issues) => latest.current.options?.onInvalid?.(message, issues)
|
|
113
|
+
});
|
|
114
|
+
return () => {
|
|
115
|
+
subscription.unsubscribe();
|
|
116
|
+
setStatus("closed");
|
|
117
|
+
};
|
|
118
|
+
}, [
|
|
119
|
+
browser,
|
|
120
|
+
topic,
|
|
121
|
+
name,
|
|
122
|
+
userId,
|
|
123
|
+
auth.status,
|
|
124
|
+
queryClient
|
|
125
|
+
]);
|
|
126
|
+
return status;
|
|
127
|
+
}
|
|
128
|
+
function claimedTenant(auth, meta) {
|
|
129
|
+
if (auth.status !== "signed-in") return void 0;
|
|
130
|
+
for (const path of tenantClaimPaths(claimsOf(meta).tenant)) {
|
|
131
|
+
const value = claimAt(auth.claims, path);
|
|
132
|
+
if (value !== void 0) return value;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Keeps a query fresh: invalidates every cached query that read a table the
|
|
137
|
+
* spec touches whenever one of them changes. Pass `null` to pause. It
|
|
138
|
+
* resubscribes when the spec, tenant or user changes.
|
|
139
|
+
*
|
|
140
|
+
* ```ts
|
|
141
|
+
* const spec = sb.spec.customers.findMany({ include: { notes: true } });
|
|
142
|
+
* const { data } = useQuery(q.$spec(spec));
|
|
143
|
+
* useLiveQuery(spec);
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
function useLiveQuery(spec, options = {}) {
|
|
147
|
+
const { browser, queryClient } = useBrowserContext();
|
|
148
|
+
const auth = useAuth();
|
|
149
|
+
const [status, setStatus] = useState("closed");
|
|
150
|
+
if (spec && !queryClient) throw new Error("better-supabase: useLiveQuery needs <BetterSupabaseProvider queryClient={...}>");
|
|
151
|
+
const key = spec ? JSON.stringify(spec) : null;
|
|
152
|
+
const tenant = options.tenant ?? claimedTenant(auth, browser.sb.meta);
|
|
153
|
+
const userId = auth.user?.id ?? null;
|
|
154
|
+
const debounceMs = options.debounceMs;
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
if (!key || !queryClient || auth.status === "loading") return void 0;
|
|
157
|
+
const live = liveQuery(browser.sb, browser.supabase, JSON.parse(key), {
|
|
158
|
+
onChange: (tables) => void invalidateTables(queryClient, tables),
|
|
159
|
+
onStatus: setStatus,
|
|
160
|
+
...tenant === void 0 ? {} : { tenant },
|
|
161
|
+
...debounceMs === void 0 ? {} : { debounceMs }
|
|
162
|
+
});
|
|
163
|
+
return () => {
|
|
164
|
+
live.unsubscribe();
|
|
165
|
+
};
|
|
166
|
+
}, [
|
|
167
|
+
browser,
|
|
168
|
+
queryClient,
|
|
169
|
+
key,
|
|
170
|
+
tenant,
|
|
171
|
+
userId,
|
|
172
|
+
auth.status,
|
|
173
|
+
debounceMs
|
|
174
|
+
]);
|
|
175
|
+
return status;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* A count that stays current: refetches only the `count` spec (a HEAD
|
|
179
|
+
* request) after each debounced change to a table it reads, and after the
|
|
180
|
+
* channel rejoins. Takes a spec or a `next.liveCount()` seed; pass `null` to
|
|
181
|
+
* pause. Doesn't need a `QueryClient`.
|
|
182
|
+
*
|
|
183
|
+
* ```tsx
|
|
184
|
+
* const { count } = useLiveCount(seed); // seed = await next.liveCount(spec)
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
function useLiveCount(source, options = {}) {
|
|
188
|
+
const { browser } = useBrowserContext();
|
|
189
|
+
const auth = useAuth();
|
|
190
|
+
const seed = source && "spec" in source ? source : void 0;
|
|
191
|
+
const spec = seed ? seed.spec : source;
|
|
192
|
+
const initial = seed?.count ?? options.initial;
|
|
193
|
+
const [state, setState] = useState({
|
|
194
|
+
key: null,
|
|
195
|
+
count: void 0,
|
|
196
|
+
error: void 0
|
|
197
|
+
});
|
|
198
|
+
const [status, setStatus] = useState("closed");
|
|
199
|
+
const key = spec ? JSON.stringify(spec) : null;
|
|
200
|
+
const tenant = options.tenant ?? claimedTenant(auth, browser.sb.meta);
|
|
201
|
+
const userId = auth.user?.id ?? null;
|
|
202
|
+
const debounceMs = options.debounceMs;
|
|
203
|
+
const hasInitial = initial !== void 0;
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (!key || auth.status === "loading") return void 0;
|
|
206
|
+
const live = liveCount(browser.sb, browser.supabase, browser.db, JSON.parse(key), {
|
|
207
|
+
immediate: !hasInitial,
|
|
208
|
+
onCount: (count) => setState({
|
|
209
|
+
key,
|
|
210
|
+
count,
|
|
211
|
+
error: void 0
|
|
212
|
+
}),
|
|
213
|
+
onError: (error) => setState((previous) => ({
|
|
214
|
+
key,
|
|
215
|
+
count: previous.key === key ? previous.count : void 0,
|
|
216
|
+
error
|
|
217
|
+
})),
|
|
218
|
+
onStatus: setStatus,
|
|
219
|
+
...tenant === void 0 ? {} : { tenant },
|
|
220
|
+
...debounceMs === void 0 ? {} : { debounceMs }
|
|
221
|
+
});
|
|
222
|
+
return () => {
|
|
223
|
+
live.unsubscribe();
|
|
224
|
+
};
|
|
225
|
+
}, [
|
|
226
|
+
browser,
|
|
227
|
+
key,
|
|
228
|
+
tenant,
|
|
229
|
+
userId,
|
|
230
|
+
auth.status,
|
|
231
|
+
debounceMs,
|
|
232
|
+
hasInitial
|
|
233
|
+
]);
|
|
234
|
+
const fresh = state.key === key;
|
|
235
|
+
return {
|
|
236
|
+
count: fresh && state.count !== void 0 ? state.count : initial,
|
|
237
|
+
status,
|
|
238
|
+
error: fresh ? state.error : void 0
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
export { useLiveCount as a, useBroadcast as i, createHooks as n, useLiveQuery as o, useAuth as r, useSupabase as s, BetterSupabaseProvider as t };
|