@weirdscience/based-client 0.1.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/README.md +220 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/index.js +127 -33
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# @weirdscience/based-client
|
|
2
|
+
|
|
3
|
+
React SDK for [Based](https://based.weirdscience.dev) — a minimal self-hosted Backend-as-a-Service.
|
|
4
|
+
|
|
5
|
+
Hooks for auth, queries, and mutations. Type-safe end-to-end when paired with `based typegen`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add @weirdscience/based-client
|
|
11
|
+
# or: npm install @weirdscience/based-client
|
|
12
|
+
# or: pnpm add @weirdscience/based-client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peer dependency: `react >=18`.
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { createClient, BasedProvider } from "@weirdscience/based-client";
|
|
21
|
+
|
|
22
|
+
const based = createClient({
|
|
23
|
+
url: process.env.NEXT_PUBLIC_BASED_URL!,
|
|
24
|
+
anonKey: process.env.NEXT_PUBLIC_BASED_ANON_KEY!,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default function App({ children }: { children: React.ReactNode }) {
|
|
28
|
+
return <BasedProvider client={based}>{children}</BasedProvider>;
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Hooks
|
|
33
|
+
|
|
34
|
+
### `useUser()`
|
|
35
|
+
|
|
36
|
+
Current authenticated user.
|
|
37
|
+
|
|
38
|
+
```tsx
|
|
39
|
+
import { useUser } from "@weirdscience/based-client";
|
|
40
|
+
|
|
41
|
+
function Profile() {
|
|
42
|
+
const { user, isLoading } = useUser();
|
|
43
|
+
if (isLoading) return <p>...</p>;
|
|
44
|
+
if (!user) return <p>Not logged in</p>;
|
|
45
|
+
return <p>{user.email}</p>;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### `useQuery(table, options?)`
|
|
50
|
+
|
|
51
|
+
Read rows from a table. Returns `{ data, total, isLoading, error, refetch }`.
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { useQuery } from "@weirdscience/based-client";
|
|
55
|
+
|
|
56
|
+
const { data, total, isLoading } = useQuery("posts", {
|
|
57
|
+
filter: { status: "published" },
|
|
58
|
+
limit: 20,
|
|
59
|
+
offset: 0,
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `useMutation(table, operation)`
|
|
64
|
+
|
|
65
|
+
Write rows. `operation` is `"create" | "update" | "delete"`. Returns `{ mutate, isLoading, error }`.
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
import { useMutation } from "@weirdscience/based-client";
|
|
69
|
+
|
|
70
|
+
function NewPost() {
|
|
71
|
+
const { mutate, isLoading } = useMutation("posts", "create");
|
|
72
|
+
return (
|
|
73
|
+
<button
|
|
74
|
+
onClick={() => mutate({ title: "Hello", content: "World" })}
|
|
75
|
+
disabled={isLoading}
|
|
76
|
+
>
|
|
77
|
+
Create
|
|
78
|
+
</button>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`update` and `delete` require an `id` field:
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
const { mutate: update } = useMutation("posts", "update");
|
|
87
|
+
await update({ id: "abc", title: "Renamed" });
|
|
88
|
+
|
|
89
|
+
const { mutate: remove } = useMutation("posts", "delete");
|
|
90
|
+
await remove({ id: "abc" });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Auth
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { useBasedClient } from "@weirdscience/based-client";
|
|
97
|
+
|
|
98
|
+
function LoginForm() {
|
|
99
|
+
const client = useBasedClient();
|
|
100
|
+
|
|
101
|
+
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
const fd = new FormData(e.currentTarget);
|
|
104
|
+
await client.auth.signIn(
|
|
105
|
+
fd.get("email") as string,
|
|
106
|
+
fd.get("password") as string
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<form onSubmit={handleSubmit}>
|
|
112
|
+
<input name="email" type="email" />
|
|
113
|
+
<input name="password" type="password" />
|
|
114
|
+
<button type="submit">Sign in</button>
|
|
115
|
+
</form>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Methods on `client.auth`:
|
|
121
|
+
|
|
122
|
+
- `signUp(email, password)` → creates an account and signs in
|
|
123
|
+
- `signIn(email, password)` → signs in
|
|
124
|
+
- `signOut()` → invalidates the session
|
|
125
|
+
- `refreshSession()` → manually refresh (happens automatically on 401)
|
|
126
|
+
|
|
127
|
+
Access tokens auto-refresh on `401`.
|
|
128
|
+
|
|
129
|
+
## Session persistence
|
|
130
|
+
|
|
131
|
+
Sessions persist across page reloads via `localStorage` by default. On mount, the client restores the saved session and validates it by calling `/auth/me`.
|
|
132
|
+
|
|
133
|
+
Use `client.ready()` or `useUser().isLoading` to avoid flashing a logged-out UI during hydration:
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
const { user, isLoading } = useUser();
|
|
137
|
+
if (isLoading) return <Spinner />;
|
|
138
|
+
if (!user) return <LoginForm />;
|
|
139
|
+
return <Dashboard user={user} />;
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Opt out or use a custom storage adapter:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
// Disable entirely (in-memory only)
|
|
146
|
+
createClient({ url, anonKey, storage: false });
|
|
147
|
+
|
|
148
|
+
// Custom storage (cookies, IndexedDB, React Native AsyncStorage, etc.)
|
|
149
|
+
createClient({
|
|
150
|
+
url,
|
|
151
|
+
anonKey,
|
|
152
|
+
storage: {
|
|
153
|
+
getItem: (k) => AsyncStorage.getItem(k),
|
|
154
|
+
setItem: (k, v) => AsyncStorage.setItem(k, v),
|
|
155
|
+
removeItem: (k) => AsyncStorage.removeItem(k),
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Storage adapters can return promises — the client awaits them.
|
|
161
|
+
|
|
162
|
+
## Type safety
|
|
163
|
+
|
|
164
|
+
Generate types for your tables from the server:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
based typegen
|
|
168
|
+
# writes based.d.ts
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Pass the generated `Tables` type as a generic:
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import type { Tables } from "./based.d.ts";
|
|
175
|
+
import { useQuery, useMutation } from "@weirdscience/based-client";
|
|
176
|
+
|
|
177
|
+
// data is typed as Tables["posts"][]
|
|
178
|
+
const { data } = useQuery<Tables, "posts">("posts", {
|
|
179
|
+
filter: { status: "published" }, // typed keys
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const { mutate } = useMutation<Tables, "posts">("posts", "create");
|
|
183
|
+
await mutate({ title: "Hello", content: "World" }); // typed payload
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Re-run `based typegen` after any schema change.
|
|
187
|
+
|
|
188
|
+
## Row-level isolation
|
|
189
|
+
|
|
190
|
+
If a table has a `user_id` (or `userId`) column, Based auto-scopes CRUD to the authenticated user. No configuration needed — just add the column:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
based table create notes user_id:text:required title:text:required body:text
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
After that:
|
|
197
|
+
|
|
198
|
+
- `useQuery("notes")` only returns the caller's notes
|
|
199
|
+
- `useMutation("notes", "create")` auto-fills `user_id`
|
|
200
|
+
- Other users' rows return 404
|
|
201
|
+
|
|
202
|
+
## Upsert
|
|
203
|
+
|
|
204
|
+
`PUT /api/:table/:id` creates if missing, updates if present. From the SDK:
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
const { mutate: upsert } = useMutation("preferences", "update");
|
|
208
|
+
|
|
209
|
+
// The URL id becomes the row id — perfect for deterministic keys like userId:key
|
|
210
|
+
await upsert({ id: "alice:theme", value: "dark" });
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Links
|
|
214
|
+
|
|
215
|
+
- [Based docs](https://based.weirdscience.dev/docs)
|
|
216
|
+
- [GitHub](https://github.com/WeirdScience-dev/based)
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAGlB,WAAW,EAEZ,MAAM,SAAS,CAAC;AAqBjB,wBAAgB,YAAY,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAyPrE"}
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
|
+
var DEFAULT_STORAGE_KEY = "based.session";
|
|
3
|
+
function defaultStorage() {
|
|
4
|
+
if (typeof globalThis === "undefined")
|
|
5
|
+
return null;
|
|
6
|
+
const ls = globalThis.localStorage;
|
|
7
|
+
if (!ls || typeof ls.getItem !== "function")
|
|
8
|
+
return null;
|
|
9
|
+
return {
|
|
10
|
+
getItem: (k) => ls.getItem(k),
|
|
11
|
+
setItem: (k, v) => ls.setItem(k, v),
|
|
12
|
+
removeItem: (k) => ls.removeItem(k)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
2
15
|
function createClient(options) {
|
|
3
16
|
const { url, anonKey } = options;
|
|
4
17
|
const baseUrl = url.replace(/\/$/, "");
|
|
18
|
+
const storage = options.storage === false ? null : options.storage ?? defaultStorage();
|
|
19
|
+
const storageKey = options.storageKey ?? DEFAULT_STORAGE_KEY;
|
|
5
20
|
let state = {
|
|
6
21
|
user: null,
|
|
7
22
|
accessToken: null,
|
|
8
|
-
refreshToken: null
|
|
23
|
+
refreshToken: null,
|
|
24
|
+
isLoading: !!storage
|
|
9
25
|
};
|
|
10
26
|
const listeners = new Set;
|
|
11
27
|
function notify() {
|
|
@@ -19,6 +35,66 @@ function createClient(options) {
|
|
|
19
35
|
function getState() {
|
|
20
36
|
return state;
|
|
21
37
|
}
|
|
38
|
+
async function persist() {
|
|
39
|
+
if (!storage)
|
|
40
|
+
return;
|
|
41
|
+
const snapshot = {
|
|
42
|
+
user: state.user,
|
|
43
|
+
accessToken: state.accessToken,
|
|
44
|
+
refreshToken: state.refreshToken
|
|
45
|
+
};
|
|
46
|
+
if (!snapshot.refreshToken) {
|
|
47
|
+
await storage.removeItem(storageKey);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
await storage.setItem(storageKey, JSON.stringify(snapshot));
|
|
51
|
+
}
|
|
52
|
+
function setState(next) {
|
|
53
|
+
state = { ...state, ...next };
|
|
54
|
+
notify();
|
|
55
|
+
persist().catch(() => {});
|
|
56
|
+
}
|
|
57
|
+
const readyPromise = (async () => {
|
|
58
|
+
if (!storage) {
|
|
59
|
+
setState({ isLoading: false });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const raw = await storage.getItem(storageKey);
|
|
64
|
+
if (!raw) {
|
|
65
|
+
setState({ isLoading: false });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const stored = JSON.parse(raw);
|
|
69
|
+
if (!stored.refreshToken) {
|
|
70
|
+
setState({ isLoading: false });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
state = {
|
|
74
|
+
user: stored.user,
|
|
75
|
+
accessToken: stored.accessToken,
|
|
76
|
+
refreshToken: stored.refreshToken,
|
|
77
|
+
isLoading: true
|
|
78
|
+
};
|
|
79
|
+
notify();
|
|
80
|
+
const user = await getUser();
|
|
81
|
+
if (!user) {
|
|
82
|
+
const refreshed = await refreshSession();
|
|
83
|
+
if (refreshed) {
|
|
84
|
+
await getUser();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} catch {
|
|
88
|
+
try {
|
|
89
|
+
await storage.removeItem(storageKey);
|
|
90
|
+
} catch {}
|
|
91
|
+
} finally {
|
|
92
|
+
setState({ isLoading: false });
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
function ready() {
|
|
96
|
+
return readyPromise;
|
|
97
|
+
}
|
|
22
98
|
async function fetchWithAuth(path, init = {}) {
|
|
23
99
|
const headers = new Headers(init.headers);
|
|
24
100
|
if (state.accessToken) {
|
|
@@ -46,16 +122,16 @@ function createClient(options) {
|
|
|
46
122
|
body: JSON.stringify({ email, password })
|
|
47
123
|
});
|
|
48
124
|
if (!res.ok) {
|
|
49
|
-
const json = await res.json();
|
|
50
|
-
throw new Error(json
|
|
125
|
+
const json = await res.json().catch(() => null);
|
|
126
|
+
throw new Error(json?.error?.message || "Sign up failed");
|
|
51
127
|
}
|
|
52
128
|
const { data } = await res.json();
|
|
53
|
-
|
|
129
|
+
setState({
|
|
54
130
|
user: data.user,
|
|
55
131
|
accessToken: data.accessToken,
|
|
56
|
-
refreshToken: data.refreshToken
|
|
57
|
-
|
|
58
|
-
|
|
132
|
+
refreshToken: data.refreshToken,
|
|
133
|
+
isLoading: false
|
|
134
|
+
});
|
|
59
135
|
return data.user;
|
|
60
136
|
}
|
|
61
137
|
async function signIn(email, password) {
|
|
@@ -65,16 +141,16 @@ function createClient(options) {
|
|
|
65
141
|
body: JSON.stringify({ email, password })
|
|
66
142
|
});
|
|
67
143
|
if (!res.ok) {
|
|
68
|
-
const json = await res.json();
|
|
69
|
-
throw new Error(json
|
|
144
|
+
const json = await res.json().catch(() => null);
|
|
145
|
+
throw new Error(json?.error?.message || "Sign in failed");
|
|
70
146
|
}
|
|
71
147
|
const { data } = await res.json();
|
|
72
|
-
|
|
148
|
+
setState({
|
|
73
149
|
user: data.user,
|
|
74
150
|
accessToken: data.accessToken,
|
|
75
|
-
refreshToken: data.refreshToken
|
|
76
|
-
|
|
77
|
-
|
|
151
|
+
refreshToken: data.refreshToken,
|
|
152
|
+
isLoading: false
|
|
153
|
+
});
|
|
78
154
|
return data.user;
|
|
79
155
|
}
|
|
80
156
|
async function signOut() {
|
|
@@ -86,8 +162,7 @@ function createClient(options) {
|
|
|
86
162
|
});
|
|
87
163
|
} catch {}
|
|
88
164
|
}
|
|
89
|
-
|
|
90
|
-
notify();
|
|
165
|
+
setState({ user: null, accessToken: null, refreshToken: null, isLoading: false });
|
|
91
166
|
}
|
|
92
167
|
async function refreshSession() {
|
|
93
168
|
if (!state.refreshToken)
|
|
@@ -99,27 +174,46 @@ function createClient(options) {
|
|
|
99
174
|
body: JSON.stringify({ refreshToken: state.refreshToken })
|
|
100
175
|
});
|
|
101
176
|
if (!res.ok) {
|
|
102
|
-
|
|
103
|
-
notify();
|
|
177
|
+
setState({ user: null, accessToken: null, refreshToken: null, isLoading: false });
|
|
104
178
|
return false;
|
|
105
179
|
}
|
|
106
180
|
const { data } = await res.json();
|
|
107
|
-
|
|
108
|
-
...state,
|
|
181
|
+
setState({
|
|
109
182
|
accessToken: data.accessToken,
|
|
110
183
|
refreshToken: data.refreshToken
|
|
111
|
-
};
|
|
112
|
-
notify();
|
|
184
|
+
});
|
|
113
185
|
return true;
|
|
114
186
|
} catch {
|
|
115
187
|
return false;
|
|
116
188
|
}
|
|
117
189
|
}
|
|
190
|
+
async function getUser() {
|
|
191
|
+
if (!state.accessToken)
|
|
192
|
+
return null;
|
|
193
|
+
try {
|
|
194
|
+
const res = await fetch(`${baseUrl}/auth/me`, {
|
|
195
|
+
headers: { Authorization: `Bearer ${state.accessToken}` }
|
|
196
|
+
});
|
|
197
|
+
if (!res.ok) {
|
|
198
|
+
if (res.status === 401) {
|
|
199
|
+
setState({ user: null });
|
|
200
|
+
}
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const { data } = await res.json();
|
|
204
|
+
const user = { id: data.id, email: data.email };
|
|
205
|
+
setState({ user });
|
|
206
|
+
return user;
|
|
207
|
+
} catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
118
211
|
return {
|
|
119
|
-
auth: { signUp, signIn, signOut, refreshSession },
|
|
212
|
+
auth: { signUp, signIn, signOut, refreshSession, getUser },
|
|
120
213
|
fetch: fetchWithAuth,
|
|
121
214
|
subscribe,
|
|
122
|
-
getState
|
|
215
|
+
getState,
|
|
216
|
+
ready
|
|
123
217
|
};
|
|
124
218
|
}
|
|
125
219
|
// src/provider.tsx
|
|
@@ -149,18 +243,18 @@ function useUser() {
|
|
|
149
243
|
const state = useSyncExternalStore(client.subscribe, () => client.getState(), () => client.getState());
|
|
150
244
|
return {
|
|
151
245
|
user: state.user,
|
|
152
|
-
isLoading:
|
|
246
|
+
isLoading: state.isLoading,
|
|
153
247
|
error: null
|
|
154
248
|
};
|
|
155
249
|
}
|
|
156
250
|
// src/hooks/use-query.ts
|
|
157
|
-
import { useState
|
|
251
|
+
import { useState, useEffect, useCallback } from "react";
|
|
158
252
|
function useQuery(table, options) {
|
|
159
253
|
const client = useBasedClient();
|
|
160
|
-
const [data, setData] =
|
|
161
|
-
const [total, setTotal] =
|
|
162
|
-
const [isLoading, setIsLoading] =
|
|
163
|
-
const [error, setError] =
|
|
254
|
+
const [data, setData] = useState(null);
|
|
255
|
+
const [total, setTotal] = useState(0);
|
|
256
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
257
|
+
const [error, setError] = useState(null);
|
|
164
258
|
const fetchData = useCallback(async () => {
|
|
165
259
|
setIsLoading(true);
|
|
166
260
|
setError(null);
|
|
@@ -196,17 +290,17 @@ function useQuery(table, options) {
|
|
|
196
290
|
setIsLoading(false);
|
|
197
291
|
}
|
|
198
292
|
}, [client, table, JSON.stringify(options)]);
|
|
199
|
-
|
|
293
|
+
useEffect(() => {
|
|
200
294
|
fetchData();
|
|
201
295
|
}, [fetchData]);
|
|
202
296
|
return { data, total, isLoading, error, refetch: fetchData };
|
|
203
297
|
}
|
|
204
298
|
// src/hooks/use-mutation.ts
|
|
205
|
-
import { useState as
|
|
299
|
+
import { useState as useState2, useCallback as useCallback2 } from "react";
|
|
206
300
|
function useMutation(table, operation) {
|
|
207
301
|
const client = useBasedClient();
|
|
208
|
-
const [isLoading, setIsLoading] =
|
|
209
|
-
const [error, setError] =
|
|
302
|
+
const [isLoading, setIsLoading] = useState2(false);
|
|
303
|
+
const [error, setError] = useState2(null);
|
|
210
304
|
const mutate = useCallback2(async (data) => {
|
|
211
305
|
setIsLoading(true);
|
|
212
306
|
setError(null);
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
export interface StorageAdapter {
|
|
2
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
3
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
4
|
+
removeItem(key: string): void | Promise<void>;
|
|
5
|
+
}
|
|
1
6
|
export interface BasedClientOptions {
|
|
2
7
|
url: string;
|
|
3
8
|
anonKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* Where to persist the session. Defaults to `localStorage` in browsers,
|
|
11
|
+
* in-memory elsewhere. Pass `false` to disable persistence entirely.
|
|
12
|
+
*/
|
|
13
|
+
storage?: StorageAdapter | false;
|
|
14
|
+
/**
|
|
15
|
+
* Key used for storage. Defaults to `based.session`.
|
|
16
|
+
*/
|
|
17
|
+
storageKey?: string;
|
|
4
18
|
}
|
|
5
19
|
export interface AuthUser {
|
|
6
20
|
id: string;
|
|
@@ -10,6 +24,11 @@ export interface AuthState {
|
|
|
10
24
|
user: AuthUser | null;
|
|
11
25
|
accessToken: string | null;
|
|
12
26
|
refreshToken: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* True during the initial session rehydration. Use this to avoid flashing
|
|
29
|
+
* a "logged out" UI before the stored session has been validated.
|
|
30
|
+
*/
|
|
31
|
+
isLoading: boolean;
|
|
13
32
|
}
|
|
14
33
|
export interface BasedClient {
|
|
15
34
|
auth: {
|
|
@@ -17,10 +36,19 @@ export interface BasedClient {
|
|
|
17
36
|
signIn(email: string, password: string): Promise<AuthUser>;
|
|
18
37
|
signOut(): Promise<void>;
|
|
19
38
|
refreshSession(): Promise<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Returns the current authenticated user by calling `/auth/me`.
|
|
41
|
+
*/
|
|
42
|
+
getUser(): Promise<AuthUser | null>;
|
|
20
43
|
};
|
|
21
44
|
fetch(path: string, init?: RequestInit): Promise<Response>;
|
|
22
45
|
subscribe(listener: () => void): () => void;
|
|
23
46
|
getState(): AuthState;
|
|
47
|
+
/**
|
|
48
|
+
* Resolves when the client finishes restoring a stored session (if any).
|
|
49
|
+
* Safe to await multiple times.
|
|
50
|
+
*/
|
|
51
|
+
ready(): Promise<void>;
|
|
24
52
|
}
|
|
25
53
|
export interface QueryOptions<T = Record<string, unknown>> {
|
|
26
54
|
filter?: Partial<Record<keyof T, string | number | boolean>>;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|