postbasejs 0.1.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.
@@ -0,0 +1,919 @@
1
+ // src/client.ts
2
+ var SESSION_STORAGE_KEY = "postbase_session";
3
+ function getStorageKey(options) {
4
+ return options?.auth?.storageKey ?? SESSION_STORAGE_KEY;
5
+ }
6
+ function isBrowser() {
7
+ return typeof window !== "undefined" && typeof document !== "undefined";
8
+ }
9
+ async function executeQuery(state) {
10
+ try {
11
+ const headers = {
12
+ "Content-Type": "application/json",
13
+ Authorization: `Bearer ${state.apiKey}`,
14
+ ...state.customHeaders
15
+ };
16
+ if (state.cookieAdapter) {
17
+ const cookies = await state.cookieAdapter.getAll();
18
+ const sessionCookie = cookies.find((c) => c.name.startsWith("postbase-session") || c.name === "next-auth.session-token" || c.name === "__Secure-next-auth.session-token");
19
+ if (sessionCookie) {
20
+ headers["X-Postbase-Session"] = sessionCookie.value;
21
+ }
22
+ }
23
+ const body = {
24
+ operation: state.operation === "upsert" ? "upsert" : state.operation,
25
+ table: state.table
26
+ };
27
+ if (state.operation === "select") {
28
+ if (state.columns && state.columns !== "*") {
29
+ body.columns = state.columns.split(",").map((c) => c.trim());
30
+ }
31
+ body.count = state.selectOptions.count;
32
+ body.head = state.selectOptions.head;
33
+ }
34
+ if (["select", "update", "delete"].includes(state.operation)) {
35
+ const filters = [...state.filters];
36
+ if (filters.length) body.filters = filters;
37
+ if (state.orFilters.length) body.orFilters = state.orFilters;
38
+ if (state.notFilters.length) body.notFilters = state.notFilters;
39
+ }
40
+ if (state.operation === "insert" || state.operation === "upsert") {
41
+ body.data = state.insertData;
42
+ if (state.upsertOnConflict) body.onConflict = state.upsertOnConflict;
43
+ }
44
+ if (state.operation === "update") {
45
+ body.data = state.updateData;
46
+ }
47
+ if (state.orderBy.length) body.order = state.orderBy;
48
+ if (state._limit !== void 0) body.limit = state._limit;
49
+ if (state._offset !== void 0) body.offset = state._offset;
50
+ if (state._range) body.range = state._range;
51
+ if (state.returning) body.returning = state.returning;
52
+ const res = await fetch(`${state.baseUrl}/api/db/query`, {
53
+ method: "POST",
54
+ headers,
55
+ body: JSON.stringify(body)
56
+ });
57
+ const json = await res.json();
58
+ if (!res.ok) return { data: null, count: null, error: json.error ?? "Query failed" };
59
+ return { data: json.data, count: json.count ?? null, error: null };
60
+ } catch (err) {
61
+ return { data: null, count: null, error: String(err) };
62
+ }
63
+ }
64
+ var QueryBuilderImpl = class _QueryBuilderImpl {
65
+ constructor(state) {
66
+ this.state = state;
67
+ }
68
+ clone(patch) {
69
+ return new _QueryBuilderImpl({ ...this.state, ...patch });
70
+ }
71
+ select(columns = "*", options = {}) {
72
+ return this.clone({ columns, selectOptions: options, operation: "select" });
73
+ }
74
+ addFilter(column, operator, value) {
75
+ return this.clone({ filters: [...this.state.filters, { column, operator, value }] });
76
+ }
77
+ eq(column, value) {
78
+ return this.addFilter(column, "eq", value);
79
+ }
80
+ neq(column, value) {
81
+ return this.addFilter(column, "neq", value);
82
+ }
83
+ gt(column, value) {
84
+ return this.addFilter(column, "gt", value);
85
+ }
86
+ gte(column, value) {
87
+ return this.addFilter(column, "gte", value);
88
+ }
89
+ lt(column, value) {
90
+ return this.addFilter(column, "lt", value);
91
+ }
92
+ lte(column, value) {
93
+ return this.addFilter(column, "lte", value);
94
+ }
95
+ like(column, pattern) {
96
+ return this.addFilter(column, "like", pattern);
97
+ }
98
+ ilike(column, pattern) {
99
+ return this.addFilter(column, "ilike", pattern);
100
+ }
101
+ in(column, values) {
102
+ return this.addFilter(column, "in", values);
103
+ }
104
+ is(column, value) {
105
+ return this.addFilter(column, "is", value);
106
+ }
107
+ contains(column, value) {
108
+ return this.addFilter(column, "contains", value);
109
+ }
110
+ overlaps(column, value) {
111
+ return this.addFilter(column, "overlaps", value);
112
+ }
113
+ textSearch(column, query, options) {
114
+ return this.addFilter(column, "textSearch", { query, config: options?.config });
115
+ }
116
+ or(filters) {
117
+ return this.clone({ orFilters: [...this.state.orFilters, filters] });
118
+ }
119
+ not(column, operator, value) {
120
+ return this.clone({ notFilters: [...this.state.notFilters, { column, operator, value }] });
121
+ }
122
+ order(column, options) {
123
+ return this.clone({
124
+ orderBy: [...this.state.orderBy, { column, ascending: options?.ascending, nullsFirst: options?.nullsFirst }]
125
+ });
126
+ }
127
+ limit(count) {
128
+ return this.clone({ _limit: count });
129
+ }
130
+ offset(count) {
131
+ return this.clone({ _offset: count });
132
+ }
133
+ range(from, to) {
134
+ return this.clone({ _range: { from, to }, _limit: to - from + 1, _offset: from });
135
+ }
136
+ insert(data, options) {
137
+ return new InsertBuilderImpl({
138
+ ...this.state,
139
+ operation: "insert",
140
+ insertData: data,
141
+ returning: options?.returning ?? "*"
142
+ });
143
+ }
144
+ upsert(data, options) {
145
+ return new InsertBuilderImpl({
146
+ ...this.state,
147
+ operation: "upsert",
148
+ insertData: data,
149
+ upsertOnConflict: options?.onConflict,
150
+ returning: options?.returning ?? "*"
151
+ });
152
+ }
153
+ update(data) {
154
+ return new UpdateBuilderImpl({
155
+ ...this.state,
156
+ operation: "update",
157
+ updateData: data,
158
+ returning: "*"
159
+ });
160
+ }
161
+ delete() {
162
+ return new DeleteBuilderImpl({
163
+ ...this.state,
164
+ operation: "delete",
165
+ returning: "*"
166
+ });
167
+ }
168
+ async single() {
169
+ const result = await executeQuery(this.clone({ _limit: 1 }).state);
170
+ if (result.error) return { data: null, error: result.error };
171
+ const rows = result.data ?? [];
172
+ if (rows.length === 0) return { data: null, error: "No rows returned" };
173
+ if (rows.length > 1) return { data: null, error: "Multiple rows returned" };
174
+ return { data: rows[0], error: null };
175
+ }
176
+ async maybeSingle() {
177
+ const result = await executeQuery(this.clone({ _limit: 1 }).state);
178
+ if (result.error) return { data: null, error: result.error };
179
+ const rows = result.data ?? [];
180
+ return { data: rows[0] ?? null, error: null };
181
+ }
182
+ // Thenable — make the builder awaitable directly
183
+ get then() {
184
+ const promise = executeQuery(this.state);
185
+ return promise.then.bind(promise);
186
+ }
187
+ };
188
+ var InsertBuilderImpl = class _InsertBuilderImpl {
189
+ constructor(state) {
190
+ this.state = state;
191
+ }
192
+ select(columns = "*") {
193
+ return new _InsertBuilderImpl({ ...this.state, returning: columns });
194
+ }
195
+ async single() {
196
+ const result = await executeQuery(this.state);
197
+ if (result.error) return { data: null, error: result.error };
198
+ const rows = result.data ?? [];
199
+ return { data: rows[0] ?? null, error: null };
200
+ }
201
+ get then() {
202
+ const promise = executeQuery(this.state);
203
+ return promise.then.bind(promise);
204
+ }
205
+ };
206
+ var UpdateBuilderImpl = class _UpdateBuilderImpl {
207
+ constructor(state) {
208
+ this.state = state;
209
+ }
210
+ addFilter(column, operator, value) {
211
+ return new _UpdateBuilderImpl({ ...this.state, filters: [...this.state.filters, { column, operator, value }] });
212
+ }
213
+ eq(column, value) {
214
+ return this.addFilter(column, "eq", value);
215
+ }
216
+ neq(column, value) {
217
+ return this.addFilter(column, "neq", value);
218
+ }
219
+ gt(column, value) {
220
+ return this.addFilter(column, "gt", value);
221
+ }
222
+ gte(column, value) {
223
+ return this.addFilter(column, "gte", value);
224
+ }
225
+ lt(column, value) {
226
+ return this.addFilter(column, "lt", value);
227
+ }
228
+ lte(column, value) {
229
+ return this.addFilter(column, "lte", value);
230
+ }
231
+ in(column, values) {
232
+ return this.addFilter(column, "in", values);
233
+ }
234
+ select(columns = "*") {
235
+ return new _UpdateBuilderImpl({ ...this.state, returning: columns });
236
+ }
237
+ async single() {
238
+ const result = await executeQuery(this.state);
239
+ if (result.error) return { data: null, error: result.error };
240
+ const rows = result.data ?? [];
241
+ return { data: rows[0] ?? null, error: null };
242
+ }
243
+ get then() {
244
+ const promise = executeQuery(this.state);
245
+ return promise.then.bind(promise);
246
+ }
247
+ };
248
+ var DeleteBuilderImpl = class _DeleteBuilderImpl {
249
+ constructor(state) {
250
+ this.state = state;
251
+ }
252
+ addFilter(column, operator, value) {
253
+ return new _DeleteBuilderImpl({ ...this.state, filters: [...this.state.filters, { column, operator, value }] });
254
+ }
255
+ eq(column, value) {
256
+ return this.addFilter(column, "eq", value);
257
+ }
258
+ neq(column, value) {
259
+ return this.addFilter(column, "neq", value);
260
+ }
261
+ gt(column, value) {
262
+ return this.addFilter(column, "gt", value);
263
+ }
264
+ gte(column, value) {
265
+ return this.addFilter(column, "gte", value);
266
+ }
267
+ lt(column, value) {
268
+ return this.addFilter(column, "lt", value);
269
+ }
270
+ lte(column, value) {
271
+ return this.addFilter(column, "lte", value);
272
+ }
273
+ in(column, values) {
274
+ return this.addFilter(column, "in", values);
275
+ }
276
+ select(columns = "*") {
277
+ return new _DeleteBuilderImpl({ ...this.state, returning: columns });
278
+ }
279
+ async single() {
280
+ const result = await executeQuery(this.state);
281
+ if (result.error) return { data: null, error: result.error };
282
+ const rows = result.data ?? [];
283
+ return { data: rows[0] ?? null, error: null };
284
+ }
285
+ get then() {
286
+ const promise = executeQuery(this.state);
287
+ return promise.then.bind(promise);
288
+ }
289
+ };
290
+ function createAuthAdmin(baseUrl, apiKey, customHeaders) {
291
+ const headers = () => ({
292
+ "Content-Type": "application/json",
293
+ Authorization: `Bearer ${apiKey}`,
294
+ ...customHeaders
295
+ });
296
+ return {
297
+ async listUsers(options) {
298
+ try {
299
+ const url = new URL(`${baseUrl}/api/auth/v1/admin/users`);
300
+ if (options?.page) url.searchParams.set("page", String(options.page));
301
+ if (options?.perPage) url.searchParams.set("perPage", String(options.perPage));
302
+ const res = await fetch(url.toString(), { headers: headers() });
303
+ const json = await res.json();
304
+ if (!res.ok) return { data: null, error: json.error ?? "Failed to list users" };
305
+ return { data: { users: json.users, total: json.total }, error: null };
306
+ } catch (err) {
307
+ return { data: null, error: String(err) };
308
+ }
309
+ },
310
+ async getUserById(id) {
311
+ try {
312
+ const res = await fetch(`${baseUrl}/api/auth/v1/admin/users/${id}`, { headers: headers() });
313
+ const json = await res.json();
314
+ if (!res.ok) return { data: { user: null }, error: json.error ?? "User not found" };
315
+ return { data: { user: json.user }, error: null };
316
+ } catch (err) {
317
+ return { data: { user: null }, error: String(err) };
318
+ }
319
+ },
320
+ async createUser(options) {
321
+ try {
322
+ const res = await fetch(`${baseUrl}/api/auth/v1/admin/users`, {
323
+ method: "POST",
324
+ headers: headers(),
325
+ body: JSON.stringify(options)
326
+ });
327
+ const json = await res.json();
328
+ if (!res.ok) return { data: { user: null }, error: json.error ?? "Failed to create user" };
329
+ return { data: { user: json.user }, error: null };
330
+ } catch (err) {
331
+ return { data: { user: null }, error: String(err) };
332
+ }
333
+ },
334
+ async updateUserById(id, attributes) {
335
+ try {
336
+ const res = await fetch(`${baseUrl}/api/auth/v1/admin/users/${id}`, {
337
+ method: "PATCH",
338
+ headers: headers(),
339
+ body: JSON.stringify(attributes)
340
+ });
341
+ const json = await res.json();
342
+ if (!res.ok) return { data: { user: null }, error: json.error ?? "Failed to update user" };
343
+ return { data: { user: json.user }, error: null };
344
+ } catch (err) {
345
+ return { data: { user: null }, error: String(err) };
346
+ }
347
+ },
348
+ async deleteUser(id) {
349
+ try {
350
+ const res = await fetch(`${baseUrl}/api/auth/v1/admin/users/${id}`, {
351
+ method: "DELETE",
352
+ headers: headers()
353
+ });
354
+ const json = await res.json();
355
+ if (!res.ok) return { data: null, error: json.error ?? "Failed to delete user" };
356
+ return { data: null, error: null };
357
+ } catch (err) {
358
+ return { data: null, error: String(err) };
359
+ }
360
+ }
361
+ };
362
+ }
363
+ function createAuthClient(baseUrl, apiKey, options, cookieAdapter) {
364
+ const storageKey = getStorageKey(options);
365
+ const listeners = [];
366
+ let refreshTimer = null;
367
+ let currentSession = null;
368
+ const customHeaders = options?.global?.headers;
369
+ const headers = () => ({
370
+ "Content-Type": "application/json",
371
+ Authorization: `Bearer ${apiKey}`,
372
+ ...customHeaders
373
+ });
374
+ function persistSession(session) {
375
+ if (!isBrowser()) return;
376
+ if (options?.auth?.persistSession === false) return;
377
+ if (session) {
378
+ localStorage.setItem(storageKey, JSON.stringify(session));
379
+ } else {
380
+ localStorage.removeItem(storageKey);
381
+ }
382
+ }
383
+ function loadPersistedSession() {
384
+ if (!isBrowser()) return null;
385
+ if (options?.auth?.persistSession === false) return null;
386
+ try {
387
+ const raw = localStorage.getItem(storageKey);
388
+ if (!raw) return null;
389
+ const session = JSON.parse(raw);
390
+ if (session.expiresAt && Date.now() / 1e3 > session.expiresAt) return null;
391
+ return session;
392
+ } catch {
393
+ return null;
394
+ }
395
+ }
396
+ function scheduleRefresh(session) {
397
+ if (!isBrowser()) return;
398
+ if (options?.auth?.autoRefreshToken === false) return;
399
+ if (refreshTimer) clearTimeout(refreshTimer);
400
+ const expiresIn = session.expiresAt - Date.now() / 1e3;
401
+ const refreshIn = Math.max((expiresIn - 60) * 1e3, 0);
402
+ refreshTimer = setTimeout(async () => {
403
+ if (session.refreshToken) {
404
+ const result = await authClient.refreshSession(session.refreshToken);
405
+ if (!result.error && result.data.session) {
406
+ notifyListeners("TOKEN_REFRESHED", result.data.session);
407
+ }
408
+ }
409
+ }, refreshIn);
410
+ }
411
+ function notifyListeners(event, session) {
412
+ currentSession = session;
413
+ persistSession(session);
414
+ if (session) scheduleRefresh(session);
415
+ listeners.forEach((fn) => fn(event, session));
416
+ }
417
+ const authClient = {
418
+ async signUp({ email, password, options: signUpOptions }) {
419
+ try {
420
+ const res = await fetch(`${baseUrl}/api/auth/v1/signup`, {
421
+ method: "POST",
422
+ headers: headers(),
423
+ body: JSON.stringify({ email, password, data: signUpOptions?.data })
424
+ });
425
+ const json = await res.json();
426
+ if (!res.ok) return { data: { user: null, session: null }, error: json.error ?? "Sign up failed" };
427
+ const session = json.session ?? null;
428
+ const user = json.user ?? null;
429
+ if (session) notifyListeners("SIGNED_IN", session);
430
+ return { data: { user, session }, error: null };
431
+ } catch (err) {
432
+ return { data: { user: null, session: null }, error: String(err) };
433
+ }
434
+ },
435
+ async signInWithPassword({ email, password }) {
436
+ try {
437
+ const res = await fetch(`${baseUrl}/api/auth/v1/token`, {
438
+ method: "POST",
439
+ headers: headers(),
440
+ body: JSON.stringify({ email, password, grant_type: "password" })
441
+ });
442
+ const json = await res.json();
443
+ if (!res.ok) return { data: { user: null, session: null }, error: json.error ?? "Sign in failed" };
444
+ const session = json.session;
445
+ const user = json.user;
446
+ notifyListeners("SIGNED_IN", session);
447
+ return { data: { user, session }, error: null };
448
+ } catch (err) {
449
+ return { data: { user: null, session: null }, error: String(err) };
450
+ }
451
+ },
452
+ async signInWithOtp({ email, options: otpOptions }) {
453
+ try {
454
+ const res = await fetch(`${baseUrl}/api/auth/v1/otp`, {
455
+ method: "POST",
456
+ headers: headers(),
457
+ body: JSON.stringify({ email, redirectTo: otpOptions?.redirectTo })
458
+ });
459
+ const json = await res.json();
460
+ if (!res.ok) return { data: null, error: json.error ?? "OTP send failed" };
461
+ return { data: null, error: null };
462
+ } catch (err) {
463
+ return { data: null, error: String(err) };
464
+ }
465
+ },
466
+ async signInWithOAuth({ provider, options: oauthOptions }) {
467
+ if (!isBrowser()) return;
468
+ const redirectTo = oauthOptions?.redirectTo ?? window.location.href;
469
+ const url = new URL(`${baseUrl}/api/auth/${provider}`);
470
+ url.searchParams.set("redirectTo", redirectTo);
471
+ if (oauthOptions?.scopes) url.searchParams.set("scopes", oauthOptions.scopes);
472
+ window.location.href = url.toString();
473
+ },
474
+ async signOut() {
475
+ try {
476
+ const session = currentSession ?? loadPersistedSession();
477
+ await fetch(`${baseUrl}/api/auth/v1/logout`, {
478
+ method: "POST",
479
+ headers: {
480
+ ...headers(),
481
+ ...session ? { "X-Postbase-Token": session.accessToken } : {}
482
+ }
483
+ });
484
+ if (refreshTimer) clearTimeout(refreshTimer);
485
+ notifyListeners("SIGNED_OUT", null);
486
+ return { error: null };
487
+ } catch (err) {
488
+ return { error: String(err) };
489
+ }
490
+ },
491
+ async getSession() {
492
+ try {
493
+ if (cookieAdapter) {
494
+ const cookies = await cookieAdapter.getAll();
495
+ const sessionCookie = cookies.find(
496
+ (c) => c.name.startsWith("postbase-session") || c.name === "next-auth.session-token" || c.name === "__Secure-next-auth.session-token"
497
+ );
498
+ if (!sessionCookie) return { data: { session: null }, error: null };
499
+ const res2 = await fetch(`${baseUrl}/api/auth/v1/session`, {
500
+ headers: { ...headers(), "X-Postbase-Session": sessionCookie.value }
501
+ });
502
+ if (!res2.ok) return { data: { session: null }, error: null };
503
+ const json2 = await res2.json();
504
+ return { data: { session: json2.session ?? null }, error: null };
505
+ }
506
+ if (isBrowser()) {
507
+ const persisted = loadPersistedSession();
508
+ if (persisted) {
509
+ currentSession = persisted;
510
+ return { data: { session: persisted }, error: null };
511
+ }
512
+ }
513
+ const res = await fetch(`${baseUrl}/api/auth/v1/session`, { headers: headers() });
514
+ if (!res.ok) return { data: { session: null }, error: null };
515
+ const json = await res.json();
516
+ return { data: { session: json.session ?? null }, error: null };
517
+ } catch (err) {
518
+ return { data: { session: null }, error: String(err) };
519
+ }
520
+ },
521
+ async getUser(jwt) {
522
+ try {
523
+ const token = jwt ?? currentSession?.accessToken ?? loadPersistedSession()?.accessToken;
524
+ const res = await fetch(`${baseUrl}/api/auth/v1/user`, {
525
+ headers: {
526
+ ...headers(),
527
+ ...token ? { "X-Postbase-Token": token } : {}
528
+ }
529
+ });
530
+ const json = await res.json();
531
+ if (!res.ok) return { data: { user: null }, error: json.error ?? "Failed to get user" };
532
+ return { data: { user: json.user }, error: null };
533
+ } catch (err) {
534
+ return { data: { user: null }, error: String(err) };
535
+ }
536
+ },
537
+ async refreshSession(refreshToken) {
538
+ try {
539
+ const token = refreshToken ?? currentSession?.refreshToken ?? loadPersistedSession()?.refreshToken;
540
+ const res = await fetch(`${baseUrl}/api/auth/v1/token`, {
541
+ method: "POST",
542
+ headers: headers(),
543
+ body: JSON.stringify({ refresh_token: token, grant_type: "refresh_token" })
544
+ });
545
+ const json = await res.json();
546
+ if (!res.ok) return { data: { user: null, session: null }, error: json.error ?? "Refresh failed" };
547
+ const session = json.session;
548
+ const user = json.user;
549
+ notifyListeners("TOKEN_REFRESHED", session);
550
+ return { data: { user, session }, error: null };
551
+ } catch (err) {
552
+ return { data: { user: null, session: null }, error: String(err) };
553
+ }
554
+ },
555
+ onAuthStateChange(callback) {
556
+ listeners.push(callback);
557
+ const session = currentSession ?? loadPersistedSession();
558
+ if (session) {
559
+ setTimeout(() => callback("SIGNED_IN", session), 0);
560
+ currentSession = session;
561
+ scheduleRefresh(session);
562
+ }
563
+ return {
564
+ data: {
565
+ subscription: {
566
+ unsubscribe() {
567
+ const idx = listeners.indexOf(callback);
568
+ if (idx > -1) listeners.splice(idx, 1);
569
+ }
570
+ }
571
+ }
572
+ };
573
+ },
574
+ admin: createAuthAdmin(baseUrl, apiKey, customHeaders)
575
+ };
576
+ return authClient;
577
+ }
578
+ function createStorageClient(baseUrl, apiKey, options) {
579
+ const customHeaders = options?.global?.headers;
580
+ const headers = () => ({ Authorization: `Bearer ${apiKey}`, ...customHeaders });
581
+ function bucketClient(bucket) {
582
+ return {
583
+ async upload(path, file, uploadOptions) {
584
+ try {
585
+ const form = new FormData();
586
+ form.append("file", file);
587
+ form.append("path", path);
588
+ if (uploadOptions?.upsert) form.append("upsert", "true");
589
+ if (uploadOptions?.cacheControl) form.append("cacheControl", uploadOptions.cacheControl);
590
+ const res = await fetch(`${baseUrl}/api/storage/v1/object/${bucket}/${encodeURIComponent(path)}`, {
591
+ method: uploadOptions?.upsert ? "PUT" : "POST",
592
+ headers: headers(),
593
+ body: form
594
+ });
595
+ const json = await res.json();
596
+ if (!res.ok) return { data: null, error: json.error ?? "Upload failed" };
597
+ return { data: { path, fullPath: `${bucket}/${path}` }, error: null };
598
+ } catch (err) {
599
+ return { data: null, error: String(err) };
600
+ }
601
+ },
602
+ async download(path) {
603
+ try {
604
+ const res = await fetch(`${baseUrl}/api/storage/v1/object/${bucket}/${encodeURIComponent(path)}`, {
605
+ headers: headers()
606
+ });
607
+ if (!res.ok) return { data: null, error: "Download failed" };
608
+ return { data: await res.blob(), error: null };
609
+ } catch (err) {
610
+ return { data: null, error: String(err) };
611
+ }
612
+ },
613
+ async remove(paths) {
614
+ try {
615
+ const res = await fetch(`${baseUrl}/api/storage/v1/object/${bucket}`, {
616
+ method: "DELETE",
617
+ headers: { ...headers(), "Content-Type": "application/json" },
618
+ body: JSON.stringify({ prefixes: paths })
619
+ });
620
+ const json = await res.json();
621
+ if (!res.ok) return { data: null, count: null, error: json.error ?? "Delete failed" };
622
+ return { data: json.data, count: json.data?.length ?? null, error: null };
623
+ } catch (err) {
624
+ return { data: null, count: null, error: String(err) };
625
+ }
626
+ },
627
+ async list(prefix, listOptions) {
628
+ try {
629
+ const res = await fetch(`${baseUrl}/api/storage/v1/object/list/${bucket}`, {
630
+ method: "POST",
631
+ headers: { ...headers(), "Content-Type": "application/json" },
632
+ body: JSON.stringify({
633
+ prefix: prefix ?? "",
634
+ limit: listOptions?.limit ?? 100,
635
+ offset: listOptions?.offset ?? 0,
636
+ sortBy: listOptions?.sortBy
637
+ })
638
+ });
639
+ const json = await res.json();
640
+ if (!res.ok) return { data: null, count: null, error: json.error ?? "List failed" };
641
+ return { data: json.data, count: json.data?.length ?? null, error: null };
642
+ } catch (err) {
643
+ return { data: null, count: null, error: String(err) };
644
+ }
645
+ },
646
+ getPublicUrl(path) {
647
+ return {
648
+ data: { publicUrl: `${baseUrl}/api/storage/v1/object/public/${bucket}/${encodeURIComponent(path)}` }
649
+ };
650
+ },
651
+ async createSignedUrl(path, expiresIn) {
652
+ try {
653
+ const res = await fetch(`${baseUrl}/api/storage/v1/object/sign/${bucket}/${encodeURIComponent(path)}`, {
654
+ method: "POST",
655
+ headers: { ...headers(), "Content-Type": "application/json" },
656
+ body: JSON.stringify({ expiresIn })
657
+ });
658
+ const json = await res.json();
659
+ if (!res.ok) return { data: null, error: json.error ?? "Sign failed" };
660
+ return { data: { signedUrl: `${baseUrl}${json.signedUrl}` }, error: null };
661
+ } catch (err) {
662
+ return { data: null, error: String(err) };
663
+ }
664
+ },
665
+ async move(fromPath, toPath) {
666
+ try {
667
+ const res = await fetch(`${baseUrl}/api/storage/v1/object/move`, {
668
+ method: "POST",
669
+ headers: { ...headers(), "Content-Type": "application/json" },
670
+ body: JSON.stringify({ bucketId: bucket, sourceKey: fromPath, destinationKey: toPath })
671
+ });
672
+ const json = await res.json();
673
+ if (!res.ok) return { error: json.error ?? "Move failed" };
674
+ return { error: null };
675
+ } catch (err) {
676
+ return { error: String(err) };
677
+ }
678
+ },
679
+ async copy(fromPath, toPath) {
680
+ try {
681
+ const res = await fetch(`${baseUrl}/api/storage/v1/object/copy`, {
682
+ method: "POST",
683
+ headers: { ...headers(), "Content-Type": "application/json" },
684
+ body: JSON.stringify({ bucketId: bucket, sourceKey: fromPath, destinationKey: toPath })
685
+ });
686
+ const json = await res.json();
687
+ if (!res.ok) return { data: null, error: json.error ?? "Copy failed" };
688
+ return { data: { path: toPath }, error: null };
689
+ } catch (err) {
690
+ return { data: null, error: String(err) };
691
+ }
692
+ }
693
+ };
694
+ }
695
+ return {
696
+ from: bucketClient,
697
+ async createBucket(name, bucketOptions) {
698
+ try {
699
+ const res = await fetch(`${baseUrl}/api/storage/v1/bucket`, {
700
+ method: "POST",
701
+ headers: { ...headers(), "Content-Type": "application/json" },
702
+ body: JSON.stringify({
703
+ name,
704
+ id: name,
705
+ public: bucketOptions?.public ?? false,
706
+ file_size_limit: bucketOptions?.fileSizeLimit,
707
+ allowed_mime_types: bucketOptions?.allowedMimeTypes
708
+ })
709
+ });
710
+ const json = await res.json();
711
+ if (!res.ok) return { data: null, error: json.error ?? "Create bucket failed" };
712
+ return { data: json.data, error: null };
713
+ } catch (err) {
714
+ return { data: null, error: String(err) };
715
+ }
716
+ },
717
+ async getBucket(id) {
718
+ try {
719
+ const res = await fetch(`${baseUrl}/api/storage/v1/bucket/${id}`, { headers: headers() });
720
+ const json = await res.json();
721
+ if (!res.ok) return { data: null, error: json.error ?? "Get bucket failed" };
722
+ return { data: json.data, error: null };
723
+ } catch (err) {
724
+ return { data: null, error: String(err) };
725
+ }
726
+ },
727
+ async listBuckets() {
728
+ try {
729
+ const res = await fetch(`${baseUrl}/api/storage/v1/bucket`, { headers: headers() });
730
+ const json = await res.json();
731
+ if (!res.ok) return { data: null, count: null, error: json.error ?? "List buckets failed" };
732
+ return { data: json.data, count: json.data?.length ?? null, error: null };
733
+ } catch (err) {
734
+ return { data: null, count: null, error: String(err) };
735
+ }
736
+ },
737
+ async updateBucket(id, bucketOptions) {
738
+ try {
739
+ const res = await fetch(`${baseUrl}/api/storage/v1/bucket/${id}`, {
740
+ method: "PUT",
741
+ headers: { ...headers(), "Content-Type": "application/json" },
742
+ body: JSON.stringify({
743
+ public: bucketOptions.public,
744
+ file_size_limit: bucketOptions.fileSizeLimit,
745
+ allowed_mime_types: bucketOptions.allowedMimeTypes
746
+ })
747
+ });
748
+ const json = await res.json();
749
+ if (!res.ok) return { data: null, error: json.error ?? "Update bucket failed" };
750
+ return { data: json.data, error: null };
751
+ } catch (err) {
752
+ return { data: null, error: String(err) };
753
+ }
754
+ },
755
+ async deleteBucket(id) {
756
+ try {
757
+ const res = await fetch(`${baseUrl}/api/storage/v1/bucket/${id}`, {
758
+ method: "DELETE",
759
+ headers: headers()
760
+ });
761
+ const json = await res.json();
762
+ if (!res.ok) return { error: json.error ?? "Delete bucket failed" };
763
+ return { error: null };
764
+ } catch (err) {
765
+ return { error: String(err) };
766
+ }
767
+ },
768
+ async emptyBucket(id) {
769
+ try {
770
+ const res = await fetch(`${baseUrl}/api/storage/v1/bucket/${id}/empty`, {
771
+ method: "POST",
772
+ headers: headers()
773
+ });
774
+ const json = await res.json();
775
+ if (!res.ok) return { error: json.error ?? "Empty bucket failed" };
776
+ return { error: null };
777
+ } catch (err) {
778
+ return { error: String(err) };
779
+ }
780
+ }
781
+ };
782
+ }
783
+ var RealtimeChannelImpl = class {
784
+ constructor(baseUrl, apiKey, channelName) {
785
+ this.baseUrl = baseUrl;
786
+ this.apiKey = apiKey;
787
+ this.channelName = channelName;
788
+ this.handlers = [];
789
+ this.ws = null;
790
+ }
791
+ on(event, callback) {
792
+ this.handlers.push({ event, callback });
793
+ return this;
794
+ }
795
+ subscribe(callback) {
796
+ if (typeof window === "undefined") return this;
797
+ this.statusCallback = callback;
798
+ const wsUrl = this.baseUrl.replace(/^http/, "ws");
799
+ try {
800
+ this.ws = new WebSocket(`${wsUrl}/api/realtime?channel=${encodeURIComponent(this.channelName)}&apikey=${this.apiKey}`);
801
+ this.ws.onopen = () => {
802
+ this.statusCallback?.("SUBSCRIBED");
803
+ };
804
+ this.ws.onmessage = (event) => {
805
+ try {
806
+ const payload = JSON.parse(event.data);
807
+ this.handlers.forEach(({ event: eventType, callback: callback2 }) => {
808
+ if (eventType === "*" || eventType === payload.eventType) {
809
+ callback2(payload);
810
+ }
811
+ });
812
+ } catch {
813
+ }
814
+ };
815
+ this.ws.onerror = () => {
816
+ this.statusCallback?.("CHANNEL_ERROR");
817
+ };
818
+ this.ws.onclose = () => {
819
+ this.statusCallback?.("CLOSED");
820
+ };
821
+ } catch {
822
+ this.statusCallback?.("CHANNEL_ERROR");
823
+ }
824
+ return this;
825
+ }
826
+ unsubscribe() {
827
+ this.ws?.close();
828
+ this.ws = null;
829
+ }
830
+ };
831
+ var channels = /* @__PURE__ */ new Set();
832
+ function createClient(url, key, options) {
833
+ const baseUrl = url.replace(/\/$/, "");
834
+ const cookieAdapter = options?.cookies;
835
+ const makeQueryState = (table) => ({
836
+ baseUrl,
837
+ apiKey: key,
838
+ table,
839
+ columns: "*",
840
+ selectOptions: {},
841
+ filters: [],
842
+ orFilters: [],
843
+ notFilters: [],
844
+ orderBy: [],
845
+ operation: "select",
846
+ customHeaders: options?.global?.headers,
847
+ cookieAdapter
848
+ });
849
+ return {
850
+ url: baseUrl,
851
+ key,
852
+ auth: createAuthClient(baseUrl, key, options, cookieAdapter),
853
+ storage: createStorageClient(baseUrl, key, options),
854
+ from(table) {
855
+ return new QueryBuilderImpl(makeQueryState(table));
856
+ },
857
+ async rpc(fn, args, rpcOptions) {
858
+ try {
859
+ const headers = {
860
+ "Content-Type": "application/json",
861
+ Authorization: `Bearer ${key}`,
862
+ ...options?.global?.headers
863
+ };
864
+ if (cookieAdapter) {
865
+ const cookies = await cookieAdapter.getAll();
866
+ const sessionCookie = cookies.find(
867
+ (c) => c.name.startsWith("postbase-session") || c.name === "next-auth.session-token" || c.name === "__Secure-next-auth.session-token"
868
+ );
869
+ if (sessionCookie) headers["X-Postbase-Session"] = sessionCookie.value;
870
+ }
871
+ const res = await fetch(`${baseUrl}/api/rpc/${fn}`, {
872
+ method: rpcOptions?.head ? "HEAD" : "POST",
873
+ headers,
874
+ body: JSON.stringify({ args: args ?? {}, count: rpcOptions?.count })
875
+ });
876
+ const json = await res.json();
877
+ if (!res.ok) return { data: null, count: null, error: json.error ?? "RPC failed" };
878
+ return { data: json.data, count: json.count ?? null, error: null };
879
+ } catch (err) {
880
+ return { data: null, count: null, error: String(err) };
881
+ }
882
+ },
883
+ channel(name) {
884
+ const ch = new RealtimeChannelImpl(baseUrl, key, name);
885
+ channels.add(ch);
886
+ return ch;
887
+ },
888
+ removeChannel(channel) {
889
+ channel.unsubscribe();
890
+ channels.delete(channel);
891
+ },
892
+ removeAllChannels() {
893
+ channels.forEach((ch) => ch.unsubscribe());
894
+ channels.clear();
895
+ }
896
+ };
897
+ }
898
+
899
+ // src/ssr/index.ts
900
+ function createServerClient(url, key, options) {
901
+ return createClient(url, key, { ...options, cookies: options.cookies });
902
+ }
903
+ function createBrowserClient(url, key, options) {
904
+ return createClient(url, key, {
905
+ ...options,
906
+ auth: {
907
+ persistSession: true,
908
+ autoRefreshToken: true,
909
+ detectSessionInUrl: true,
910
+ ...options?.auth
911
+ }
912
+ });
913
+ }
914
+ export {
915
+ createBrowserClient,
916
+ createClient,
917
+ createServerClient
918
+ };
919
+ //# sourceMappingURL=index.mjs.map