@linabase/js 0.5.1 → 0.5.3
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 +2 -2
- package/dist/index.cjs +328 -25
- package/dist/index.d.cts +399 -1
- package/dist/index.d.ts +399 -1
- package/dist/index.js +328 -25
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -18,7 +18,23 @@ var DatabaseClient = class {
|
|
|
18
18
|
this.params = new URLSearchParams();
|
|
19
19
|
}
|
|
20
20
|
// ─── Query Methods ──────────────────────────────────────
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Pick which columns to return. Supports nested joins (`"*, comments(*)"`)
|
|
23
|
+
* and column aliases (`"full_name:name"`). When chained after a mutation
|
|
24
|
+
* (`insert` / `update` / `upsert` / `delete`), it switches the request to
|
|
25
|
+
* `Prefer: return=representation` so the modified rows come back.
|
|
26
|
+
*
|
|
27
|
+
* @param columns - Postgres-style column list. Defaults to `"*"`.
|
|
28
|
+
* @param options - `count` returns a row count alongside the data; `head`
|
|
29
|
+
* omits the rows entirely (useful for count-only queries).
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* await linabase.from("posts").select("id, title");
|
|
34
|
+
* await linabase.from("posts").select("*, author:profiles(name)");
|
|
35
|
+
* await linabase.from("posts").select("*", { count: "exact", head: true });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
22
38
|
select(columns, options) {
|
|
23
39
|
const isMutation = this.method === "POST" || this.method === "PATCH" || this.method === "DELETE";
|
|
24
40
|
if (!isMutation) {
|
|
@@ -30,6 +46,15 @@ var DatabaseClient = class {
|
|
|
30
46
|
if (options?.count) this.preferHeaders.push(`count=${options.count}`);
|
|
31
47
|
return this;
|
|
32
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Insert one row or many. Returns the inserted rows by default.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* await linabase.from("posts").insert({ title: "Hi" });
|
|
55
|
+
* await linabase.from("posts").insert([{ title: "A" }, { title: "B" }]);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
33
58
|
insert(data, options) {
|
|
34
59
|
this.method = "POST";
|
|
35
60
|
this.body = data;
|
|
@@ -51,12 +76,36 @@ var DatabaseClient = class {
|
|
|
51
76
|
if (options?.defaultToNull === false) this.preferHeaders.push("missing=default");
|
|
52
77
|
return this;
|
|
53
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Update rows that match the active filters.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* await linabase
|
|
85
|
+
* .from("posts")
|
|
86
|
+
* .update({ published: true })
|
|
87
|
+
* .eq("id", 42);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
54
90
|
update(data) {
|
|
55
91
|
this.method = "PATCH";
|
|
56
92
|
this.body = data;
|
|
57
93
|
this.preferHeaders.push("return=representation");
|
|
58
94
|
return this;
|
|
59
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Delete rows that match the active filters. **Always** combine with at
|
|
98
|
+
* least one filter (e.g. `.eq("id", x)`) — calling `.delete()` on a bare
|
|
99
|
+
* builder removes every row in the table.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* await linabase
|
|
104
|
+
* .from("posts")
|
|
105
|
+
* .delete()
|
|
106
|
+
* .eq("id", 42);
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
60
109
|
delete(options) {
|
|
61
110
|
this.method = "DELETE";
|
|
62
111
|
if (options?.count) this.preferHeaders.push(`count=${options.count}`);
|
|
@@ -83,36 +132,36 @@ var DatabaseClient = class {
|
|
|
83
132
|
}
|
|
84
133
|
// ─── Comparison Filters ─────────────────────────────────
|
|
85
134
|
eq(column, value) {
|
|
86
|
-
this.params.
|
|
135
|
+
this.params.append(column, `eq.${value}`);
|
|
87
136
|
return this;
|
|
88
137
|
}
|
|
89
138
|
neq(column, value) {
|
|
90
|
-
this.params.
|
|
139
|
+
this.params.append(column, `neq.${value}`);
|
|
91
140
|
return this;
|
|
92
141
|
}
|
|
93
142
|
gt(column, value) {
|
|
94
|
-
this.params.
|
|
143
|
+
this.params.append(column, `gt.${value}`);
|
|
95
144
|
return this;
|
|
96
145
|
}
|
|
97
146
|
gte(column, value) {
|
|
98
|
-
this.params.
|
|
147
|
+
this.params.append(column, `gte.${value}`);
|
|
99
148
|
return this;
|
|
100
149
|
}
|
|
101
150
|
lt(column, value) {
|
|
102
|
-
this.params.
|
|
151
|
+
this.params.append(column, `lt.${value}`);
|
|
103
152
|
return this;
|
|
104
153
|
}
|
|
105
154
|
lte(column, value) {
|
|
106
|
-
this.params.
|
|
155
|
+
this.params.append(column, `lte.${value}`);
|
|
107
156
|
return this;
|
|
108
157
|
}
|
|
109
158
|
// ─── Pattern Matching ───────────────────────────────────
|
|
110
159
|
like(column, pattern) {
|
|
111
|
-
this.params.
|
|
160
|
+
this.params.append(column, `like.${pattern}`);
|
|
112
161
|
return this;
|
|
113
162
|
}
|
|
114
163
|
ilike(column, pattern) {
|
|
115
|
-
this.params.
|
|
164
|
+
this.params.append(column, `ilike.${pattern}`);
|
|
116
165
|
return this;
|
|
117
166
|
}
|
|
118
167
|
/** Regex match (~) when called with (column, pattern). Object-based multi-column filter when called with ({ col: val }). */
|
|
@@ -122,76 +171,76 @@ var DatabaseClient = class {
|
|
|
122
171
|
this.eq(key, value);
|
|
123
172
|
}
|
|
124
173
|
} else if (pattern !== void 0) {
|
|
125
|
-
this.params.
|
|
174
|
+
this.params.append(columnOrFilter, `match.${pattern}`);
|
|
126
175
|
}
|
|
127
176
|
return this;
|
|
128
177
|
}
|
|
129
178
|
/** Case-insensitive regex match (~*) */
|
|
130
179
|
imatch(column, pattern) {
|
|
131
|
-
this.params.
|
|
180
|
+
this.params.append(column, `imatch.${pattern}`);
|
|
132
181
|
return this;
|
|
133
182
|
}
|
|
134
183
|
// ─── Array/Set Filters ──────────────────────────────────
|
|
135
184
|
in(column, values) {
|
|
136
|
-
this.params.
|
|
185
|
+
this.params.append(column, `in.(${values.join(",")})`);
|
|
137
186
|
return this;
|
|
138
187
|
}
|
|
139
188
|
/** Contains (@>) - array or JSONB containment */
|
|
140
189
|
contains(column, value) {
|
|
141
|
-
this.params.
|
|
190
|
+
this.params.append(column, `cs.${JSON.stringify(value)}`);
|
|
142
191
|
return this;
|
|
143
192
|
}
|
|
144
193
|
/** Contained by (<@) */
|
|
145
194
|
containedBy(column, value) {
|
|
146
|
-
this.params.
|
|
195
|
+
this.params.append(column, `cd.${JSON.stringify(value)}`);
|
|
147
196
|
return this;
|
|
148
197
|
}
|
|
149
198
|
/** Overlap (&&) - ranges or arrays */
|
|
150
199
|
overlaps(column, value) {
|
|
151
|
-
this.params.
|
|
200
|
+
this.params.append(column, `ov.${JSON.stringify(value)}`);
|
|
152
201
|
return this;
|
|
153
202
|
}
|
|
154
203
|
// ─── Null/Boolean ───────────────────────────────────────
|
|
155
204
|
is(column, value) {
|
|
156
205
|
const v = value === null ? "null" : String(value);
|
|
157
|
-
this.params.
|
|
206
|
+
this.params.append(column, `is.${v}`);
|
|
158
207
|
return this;
|
|
159
208
|
}
|
|
160
209
|
/** IS DISTINCT FROM */
|
|
161
210
|
isDistinct(column, value) {
|
|
162
|
-
this.params.
|
|
211
|
+
this.params.append(column, `isdistinct.${value}`);
|
|
163
212
|
return this;
|
|
164
213
|
}
|
|
165
214
|
// ─── Range Type Filters ──────────────────────────────────
|
|
166
215
|
/** Strictly left of: [a,b] << [c,d] */
|
|
167
216
|
rangeLt(column, range) {
|
|
168
|
-
this.params.
|
|
217
|
+
this.params.append(column, `sl.${range}`);
|
|
169
218
|
return this;
|
|
170
219
|
}
|
|
171
220
|
/** Strictly right of: [a,b] >> [c,d] */
|
|
172
221
|
rangeGt(column, range) {
|
|
173
|
-
this.params.
|
|
222
|
+
this.params.append(column, `sr.${range}`);
|
|
174
223
|
return this;
|
|
175
224
|
}
|
|
176
225
|
/** Does not extend to the left of */
|
|
177
226
|
rangeGte(column, range) {
|
|
178
|
-
this.params.
|
|
227
|
+
this.params.append(column, `nxl.${range}`);
|
|
179
228
|
return this;
|
|
180
229
|
}
|
|
181
230
|
/** Does not extend to the right of */
|
|
182
231
|
rangeLte(column, range) {
|
|
183
|
-
this.params.
|
|
232
|
+
this.params.append(column, `nxr.${range}`);
|
|
184
233
|
return this;
|
|
185
234
|
}
|
|
186
235
|
/** Range is adjacent to */
|
|
187
236
|
rangeAdjacent(column, range) {
|
|
188
|
-
this.params.
|
|
237
|
+
this.params.append(column, `adj.${range}`);
|
|
189
238
|
return this;
|
|
190
239
|
}
|
|
191
240
|
// ─── Negation ───────────────────────────────────────────
|
|
192
241
|
/** Negate a filter: not.eq, not.in, not.is, etc. */
|
|
193
242
|
not(column, operator, value) {
|
|
194
|
-
this.params.
|
|
243
|
+
this.params.append(column, `not.${operator}.${value}`);
|
|
195
244
|
return this;
|
|
196
245
|
}
|
|
197
246
|
// ─── Logical ────────────────────────────────────────────
|
|
@@ -209,7 +258,7 @@ var DatabaseClient = class {
|
|
|
209
258
|
}
|
|
210
259
|
/** Generic filter: filter("column", "operator", "value") */
|
|
211
260
|
filter(column, operator, value) {
|
|
212
|
-
this.params.
|
|
261
|
+
this.params.append(column, `${operator}.${value}`);
|
|
213
262
|
return this;
|
|
214
263
|
}
|
|
215
264
|
// ─── Full-Text Search ───────────────────────────────────
|
|
@@ -217,7 +266,7 @@ var DatabaseClient = class {
|
|
|
217
266
|
textSearch(column, query, options) {
|
|
218
267
|
const op = options?.type === "plain" ? "plfts" : options?.type === "phrase" ? "phfts" : options?.type === "websearch" ? "wfts" : "fts";
|
|
219
268
|
const config = options?.config ? `(${options.config})` : "";
|
|
220
|
-
this.params.
|
|
269
|
+
this.params.append(column, `${op}${config}.${query}`);
|
|
221
270
|
return this;
|
|
222
271
|
}
|
|
223
272
|
// ─── Ordering & Pagination ──────────────────────────────
|
|
@@ -498,6 +547,24 @@ var BucketClient = class {
|
|
|
498
547
|
this.bucket = bucket;
|
|
499
548
|
this.baseUrl = baseUrl || "";
|
|
500
549
|
}
|
|
550
|
+
/**
|
|
551
|
+
* Upload a file to the bucket at the given path.
|
|
552
|
+
*
|
|
553
|
+
* @param path - Object key inside the bucket (e.g. `userId/avatar.png`).
|
|
554
|
+
* @param file - File contents as `Blob`, `File`, or `ArrayBuffer`.
|
|
555
|
+
* @param options.contentType - Override the content type. Inferred from
|
|
556
|
+
* `File`/`Blob` when not set.
|
|
557
|
+
* @param options.upsert - If true, replaces an existing object at the same
|
|
558
|
+
* path. Defaults to false (returns an error if the path is taken).
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* ```ts
|
|
562
|
+
* const file = e.target.files![0];
|
|
563
|
+
* await linabase.storage
|
|
564
|
+
* .from("avatars")
|
|
565
|
+
* .upload(`${userId}/avatar.png`, file, { upsert: true });
|
|
566
|
+
* ```
|
|
567
|
+
*/
|
|
501
568
|
async upload(path, file, options) {
|
|
502
569
|
try {
|
|
503
570
|
const formData = new FormData();
|
|
@@ -519,6 +586,18 @@ var BucketClient = class {
|
|
|
519
586
|
return { data: null, error: { message: err.message } };
|
|
520
587
|
}
|
|
521
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* Download an object as a `Blob`.
|
|
591
|
+
*
|
|
592
|
+
* @example
|
|
593
|
+
* ```ts
|
|
594
|
+
* const { data } = await linabase.storage.from("docs").download("file.pdf");
|
|
595
|
+
* if (data) {
|
|
596
|
+
* const url = URL.createObjectURL(data);
|
|
597
|
+
* window.open(url);
|
|
598
|
+
* }
|
|
599
|
+
* ```
|
|
600
|
+
*/
|
|
522
601
|
async download(path) {
|
|
523
602
|
try {
|
|
524
603
|
const res = await this.request(
|
|
@@ -567,6 +646,19 @@ var BucketClient = class {
|
|
|
567
646
|
return { data: null, error: { message: err.message } };
|
|
568
647
|
}
|
|
569
648
|
}
|
|
649
|
+
/**
|
|
650
|
+
* Build a public URL for an object in a public bucket. No request is made;
|
|
651
|
+
* this just constructs the URL synchronously.
|
|
652
|
+
*
|
|
653
|
+
* For private buckets, use {@link createSignedUrl} instead.
|
|
654
|
+
*
|
|
655
|
+
* @example
|
|
656
|
+
* ```ts
|
|
657
|
+
* const { data: { publicUrl } } = linabase.storage
|
|
658
|
+
* .from("avatars")
|
|
659
|
+
* .getPublicUrl(`${userId}/avatar.png`, { transform: { width: 64, height: 64 } });
|
|
660
|
+
* ```
|
|
661
|
+
*/
|
|
570
662
|
getPublicUrl(path, options) {
|
|
571
663
|
let url;
|
|
572
664
|
if (options?.transform) {
|
|
@@ -585,6 +677,18 @@ var BucketClient = class {
|
|
|
585
677
|
}
|
|
586
678
|
return { data: { publicUrl: url } };
|
|
587
679
|
}
|
|
680
|
+
/**
|
|
681
|
+
* Generate a time-limited signed URL for a private object. Valid for
|
|
682
|
+
* `expiresIn` seconds.
|
|
683
|
+
*
|
|
684
|
+
* @example
|
|
685
|
+
* ```ts
|
|
686
|
+
* const { data } = await linabase.storage
|
|
687
|
+
* .from("docs")
|
|
688
|
+
* .createSignedUrl(`${userId}/report.pdf`, 3600);
|
|
689
|
+
* if (data) window.open(data.signedUrl);
|
|
690
|
+
* ```
|
|
691
|
+
*/
|
|
588
692
|
async createSignedUrl(path, expiresIn) {
|
|
589
693
|
try {
|
|
590
694
|
const res = await this.request(
|
|
@@ -755,6 +859,22 @@ var AuthClient = class {
|
|
|
755
859
|
}
|
|
756
860
|
}
|
|
757
861
|
// ─── Email/Password ────────────────────────────────────────
|
|
862
|
+
/**
|
|
863
|
+
* Create a new user account with email + password.
|
|
864
|
+
*
|
|
865
|
+
* On success, the user is signed in and the session is persisted. Pass
|
|
866
|
+
* extra metadata via `data`; it is stored on the user record and
|
|
867
|
+
* available later as `user.user_metadata`.
|
|
868
|
+
*
|
|
869
|
+
* @example
|
|
870
|
+
* ```ts
|
|
871
|
+
* const { data, error } = await linabase.auth.signUp({
|
|
872
|
+
* email: "alice@example.com",
|
|
873
|
+
* password: "correct horse battery staple",
|
|
874
|
+
* data: { full_name: "Alice" },
|
|
875
|
+
* });
|
|
876
|
+
* ```
|
|
877
|
+
*/
|
|
758
878
|
async signUp(params) {
|
|
759
879
|
try {
|
|
760
880
|
const res = await this.request("/auth/v1/signup", {
|
|
@@ -771,6 +891,21 @@ var AuthClient = class {
|
|
|
771
891
|
return { data: null, error: { message: err.message } };
|
|
772
892
|
}
|
|
773
893
|
}
|
|
894
|
+
/**
|
|
895
|
+
* Sign in an existing user with email + password.
|
|
896
|
+
*
|
|
897
|
+
* Alias of {@link signInWithPassword}. On success, the session is persisted
|
|
898
|
+
* and subsequent SDK calls use the returned access token automatically.
|
|
899
|
+
*
|
|
900
|
+
* @example
|
|
901
|
+
* ```ts
|
|
902
|
+
* const { data, error } = await linabase.auth.signIn({
|
|
903
|
+
* email: "alice@example.com",
|
|
904
|
+
* password: "correct horse battery staple",
|
|
905
|
+
* });
|
|
906
|
+
* if (error) console.error(error.message);
|
|
907
|
+
* ```
|
|
908
|
+
*/
|
|
774
909
|
async signIn(params) {
|
|
775
910
|
try {
|
|
776
911
|
const res = await this.request("/auth/v1/token?grant_type=password", {
|
|
@@ -812,6 +947,105 @@ var AuthClient = class {
|
|
|
812
947
|
return { data: null, error: { message: err.message } };
|
|
813
948
|
}
|
|
814
949
|
}
|
|
950
|
+
// ─── Device-bound (Mobile) ──────────────────────────────────
|
|
951
|
+
/**
|
|
952
|
+
* Sign in with a device-bound secret. Intended for mobile clients that
|
|
953
|
+
* generate a random secret on first launch and persist it in the device
|
|
954
|
+
* keychain. No email or password is involved.
|
|
955
|
+
*
|
|
956
|
+
* Pass `create: true` on first launch to register a new anonymous user.
|
|
957
|
+
* Subsequent launches should call without `create` (or with `create: false`)
|
|
958
|
+
* to refuse silent account creation if the keychain entry was lost.
|
|
959
|
+
*
|
|
960
|
+
* Requires "Device-bound sign-ins" to be enabled in the project's Auth
|
|
961
|
+
* Settings.
|
|
962
|
+
*
|
|
963
|
+
* @example
|
|
964
|
+
* ```ts
|
|
965
|
+
* const secret = getOrCreateDeviceSecret();
|
|
966
|
+
* const { data, error } = await linabase.auth.signInWithDevice({
|
|
967
|
+
* deviceSecret: secret,
|
|
968
|
+
* create: true,
|
|
969
|
+
* });
|
|
970
|
+
* ```
|
|
971
|
+
*/
|
|
972
|
+
async signInWithDevice(params) {
|
|
973
|
+
try {
|
|
974
|
+
const res = await this.request("/auth/v1/token?grant_type=device", {
|
|
975
|
+
method: "POST",
|
|
976
|
+
body: JSON.stringify({
|
|
977
|
+
device_secret: params.deviceSecret,
|
|
978
|
+
create: params.create,
|
|
979
|
+
data: params.data
|
|
980
|
+
})
|
|
981
|
+
});
|
|
982
|
+
const data = await res.json();
|
|
983
|
+
if (!res.ok) return { data: null, error: data };
|
|
984
|
+
const session = this.parseAuthResponse(data);
|
|
985
|
+
this.setSession(session, true);
|
|
986
|
+
this.emit("SIGNED_IN", session);
|
|
987
|
+
return { data: session, error: null };
|
|
988
|
+
} catch (err) {
|
|
989
|
+
return { data: null, error: { message: err.message } };
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
// ─── Game Center (iOS) ──────────────────────────────────────
|
|
993
|
+
/**
|
|
994
|
+
* Sign in with an Apple Game Center identity assertion. The mobile client
|
|
995
|
+
* obtains the assertion fields via
|
|
996
|
+
* `GKLocalPlayer.local.fetchItems(forIdentityVerificationSignature:)` (iOS
|
|
997
|
+
* 13.5+) and forwards them here. The project must have the app's bundle ID
|
|
998
|
+
* configured in the Game Center allowlist.
|
|
999
|
+
*/
|
|
1000
|
+
async signInWithGameCenter(params) {
|
|
1001
|
+
try {
|
|
1002
|
+
const res = await this.request("/auth/v1/token?grant_type=game_center", {
|
|
1003
|
+
method: "POST",
|
|
1004
|
+
body: JSON.stringify({
|
|
1005
|
+
player_id: params.playerId,
|
|
1006
|
+
bundle_id: params.bundleId,
|
|
1007
|
+
public_key_url: params.publicKeyURL,
|
|
1008
|
+
signature: params.signature,
|
|
1009
|
+
salt: params.salt,
|
|
1010
|
+
timestamp: params.timestamp,
|
|
1011
|
+
display_name: params.displayName
|
|
1012
|
+
})
|
|
1013
|
+
});
|
|
1014
|
+
const data = await res.json();
|
|
1015
|
+
if (!res.ok) return { data: null, error: data };
|
|
1016
|
+
const session = this.parseAuthResponse(data);
|
|
1017
|
+
this.setSession(session, true);
|
|
1018
|
+
this.emit("SIGNED_IN", session);
|
|
1019
|
+
return { data: session, error: null };
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
return { data: null, error: { message: err.message } };
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
// ─── Google Play Games (Android) ────────────────────────────
|
|
1025
|
+
/**
|
|
1026
|
+
* Sign in with a Google Play Games server auth code obtained via
|
|
1027
|
+
* `PlayGamesSignInClient.requestServerSideAccess(serverClientId)`. The
|
|
1028
|
+
* project must have the matching OAuth client configured in Auth Settings.
|
|
1029
|
+
*/
|
|
1030
|
+
async signInWithPlayGames(params) {
|
|
1031
|
+
try {
|
|
1032
|
+
const res = await this.request("/auth/v1/token?grant_type=play_games", {
|
|
1033
|
+
method: "POST",
|
|
1034
|
+
body: JSON.stringify({
|
|
1035
|
+
server_auth_code: params.serverAuthCode,
|
|
1036
|
+
redirect_uri: params.redirectUri
|
|
1037
|
+
})
|
|
1038
|
+
});
|
|
1039
|
+
const data = await res.json();
|
|
1040
|
+
if (!res.ok) return { data: null, error: data };
|
|
1041
|
+
const session = this.parseAuthResponse(data);
|
|
1042
|
+
this.setSession(session, true);
|
|
1043
|
+
this.emit("SIGNED_IN", session);
|
|
1044
|
+
return { data: session, error: null };
|
|
1045
|
+
} catch (err) {
|
|
1046
|
+
return { data: null, error: { message: err.message } };
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
815
1049
|
// ─── OAuth ─────────────────────────────────────────────────
|
|
816
1050
|
signInWithOAuth(params) {
|
|
817
1051
|
const queryParams = new URLSearchParams({ provider: params.provider });
|
|
@@ -825,6 +1059,16 @@ var AuthClient = class {
|
|
|
825
1059
|
return { url };
|
|
826
1060
|
}
|
|
827
1061
|
// ─── Session Management ────────────────────────────────────
|
|
1062
|
+
/**
|
|
1063
|
+
* Sign the current user out, clear the persisted session, and revoke the
|
|
1064
|
+
* refresh token on the server. Fires a `SIGNED_OUT` event to listeners
|
|
1065
|
+
* registered with `onAuthStateChange`.
|
|
1066
|
+
*
|
|
1067
|
+
* @example
|
|
1068
|
+
* ```ts
|
|
1069
|
+
* await linabase.auth.signOut();
|
|
1070
|
+
* ```
|
|
1071
|
+
*/
|
|
828
1072
|
async signOut() {
|
|
829
1073
|
try {
|
|
830
1074
|
await this.request("/auth/v1/logout", { method: "POST" });
|
|
@@ -835,6 +1079,17 @@ var AuthClient = class {
|
|
|
835
1079
|
return { error: { message: err.message } };
|
|
836
1080
|
}
|
|
837
1081
|
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Return the current persisted session. Auto-refreshes if the access
|
|
1084
|
+
* token is expired and a refresh token is available. Returns
|
|
1085
|
+
* `{ session: null }` when the user isn't signed in.
|
|
1086
|
+
*
|
|
1087
|
+
* @example
|
|
1088
|
+
* ```ts
|
|
1089
|
+
* const { data: { session } } = await linabase.auth.getSession();
|
|
1090
|
+
* if (session) console.log("Signed in as", session.user.email);
|
|
1091
|
+
* ```
|
|
1092
|
+
*/
|
|
838
1093
|
async getSession() {
|
|
839
1094
|
if (this.currentSession) {
|
|
840
1095
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -1035,6 +1290,41 @@ var AuthClient = class {
|
|
|
1035
1290
|
} catch (err) {
|
|
1036
1291
|
return { error: { message: err.message } };
|
|
1037
1292
|
}
|
|
1293
|
+
},
|
|
1294
|
+
async listSessions(userId) {
|
|
1295
|
+
try {
|
|
1296
|
+
const res = await request(`/auth/v1/admin/users/${userId}/sessions`);
|
|
1297
|
+
const data = await res.json();
|
|
1298
|
+
if (!res.ok) return { data: null, error: data };
|
|
1299
|
+
return { data: data.sessions, error: null };
|
|
1300
|
+
} catch (err) {
|
|
1301
|
+
return { data: null, error: { message: err.message } };
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
async deleteSession(userId, sessionId) {
|
|
1305
|
+
try {
|
|
1306
|
+
const res = await request(`/auth/v1/admin/users/${userId}/sessions/${sessionId}`, {
|
|
1307
|
+
method: "DELETE"
|
|
1308
|
+
});
|
|
1309
|
+
const data = await res.json().catch(() => ({}));
|
|
1310
|
+
if (!res.ok) return { data: null, error: data };
|
|
1311
|
+
return { data, error: null };
|
|
1312
|
+
} catch (err) {
|
|
1313
|
+
return { data: null, error: { message: err.message } };
|
|
1314
|
+
}
|
|
1315
|
+
},
|
|
1316
|
+
async deleteOtherSessions(userId, exceptSessionId) {
|
|
1317
|
+
try {
|
|
1318
|
+
const qs = exceptSessionId ? `?except=${encodeURIComponent(exceptSessionId)}` : "";
|
|
1319
|
+
const res = await request(`/auth/v1/admin/users/${userId}/sessions${qs}`, {
|
|
1320
|
+
method: "DELETE"
|
|
1321
|
+
});
|
|
1322
|
+
const data = await res.json().catch(() => ({}));
|
|
1323
|
+
if (!res.ok) return { data: null, error: data };
|
|
1324
|
+
return { data, error: null };
|
|
1325
|
+
} catch (err) {
|
|
1326
|
+
return { data: null, error: { message: err.message } };
|
|
1327
|
+
}
|
|
1038
1328
|
}
|
|
1039
1329
|
};
|
|
1040
1330
|
}
|
|
@@ -1148,6 +1438,19 @@ var FunctionsClient = class {
|
|
|
1148
1438
|
constructor(request) {
|
|
1149
1439
|
this.request = request;
|
|
1150
1440
|
}
|
|
1441
|
+
/**
|
|
1442
|
+
* Call a function by name.
|
|
1443
|
+
*
|
|
1444
|
+
* @param name - Function slug as registered on the project.
|
|
1445
|
+
* @param options - Body, method, and optional extra headers.
|
|
1446
|
+
* @returns `{ data, error }` — `data` is the parsed JSON response, `error`
|
|
1447
|
+
* is an object with `message` if the call failed (HTTP non-2xx or thrown).
|
|
1448
|
+
*
|
|
1449
|
+
* @example
|
|
1450
|
+
* ```ts
|
|
1451
|
+
* await linabase.functions.invoke("ping");
|
|
1452
|
+
* ```
|
|
1453
|
+
*/
|
|
1151
1454
|
async invoke(name, options) {
|
|
1152
1455
|
try {
|
|
1153
1456
|
const method = options?.method || "POST";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linabase/js",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "JavaScript/TypeScript client SDK for Linabase (database, storage, auth)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -43,7 +43,9 @@
|
|
|
43
43
|
"test:watch": "vitest",
|
|
44
44
|
"test:coverage": "vitest run --coverage",
|
|
45
45
|
"typecheck": "tsc --noEmit",
|
|
46
|
-
"lint": "eslint src/"
|
|
46
|
+
"lint": "eslint src/",
|
|
47
|
+
"docs:json": "typedoc --json dist/typedoc.json",
|
|
48
|
+
"docs:reference": "pnpm docs:json && node scripts/build-reference.mjs"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
51
|
"@linabase/db": "workspace:*",
|
|
@@ -52,6 +54,7 @@
|
|
|
52
54
|
"@vitest/coverage-v8": "^3.2.4",
|
|
53
55
|
"pg": "^8.13.0",
|
|
54
56
|
"tsup": "^8.3.0",
|
|
57
|
+
"typedoc": "^0.28.19",
|
|
55
58
|
"typescript": "^5.7.0",
|
|
56
59
|
"vitest": "^3.2.1"
|
|
57
60
|
}
|