@linabase/js 0.1.1 → 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/dist/index.d.ts +75 -26
- package/dist/index.js +165 -47
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
type RequestFn$
|
|
1
|
+
type RequestFn$3 = (path: string, options?: RequestInit) => Promise<Response>;
|
|
2
|
+
interface PostgrestError {
|
|
3
|
+
message: string;
|
|
4
|
+
code?: string;
|
|
5
|
+
details?: string;
|
|
6
|
+
hint?: string;
|
|
7
|
+
}
|
|
2
8
|
interface QueryResult<T = any> {
|
|
3
9
|
data: T[] | null;
|
|
4
|
-
error:
|
|
5
|
-
message: string;
|
|
6
|
-
} | null;
|
|
10
|
+
error: PostgrestError | null;
|
|
7
11
|
count: number | null;
|
|
8
12
|
}
|
|
9
13
|
interface SingleQueryResult<T = any> {
|
|
10
14
|
data: T | null;
|
|
11
|
-
error:
|
|
12
|
-
message: string;
|
|
13
|
-
} | null;
|
|
15
|
+
error: PostgrestError | null;
|
|
14
16
|
count: number | null;
|
|
15
17
|
}
|
|
16
18
|
interface CsvQueryResult {
|
|
17
19
|
data: string | null;
|
|
18
|
-
error:
|
|
19
|
-
message: string;
|
|
20
|
-
} | null;
|
|
20
|
+
error: PostgrestError | null;
|
|
21
21
|
count: number | null;
|
|
22
22
|
}
|
|
23
23
|
declare class DatabaseClient {
|
|
@@ -33,13 +33,17 @@ declare class DatabaseClient {
|
|
|
33
33
|
private _csv;
|
|
34
34
|
private _abortSignal;
|
|
35
35
|
private _schema;
|
|
36
|
-
constructor(request: RequestFn$
|
|
36
|
+
constructor(request: RequestFn$3, table: string);
|
|
37
37
|
/** Select columns. Supports nested joins: "*, comments(*)" and aliases: "full_name:name" */
|
|
38
|
-
select(columns?: string
|
|
38
|
+
select(columns?: string, options?: {
|
|
39
|
+
count?: "exact" | "estimated" | "planned";
|
|
40
|
+
head?: boolean;
|
|
41
|
+
}): this;
|
|
39
42
|
insert(data: Record<string, any> | Record<string, any>[]): this;
|
|
40
43
|
/** Upsert: insert or update on conflict. Uses merge-duplicates by default. */
|
|
41
44
|
upsert(data: Record<string, any> | Record<string, any>[], options?: {
|
|
42
45
|
ignoreDuplicates?: boolean;
|
|
46
|
+
onConflict?: string;
|
|
43
47
|
}): this;
|
|
44
48
|
update(data: Record<string, any>): this;
|
|
45
49
|
delete(): this;
|
|
@@ -51,8 +55,8 @@ declare class DatabaseClient {
|
|
|
51
55
|
lte(column: string, value: any): this;
|
|
52
56
|
like(column: string, pattern: string): this;
|
|
53
57
|
ilike(column: string, pattern: string): this;
|
|
54
|
-
/** Regex match (~) */
|
|
55
|
-
match(
|
|
58
|
+
/** Regex match (~) when called with (column, pattern). Object-based multi-column filter when called with ({ col: val }). */
|
|
59
|
+
match(columnOrFilter: string | Record<string, any>, pattern?: string): this;
|
|
56
60
|
/** Case-insensitive regex match (~*) */
|
|
57
61
|
imatch(column: string, pattern: string): this;
|
|
58
62
|
in(column: string, values: any[]): this;
|
|
@@ -62,7 +66,7 @@ declare class DatabaseClient {
|
|
|
62
66
|
containedBy(column: string, value: any): this;
|
|
63
67
|
/** Overlap (&&) - ranges or arrays */
|
|
64
68
|
overlaps(column: string, value: any): this;
|
|
65
|
-
is(column: string, value: "null" | "true" | "false"): this;
|
|
69
|
+
is(column: string, value: "null" | "true" | "false" | null | boolean): this;
|
|
66
70
|
/** IS DISTINCT FROM */
|
|
67
71
|
isDistinct(column: string, value: any): this;
|
|
68
72
|
/** Negate a filter: not.eq, not.in, not.is, etc. */
|
|
@@ -97,11 +101,16 @@ declare class DatabaseClient {
|
|
|
97
101
|
schema(schemaName: string): this;
|
|
98
102
|
private reset;
|
|
99
103
|
execute(): Promise<QueryResult | SingleQueryResult | CsvQueryResult>;
|
|
100
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Makes DatabaseClient thenable (await-able). Resolves to
|
|
106
|
+
* `{ data, error, count }` where data is permissively typed so
|
|
107
|
+
* callers can access properties without generated database types.
|
|
108
|
+
*/
|
|
109
|
+
then<T = any, U = never>(onfulfilled?: ((value: any) => T | PromiseLike<T>) | null, onrejected?: ((reason: any) => U | PromiseLike<U>) | null): Promise<T | U>;
|
|
101
110
|
}
|
|
102
111
|
declare class RpcClient {
|
|
103
112
|
private request;
|
|
104
|
-
constructor(request: RequestFn$
|
|
113
|
+
constructor(request: RequestFn$3);
|
|
105
114
|
/** Call a Postgres function: rpc("my_function", { arg1: "value" }, { count: "exact" }) */
|
|
106
115
|
call(fn: string, args?: Record<string, any>, options?: {
|
|
107
116
|
count?: "exact" | "estimated" | "planned";
|
|
@@ -109,11 +118,11 @@ declare class RpcClient {
|
|
|
109
118
|
}): Promise<QueryResult>;
|
|
110
119
|
}
|
|
111
120
|
|
|
112
|
-
type RequestFn$
|
|
121
|
+
type RequestFn$2 = (path: string, options?: RequestInit) => Promise<Response>;
|
|
113
122
|
declare class StorageClient {
|
|
114
123
|
private request;
|
|
115
124
|
private baseUrl;
|
|
116
|
-
constructor(request: RequestFn$
|
|
125
|
+
constructor(request: RequestFn$2, baseUrl?: string);
|
|
117
126
|
from(bucket: string): BucketClient;
|
|
118
127
|
listBuckets(): Promise<{
|
|
119
128
|
data: any[];
|
|
@@ -130,9 +139,11 @@ declare class BucketClient {
|
|
|
130
139
|
private request;
|
|
131
140
|
private bucket;
|
|
132
141
|
private baseUrl;
|
|
133
|
-
constructor(request: RequestFn$
|
|
142
|
+
constructor(request: RequestFn$2, bucket: string, baseUrl?: string);
|
|
134
143
|
upload(path: string, file: Blob | File | ArrayBuffer, options?: {
|
|
135
144
|
contentType?: string;
|
|
145
|
+
upsert?: boolean;
|
|
146
|
+
headers?: Record<string, string>;
|
|
136
147
|
}): Promise<{
|
|
137
148
|
data: any;
|
|
138
149
|
error: any;
|
|
@@ -141,11 +152,12 @@ declare class BucketClient {
|
|
|
141
152
|
data: Blob | null;
|
|
142
153
|
error: any;
|
|
143
154
|
}>;
|
|
144
|
-
list(): Promise<{
|
|
155
|
+
list(prefix?: string): Promise<{
|
|
145
156
|
data: any[];
|
|
146
157
|
error: any;
|
|
147
158
|
}>;
|
|
148
|
-
remove(
|
|
159
|
+
remove(pathOrPaths: string | string[]): Promise<{
|
|
160
|
+
data: any;
|
|
149
161
|
error: any;
|
|
150
162
|
}>;
|
|
151
163
|
getPublicUrl(path: string, options?: {
|
|
@@ -189,7 +201,7 @@ declare class BucketClient {
|
|
|
189
201
|
}>;
|
|
190
202
|
}
|
|
191
203
|
|
|
192
|
-
type RequestFn = (path: string, options?: RequestInit) => Promise<Response>;
|
|
204
|
+
type RequestFn$1 = (path: string, options?: RequestInit) => Promise<Response>;
|
|
193
205
|
type OAuthProvider = "google" | "github" | "apple" | "microsoft" | "linkedin" | "slack" | "gitlab" | "bitbucket" | "discord" | "facebook" | "twitter" | "reddit" | "twitch" | "tiktok" | "spotify" | "telegram" | "roblox";
|
|
194
206
|
interface AuthSession {
|
|
195
207
|
access_token: string;
|
|
@@ -206,6 +218,8 @@ interface AuthUser {
|
|
|
206
218
|
role: string;
|
|
207
219
|
raw_user_meta_data: Record<string, unknown>;
|
|
208
220
|
raw_app_meta_data: Record<string, unknown>;
|
|
221
|
+
/** Alias for raw_user_meta_data (Supabase compatibility) */
|
|
222
|
+
user_metadata: Record<string, unknown>;
|
|
209
223
|
created_at: string;
|
|
210
224
|
updated_at: string;
|
|
211
225
|
}
|
|
@@ -218,8 +232,13 @@ declare class AuthClient {
|
|
|
218
232
|
* header when the user signs in/out or a token is refreshed.
|
|
219
233
|
*/
|
|
220
234
|
onSessionChange: ((session: AuthSession | null) => void) | null;
|
|
221
|
-
constructor(request: RequestFn);
|
|
222
|
-
|
|
235
|
+
constructor(request: RequestFn$1);
|
|
236
|
+
/**
|
|
237
|
+
* Set (or clear) the current session. Use this to restore a persisted
|
|
238
|
+
* session on app launch (e.g., from AsyncStorage / SecureStore).
|
|
239
|
+
* Emits INITIAL_SESSION to onAuthStateChange listeners.
|
|
240
|
+
*/
|
|
241
|
+
setSession(session: AuthSession | null): void;
|
|
223
242
|
signUp(params: {
|
|
224
243
|
email: string;
|
|
225
244
|
password: string;
|
|
@@ -243,6 +262,14 @@ declare class AuthClient {
|
|
|
243
262
|
data: AuthSession | null;
|
|
244
263
|
error: any;
|
|
245
264
|
}>;
|
|
265
|
+
signInWithIdToken(params: {
|
|
266
|
+
provider: OAuthProvider;
|
|
267
|
+
token: string;
|
|
268
|
+
nonce?: string;
|
|
269
|
+
}): Promise<{
|
|
270
|
+
data: AuthSession | null;
|
|
271
|
+
error: any;
|
|
272
|
+
}>;
|
|
246
273
|
signInWithOAuth(params: {
|
|
247
274
|
provider: OAuthProvider;
|
|
248
275
|
redirectTo?: string;
|
|
@@ -372,6 +399,21 @@ declare class AuthClient {
|
|
|
372
399
|
private parseAuthResponse;
|
|
373
400
|
}
|
|
374
401
|
|
|
402
|
+
type RequestFn = (path: string, options?: RequestInit) => Promise<Response>;
|
|
403
|
+
interface FunctionInvokeOptions {
|
|
404
|
+
body?: Record<string, any>;
|
|
405
|
+
method?: "GET" | "POST";
|
|
406
|
+
headers?: Record<string, string>;
|
|
407
|
+
}
|
|
408
|
+
declare class FunctionsClient {
|
|
409
|
+
private request;
|
|
410
|
+
constructor(request: RequestFn);
|
|
411
|
+
invoke<T = any>(name: string, options?: FunctionInvokeOptions): Promise<{
|
|
412
|
+
data: T | null;
|
|
413
|
+
error: any;
|
|
414
|
+
}>;
|
|
415
|
+
}
|
|
416
|
+
|
|
375
417
|
interface LinabaseConfig {
|
|
376
418
|
url: string;
|
|
377
419
|
anonKey?: string;
|
|
@@ -385,8 +427,15 @@ interface LinabaseClient {
|
|
|
385
427
|
rpc: (fn: string, args?: Record<string, any>) => ReturnType<RpcClient["call"]>;
|
|
386
428
|
storage: StorageClient;
|
|
387
429
|
auth: AuthClient;
|
|
430
|
+
functions: FunctionsClient;
|
|
431
|
+
/** Realtime channel (stub; not yet supported). Returns a chainable no-op. */
|
|
432
|
+
channel: (name: string) => any;
|
|
433
|
+
/** Remove a realtime channel (stub; not yet supported). */
|
|
434
|
+
removeChannel: (channel: any) => void;
|
|
388
435
|
generateTypes: () => Promise<string>;
|
|
436
|
+
/** Returns a new client that targets the given branch via X-Branch header. */
|
|
437
|
+
branch: (slug: string) => LinabaseClient;
|
|
389
438
|
}
|
|
390
439
|
declare function createClient(config: LinabaseConfig): LinabaseClient;
|
|
391
440
|
|
|
392
|
-
export { AuthClient, BucketClient, type CsvQueryResult, DatabaseClient, type LinabaseClient, type LinabaseConfig, type OAuthProvider, type QueryResult, RpcClient, type SingleQueryResult, StorageClient, createClient };
|
|
441
|
+
export { AuthClient, type AuthSession, type AuthUser, BucketClient, type CsvQueryResult, DatabaseClient, type FunctionInvokeOptions, FunctionsClient, type LinabaseClient, type LinabaseConfig, type OAuthProvider, type PostgrestError, type QueryResult, RpcClient, type SingleQueryResult, StorageClient, createClient };
|
package/dist/index.js
CHANGED
|
@@ -19,9 +19,11 @@ var DatabaseClient = class {
|
|
|
19
19
|
}
|
|
20
20
|
// ─── Query Methods ──────────────────────────────────────
|
|
21
21
|
/** Select columns. Supports nested joins: "*, comments(*)" and aliases: "full_name:name" */
|
|
22
|
-
select(columns) {
|
|
22
|
+
select(columns, options) {
|
|
23
23
|
this.method = "GET";
|
|
24
24
|
if (columns) this.params.set("select", columns);
|
|
25
|
+
if (options?.count) this.preferHeaders.push(`count=${options.count}`);
|
|
26
|
+
if (options?.head) this.method = "GET";
|
|
25
27
|
return this;
|
|
26
28
|
}
|
|
27
29
|
insert(data) {
|
|
@@ -38,6 +40,9 @@ var DatabaseClient = class {
|
|
|
38
40
|
this.preferHeaders.push(
|
|
39
41
|
options?.ignoreDuplicates ? "resolution=ignore-duplicates" : "resolution=merge-duplicates"
|
|
40
42
|
);
|
|
43
|
+
if (options?.onConflict) {
|
|
44
|
+
this.params.set("on_conflict", options.onConflict);
|
|
45
|
+
}
|
|
41
46
|
return this;
|
|
42
47
|
}
|
|
43
48
|
update(data) {
|
|
@@ -84,9 +89,15 @@ var DatabaseClient = class {
|
|
|
84
89
|
this.params.set(column, `ilike.${pattern}`);
|
|
85
90
|
return this;
|
|
86
91
|
}
|
|
87
|
-
/** Regex match (~) */
|
|
88
|
-
match(
|
|
89
|
-
|
|
92
|
+
/** Regex match (~) when called with (column, pattern). Object-based multi-column filter when called with ({ col: val }). */
|
|
93
|
+
match(columnOrFilter, pattern) {
|
|
94
|
+
if (typeof columnOrFilter === "object") {
|
|
95
|
+
for (const [key, value] of Object.entries(columnOrFilter)) {
|
|
96
|
+
this.eq(key, value);
|
|
97
|
+
}
|
|
98
|
+
} else if (pattern !== void 0) {
|
|
99
|
+
this.params.set(columnOrFilter, `match.${pattern}`);
|
|
100
|
+
}
|
|
90
101
|
return this;
|
|
91
102
|
}
|
|
92
103
|
/** Case-insensitive regex match (~*) */
|
|
@@ -116,7 +127,8 @@ var DatabaseClient = class {
|
|
|
116
127
|
}
|
|
117
128
|
// ─── Null/Boolean ───────────────────────────────────────
|
|
118
129
|
is(column, value) {
|
|
119
|
-
|
|
130
|
+
const v = value === null ? "null" : String(value);
|
|
131
|
+
this.params.set(column, `is.${v}`);
|
|
120
132
|
return this;
|
|
121
133
|
}
|
|
122
134
|
/** IS DISTINCT FROM */
|
|
@@ -308,8 +320,13 @@ var DatabaseClient = class {
|
|
|
308
320
|
this.reset();
|
|
309
321
|
}
|
|
310
322
|
}
|
|
311
|
-
|
|
312
|
-
|
|
323
|
+
/**
|
|
324
|
+
* Makes DatabaseClient thenable (await-able). Resolves to
|
|
325
|
+
* `{ data, error, count }` where data is permissively typed so
|
|
326
|
+
* callers can access properties without generated database types.
|
|
327
|
+
*/
|
|
328
|
+
then(onfulfilled, onrejected) {
|
|
329
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
313
330
|
}
|
|
314
331
|
};
|
|
315
332
|
var RpcClient = class {
|
|
@@ -423,7 +440,7 @@ var BucketClient = class {
|
|
|
423
440
|
`/storage/${this.bucket}/${path}`
|
|
424
441
|
);
|
|
425
442
|
if (!res.ok) {
|
|
426
|
-
const err = await res.json().catch(() => ({}));
|
|
443
|
+
const err = await res.json().catch(() => ({ message: `Request failed (${res.status})` }));
|
|
427
444
|
return { data: null, error: err };
|
|
428
445
|
}
|
|
429
446
|
const blob = await res.blob();
|
|
@@ -432,30 +449,37 @@ var BucketClient = class {
|
|
|
432
449
|
return { data: null, error: { message: err.message } };
|
|
433
450
|
}
|
|
434
451
|
}
|
|
435
|
-
async list() {
|
|
452
|
+
async list(prefix) {
|
|
436
453
|
try {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
);
|
|
454
|
+
let url = `/api/storage/objects?bucketName=${this.bucket}`;
|
|
455
|
+
if (prefix) url += `&prefix=${encodeURIComponent(prefix)}`;
|
|
456
|
+
const res = await this.request(url);
|
|
440
457
|
const data = await res.json();
|
|
441
458
|
return { data: data.objects || [], error: null };
|
|
442
459
|
} catch (err) {
|
|
443
460
|
return { data: [], error: { message: err.message } };
|
|
444
461
|
}
|
|
445
462
|
}
|
|
446
|
-
async remove(
|
|
463
|
+
async remove(pathOrPaths) {
|
|
464
|
+
const paths = Array.isArray(pathOrPaths) ? pathOrPaths : [pathOrPaths];
|
|
447
465
|
try {
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
466
|
+
const results = await Promise.all(
|
|
467
|
+
paths.map(async (p) => {
|
|
468
|
+
const res = await this.request(
|
|
469
|
+
`/storage/${this.bucket}/${p}`,
|
|
470
|
+
{ method: "DELETE" }
|
|
471
|
+
);
|
|
472
|
+
if (!res.ok) {
|
|
473
|
+
const data = await res.json().catch(() => ({ message: `Request failed (${res.status})` }));
|
|
474
|
+
return { error: data };
|
|
475
|
+
}
|
|
476
|
+
return { error: null };
|
|
477
|
+
})
|
|
451
478
|
);
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
return { error: data };
|
|
455
|
-
}
|
|
456
|
-
return { error: null };
|
|
479
|
+
const firstError = results.find((r) => r.error);
|
|
480
|
+
return { data: firstError ? null : paths, error: firstError?.error ?? null };
|
|
457
481
|
} catch (err) {
|
|
458
|
-
return { error: { message: err.message } };
|
|
482
|
+
return { data: null, error: { message: err.message } };
|
|
459
483
|
}
|
|
460
484
|
}
|
|
461
485
|
getPublicUrl(path, options) {
|
|
@@ -529,7 +553,7 @@ var BucketClient = class {
|
|
|
529
553
|
body: JSON.stringify({ from: fromPath, to: toPath })
|
|
530
554
|
});
|
|
531
555
|
if (!res.ok) {
|
|
532
|
-
const data = await res.json().catch(() => ({}));
|
|
556
|
+
const data = await res.json().catch(() => ({ message: `Request failed (${res.status})` }));
|
|
533
557
|
return { error: data };
|
|
534
558
|
}
|
|
535
559
|
return { error: null };
|
|
@@ -544,7 +568,7 @@ var BucketClient = class {
|
|
|
544
568
|
body: JSON.stringify({ from: fromPath, to: toPath })
|
|
545
569
|
});
|
|
546
570
|
if (!res.ok) {
|
|
547
|
-
const data = await res.json().catch(() => ({}));
|
|
571
|
+
const data = await res.json().catch(() => ({ message: `Request failed (${res.status})` }));
|
|
548
572
|
return { error: data };
|
|
549
573
|
}
|
|
550
574
|
return { error: null };
|
|
@@ -567,9 +591,15 @@ var AuthClient = class {
|
|
|
567
591
|
constructor(request) {
|
|
568
592
|
this.request = request;
|
|
569
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Set (or clear) the current session. Use this to restore a persisted
|
|
596
|
+
* session on app launch (e.g., from AsyncStorage / SecureStore).
|
|
597
|
+
* Emits INITIAL_SESSION to onAuthStateChange listeners.
|
|
598
|
+
*/
|
|
570
599
|
setSession(session) {
|
|
571
600
|
this.currentSession = session;
|
|
572
601
|
if (this.onSessionChange) this.onSessionChange(session);
|
|
602
|
+
this.emit("INITIAL_SESSION", session);
|
|
573
603
|
}
|
|
574
604
|
// ─── Email/Password ────────────────────────────────────────
|
|
575
605
|
async signUp(params) {
|
|
@@ -608,6 +638,27 @@ var AuthClient = class {
|
|
|
608
638
|
async signInWithPassword(params) {
|
|
609
639
|
return this.signIn(params);
|
|
610
640
|
}
|
|
641
|
+
// ─── ID Token (Mobile OAuth) ────────────────────────────────
|
|
642
|
+
async signInWithIdToken(params) {
|
|
643
|
+
try {
|
|
644
|
+
const res = await this.request("/auth/v1/token?grant_type=id_token", {
|
|
645
|
+
method: "POST",
|
|
646
|
+
body: JSON.stringify({
|
|
647
|
+
provider: params.provider,
|
|
648
|
+
id_token: params.token,
|
|
649
|
+
nonce: params.nonce
|
|
650
|
+
})
|
|
651
|
+
});
|
|
652
|
+
const data = await res.json();
|
|
653
|
+
if (!res.ok) return { data: null, error: data };
|
|
654
|
+
const session = this.parseAuthResponse(data);
|
|
655
|
+
this.setSession(session);
|
|
656
|
+
this.emit("SIGNED_IN", session);
|
|
657
|
+
return { data: session, error: null };
|
|
658
|
+
} catch (err) {
|
|
659
|
+
return { data: null, error: { message: err.message } };
|
|
660
|
+
}
|
|
661
|
+
}
|
|
611
662
|
// ─── OAuth ─────────────────────────────────────────────────
|
|
612
663
|
signInWithOAuth(params) {
|
|
613
664
|
const queryParams = new URLSearchParams({ provider: params.provider });
|
|
@@ -804,7 +855,7 @@ var AuthClient = class {
|
|
|
804
855
|
method: "DELETE"
|
|
805
856
|
});
|
|
806
857
|
if (!res.ok) {
|
|
807
|
-
const data = await res.json().catch(() => ({}));
|
|
858
|
+
const data = await res.json().catch(() => ({ message: `Request failed (${res.status})` }));
|
|
808
859
|
return { error: data };
|
|
809
860
|
}
|
|
810
861
|
return { error: null };
|
|
@@ -862,7 +913,7 @@ var AuthClient = class {
|
|
|
862
913
|
method: "DELETE"
|
|
863
914
|
});
|
|
864
915
|
if (!res.ok) {
|
|
865
|
-
const data = await res.json().catch(() => ({}));
|
|
916
|
+
const data = await res.json().catch(() => ({ message: `Request failed (${res.status})` }));
|
|
866
917
|
return { error: data };
|
|
867
918
|
}
|
|
868
919
|
return { error: null };
|
|
@@ -897,17 +948,53 @@ var AuthClient = class {
|
|
|
897
948
|
}
|
|
898
949
|
}
|
|
899
950
|
parseAuthResponse(data) {
|
|
951
|
+
const user = data.user || {};
|
|
900
952
|
return {
|
|
901
953
|
access_token: data.session?.access_token || data.access_token,
|
|
902
954
|
refresh_token: data.session?.refresh_token || data.refresh_token,
|
|
903
955
|
token_type: data.session?.token_type || data.token_type || "bearer",
|
|
904
956
|
expires_in: data.session?.expires_in || data.expires_in || 3600,
|
|
905
957
|
expires_at: data.session?.expires_at || data.expires_at || Math.floor(Date.now() / 1e3) + 3600,
|
|
906
|
-
user:
|
|
958
|
+
user: {
|
|
959
|
+
...user,
|
|
960
|
+
// Supabase-compatible alias
|
|
961
|
+
user_metadata: user.raw_user_meta_data || user.user_metadata || {}
|
|
962
|
+
}
|
|
907
963
|
};
|
|
908
964
|
}
|
|
909
965
|
};
|
|
910
966
|
|
|
967
|
+
// src/functions.ts
|
|
968
|
+
var FunctionsClient = class {
|
|
969
|
+
request;
|
|
970
|
+
constructor(request) {
|
|
971
|
+
this.request = request;
|
|
972
|
+
}
|
|
973
|
+
async invoke(name, options) {
|
|
974
|
+
try {
|
|
975
|
+
const method = options?.method || "POST";
|
|
976
|
+
const init = { method, headers: options?.headers };
|
|
977
|
+
if (options?.body && method !== "GET") {
|
|
978
|
+
init.body = JSON.stringify(options.body);
|
|
979
|
+
}
|
|
980
|
+
let path = `/functions/v1/${name}`;
|
|
981
|
+
if (options?.body && method === "GET") {
|
|
982
|
+
const params = new URLSearchParams();
|
|
983
|
+
for (const [k, v] of Object.entries(options.body)) {
|
|
984
|
+
if (v !== void 0 && v !== null) params.set(k, String(v));
|
|
985
|
+
}
|
|
986
|
+
path += `?${params}`;
|
|
987
|
+
}
|
|
988
|
+
const res = await this.request(path, init);
|
|
989
|
+
const data = await res.json();
|
|
990
|
+
if (!res.ok) return { data: null, error: data };
|
|
991
|
+
return { data, error: null };
|
|
992
|
+
} catch (err) {
|
|
993
|
+
return { data: null, error: { message: err.message } };
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
|
|
911
998
|
// src/client.ts
|
|
912
999
|
function createClient(config) {
|
|
913
1000
|
const baseUrl = config.url.replace(/\/$/, "");
|
|
@@ -931,36 +1018,67 @@ function createClient(config) {
|
|
|
931
1018
|
headers: mergedHeaders
|
|
932
1019
|
});
|
|
933
1020
|
}
|
|
934
|
-
const rpcClient = new RpcClient(request);
|
|
935
1021
|
const authClient = new AuthClient(request);
|
|
936
1022
|
authClient.onSessionChange = (session) => {
|
|
937
1023
|
accessToken = session?.access_token || null;
|
|
938
1024
|
};
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
1025
|
+
function buildClient(reqFn, branchSlug) {
|
|
1026
|
+
const rc = new RpcClient(reqFn);
|
|
1027
|
+
const ac = new AuthClient(reqFn);
|
|
1028
|
+
ac.onSessionChange = (session) => {
|
|
1029
|
+
accessToken = session?.access_token || null;
|
|
1030
|
+
};
|
|
1031
|
+
return {
|
|
1032
|
+
from: (table) => new DatabaseClient(reqFn, table),
|
|
1033
|
+
schema: (schemaName) => ({
|
|
1034
|
+
from: (table) => {
|
|
1035
|
+
const client = new DatabaseClient(reqFn, table);
|
|
1036
|
+
client._schema = schemaName;
|
|
1037
|
+
return client;
|
|
1038
|
+
}
|
|
1039
|
+
}),
|
|
1040
|
+
rpc: (fn, args) => rc.call(fn, args),
|
|
1041
|
+
storage: new StorageClient(reqFn, baseUrl),
|
|
1042
|
+
auth: branchSlug ? authClient : ac,
|
|
1043
|
+
functions: new FunctionsClient(reqFn),
|
|
1044
|
+
/** Realtime channel (stub; not yet supported). Returns a chainable no-op. */
|
|
1045
|
+
channel: (_name) => {
|
|
1046
|
+
const noop = { on: () => noop, subscribe: () => noop, unsubscribe: () => {
|
|
1047
|
+
} };
|
|
1048
|
+
return noop;
|
|
1049
|
+
},
|
|
1050
|
+
/** Remove a realtime channel (stub; not yet supported). */
|
|
1051
|
+
removeChannel: (_channel) => {
|
|
1052
|
+
},
|
|
1053
|
+
generateTypes: async () => {
|
|
1054
|
+
const restUrl = baseUrl.replace(/:3100/, ":3107");
|
|
1055
|
+
const headers = { Authorization: `Bearer ${apiKey}` };
|
|
1056
|
+
if (branchSlug) headers["X-Branch"] = branchSlug;
|
|
1057
|
+
const res = await fetch(`${restUrl}/rest/v1/types`, { headers });
|
|
1058
|
+
return res.text();
|
|
1059
|
+
},
|
|
1060
|
+
branch: (slug) => {
|
|
1061
|
+
if (branchSlug) throw new Error("Cannot nest branch() calls");
|
|
1062
|
+
function branchRequest(path, options = {}) {
|
|
1063
|
+
return reqFn(path, {
|
|
1064
|
+
...options,
|
|
1065
|
+
headers: {
|
|
1066
|
+
...options.headers,
|
|
1067
|
+
"X-Branch": slug
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
return buildClient(branchRequest, slug);
|
|
946
1072
|
}
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
auth: authClient,
|
|
951
|
-
generateTypes: async () => {
|
|
952
|
-
const restUrl = baseUrl.replace(/:3100/, ":3107");
|
|
953
|
-
const res = await fetch(`${restUrl}/rest/v1/types`, {
|
|
954
|
-
headers: { Authorization: `Bearer ${apiKey}` }
|
|
955
|
-
});
|
|
956
|
-
return res.text();
|
|
957
|
-
}
|
|
958
|
-
};
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
return buildClient(request);
|
|
959
1076
|
}
|
|
960
1077
|
export {
|
|
961
1078
|
AuthClient,
|
|
962
1079
|
BucketClient,
|
|
963
1080
|
DatabaseClient,
|
|
1081
|
+
FunctionsClient,
|
|
964
1082
|
RpcClient,
|
|
965
1083
|
StorageClient,
|
|
966
1084
|
createClient
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linabase/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "JavaScript/TypeScript client SDK for Linabase (database, storage, auth)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"@linabase/db": "workspace:*",
|
|
48
48
|
"@linabase/rest-api": "workspace:*",
|
|
49
49
|
"@types/pg": "^8.11.0",
|
|
50
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
50
51
|
"pg": "^8.13.0",
|
|
51
52
|
"tsup": "^8.3.0",
|
|
52
53
|
"typescript": "^5.7.0",
|