@ragable/sdk 0.5.1 → 0.6.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.mts +399 -190
- package/dist/index.d.ts +399 -190
- package/dist/index.js +1297 -466
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1283 -466
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -10,14 +10,49 @@ function bindFetch(custom) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
var DEFAULT_RAGABLE_API_BASE = "https://ragable-341305259977.asia-southeast1.run.app/api";
|
|
13
|
-
var
|
|
13
|
+
var RagableSdkError = class extends Error {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = this.constructor.name;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var RagableError = class extends RagableSdkError {
|
|
14
20
|
constructor(message, status, body) {
|
|
15
21
|
super(message);
|
|
22
|
+
__publicField(this, "__type", "RagableError");
|
|
16
23
|
__publicField(this, "status");
|
|
17
24
|
__publicField(this, "body");
|
|
18
|
-
this
|
|
25
|
+
__publicField(this, "code");
|
|
26
|
+
__publicField(this, "details");
|
|
19
27
|
this.status = status;
|
|
20
28
|
this.body = body;
|
|
29
|
+
if (body && typeof body === "object") {
|
|
30
|
+
const b = body;
|
|
31
|
+
this.code = typeof b.code === "string" ? b.code : void 0;
|
|
32
|
+
this.details = typeof b.details === "string" ? b.details : void 0;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var RagableNetworkError = class extends RagableSdkError {
|
|
37
|
+
constructor(message, cause) {
|
|
38
|
+
super(message);
|
|
39
|
+
__publicField(this, "__type", "RagableNetworkError");
|
|
40
|
+
__publicField(this, "cause");
|
|
41
|
+
this.cause = cause;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var RagableAbortError = class extends RagableSdkError {
|
|
45
|
+
constructor(message = "Request aborted") {
|
|
46
|
+
super(message);
|
|
47
|
+
__publicField(this, "__type", "RagableAbortError");
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var RagableTimeoutError = class extends RagableSdkError {
|
|
51
|
+
constructor(timeoutMs) {
|
|
52
|
+
super(`Request timed out after ${timeoutMs}ms`);
|
|
53
|
+
__publicField(this, "__type", "RagableTimeoutError");
|
|
54
|
+
__publicField(this, "timeoutMs");
|
|
55
|
+
this.timeoutMs = timeoutMs;
|
|
21
56
|
}
|
|
22
57
|
};
|
|
23
58
|
function extractErrorMessage(payload, fallback) {
|
|
@@ -345,42 +380,191 @@ var AgentsClient = class {
|
|
|
345
380
|
}
|
|
346
381
|
};
|
|
347
382
|
|
|
348
|
-
// src/
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
383
|
+
// src/transport.ts
|
|
384
|
+
var DEFAULT_RETRY = {
|
|
385
|
+
maxRetries: 3,
|
|
386
|
+
baseDelayMs: 200,
|
|
387
|
+
maxDelayMs: 5e3,
|
|
388
|
+
retryOn: [408, 425, 429, 502, 503, 504],
|
|
389
|
+
respectRetryAfter: true
|
|
390
|
+
};
|
|
391
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
392
|
+
function jitteredDelay(base, attempt, max) {
|
|
393
|
+
const exp = Math.min(base * 2 ** attempt, max);
|
|
394
|
+
return Math.round(exp * (0.5 + Math.random() * 0.5));
|
|
395
|
+
}
|
|
396
|
+
function parseRetryAfter(header) {
|
|
397
|
+
if (!header) return null;
|
|
398
|
+
const seconds = Number(header);
|
|
399
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
400
|
+
const date = Date.parse(header);
|
|
401
|
+
if (Number.isFinite(date)) return Math.max(0, date - Date.now());
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
var _uuidCounter = 0;
|
|
405
|
+
function generateIdempotencyKey() {
|
|
406
|
+
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
407
|
+
return crypto.randomUUID();
|
|
356
408
|
}
|
|
409
|
+
_uuidCounter++;
|
|
410
|
+
return `idk-${Date.now()}-${_uuidCounter}-${Math.random().toString(36).slice(2, 10)}`;
|
|
357
411
|
}
|
|
358
|
-
function
|
|
359
|
-
|
|
360
|
-
return `"${name}"`;
|
|
412
|
+
function requestCacheKey(req) {
|
|
413
|
+
return `${req.method}:${req.url}`;
|
|
361
414
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
415
|
+
var Transport = class {
|
|
416
|
+
constructor(options = {}) {
|
|
417
|
+
__publicField(this, "fetchImpl");
|
|
418
|
+
__publicField(this, "defaultHeaders");
|
|
419
|
+
__publicField(this, "defaultRetry");
|
|
420
|
+
__publicField(this, "defaultTimeoutMs");
|
|
421
|
+
__publicField(this, "onRequest");
|
|
422
|
+
__publicField(this, "onResponse");
|
|
423
|
+
__publicField(this, "onRetry");
|
|
424
|
+
__publicField(this, "inflightGets", /* @__PURE__ */ new Map());
|
|
425
|
+
__publicField(this, "_refreshHandler", null);
|
|
426
|
+
this.fetchImpl = bindFetch(options.fetch);
|
|
427
|
+
this.defaultHeaders = options.headers;
|
|
428
|
+
this.defaultRetry = { ...DEFAULT_RETRY, ...options.retry };
|
|
429
|
+
this.defaultTimeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
430
|
+
this.onRequest = options.onRequest;
|
|
431
|
+
this.onResponse = options.onResponse;
|
|
432
|
+
this.onRetry = options.onRetry;
|
|
433
|
+
}
|
|
434
|
+
setRefreshHandler(handler) {
|
|
435
|
+
this._refreshHandler = handler;
|
|
436
|
+
}
|
|
437
|
+
async execute(req) {
|
|
438
|
+
if (req.method === "GET") {
|
|
439
|
+
const key = requestCacheKey(req);
|
|
440
|
+
const existing = this.inflightGets.get(key);
|
|
441
|
+
if (existing) return existing;
|
|
442
|
+
const promise = this._executeWithRetry(req).finally(() => {
|
|
443
|
+
this.inflightGets.delete(key);
|
|
444
|
+
});
|
|
445
|
+
this.inflightGets.set(key, promise);
|
|
446
|
+
return promise;
|
|
447
|
+
}
|
|
448
|
+
return this._executeWithRetry(req);
|
|
449
|
+
}
|
|
450
|
+
async _executeWithRetry(req) {
|
|
451
|
+
const retryOpts = {
|
|
452
|
+
...this.defaultRetry,
|
|
453
|
+
...req.retry
|
|
454
|
+
};
|
|
455
|
+
const timeoutMs = req.timeoutMs ?? this.defaultTimeoutMs;
|
|
456
|
+
const headers = new Headers(this.defaultHeaders);
|
|
457
|
+
req.headers.forEach((v, k) => headers.set(k, v));
|
|
458
|
+
if (req.idempotencyKey) {
|
|
459
|
+
headers.set("Idempotency-Key", req.idempotencyKey);
|
|
460
|
+
}
|
|
461
|
+
const finalReq = { ...req, headers };
|
|
462
|
+
this.onRequest?.(finalReq);
|
|
463
|
+
let lastError;
|
|
464
|
+
const maxAttempts = 1 + retryOpts.maxRetries;
|
|
465
|
+
let did401Refresh = false;
|
|
466
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
467
|
+
try {
|
|
468
|
+
const response = await this._singleFetch(finalReq, timeoutMs);
|
|
469
|
+
if (response.status === 401 && this._refreshHandler && !did401Refresh) {
|
|
470
|
+
did401Refresh = true;
|
|
471
|
+
const newToken = await this._refreshHandler();
|
|
472
|
+
if (newToken) {
|
|
473
|
+
finalReq.headers.set("Authorization", `Bearer ${newToken}`);
|
|
474
|
+
attempt--;
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (!response.ok && retryOpts.retryOn.includes(response.status) && attempt < maxAttempts - 1) {
|
|
479
|
+
let delayMs = jitteredDelay(retryOpts.baseDelayMs, attempt, retryOpts.maxDelayMs);
|
|
480
|
+
if (retryOpts.respectRetryAfter) {
|
|
481
|
+
const ra = parseRetryAfter(response.headers.get("retry-after"));
|
|
482
|
+
if (ra !== null) delayMs = Math.min(ra, retryOpts.maxDelayMs);
|
|
483
|
+
}
|
|
484
|
+
this.onRetry?.(finalReq, attempt + 1, delayMs, `HTTP ${response.status}`);
|
|
485
|
+
await sleep(delayMs);
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
return response;
|
|
489
|
+
} catch (e) {
|
|
490
|
+
if (e instanceof RagableAbortError || e instanceof RagableTimeoutError) {
|
|
491
|
+
throw e;
|
|
492
|
+
}
|
|
493
|
+
lastError = e;
|
|
494
|
+
if (attempt < maxAttempts - 1) {
|
|
495
|
+
const delayMs = jitteredDelay(retryOpts.baseDelayMs, attempt, retryOpts.maxDelayMs);
|
|
496
|
+
this.onRetry?.(finalReq, attempt + 1, delayMs, e.message);
|
|
497
|
+
await sleep(delayMs);
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
throw lastError instanceof RagableNetworkError ? lastError : new RagableNetworkError(
|
|
503
|
+
lastError?.message ?? "Network request failed",
|
|
504
|
+
lastError
|
|
369
505
|
);
|
|
370
506
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
507
|
+
async _singleFetch(req, timeoutMs) {
|
|
508
|
+
const controller = new AbortController();
|
|
509
|
+
const signals = [controller.signal];
|
|
510
|
+
if (req.signal) signals.push(req.signal);
|
|
511
|
+
const combinedSignal = signals.length === 1 ? controller.signal : AbortSignal.any ? AbortSignal.any(signals) : controller.signal;
|
|
512
|
+
if (req.signal?.aborted) {
|
|
513
|
+
throw new RagableAbortError();
|
|
514
|
+
}
|
|
515
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
516
|
+
const externalAbortHandler = req.signal ? () => controller.abort() : null;
|
|
517
|
+
if (externalAbortHandler && req.signal) {
|
|
518
|
+
req.signal.addEventListener("abort", externalAbortHandler, { once: true });
|
|
519
|
+
}
|
|
520
|
+
const start = Date.now();
|
|
521
|
+
try {
|
|
522
|
+
const response = await this.fetchImpl(req.url, {
|
|
523
|
+
method: req.method,
|
|
524
|
+
headers: req.headers,
|
|
525
|
+
body: req.body,
|
|
526
|
+
signal: combinedSignal
|
|
527
|
+
});
|
|
528
|
+
this.onResponse?.(req, response, Date.now() - start);
|
|
529
|
+
return response;
|
|
530
|
+
} catch (e) {
|
|
531
|
+
if (e.name === "AbortError") {
|
|
532
|
+
if (req.signal?.aborted) throw new RagableAbortError();
|
|
533
|
+
throw new RagableTimeoutError(timeoutMs);
|
|
534
|
+
}
|
|
535
|
+
throw new RagableNetworkError(e.message, e);
|
|
536
|
+
} finally {
|
|
537
|
+
clearTimeout(timer);
|
|
538
|
+
if (externalAbortHandler && req.signal) {
|
|
539
|
+
req.signal.removeEventListener("abort", externalAbortHandler);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
383
543
|
};
|
|
544
|
+
function sleep(ms) {
|
|
545
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
546
|
+
}
|
|
547
|
+
async function parseTransportResponse(response) {
|
|
548
|
+
if (response.status === 204) return null;
|
|
549
|
+
const text = await response.text();
|
|
550
|
+
if (!text) return null;
|
|
551
|
+
let payload;
|
|
552
|
+
try {
|
|
553
|
+
payload = JSON.parse(text);
|
|
554
|
+
} catch {
|
|
555
|
+
if (!response.ok) {
|
|
556
|
+
throw new RagableError(text.slice(0, 200), response.status, null);
|
|
557
|
+
}
|
|
558
|
+
return text;
|
|
559
|
+
}
|
|
560
|
+
if (!response.ok) {
|
|
561
|
+
const message = extractErrorMessage(payload, response.statusText);
|
|
562
|
+
throw new RagableError(message, response.status, payload);
|
|
563
|
+
}
|
|
564
|
+
return payload;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// src/browser-postgrest.ts
|
|
384
568
|
async function asPostgrestResponse(fn) {
|
|
385
569
|
try {
|
|
386
570
|
const data = await fn();
|
|
@@ -390,28 +574,75 @@ async function asPostgrestResponse(fn) {
|
|
|
390
574
|
return { data: null, error: err };
|
|
391
575
|
}
|
|
392
576
|
}
|
|
393
|
-
function
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
577
|
+
function encodeFilterValue(op, value) {
|
|
578
|
+
if (op === "is") return `is.${value}`;
|
|
579
|
+
if (op === "in") {
|
|
580
|
+
const vals = value;
|
|
581
|
+
return `in.(${vals.map(String).join(",")})`;
|
|
582
|
+
}
|
|
583
|
+
return `${op}.${value}`;
|
|
584
|
+
}
|
|
585
|
+
async function parsePostgRESTResponse(response) {
|
|
586
|
+
if (response.status === 204) return null;
|
|
587
|
+
const text = await response.text();
|
|
588
|
+
if (!text) return null;
|
|
589
|
+
let payload;
|
|
590
|
+
try {
|
|
591
|
+
payload = JSON.parse(text);
|
|
592
|
+
} catch {
|
|
593
|
+
throw new RagableError(
|
|
594
|
+
`PostgREST response parse error: ${text.slice(0, 200)}`,
|
|
595
|
+
response.status,
|
|
596
|
+
null
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
if (!response.ok) {
|
|
600
|
+
const msg = typeof payload === "object" && payload !== null ? payload.message ?? payload.error ?? response.statusText : response.statusText;
|
|
601
|
+
const code = typeof payload === "object" && payload !== null ? payload.code : void 0;
|
|
602
|
+
throw new RagableError(String(msg), response.status, { code });
|
|
603
|
+
}
|
|
604
|
+
return payload;
|
|
605
|
+
}
|
|
606
|
+
function addFilterMethods(builder) {
|
|
607
|
+
const b = builder;
|
|
608
|
+
for (const op of ["eq", "neq", "gt", "gte", "lt", "lte", "like", "ilike"]) {
|
|
609
|
+
b[op] = function(column, value) {
|
|
610
|
+
b.filters.push({ op, column, value });
|
|
611
|
+
return b;
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
b.is = function(column, value) {
|
|
615
|
+
b.filters.push({ op: "is", column, value });
|
|
616
|
+
return b;
|
|
617
|
+
};
|
|
618
|
+
b.in = function(column, values) {
|
|
619
|
+
b.filters.push({ op: "in", column, value: values });
|
|
620
|
+
return b;
|
|
621
|
+
};
|
|
622
|
+
b.match = function(query) {
|
|
623
|
+
for (const [col, val] of Object.entries(query)) {
|
|
624
|
+
if (val === null) {
|
|
625
|
+
b.filters.push({ op: "is", column: col, value: null });
|
|
626
|
+
} else {
|
|
627
|
+
b.filters.push({ op: "eq", column: col, value: val });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return b;
|
|
631
|
+
};
|
|
632
|
+
return b;
|
|
404
633
|
}
|
|
405
634
|
var PostgrestSelectBuilder = class {
|
|
406
|
-
constructor(
|
|
407
|
-
this.
|
|
635
|
+
constructor(pgFetch, databaseInstanceId, table, columns) {
|
|
636
|
+
this.pgFetch = pgFetch;
|
|
408
637
|
this.databaseInstanceId = databaseInstanceId;
|
|
409
638
|
this.table = table;
|
|
410
639
|
this.columns = columns;
|
|
411
640
|
__publicField(this, "filters", []);
|
|
412
641
|
__publicField(this, "_limit");
|
|
642
|
+
__publicField(this, "_offset");
|
|
413
643
|
__publicField(this, "_order");
|
|
414
|
-
|
|
644
|
+
__publicField(this, "_signal");
|
|
645
|
+
addFilterMethods(this);
|
|
415
646
|
}
|
|
416
647
|
eq(column, value) {
|
|
417
648
|
this.filters.push({ op: "eq", column, value });
|
|
@@ -445,140 +676,173 @@ var PostgrestSelectBuilder = class {
|
|
|
445
676
|
this.filters.push({ op: "ilike", column, value });
|
|
446
677
|
return this;
|
|
447
678
|
}
|
|
679
|
+
is(column, value) {
|
|
680
|
+
this.filters.push({ op: "is", column, value });
|
|
681
|
+
return this;
|
|
682
|
+
}
|
|
683
|
+
in(column, values) {
|
|
684
|
+
this.filters.push({ op: "in", column, value: values });
|
|
685
|
+
return this;
|
|
686
|
+
}
|
|
687
|
+
match(query) {
|
|
688
|
+
for (const [col, val] of Object.entries(query)) {
|
|
689
|
+
if (val === null) {
|
|
690
|
+
this.filters.push({ op: "is", column: col, value: null });
|
|
691
|
+
} else {
|
|
692
|
+
this.filters.push({ op: "eq", column: col, value: val });
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return this;
|
|
696
|
+
}
|
|
448
697
|
limit(n) {
|
|
449
698
|
this._limit = n;
|
|
450
699
|
return this;
|
|
451
700
|
}
|
|
701
|
+
offset(n) {
|
|
702
|
+
this._offset = n;
|
|
703
|
+
return this;
|
|
704
|
+
}
|
|
705
|
+
range(from, to) {
|
|
706
|
+
this._offset = from;
|
|
707
|
+
this._limit = to - from + 1;
|
|
708
|
+
return this;
|
|
709
|
+
}
|
|
452
710
|
order(column, options) {
|
|
453
|
-
this._order = {
|
|
711
|
+
this._order = {
|
|
712
|
+
column,
|
|
713
|
+
ascending: options?.ascending !== false,
|
|
714
|
+
nullsFirst: options?.nullsFirst
|
|
715
|
+
};
|
|
716
|
+
return this;
|
|
717
|
+
}
|
|
718
|
+
abortSignal(signal) {
|
|
719
|
+
this._signal = signal;
|
|
454
720
|
return this;
|
|
455
721
|
}
|
|
456
|
-
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
722
|
+
buildSearchParams() {
|
|
723
|
+
const sp = new URLSearchParams();
|
|
724
|
+
if (this.columns && this.columns !== "*") {
|
|
725
|
+
sp.set("select", this.columns);
|
|
726
|
+
}
|
|
727
|
+
for (const f of this.filters) {
|
|
728
|
+
sp.append(f.column, encodeFilterValue(f.op, f.value));
|
|
729
|
+
}
|
|
460
730
|
if (this._order) {
|
|
461
|
-
|
|
731
|
+
let orderStr = `${this._order.column}.${this._order.ascending ? "asc" : "desc"}`;
|
|
732
|
+
if (this._order.nullsFirst === true) orderStr += ".nullsfirst";
|
|
733
|
+
else if (this._order.nullsFirst === false) orderStr += ".nullslast";
|
|
734
|
+
sp.set("order", orderStr);
|
|
735
|
+
}
|
|
736
|
+
if (this._limit != null) {
|
|
737
|
+
sp.set("limit", String(Math.max(0, Math.floor(this._limit))));
|
|
462
738
|
}
|
|
463
|
-
if (
|
|
464
|
-
|
|
739
|
+
if (this._offset != null && this._offset > 0) {
|
|
740
|
+
sp.set("offset", String(Math.max(0, Math.floor(this._offset))));
|
|
465
741
|
}
|
|
466
|
-
return
|
|
742
|
+
return sp;
|
|
467
743
|
}
|
|
468
744
|
then(onfulfilled, onrejected) {
|
|
469
745
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
470
746
|
}
|
|
471
747
|
async executeMany() {
|
|
472
748
|
return asPostgrestResponse(async () => {
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
749
|
+
const response = await this.pgFetch({
|
|
750
|
+
method: "GET",
|
|
751
|
+
table: this.table,
|
|
752
|
+
searchParams: this.buildSearchParams(),
|
|
476
753
|
databaseInstanceId: this.databaseInstanceId,
|
|
477
|
-
|
|
478
|
-
params,
|
|
479
|
-
readOnly: true
|
|
754
|
+
signal: this._signal
|
|
480
755
|
});
|
|
481
|
-
return
|
|
756
|
+
return parsePostgRESTResponse(response);
|
|
482
757
|
});
|
|
483
758
|
}
|
|
484
759
|
async single() {
|
|
485
760
|
return asPostgrestResponse(async () => {
|
|
486
|
-
const
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
761
|
+
const sp = this.buildSearchParams();
|
|
762
|
+
const response = await this.pgFetch({
|
|
763
|
+
method: "GET",
|
|
764
|
+
table: this.table,
|
|
765
|
+
searchParams: sp,
|
|
766
|
+
headers: { Accept: "application/vnd.pgrst.object+json" },
|
|
490
767
|
databaseInstanceId: this.databaseInstanceId,
|
|
491
|
-
|
|
492
|
-
params,
|
|
493
|
-
readOnly: true
|
|
768
|
+
signal: this._signal
|
|
494
769
|
});
|
|
495
|
-
|
|
496
|
-
throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
|
|
497
|
-
code: "PGRST116"
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
return res.rows[0];
|
|
770
|
+
return parsePostgRESTResponse(response);
|
|
501
771
|
});
|
|
502
772
|
}
|
|
503
773
|
async maybeSingle() {
|
|
504
774
|
return asPostgrestResponse(async () => {
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
775
|
+
const response = await this.pgFetch({
|
|
776
|
+
method: "GET",
|
|
777
|
+
table: this.table,
|
|
778
|
+
searchParams: this.buildSearchParams(),
|
|
509
779
|
databaseInstanceId: this.databaseInstanceId,
|
|
510
|
-
|
|
511
|
-
params,
|
|
512
|
-
readOnly: true
|
|
780
|
+
signal: this._signal
|
|
513
781
|
});
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
782
|
+
const rows = await parsePostgRESTResponse(response);
|
|
783
|
+
if (rows.length > 1) {
|
|
784
|
+
throw new RagableError(
|
|
785
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
786
|
+
406,
|
|
787
|
+
{ code: "PGRST116" }
|
|
788
|
+
);
|
|
518
789
|
}
|
|
519
|
-
return
|
|
790
|
+
return rows[0] ?? null;
|
|
520
791
|
});
|
|
521
792
|
}
|
|
522
793
|
};
|
|
523
794
|
var PostgrestInsertRootBuilder = class {
|
|
524
|
-
constructor(
|
|
525
|
-
this.
|
|
795
|
+
constructor(pgFetch, databaseInstanceId, table, rows) {
|
|
796
|
+
this.pgFetch = pgFetch;
|
|
526
797
|
this.databaseInstanceId = databaseInstanceId;
|
|
527
798
|
this.table = table;
|
|
528
799
|
this.rows = rows;
|
|
529
|
-
|
|
800
|
+
__publicField(this, "_signal");
|
|
530
801
|
}
|
|
531
|
-
/**
|
|
532
|
-
* Return inserted rows (Supabase: chain `.select()` to get data).
|
|
533
|
-
* @see https://supabase.com/docs/reference/javascript/insert
|
|
534
|
-
*/
|
|
535
802
|
select(columns = "*") {
|
|
536
803
|
return new PostgrestInsertReturningBuilder(
|
|
537
|
-
this.
|
|
804
|
+
this.pgFetch,
|
|
538
805
|
this.databaseInstanceId,
|
|
539
806
|
this.table,
|
|
540
807
|
this.rows,
|
|
541
|
-
columns
|
|
808
|
+
columns,
|
|
809
|
+
this._signal
|
|
542
810
|
);
|
|
543
811
|
}
|
|
812
|
+
abortSignal(signal) {
|
|
813
|
+
this._signal = signal;
|
|
814
|
+
return this;
|
|
815
|
+
}
|
|
544
816
|
then(onfulfilled, onrejected) {
|
|
545
817
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
546
818
|
}
|
|
547
819
|
async executeNoReturn() {
|
|
548
820
|
return asPostgrestResponse(async () => {
|
|
549
821
|
if (this.rows.length === 0) return null;
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
for (const k of keys) {
|
|
558
|
-
params.push(row[k]);
|
|
559
|
-
placeholders.push(`$${params.length}`);
|
|
560
|
-
}
|
|
561
|
-
valueGroups.push(`(${placeholders.join(", ")})`);
|
|
562
|
-
}
|
|
563
|
-
const cols = keys.map(quoteIdent).join(", ");
|
|
564
|
-
const sql = `INSERT INTO ${tbl} (${cols}) VALUES ${valueGroups.join(", ")}`;
|
|
565
|
-
await this.run({
|
|
822
|
+
const body = this.rows.length === 1 ? this.rows[0] : this.rows;
|
|
823
|
+
const response = await this.pgFetch({
|
|
824
|
+
method: "POST",
|
|
825
|
+
table: this.table,
|
|
826
|
+
searchParams: new URLSearchParams(),
|
|
827
|
+
body,
|
|
828
|
+
headers: { Prefer: "return=minimal" },
|
|
566
829
|
databaseInstanceId: this.databaseInstanceId,
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
readOnly: false
|
|
830
|
+
signal: this._signal,
|
|
831
|
+
idempotencyKey: generateIdempotencyKey()
|
|
570
832
|
});
|
|
833
|
+
await parsePostgRESTResponse(response);
|
|
571
834
|
return null;
|
|
572
835
|
});
|
|
573
836
|
}
|
|
574
837
|
};
|
|
575
838
|
var PostgrestInsertReturningBuilder = class {
|
|
576
|
-
constructor(
|
|
577
|
-
this.
|
|
839
|
+
constructor(pgFetch, databaseInstanceId, table, rows, returning, _signal) {
|
|
840
|
+
this.pgFetch = pgFetch;
|
|
578
841
|
this.databaseInstanceId = databaseInstanceId;
|
|
579
842
|
this.table = table;
|
|
580
843
|
this.rows = rows;
|
|
581
844
|
this.returning = returning;
|
|
845
|
+
this._signal = _signal;
|
|
582
846
|
}
|
|
583
847
|
then(onfulfilled, onrejected) {
|
|
584
848
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
@@ -586,28 +850,22 @@ var PostgrestInsertReturningBuilder = class {
|
|
|
586
850
|
async executeMany() {
|
|
587
851
|
return asPostgrestResponse(async () => {
|
|
588
852
|
if (this.rows.length === 0) return [];
|
|
589
|
-
const
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const valueGroups = [];
|
|
594
|
-
for (const row of this.rows) {
|
|
595
|
-
const placeholders = [];
|
|
596
|
-
for (const k of keys) {
|
|
597
|
-
params.push(row[k]);
|
|
598
|
-
placeholders.push(`$${params.length}`);
|
|
599
|
-
}
|
|
600
|
-
valueGroups.push(`(${placeholders.join(", ")})`);
|
|
853
|
+
const body = this.rows.length === 1 ? this.rows[0] : this.rows;
|
|
854
|
+
const sp = new URLSearchParams();
|
|
855
|
+
if (this.returning && this.returning !== "*") {
|
|
856
|
+
sp.set("select", this.returning);
|
|
601
857
|
}
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
858
|
+
const response = await this.pgFetch({
|
|
859
|
+
method: "POST",
|
|
860
|
+
table: this.table,
|
|
861
|
+
searchParams: sp,
|
|
862
|
+
body,
|
|
863
|
+
headers: { Prefer: "return=representation" },
|
|
605
864
|
databaseInstanceId: this.databaseInstanceId,
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
readOnly: false
|
|
865
|
+
signal: this._signal,
|
|
866
|
+
idempotencyKey: generateIdempotencyKey()
|
|
609
867
|
});
|
|
610
|
-
return
|
|
868
|
+
return parsePostgRESTResponse(response);
|
|
611
869
|
});
|
|
612
870
|
}
|
|
613
871
|
async single() {
|
|
@@ -616,9 +874,11 @@ var PostgrestInsertReturningBuilder = class {
|
|
|
616
874
|
if (error) throw error;
|
|
617
875
|
const rows = data ?? [];
|
|
618
876
|
if (rows.length === 0 || rows.length > 1) {
|
|
619
|
-
throw new RagableError(
|
|
620
|
-
|
|
621
|
-
|
|
877
|
+
throw new RagableError(
|
|
878
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
879
|
+
406,
|
|
880
|
+
{ code: "PGRST116" }
|
|
881
|
+
);
|
|
622
882
|
}
|
|
623
883
|
return rows[0];
|
|
624
884
|
});
|
|
@@ -629,22 +889,24 @@ var PostgrestInsertReturningBuilder = class {
|
|
|
629
889
|
if (error) throw error;
|
|
630
890
|
const rows = data ?? [];
|
|
631
891
|
if (rows.length > 1) {
|
|
632
|
-
throw new RagableError(
|
|
633
|
-
|
|
634
|
-
|
|
892
|
+
throw new RagableError(
|
|
893
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
894
|
+
406,
|
|
895
|
+
{ code: "PGRST116" }
|
|
896
|
+
);
|
|
635
897
|
}
|
|
636
898
|
return rows[0] ?? null;
|
|
637
899
|
});
|
|
638
900
|
}
|
|
639
901
|
};
|
|
640
902
|
var PostgrestUpdateRootBuilder = class {
|
|
641
|
-
constructor(
|
|
642
|
-
this.
|
|
903
|
+
constructor(pgFetch, databaseInstanceId, table, patch) {
|
|
904
|
+
this.pgFetch = pgFetch;
|
|
643
905
|
this.databaseInstanceId = databaseInstanceId;
|
|
644
906
|
this.table = table;
|
|
645
907
|
this.patch = patch;
|
|
646
908
|
__publicField(this, "filters", []);
|
|
647
|
-
|
|
909
|
+
__publicField(this, "_signal");
|
|
648
910
|
}
|
|
649
911
|
eq(column, value) {
|
|
650
912
|
this.filters.push({ op: "eq", column, value });
|
|
@@ -678,64 +940,79 @@ var PostgrestUpdateRootBuilder = class {
|
|
|
678
940
|
this.filters.push({ op: "ilike", column, value });
|
|
679
941
|
return this;
|
|
680
942
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
943
|
+
is(column, value) {
|
|
944
|
+
this.filters.push({ op: "is", column, value });
|
|
945
|
+
return this;
|
|
946
|
+
}
|
|
947
|
+
in(column, values) {
|
|
948
|
+
this.filters.push({ op: "in", column, value: values });
|
|
949
|
+
return this;
|
|
950
|
+
}
|
|
951
|
+
match(query) {
|
|
952
|
+
for (const [col, val] of Object.entries(query)) {
|
|
953
|
+
if (val === null) {
|
|
954
|
+
this.filters.push({ op: "is", column: col, value: null });
|
|
955
|
+
} else {
|
|
956
|
+
this.filters.push({ op: "eq", column: col, value: val });
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return this;
|
|
960
|
+
}
|
|
685
961
|
select(columns = "*") {
|
|
686
962
|
return new PostgrestUpdateReturningBuilder(
|
|
687
|
-
this.
|
|
963
|
+
this.pgFetch,
|
|
688
964
|
this.databaseInstanceId,
|
|
689
965
|
this.table,
|
|
690
966
|
this.patch,
|
|
691
967
|
this.filters,
|
|
692
|
-
columns
|
|
968
|
+
columns,
|
|
969
|
+
this._signal
|
|
693
970
|
);
|
|
694
971
|
}
|
|
972
|
+
abortSignal(signal) {
|
|
973
|
+
this._signal = signal;
|
|
974
|
+
return this;
|
|
975
|
+
}
|
|
695
976
|
then(onfulfilled, onrejected) {
|
|
696
977
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
697
978
|
}
|
|
979
|
+
buildSearchParams() {
|
|
980
|
+
const sp = new URLSearchParams();
|
|
981
|
+
for (const f of this.filters) {
|
|
982
|
+
sp.append(f.column, encodeFilterValue(f.op, f.value));
|
|
983
|
+
}
|
|
984
|
+
return sp;
|
|
985
|
+
}
|
|
698
986
|
async executeNoReturn() {
|
|
699
987
|
return asPostgrestResponse(async () => {
|
|
700
988
|
const keys = Object.keys(this.patch);
|
|
701
989
|
if (keys.length === 0) {
|
|
702
990
|
throw new RagableError("Empty update payload", 400, null);
|
|
703
991
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
const params = [];
|
|
713
|
-
const sets = [];
|
|
714
|
-
for (const k of keys) {
|
|
715
|
-
params.push(this.patch[k]);
|
|
716
|
-
sets.push(`${quoteIdent(k)} = $${params.length}`);
|
|
717
|
-
}
|
|
718
|
-
const { clause } = buildWhere(this.filters, params);
|
|
719
|
-
const tbl = quoteIdent(this.table);
|
|
720
|
-
const sql = `UPDATE ${tbl} SET ${sets.join(", ")}${clause}`;
|
|
721
|
-
await this.run({
|
|
992
|
+
const response = await this.pgFetch({
|
|
993
|
+
method: "PATCH",
|
|
994
|
+
table: this.table,
|
|
995
|
+
searchParams: this.buildSearchParams(),
|
|
996
|
+
body: this.patch,
|
|
997
|
+
headers: { Prefer: "return=minimal" },
|
|
722
998
|
databaseInstanceId: this.databaseInstanceId,
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
readOnly: false
|
|
999
|
+
signal: this._signal,
|
|
1000
|
+
idempotencyKey: generateIdempotencyKey()
|
|
726
1001
|
});
|
|
1002
|
+
await parsePostgRESTResponse(response);
|
|
727
1003
|
return null;
|
|
728
1004
|
});
|
|
729
1005
|
}
|
|
730
1006
|
};
|
|
731
1007
|
var PostgrestUpdateReturningBuilder = class {
|
|
732
|
-
constructor(
|
|
733
|
-
this.
|
|
1008
|
+
constructor(pgFetch, databaseInstanceId, table, patch, filters, returning, _signal) {
|
|
1009
|
+
this.pgFetch = pgFetch;
|
|
734
1010
|
this.databaseInstanceId = databaseInstanceId;
|
|
735
1011
|
this.table = table;
|
|
736
1012
|
this.patch = patch;
|
|
737
1013
|
this.filters = filters;
|
|
738
1014
|
this.returning = returning;
|
|
1015
|
+
this._signal = _signal;
|
|
739
1016
|
}
|
|
740
1017
|
then(onfulfilled, onrejected) {
|
|
741
1018
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
@@ -746,30 +1023,24 @@ var PostgrestUpdateReturningBuilder = class {
|
|
|
746
1023
|
if (keys.length === 0) {
|
|
747
1024
|
throw new RagableError("Empty update payload", 400, null);
|
|
748
1025
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
"UPDATE requires a filter (e.g. .eq('id', value)) \u2014 refusing unscoped update",
|
|
753
|
-
400,
|
|
754
|
-
null
|
|
755
|
-
);
|
|
1026
|
+
const sp = new URLSearchParams();
|
|
1027
|
+
for (const f of this.filters) {
|
|
1028
|
+
sp.append(f.column, encodeFilterValue(f.op, f.value));
|
|
756
1029
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
for (const k of keys) {
|
|
760
|
-
params.push(this.patch[k]);
|
|
761
|
-
sets.push(`${quoteIdent(k)} = $${params.length}`);
|
|
1030
|
+
if (this.returning && this.returning !== "*") {
|
|
1031
|
+
sp.set("select", this.returning);
|
|
762
1032
|
}
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1033
|
+
const response = await this.pgFetch({
|
|
1034
|
+
method: "PATCH",
|
|
1035
|
+
table: this.table,
|
|
1036
|
+
searchParams: sp,
|
|
1037
|
+
body: this.patch,
|
|
1038
|
+
headers: { Prefer: "return=representation" },
|
|
767
1039
|
databaseInstanceId: this.databaseInstanceId,
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
readOnly: false
|
|
1040
|
+
signal: this._signal,
|
|
1041
|
+
idempotencyKey: generateIdempotencyKey()
|
|
771
1042
|
});
|
|
772
|
-
return
|
|
1043
|
+
return parsePostgRESTResponse(response);
|
|
773
1044
|
});
|
|
774
1045
|
}
|
|
775
1046
|
async single() {
|
|
@@ -778,9 +1049,11 @@ var PostgrestUpdateReturningBuilder = class {
|
|
|
778
1049
|
if (error) throw error;
|
|
779
1050
|
const rows = data ?? [];
|
|
780
1051
|
if (rows.length === 0 || rows.length > 1) {
|
|
781
|
-
throw new RagableError(
|
|
782
|
-
|
|
783
|
-
|
|
1052
|
+
throw new RagableError(
|
|
1053
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1054
|
+
406,
|
|
1055
|
+
{ code: "PGRST116" }
|
|
1056
|
+
);
|
|
784
1057
|
}
|
|
785
1058
|
return rows[0];
|
|
786
1059
|
});
|
|
@@ -791,21 +1064,23 @@ var PostgrestUpdateReturningBuilder = class {
|
|
|
791
1064
|
if (error) throw error;
|
|
792
1065
|
const rows = data ?? [];
|
|
793
1066
|
if (rows.length > 1) {
|
|
794
|
-
throw new RagableError(
|
|
795
|
-
|
|
796
|
-
|
|
1067
|
+
throw new RagableError(
|
|
1068
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1069
|
+
406,
|
|
1070
|
+
{ code: "PGRST116" }
|
|
1071
|
+
);
|
|
797
1072
|
}
|
|
798
1073
|
return rows[0] ?? null;
|
|
799
1074
|
});
|
|
800
1075
|
}
|
|
801
1076
|
};
|
|
802
1077
|
var PostgrestDeleteRootBuilder = class {
|
|
803
|
-
constructor(
|
|
804
|
-
this.
|
|
1078
|
+
constructor(pgFetch, databaseInstanceId, table) {
|
|
1079
|
+
this.pgFetch = pgFetch;
|
|
805
1080
|
this.databaseInstanceId = databaseInstanceId;
|
|
806
1081
|
this.table = table;
|
|
807
1082
|
__publicField(this, "filters", []);
|
|
808
|
-
|
|
1083
|
+
__publicField(this, "_signal");
|
|
809
1084
|
}
|
|
810
1085
|
eq(column, value) {
|
|
811
1086
|
this.filters.push({ op: "eq", column, value });
|
|
@@ -839,76 +1114,92 @@ var PostgrestDeleteRootBuilder = class {
|
|
|
839
1114
|
this.filters.push({ op: "ilike", column, value });
|
|
840
1115
|
return this;
|
|
841
1116
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
1117
|
+
is(column, value) {
|
|
1118
|
+
this.filters.push({ op: "is", column, value });
|
|
1119
|
+
return this;
|
|
1120
|
+
}
|
|
1121
|
+
in(column, values) {
|
|
1122
|
+
this.filters.push({ op: "in", column, value: values });
|
|
1123
|
+
return this;
|
|
1124
|
+
}
|
|
1125
|
+
match(query) {
|
|
1126
|
+
for (const [col, val] of Object.entries(query)) {
|
|
1127
|
+
if (val === null) {
|
|
1128
|
+
this.filters.push({ op: "is", column: col, value: null });
|
|
1129
|
+
} else {
|
|
1130
|
+
this.filters.push({ op: "eq", column: col, value: val });
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
return this;
|
|
1134
|
+
}
|
|
846
1135
|
select(columns = "*") {
|
|
847
1136
|
return new PostgrestDeleteReturningBuilder(
|
|
848
|
-
this.
|
|
1137
|
+
this.pgFetch,
|
|
849
1138
|
this.databaseInstanceId,
|
|
850
1139
|
this.table,
|
|
851
1140
|
this.filters,
|
|
852
|
-
columns
|
|
1141
|
+
columns,
|
|
1142
|
+
this._signal
|
|
853
1143
|
);
|
|
854
1144
|
}
|
|
1145
|
+
abortSignal(signal) {
|
|
1146
|
+
this._signal = signal;
|
|
1147
|
+
return this;
|
|
1148
|
+
}
|
|
855
1149
|
then(onfulfilled, onrejected) {
|
|
856
1150
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
857
1151
|
}
|
|
858
1152
|
async executeNoReturn() {
|
|
859
1153
|
return asPostgrestResponse(async () => {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
400,
|
|
864
|
-
null
|
|
865
|
-
);
|
|
1154
|
+
const sp = new URLSearchParams();
|
|
1155
|
+
for (const f of this.filters) {
|
|
1156
|
+
sp.append(f.column, encodeFilterValue(f.op, f.value));
|
|
866
1157
|
}
|
|
867
|
-
const
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1158
|
+
const response = await this.pgFetch({
|
|
1159
|
+
method: "DELETE",
|
|
1160
|
+
table: this.table,
|
|
1161
|
+
searchParams: sp,
|
|
1162
|
+
headers: { Prefer: "return=minimal" },
|
|
872
1163
|
databaseInstanceId: this.databaseInstanceId,
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
readOnly: false
|
|
1164
|
+
signal: this._signal,
|
|
1165
|
+
idempotencyKey: generateIdempotencyKey()
|
|
876
1166
|
});
|
|
1167
|
+
await parsePostgRESTResponse(response);
|
|
877
1168
|
return null;
|
|
878
1169
|
});
|
|
879
1170
|
}
|
|
880
1171
|
};
|
|
881
1172
|
var PostgrestDeleteReturningBuilder = class {
|
|
882
|
-
constructor(
|
|
883
|
-
this.
|
|
1173
|
+
constructor(pgFetch, databaseInstanceId, table, filters, returning, _signal) {
|
|
1174
|
+
this.pgFetch = pgFetch;
|
|
884
1175
|
this.databaseInstanceId = databaseInstanceId;
|
|
885
1176
|
this.table = table;
|
|
886
1177
|
this.filters = filters;
|
|
887
1178
|
this.returning = returning;
|
|
1179
|
+
this._signal = _signal;
|
|
888
1180
|
}
|
|
889
1181
|
then(onfulfilled, onrejected) {
|
|
890
1182
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
891
1183
|
}
|
|
892
1184
|
async executeMany() {
|
|
893
1185
|
return asPostgrestResponse(async () => {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
);
|
|
1186
|
+
const sp = new URLSearchParams();
|
|
1187
|
+
for (const f of this.filters) {
|
|
1188
|
+
sp.append(f.column, encodeFilterValue(f.op, f.value));
|
|
1189
|
+
}
|
|
1190
|
+
if (this.returning && this.returning !== "*") {
|
|
1191
|
+
sp.set("select", this.returning);
|
|
900
1192
|
}
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
1193
|
+
const response = await this.pgFetch({
|
|
1194
|
+
method: "DELETE",
|
|
1195
|
+
table: this.table,
|
|
1196
|
+
searchParams: sp,
|
|
1197
|
+
headers: { Prefer: "return=representation" },
|
|
906
1198
|
databaseInstanceId: this.databaseInstanceId,
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
readOnly: false
|
|
1199
|
+
signal: this._signal,
|
|
1200
|
+
idempotencyKey: generateIdempotencyKey()
|
|
910
1201
|
});
|
|
911
|
-
return
|
|
1202
|
+
return parsePostgRESTResponse(response);
|
|
912
1203
|
});
|
|
913
1204
|
}
|
|
914
1205
|
async single() {
|
|
@@ -917,9 +1208,11 @@ var PostgrestDeleteReturningBuilder = class {
|
|
|
917
1208
|
if (error) throw error;
|
|
918
1209
|
const rows = data ?? [];
|
|
919
1210
|
if (rows.length === 0 || rows.length > 1) {
|
|
920
|
-
throw new RagableError(
|
|
921
|
-
|
|
922
|
-
|
|
1211
|
+
throw new RagableError(
|
|
1212
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1213
|
+
406,
|
|
1214
|
+
{ code: "PGRST116" }
|
|
1215
|
+
);
|
|
923
1216
|
}
|
|
924
1217
|
return rows[0];
|
|
925
1218
|
});
|
|
@@ -930,84 +1223,66 @@ var PostgrestDeleteReturningBuilder = class {
|
|
|
930
1223
|
if (error) throw error;
|
|
931
1224
|
const rows = data ?? [];
|
|
932
1225
|
if (rows.length > 1) {
|
|
933
|
-
throw new RagableError(
|
|
934
|
-
|
|
935
|
-
|
|
1226
|
+
throw new RagableError(
|
|
1227
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1228
|
+
406,
|
|
1229
|
+
{ code: "PGRST116" }
|
|
1230
|
+
);
|
|
936
1231
|
}
|
|
937
1232
|
return rows[0] ?? null;
|
|
938
1233
|
});
|
|
939
1234
|
}
|
|
940
1235
|
};
|
|
941
1236
|
var PostgrestUpsertRootBuilder = class {
|
|
942
|
-
constructor(
|
|
943
|
-
this.
|
|
1237
|
+
constructor(pgFetch, databaseInstanceId, table, rows, onConflict, ignoreDuplicates) {
|
|
1238
|
+
this.pgFetch = pgFetch;
|
|
944
1239
|
this.databaseInstanceId = databaseInstanceId;
|
|
945
1240
|
this.table = table;
|
|
946
1241
|
this.rows = rows;
|
|
1242
|
+
this.onConflict = onConflict;
|
|
947
1243
|
this.ignoreDuplicates = ignoreDuplicates;
|
|
948
|
-
__publicField(this, "
|
|
949
|
-
assertIdent(table, "table");
|
|
950
|
-
this.conflictCols = parseConflictColumns(onConflict);
|
|
1244
|
+
__publicField(this, "_signal");
|
|
951
1245
|
}
|
|
952
|
-
/**
|
|
953
|
-
* Return upserted rows (Supabase: `.upsert().select()`).
|
|
954
|
-
* @see https://supabase.com/docs/reference/javascript/upsert
|
|
955
|
-
*/
|
|
956
1246
|
select(columns = "*") {
|
|
957
1247
|
return new PostgrestUpsertReturningBuilder(this, columns);
|
|
958
1248
|
}
|
|
1249
|
+
abortSignal(signal) {
|
|
1250
|
+
this._signal = signal;
|
|
1251
|
+
return this;
|
|
1252
|
+
}
|
|
959
1253
|
then(onfulfilled, onrejected) {
|
|
960
1254
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
961
1255
|
}
|
|
962
1256
|
async executeNoReturn() {
|
|
963
1257
|
return asPostgrestResponse(async () => {
|
|
964
|
-
await this.runUpsert(null);
|
|
1258
|
+
await this.runUpsert("return=minimal", null);
|
|
965
1259
|
return null;
|
|
966
1260
|
});
|
|
967
1261
|
}
|
|
968
|
-
async runUpsert(
|
|
969
|
-
if (this.rows.length === 0)
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
const conflictQuoted = this.conflictCols.map(quoteIdent).join(", ");
|
|
976
|
-
const params = [];
|
|
977
|
-
const valueGroups = [];
|
|
978
|
-
for (const row of this.rows) {
|
|
979
|
-
const placeholders = [];
|
|
980
|
-
for (const k of keys) {
|
|
981
|
-
params.push(row[k]);
|
|
982
|
-
placeholders.push(`$${params.length}`);
|
|
983
|
-
}
|
|
984
|
-
valueGroups.push(`(${placeholders.join(", ")})`);
|
|
985
|
-
}
|
|
986
|
-
const cols = keys.map(quoteIdent).join(", ");
|
|
987
|
-
let sql = `INSERT INTO ${tbl} (${cols}) VALUES ${valueGroups.join(", ")} ON CONFLICT (${conflictQuoted})`;
|
|
988
|
-
if (this.ignoreDuplicates) {
|
|
989
|
-
sql += " DO NOTHING";
|
|
990
|
-
} else {
|
|
991
|
-
const setParts = keys.filter((k) => !this.conflictCols.includes(k)).map((k) => `${quoteIdent(k)} = EXCLUDED.${quoteIdent(k)}`);
|
|
992
|
-
if (setParts.length === 0) {
|
|
993
|
-
sql += " DO NOTHING";
|
|
994
|
-
} else {
|
|
995
|
-
sql += ` DO UPDATE SET ${setParts.join(", ")}`;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
if (returning) {
|
|
999
|
-
sql += ` RETURNING ${returning}`;
|
|
1262
|
+
async runUpsert(prefer, selectCols) {
|
|
1263
|
+
if (this.rows.length === 0) return [];
|
|
1264
|
+
const body = this.rows.length === 1 ? this.rows[0] : this.rows;
|
|
1265
|
+
const sp = new URLSearchParams();
|
|
1266
|
+
sp.set("on_conflict", this.onConflict);
|
|
1267
|
+
if (selectCols && selectCols !== "*") {
|
|
1268
|
+
sp.set("select", selectCols);
|
|
1000
1269
|
}
|
|
1001
|
-
|
|
1270
|
+
const resolution = this.ignoreDuplicates ? "resolution=ignore-duplicates" : "resolution=merge-duplicates";
|
|
1271
|
+
const response = await this.pgFetch({
|
|
1272
|
+
method: "POST",
|
|
1273
|
+
table: this.table,
|
|
1274
|
+
searchParams: sp,
|
|
1275
|
+
body,
|
|
1276
|
+
headers: { Prefer: `${prefer},${resolution}` },
|
|
1002
1277
|
databaseInstanceId: this.databaseInstanceId,
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
readOnly: false
|
|
1278
|
+
signal: this._signal,
|
|
1279
|
+
idempotencyKey: generateIdempotencyKey()
|
|
1006
1280
|
});
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1281
|
+
if (prefer.includes("return=minimal")) {
|
|
1282
|
+
await parsePostgRESTResponse(response);
|
|
1283
|
+
return [];
|
|
1284
|
+
}
|
|
1285
|
+
return parsePostgRESTResponse(response);
|
|
1011
1286
|
}
|
|
1012
1287
|
};
|
|
1013
1288
|
var PostgrestUpsertReturningBuilder = class {
|
|
@@ -1020,8 +1295,7 @@ var PostgrestUpsertReturningBuilder = class {
|
|
|
1020
1295
|
}
|
|
1021
1296
|
async executeMany() {
|
|
1022
1297
|
return asPostgrestResponse(async () => {
|
|
1023
|
-
|
|
1024
|
-
return res.rows;
|
|
1298
|
+
return this.root.runUpsert("return=representation", this.returning);
|
|
1025
1299
|
});
|
|
1026
1300
|
}
|
|
1027
1301
|
async single() {
|
|
@@ -1030,9 +1304,11 @@ var PostgrestUpsertReturningBuilder = class {
|
|
|
1030
1304
|
if (error) throw error;
|
|
1031
1305
|
const rows = data ?? [];
|
|
1032
1306
|
if (rows.length === 0 || rows.length > 1) {
|
|
1033
|
-
throw new RagableError(
|
|
1034
|
-
|
|
1035
|
-
|
|
1307
|
+
throw new RagableError(
|
|
1308
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1309
|
+
406,
|
|
1310
|
+
{ code: "PGRST116" }
|
|
1311
|
+
);
|
|
1036
1312
|
}
|
|
1037
1313
|
return rows[0];
|
|
1038
1314
|
});
|
|
@@ -1043,23 +1319,25 @@ var PostgrestUpsertReturningBuilder = class {
|
|
|
1043
1319
|
if (error) throw error;
|
|
1044
1320
|
const rows = data ?? [];
|
|
1045
1321
|
if (rows.length > 1) {
|
|
1046
|
-
throw new RagableError(
|
|
1047
|
-
|
|
1048
|
-
|
|
1322
|
+
throw new RagableError(
|
|
1323
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1324
|
+
406,
|
|
1325
|
+
{ code: "PGRST116" }
|
|
1326
|
+
);
|
|
1049
1327
|
}
|
|
1050
1328
|
return rows[0] ?? null;
|
|
1051
1329
|
});
|
|
1052
1330
|
}
|
|
1053
1331
|
};
|
|
1054
1332
|
var PostgrestTableApi = class {
|
|
1055
|
-
constructor(
|
|
1056
|
-
this.
|
|
1333
|
+
constructor(pgFetch, databaseInstanceId, table) {
|
|
1334
|
+
this.pgFetch = pgFetch;
|
|
1057
1335
|
this.databaseInstanceId = databaseInstanceId;
|
|
1058
1336
|
this.table = table;
|
|
1059
1337
|
}
|
|
1060
1338
|
select(columns = "*") {
|
|
1061
1339
|
return new PostgrestSelectBuilder(
|
|
1062
|
-
this.
|
|
1340
|
+
this.pgFetch,
|
|
1063
1341
|
this.databaseInstanceId,
|
|
1064
1342
|
this.table,
|
|
1065
1343
|
columns
|
|
@@ -1068,7 +1346,7 @@ var PostgrestTableApi = class {
|
|
|
1068
1346
|
insert(values) {
|
|
1069
1347
|
const rows = Array.isArray(values) ? values : [values];
|
|
1070
1348
|
return new PostgrestInsertRootBuilder(
|
|
1071
|
-
this.
|
|
1349
|
+
this.pgFetch,
|
|
1072
1350
|
this.databaseInstanceId,
|
|
1073
1351
|
this.table,
|
|
1074
1352
|
rows
|
|
@@ -1076,7 +1354,7 @@ var PostgrestTableApi = class {
|
|
|
1076
1354
|
}
|
|
1077
1355
|
update(patch) {
|
|
1078
1356
|
return new PostgrestUpdateRootBuilder(
|
|
1079
|
-
this.
|
|
1357
|
+
this.pgFetch,
|
|
1080
1358
|
this.databaseInstanceId,
|
|
1081
1359
|
this.table,
|
|
1082
1360
|
patch
|
|
@@ -1084,19 +1362,15 @@ var PostgrestTableApi = class {
|
|
|
1084
1362
|
}
|
|
1085
1363
|
delete() {
|
|
1086
1364
|
return new PostgrestDeleteRootBuilder(
|
|
1087
|
-
this.
|
|
1365
|
+
this.pgFetch,
|
|
1088
1366
|
this.databaseInstanceId,
|
|
1089
1367
|
this.table
|
|
1090
1368
|
);
|
|
1091
1369
|
}
|
|
1092
|
-
/**
|
|
1093
|
-
* `INSERT ... ON CONFLICT ... DO UPDATE` (or `DO NOTHING` with `ignoreDuplicates`).
|
|
1094
|
-
* @see https://supabase.com/docs/reference/javascript/upsert
|
|
1095
|
-
*/
|
|
1096
1370
|
upsert(values, options) {
|
|
1097
1371
|
const rows = Array.isArray(values) ? values : [values];
|
|
1098
1372
|
return new PostgrestUpsertRootBuilder(
|
|
1099
|
-
this.
|
|
1373
|
+
this.pgFetch,
|
|
1100
1374
|
this.databaseInstanceId,
|
|
1101
1375
|
this.table,
|
|
1102
1376
|
rows,
|
|
@@ -1106,57 +1380,139 @@ var PostgrestTableApi = class {
|
|
|
1106
1380
|
}
|
|
1107
1381
|
};
|
|
1108
1382
|
|
|
1109
|
-
// src/
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
throw new Error(
|
|
1118
|
-
"authGroupId is required for auth and database methods on the browser client"
|
|
1119
|
-
);
|
|
1383
|
+
// src/auth-storage.ts
|
|
1384
|
+
var LocalStorageAdapter = class {
|
|
1385
|
+
getItem(key) {
|
|
1386
|
+
try {
|
|
1387
|
+
return globalThis.localStorage.getItem(key);
|
|
1388
|
+
} catch {
|
|
1389
|
+
return null;
|
|
1390
|
+
}
|
|
1120
1391
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
throw new Error(
|
|
1127
|
-
"getAccessToken is required for this call (return the end-user access JWT)"
|
|
1128
|
-
);
|
|
1392
|
+
setItem(key, value) {
|
|
1393
|
+
try {
|
|
1394
|
+
globalThis.localStorage.setItem(key, value);
|
|
1395
|
+
} catch {
|
|
1396
|
+
}
|
|
1129
1397
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1398
|
+
removeItem(key) {
|
|
1399
|
+
try {
|
|
1400
|
+
globalThis.localStorage.removeItem(key);
|
|
1401
|
+
} catch {
|
|
1402
|
+
}
|
|
1133
1403
|
}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1404
|
+
};
|
|
1405
|
+
var SessionStorageAdapter = class {
|
|
1406
|
+
getItem(key) {
|
|
1407
|
+
try {
|
|
1408
|
+
return globalThis.sessionStorage.getItem(key);
|
|
1409
|
+
} catch {
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1140
1412
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
);
|
|
1413
|
+
setItem(key, value) {
|
|
1414
|
+
try {
|
|
1415
|
+
globalThis.sessionStorage.setItem(key, value);
|
|
1416
|
+
} catch {
|
|
1417
|
+
}
|
|
1147
1418
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
const message = extractErrorMessage(payload, response.statusText);
|
|
1154
|
-
throw new RagableError(message, response.status, payload);
|
|
1419
|
+
removeItem(key) {
|
|
1420
|
+
try {
|
|
1421
|
+
globalThis.sessionStorage.removeItem(key);
|
|
1422
|
+
} catch {
|
|
1423
|
+
}
|
|
1155
1424
|
}
|
|
1156
|
-
|
|
1425
|
+
};
|
|
1426
|
+
var MemoryStorageAdapter = class {
|
|
1427
|
+
constructor() {
|
|
1428
|
+
__publicField(this, "store", /* @__PURE__ */ new Map());
|
|
1429
|
+
}
|
|
1430
|
+
getItem(key) {
|
|
1431
|
+
return this.store.get(key) ?? null;
|
|
1432
|
+
}
|
|
1433
|
+
setItem(key, value) {
|
|
1434
|
+
this.store.set(key, value);
|
|
1435
|
+
}
|
|
1436
|
+
removeItem(key) {
|
|
1437
|
+
this.store.delete(key);
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
var CookieStorageAdapter = class {
|
|
1441
|
+
constructor(maxAge = 30 * 24 * 60 * 60, path = "/", sameSite = "Lax") {
|
|
1442
|
+
this.maxAge = maxAge;
|
|
1443
|
+
this.path = path;
|
|
1444
|
+
this.sameSite = sameSite;
|
|
1445
|
+
}
|
|
1446
|
+
getItem(key) {
|
|
1447
|
+
if (typeof document === "undefined") return null;
|
|
1448
|
+
const match = document.cookie.split("; ").find((c) => c.startsWith(`${encodeURIComponent(key)}=`));
|
|
1449
|
+
if (!match) return null;
|
|
1450
|
+
return decodeURIComponent(match.split("=").slice(1).join("="));
|
|
1451
|
+
}
|
|
1452
|
+
setItem(key, value) {
|
|
1453
|
+
if (typeof document === "undefined") return;
|
|
1454
|
+
const parts = [
|
|
1455
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`,
|
|
1456
|
+
`path=${this.path}`,
|
|
1457
|
+
`max-age=${this.maxAge}`,
|
|
1458
|
+
`SameSite=${this.sameSite}`
|
|
1459
|
+
];
|
|
1460
|
+
if (this.sameSite === "None") parts.push("Secure");
|
|
1461
|
+
document.cookie = parts.join("; ");
|
|
1462
|
+
}
|
|
1463
|
+
removeItem(key) {
|
|
1464
|
+
if (typeof document === "undefined") return;
|
|
1465
|
+
document.cookie = `${encodeURIComponent(key)}=; path=${this.path}; max-age=0`;
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
function detectStorage() {
|
|
1469
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.localStorage !== "undefined") {
|
|
1470
|
+
try {
|
|
1471
|
+
const testKey = "__ragable_test__";
|
|
1472
|
+
globalThis.localStorage.setItem(testKey, "1");
|
|
1473
|
+
globalThis.localStorage.removeItem(testKey);
|
|
1474
|
+
return new LocalStorageAdapter();
|
|
1475
|
+
} catch {
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
return new MemoryStorageAdapter();
|
|
1157
1479
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1480
|
+
var AuthBroadcastChannel = class {
|
|
1481
|
+
constructor(channelName) {
|
|
1482
|
+
__publicField(this, "channel", null);
|
|
1483
|
+
if (typeof BroadcastChannel !== "undefined") {
|
|
1484
|
+
try {
|
|
1485
|
+
this.channel = new BroadcastChannel(channelName);
|
|
1486
|
+
} catch {
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
onMessage(cb) {
|
|
1491
|
+
if (this.channel) {
|
|
1492
|
+
this.channel.onmessage = (e) => {
|
|
1493
|
+
const data = e.data;
|
|
1494
|
+
if (data && typeof data.type === "string") {
|
|
1495
|
+
cb(data);
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
postSessionUpdated(serialized) {
|
|
1501
|
+
this.channel?.postMessage({ type: "SESSION_UPDATED", payload: serialized });
|
|
1502
|
+
}
|
|
1503
|
+
postSessionRemoved() {
|
|
1504
|
+
this.channel?.postMessage({ type: "SESSION_REMOVED" });
|
|
1505
|
+
}
|
|
1506
|
+
close() {
|
|
1507
|
+
this.channel?.close();
|
|
1508
|
+
this.channel = null;
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
|
|
1512
|
+
// src/auth.ts
|
|
1513
|
+
function parseExpiresInSeconds(raw) {
|
|
1514
|
+
if (typeof raw === "number") return raw;
|
|
1515
|
+
const s = raw.trim().toLowerCase();
|
|
1160
1516
|
const m = /^(\d+)([smhd])?$/.exec(s);
|
|
1161
1517
|
if (m) {
|
|
1162
1518
|
const n = Number(m[1]);
|
|
@@ -1167,147 +1523,541 @@ function parseExpiresInSeconds(expiresIn) {
|
|
|
1167
1523
|
const asNum = Number(s);
|
|
1168
1524
|
return Number.isFinite(asNum) ? asNum : 0;
|
|
1169
1525
|
}
|
|
1170
|
-
function
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
var RagableBrowserAuthClient = class {
|
|
1180
|
-
constructor(options) {
|
|
1181
|
-
this.options = options;
|
|
1182
|
-
__publicField(this, "fetchImpl");
|
|
1183
|
-
this.fetchImpl = bindFetch(options.fetch);
|
|
1526
|
+
async function parseJsonOrThrow(response) {
|
|
1527
|
+
const text = await response.text();
|
|
1528
|
+
let payload;
|
|
1529
|
+
try {
|
|
1530
|
+
payload = text ? JSON.parse(text) : null;
|
|
1531
|
+
} catch {
|
|
1532
|
+
throw new RagableError(`Response parse error: ${text.slice(0, 200)}`, response.status, null);
|
|
1184
1533
|
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1534
|
+
if (!response.ok) {
|
|
1535
|
+
const message = extractErrorMessage(payload, response.statusText);
|
|
1536
|
+
throw new RagableError(message, response.status, payload);
|
|
1187
1537
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1538
|
+
return payload;
|
|
1539
|
+
}
|
|
1540
|
+
var _subCounter = 0;
|
|
1541
|
+
var RagableAuth = class {
|
|
1542
|
+
constructor(config) {
|
|
1543
|
+
__publicField(this, "fetchImpl");
|
|
1544
|
+
__publicField(this, "baseUrl");
|
|
1545
|
+
__publicField(this, "authGroupId");
|
|
1546
|
+
__publicField(this, "defaultHeaders");
|
|
1547
|
+
__publicField(this, "persistSession");
|
|
1548
|
+
__publicField(this, "autoRefreshToken");
|
|
1549
|
+
__publicField(this, "storage");
|
|
1550
|
+
__publicField(this, "storageKey");
|
|
1551
|
+
__publicField(this, "refreshSkewSeconds");
|
|
1552
|
+
__publicField(this, "debug");
|
|
1553
|
+
__publicField(this, "currentSession", null);
|
|
1554
|
+
__publicField(this, "refreshTimer", null);
|
|
1555
|
+
__publicField(this, "refreshPromise", null);
|
|
1556
|
+
__publicField(this, "listeners", /* @__PURE__ */ new Map());
|
|
1557
|
+
__publicField(this, "broadcast", null);
|
|
1558
|
+
__publicField(this, "visibilityHandler", null);
|
|
1559
|
+
__publicField(this, "initialized", false);
|
|
1560
|
+
this.baseUrl = config.baseUrl;
|
|
1561
|
+
this.authGroupId = config.authGroupId;
|
|
1562
|
+
this.fetchImpl = bindFetch(config.fetch);
|
|
1563
|
+
this.defaultHeaders = config.headers;
|
|
1564
|
+
const auth = config.auth ?? {};
|
|
1565
|
+
this.persistSession = auth.persistSession !== false;
|
|
1566
|
+
this.autoRefreshToken = auth.autoRefreshToken !== false;
|
|
1567
|
+
this.storage = auth.storage ?? detectStorage();
|
|
1568
|
+
this.storageKey = auth.storageKey ?? `ragable.session.${this.authGroupId}`;
|
|
1569
|
+
this.refreshSkewSeconds = auth.refreshSkewSeconds ?? 60;
|
|
1570
|
+
this.debug = auth.debug ?? false;
|
|
1571
|
+
this.broadcast = new AuthBroadcastChannel(`ragable-auth-${this.authGroupId}`);
|
|
1572
|
+
this.broadcast.onMessage((msg) => {
|
|
1573
|
+
if (msg.type === "SESSION_UPDATED") {
|
|
1574
|
+
try {
|
|
1575
|
+
const session = JSON.parse(msg.payload);
|
|
1576
|
+
this.currentSession = session;
|
|
1577
|
+
this.scheduleRefresh(session);
|
|
1578
|
+
this.emit("TOKEN_REFRESHED", session);
|
|
1579
|
+
} catch {
|
|
1580
|
+
}
|
|
1581
|
+
} else if (msg.type === "SESSION_REMOVED") {
|
|
1582
|
+
this.currentSession = null;
|
|
1583
|
+
this.clearRefreshTimer();
|
|
1584
|
+
this.emit("SIGNED_OUT", null);
|
|
1585
|
+
}
|
|
1586
|
+
});
|
|
1587
|
+
this.setupVisibilityListener();
|
|
1588
|
+
}
|
|
1589
|
+
log(...args) {
|
|
1590
|
+
if (this.debug) console.debug("[RagableAuth]", ...args);
|
|
1591
|
+
}
|
|
1592
|
+
// ── Lifecycle ──────────────────────────────────────────────────────────────
|
|
1593
|
+
async initialize() {
|
|
1594
|
+
if (this.initialized) return this.currentSession;
|
|
1595
|
+
this.initialized = true;
|
|
1596
|
+
if (this.persistSession) {
|
|
1597
|
+
try {
|
|
1598
|
+
const raw = await this.storage.getItem(this.storageKey);
|
|
1599
|
+
if (raw) {
|
|
1600
|
+
const session = JSON.parse(raw);
|
|
1601
|
+
if (session.expires_at && session.expires_at > nowSeconds()) {
|
|
1602
|
+
this.currentSession = session;
|
|
1603
|
+
this.scheduleRefresh(session);
|
|
1604
|
+
this.log("Restored session from storage");
|
|
1605
|
+
} else if (session.refresh_token) {
|
|
1606
|
+
this.log("Stored session expired, attempting refresh");
|
|
1607
|
+
const refreshed = await this._doRefresh(session.refresh_token);
|
|
1608
|
+
if (refreshed) {
|
|
1609
|
+
this.currentSession = refreshed;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
} catch (e) {
|
|
1614
|
+
this.log("Failed to restore session", e);
|
|
1615
|
+
}
|
|
1192
1616
|
}
|
|
1193
|
-
|
|
1617
|
+
this.emit("INITIAL_SESSION", this.currentSession);
|
|
1618
|
+
return this.currentSession;
|
|
1194
1619
|
}
|
|
1195
|
-
|
|
1196
|
-
const gid = requireAuthGroupId(this.options);
|
|
1197
|
-
return `/auth-groups/${gid}/auth`;
|
|
1198
|
-
}
|
|
1199
|
-
/** Supabase: `signUp` → `{ data: { user, session }, error }` */
|
|
1620
|
+
// ── Auth methods ───────────────────────────────────────────────────────────
|
|
1200
1621
|
async signUp(credentials) {
|
|
1201
1622
|
return asPostgrestResponse(async () => {
|
|
1202
1623
|
const name = typeof credentials.options?.data?.name === "string" ? credentials.options.data.name : void 0;
|
|
1203
|
-
const
|
|
1624
|
+
const raw = await this.fetchAuth("/register", "POST", {
|
|
1204
1625
|
email: credentials.email,
|
|
1205
1626
|
password: credentials.password,
|
|
1206
|
-
name
|
|
1627
|
+
...name !== void 0 ? { name } : {}
|
|
1207
1628
|
});
|
|
1208
|
-
|
|
1629
|
+
const session = this.rawToSession(raw);
|
|
1630
|
+
await this.setSessionInternal(session, "SIGNED_IN");
|
|
1631
|
+
return { user: session.user, session };
|
|
1209
1632
|
});
|
|
1210
1633
|
}
|
|
1211
|
-
/** Supabase: `signInWithPassword` */
|
|
1212
1634
|
async signInWithPassword(credentials) {
|
|
1213
1635
|
return asPostgrestResponse(async () => {
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1636
|
+
const raw = await this.fetchAuth("/login", "POST", {
|
|
1637
|
+
email: credentials.email,
|
|
1638
|
+
password: credentials.password
|
|
1639
|
+
});
|
|
1640
|
+
const session = this.rawToSession(raw);
|
|
1641
|
+
await this.setSessionInternal(session, "SIGNED_IN");
|
|
1642
|
+
return { user: session.user, session };
|
|
1216
1643
|
});
|
|
1217
1644
|
}
|
|
1218
|
-
|
|
1645
|
+
async signOut(_options) {
|
|
1646
|
+
this.currentSession = null;
|
|
1647
|
+
this.clearRefreshTimer();
|
|
1648
|
+
if (this.persistSession) {
|
|
1649
|
+
await this.storage.removeItem(this.storageKey);
|
|
1650
|
+
}
|
|
1651
|
+
this.broadcast?.postSessionRemoved();
|
|
1652
|
+
this.emit("SIGNED_OUT", null);
|
|
1653
|
+
return { error: null };
|
|
1654
|
+
}
|
|
1219
1655
|
async refreshSession(refreshToken) {
|
|
1220
1656
|
return asPostgrestResponse(async () => {
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1657
|
+
const token = refreshToken ?? this.currentSession?.refresh_token;
|
|
1658
|
+
if (!token) throw new RagableError("No refresh token available", 401, null);
|
|
1659
|
+
const session = await this.singleFlightRefresh(token);
|
|
1660
|
+
if (!session) throw new RagableError("Refresh failed", 401, null);
|
|
1661
|
+
return { session, user: session.user };
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
async getSession() {
|
|
1665
|
+
if (!this.initialized) await this.initialize();
|
|
1666
|
+
return { data: { session: this.currentSession }, error: null };
|
|
1667
|
+
}
|
|
1668
|
+
async getUser() {
|
|
1669
|
+
return asPostgrestResponse(async () => {
|
|
1670
|
+
const token = this.currentSession?.access_token;
|
|
1671
|
+
if (!token) throw new RagableError("Not authenticated", 401, null);
|
|
1672
|
+
return this.fetchAuthWithBearer("/me", "GET", token);
|
|
1673
|
+
});
|
|
1674
|
+
}
|
|
1675
|
+
async setSession(tokens) {
|
|
1676
|
+
return asPostgrestResponse(async () => {
|
|
1677
|
+
const me = await this.fetchAuthWithBearer("/me", "GET", tokens.access_token);
|
|
1678
|
+
const decoded = decodeJwtExpiry(tokens.access_token);
|
|
1679
|
+
const expiresIn = decoded ? decoded - nowSeconds() : 3600;
|
|
1223
1680
|
const session = {
|
|
1224
|
-
access_token: tokens.
|
|
1225
|
-
refresh_token: tokens.
|
|
1226
|
-
expires_in:
|
|
1681
|
+
access_token: tokens.access_token,
|
|
1682
|
+
refresh_token: tokens.refresh_token,
|
|
1683
|
+
expires_in: expiresIn,
|
|
1684
|
+
expires_at: nowSeconds() + expiresIn,
|
|
1227
1685
|
token_type: "bearer",
|
|
1228
1686
|
user: me.user
|
|
1229
1687
|
};
|
|
1688
|
+
await this.setSessionInternal(session, "SIGNED_IN");
|
|
1230
1689
|
return { session, user: me.user };
|
|
1231
1690
|
});
|
|
1232
1691
|
}
|
|
1233
|
-
/** Supabase: `getUser()` — needs `getAccessToken` on the client */
|
|
1234
|
-
async getUser() {
|
|
1235
|
-
return asPostgrestResponse(() => this.getMe());
|
|
1236
|
-
}
|
|
1237
|
-
/** Supabase: `updateUser` */
|
|
1238
1692
|
async updateUser(attributes) {
|
|
1239
|
-
return asPostgrestResponse(
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1693
|
+
return asPostgrestResponse(async () => {
|
|
1694
|
+
const token = this.currentSession?.access_token;
|
|
1695
|
+
if (!token) throw new RagableError("Not authenticated", 401, null);
|
|
1696
|
+
const result = await this.fetchAuthWithBearer("/me", "PATCH", token, {
|
|
1697
|
+
...attributes.password !== void 0 ? { password: attributes.password } : {},
|
|
1698
|
+
...attributes.data?.name !== void 0 ? { name: attributes.data.name } : {}
|
|
1699
|
+
});
|
|
1700
|
+
if (this.currentSession) {
|
|
1701
|
+
this.currentSession = { ...this.currentSession, user: result.user };
|
|
1702
|
+
await this.persistCurrentSession();
|
|
1703
|
+
}
|
|
1704
|
+
this.emit("USER_UPDATED", this.currentSession);
|
|
1705
|
+
return result;
|
|
1706
|
+
});
|
|
1245
1707
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1708
|
+
// ── Event subscription ─────────────────────────────────────────────────────
|
|
1709
|
+
onAuthStateChange(callback) {
|
|
1710
|
+
_subCounter++;
|
|
1711
|
+
const id = `sub-${_subCounter}`;
|
|
1712
|
+
this.listeners.set(id, callback);
|
|
1713
|
+
const unsubscribe = () => {
|
|
1714
|
+
this.listeners.delete(id);
|
|
1715
|
+
};
|
|
1716
|
+
return { data: { subscription: { id, unsubscribe } } };
|
|
1252
1717
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
const response = await this.fetchImpl(`${this.toUrl(this.authPrefix())}/me`, {
|
|
1257
|
-
method: "GET",
|
|
1258
|
-
headers
|
|
1259
|
-
});
|
|
1260
|
-
return parseJsonOrThrow(response);
|
|
1718
|
+
// ── Accessors ──────────────────────────────────────────────────────────────
|
|
1719
|
+
getAccessToken() {
|
|
1720
|
+
return this.currentSession?.access_token ?? null;
|
|
1261
1721
|
}
|
|
1722
|
+
getCurrentSession() {
|
|
1723
|
+
return this.currentSession;
|
|
1724
|
+
}
|
|
1725
|
+
// ── Back-compat: raw Ragable auth methods ──────────────────────────────────
|
|
1262
1726
|
async register(body) {
|
|
1263
|
-
const
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
});
|
|
1268
|
-
return parseJsonOrThrow(response);
|
|
1727
|
+
const raw = await this.fetchAuth("/register", "POST", body);
|
|
1728
|
+
const session = this.rawToSession(raw);
|
|
1729
|
+
await this.setSessionInternal(session, "SIGNED_IN");
|
|
1730
|
+
return raw;
|
|
1269
1731
|
}
|
|
1270
1732
|
async login(body) {
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
});
|
|
1276
|
-
return parseJsonOrThrow(response);
|
|
1733
|
+
const raw = await this.fetchAuth("/login", "POST", body);
|
|
1734
|
+
const session = this.rawToSession(raw);
|
|
1735
|
+
await this.setSessionInternal(session, "SIGNED_IN");
|
|
1736
|
+
return raw;
|
|
1277
1737
|
}
|
|
1278
1738
|
async refresh(body) {
|
|
1279
|
-
|
|
1280
|
-
method: "POST",
|
|
1281
|
-
headers: this.baseHeaders(true),
|
|
1282
|
-
body: JSON.stringify(body)
|
|
1283
|
-
});
|
|
1284
|
-
return parseJsonOrThrow(response);
|
|
1739
|
+
return this.fetchAuth("/refresh", "POST", body);
|
|
1285
1740
|
}
|
|
1286
1741
|
async getMe() {
|
|
1287
|
-
const token =
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1742
|
+
const token = this.currentSession?.access_token;
|
|
1743
|
+
if (!token) throw new RagableError("Not authenticated", 401, null);
|
|
1744
|
+
return this.fetchAuthWithBearer("/me", "GET", token);
|
|
1745
|
+
}
|
|
1746
|
+
async updateMe(body) {
|
|
1747
|
+
const token = this.currentSession?.access_token;
|
|
1748
|
+
if (!token) throw new RagableError("Not authenticated", 401, null);
|
|
1749
|
+
const result = await this.fetchAuthWithBearer("/me", "PATCH", token, body);
|
|
1750
|
+
if (this.currentSession) {
|
|
1751
|
+
this.currentSession = { ...this.currentSession, user: result.user };
|
|
1752
|
+
await this.persistCurrentSession();
|
|
1753
|
+
}
|
|
1754
|
+
this.emit("USER_UPDATED", this.currentSession);
|
|
1755
|
+
return result;
|
|
1756
|
+
}
|
|
1757
|
+
// ── Cleanup ────────────────────────────────────────────────────────────────
|
|
1758
|
+
destroy() {
|
|
1759
|
+
this.clearRefreshTimer();
|
|
1760
|
+
this.broadcast?.close();
|
|
1761
|
+
this.listeners.clear();
|
|
1762
|
+
if (this.visibilityHandler && typeof document !== "undefined") {
|
|
1763
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
// ─── Internal ──────────────────────────────────────────────────────────────
|
|
1767
|
+
authPrefix() {
|
|
1768
|
+
return `/auth-groups/${this.authGroupId}/auth`;
|
|
1769
|
+
}
|
|
1770
|
+
toUrl(path) {
|
|
1771
|
+
return `${this.baseUrl}${this.authPrefix()}${path}`;
|
|
1772
|
+
}
|
|
1773
|
+
baseHeaders(json) {
|
|
1774
|
+
const h = new Headers(this.defaultHeaders);
|
|
1775
|
+
if (json) h.set("Content-Type", "application/json");
|
|
1776
|
+
return h;
|
|
1777
|
+
}
|
|
1778
|
+
async fetchAuth(path, method, body) {
|
|
1779
|
+
const headers = this.baseHeaders(body !== void 0);
|
|
1780
|
+
const response = await this.fetchImpl(this.toUrl(path), {
|
|
1781
|
+
method,
|
|
1782
|
+
headers,
|
|
1783
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
1293
1784
|
});
|
|
1294
1785
|
return parseJsonOrThrow(response);
|
|
1295
1786
|
}
|
|
1296
|
-
async
|
|
1297
|
-
const
|
|
1298
|
-
const headers = this.baseHeaders(true);
|
|
1787
|
+
async fetchAuthWithBearer(path, method, token, body) {
|
|
1788
|
+
const headers = this.baseHeaders(body !== void 0);
|
|
1299
1789
|
headers.set("Authorization", `Bearer ${token}`);
|
|
1300
|
-
const response = await this.fetchImpl(
|
|
1301
|
-
method
|
|
1790
|
+
const response = await this.fetchImpl(this.toUrl(path), {
|
|
1791
|
+
method,
|
|
1302
1792
|
headers,
|
|
1303
|
-
body: JSON.stringify(body)
|
|
1793
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
1304
1794
|
});
|
|
1305
1795
|
return parseJsonOrThrow(response);
|
|
1306
1796
|
}
|
|
1797
|
+
rawToSession(raw) {
|
|
1798
|
+
const expiresIn = parseExpiresInSeconds(raw.expiresIn);
|
|
1799
|
+
return {
|
|
1800
|
+
access_token: raw.accessToken,
|
|
1801
|
+
refresh_token: raw.refreshToken,
|
|
1802
|
+
expires_in: expiresIn,
|
|
1803
|
+
expires_at: nowSeconds() + expiresIn,
|
|
1804
|
+
token_type: "bearer",
|
|
1805
|
+
user: raw.user
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
async setSessionInternal(session, event) {
|
|
1809
|
+
this.currentSession = session;
|
|
1810
|
+
await this.persistCurrentSession();
|
|
1811
|
+
this.broadcast?.postSessionUpdated(JSON.stringify(session));
|
|
1812
|
+
this.scheduleRefresh(session);
|
|
1813
|
+
this.emit(event, session);
|
|
1814
|
+
}
|
|
1815
|
+
async persistCurrentSession() {
|
|
1816
|
+
if (!this.persistSession || !this.currentSession) return;
|
|
1817
|
+
try {
|
|
1818
|
+
await this.storage.setItem(this.storageKey, JSON.stringify(this.currentSession));
|
|
1819
|
+
} catch (e) {
|
|
1820
|
+
this.log("Failed to persist session", e);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
emit(event, session) {
|
|
1824
|
+
this.log(event, session?.user);
|
|
1825
|
+
for (const cb of this.listeners.values()) {
|
|
1826
|
+
try {
|
|
1827
|
+
cb(event, session);
|
|
1828
|
+
} catch (e) {
|
|
1829
|
+
this.log("Listener threw", e);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
// ─── Refresh scheduling ────────────────────────────────────────────────────
|
|
1834
|
+
scheduleRefresh(session) {
|
|
1835
|
+
this.clearRefreshTimer();
|
|
1836
|
+
if (!this.autoRefreshToken) return;
|
|
1837
|
+
const secondsUntilExpiry = session.expires_at - nowSeconds();
|
|
1838
|
+
const refreshIn = Math.max(0, secondsUntilExpiry - this.refreshSkewSeconds);
|
|
1839
|
+
this.log(`Scheduling refresh in ${refreshIn}s`);
|
|
1840
|
+
this.refreshTimer = setTimeout(() => {
|
|
1841
|
+
this.singleFlightRefresh(session.refresh_token).catch((e) => {
|
|
1842
|
+
this.log("Scheduled refresh failed", e);
|
|
1843
|
+
});
|
|
1844
|
+
}, refreshIn * 1e3);
|
|
1845
|
+
}
|
|
1846
|
+
clearRefreshTimer() {
|
|
1847
|
+
if (this.refreshTimer !== null) {
|
|
1848
|
+
clearTimeout(this.refreshTimer);
|
|
1849
|
+
this.refreshTimer = null;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
async singleFlightRefresh(refreshToken) {
|
|
1853
|
+
if (this.refreshPromise) return this.refreshPromise;
|
|
1854
|
+
this.refreshPromise = this._doRefresh(refreshToken).finally(() => {
|
|
1855
|
+
this.refreshPromise = null;
|
|
1856
|
+
});
|
|
1857
|
+
return this.refreshPromise;
|
|
1858
|
+
}
|
|
1859
|
+
async _doRefresh(refreshToken) {
|
|
1860
|
+
try {
|
|
1861
|
+
const raw = await this.fetchAuth("/refresh", "POST", { refreshToken });
|
|
1862
|
+
const me = await this.fetchAuthWithBearer("/me", "GET", raw.accessToken);
|
|
1863
|
+
const expiresIn = parseExpiresInSeconds(raw.expiresIn);
|
|
1864
|
+
const session = {
|
|
1865
|
+
access_token: raw.accessToken,
|
|
1866
|
+
refresh_token: raw.refreshToken,
|
|
1867
|
+
expires_in: expiresIn,
|
|
1868
|
+
expires_at: nowSeconds() + expiresIn,
|
|
1869
|
+
token_type: "bearer",
|
|
1870
|
+
user: me.user
|
|
1871
|
+
};
|
|
1872
|
+
await this.setSessionInternal(session, "TOKEN_REFRESHED");
|
|
1873
|
+
return session;
|
|
1874
|
+
} catch (e) {
|
|
1875
|
+
this.log("Refresh failed", e);
|
|
1876
|
+
return null;
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
// ─── Visibility listener ───────────────────────────────────────────────────
|
|
1880
|
+
setupVisibilityListener() {
|
|
1881
|
+
if (typeof document === "undefined") return;
|
|
1882
|
+
this.visibilityHandler = () => {
|
|
1883
|
+
if (document.visibilityState === "visible" && this.currentSession) {
|
|
1884
|
+
const secondsUntilExpiry = this.currentSession.expires_at - nowSeconds();
|
|
1885
|
+
if (secondsUntilExpiry <= this.refreshSkewSeconds) {
|
|
1886
|
+
this.singleFlightRefresh(this.currentSession.refresh_token).catch(() => {
|
|
1887
|
+
});
|
|
1888
|
+
} else {
|
|
1889
|
+
this.scheduleRefresh(this.currentSession);
|
|
1890
|
+
}
|
|
1891
|
+
} else {
|
|
1892
|
+
this.clearRefreshTimer();
|
|
1893
|
+
}
|
|
1894
|
+
};
|
|
1895
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
1896
|
+
}
|
|
1897
|
+
};
|
|
1898
|
+
function nowSeconds() {
|
|
1899
|
+
return Math.floor(Date.now() / 1e3);
|
|
1900
|
+
}
|
|
1901
|
+
function decodeJwtExpiry(jwt) {
|
|
1902
|
+
try {
|
|
1903
|
+
const parts = jwt.split(".");
|
|
1904
|
+
if (parts.length !== 3) return null;
|
|
1905
|
+
const payload = JSON.parse(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")));
|
|
1906
|
+
return typeof payload.exp === "number" ? payload.exp : null;
|
|
1907
|
+
} catch {
|
|
1908
|
+
return null;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
// src/browser.ts
|
|
1913
|
+
function normalizeBrowserApiBase(baseUrl) {
|
|
1914
|
+
const trimmed = (baseUrl ?? DEFAULT_RAGABLE_API_BASE).trim().replace(/\/+$/, "");
|
|
1915
|
+
return trimmed.endsWith("/api") ? trimmed : `${trimmed}/api`;
|
|
1916
|
+
}
|
|
1917
|
+
function requireAuthGroupId(options) {
|
|
1918
|
+
const id = options.authGroupId?.trim();
|
|
1919
|
+
if (!id) {
|
|
1920
|
+
throw new Error(
|
|
1921
|
+
"authGroupId is required for auth and database methods on the browser client"
|
|
1922
|
+
);
|
|
1923
|
+
}
|
|
1924
|
+
return id;
|
|
1925
|
+
}
|
|
1926
|
+
async function requireAccessToken(options, ragableAuth) {
|
|
1927
|
+
if (ragableAuth) {
|
|
1928
|
+
const token = ragableAuth.getAccessToken();
|
|
1929
|
+
if (token) return token;
|
|
1930
|
+
}
|
|
1931
|
+
const getter = options.getAccessToken;
|
|
1932
|
+
if (getter) {
|
|
1933
|
+
const token = await getter();
|
|
1934
|
+
if (token?.trim()) return token.trim();
|
|
1935
|
+
}
|
|
1936
|
+
throw new Error(
|
|
1937
|
+
"No access token available (sign in or provide getAccessToken)"
|
|
1938
|
+
);
|
|
1939
|
+
}
|
|
1940
|
+
async function resolveDatabaseAuthBearer(options, ragableAuth) {
|
|
1941
|
+
const mode = options.dataAuth ?? "user";
|
|
1942
|
+
if (mode === "user") {
|
|
1943
|
+
return requireAccessToken(options, ragableAuth);
|
|
1944
|
+
}
|
|
1945
|
+
const fromGetter = options.getDataStaticKey ? await options.getDataStaticKey() : null;
|
|
1946
|
+
const key = (fromGetter?.trim() || options.dataStaticKey?.trim()) ?? "";
|
|
1947
|
+
if (!key) {
|
|
1948
|
+
throw new Error(
|
|
1949
|
+
mode === "publicAnon" ? "dataAuth publicAnon requires getDataStaticKey or dataStaticKey" : "dataAuth admin requires getDataStaticKey or dataStaticKey"
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
return key;
|
|
1953
|
+
}
|
|
1954
|
+
async function parseJsonOrThrow2(response) {
|
|
1955
|
+
const payload = await parseMaybeJsonBody(response);
|
|
1956
|
+
if (!response.ok) {
|
|
1957
|
+
const message = extractErrorMessage(payload, response.statusText);
|
|
1958
|
+
throw new RagableError(message, response.status, payload);
|
|
1959
|
+
}
|
|
1960
|
+
return payload;
|
|
1961
|
+
}
|
|
1962
|
+
var RagableBrowserAuthClient = class {
|
|
1963
|
+
constructor(_options, ragableAuth = null) {
|
|
1964
|
+
this.ragableAuth = ragableAuth;
|
|
1965
|
+
}
|
|
1966
|
+
get auth() {
|
|
1967
|
+
if (!this.ragableAuth) {
|
|
1968
|
+
throw new Error("Auth not initialized \u2014 provide authGroupId to enable auth");
|
|
1969
|
+
}
|
|
1970
|
+
return this.ragableAuth;
|
|
1971
|
+
}
|
|
1972
|
+
async signUp(credentials) {
|
|
1973
|
+
const result = await this.auth.signUp(credentials);
|
|
1974
|
+
if (result.error) return { data: null, error: result.error };
|
|
1975
|
+
const session = result.data.session;
|
|
1976
|
+
return {
|
|
1977
|
+
data: {
|
|
1978
|
+
user: session.user,
|
|
1979
|
+
session: {
|
|
1980
|
+
access_token: session.access_token,
|
|
1981
|
+
refresh_token: session.refresh_token,
|
|
1982
|
+
expires_in: session.expires_in,
|
|
1983
|
+
token_type: "bearer",
|
|
1984
|
+
user: session.user
|
|
1985
|
+
}
|
|
1986
|
+
},
|
|
1987
|
+
error: null
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
async signInWithPassword(credentials) {
|
|
1991
|
+
const result = await this.auth.signInWithPassword(credentials);
|
|
1992
|
+
if (result.error) return { data: null, error: result.error };
|
|
1993
|
+
const session = result.data.session;
|
|
1994
|
+
return {
|
|
1995
|
+
data: {
|
|
1996
|
+
user: session.user,
|
|
1997
|
+
session: {
|
|
1998
|
+
access_token: session.access_token,
|
|
1999
|
+
refresh_token: session.refresh_token,
|
|
2000
|
+
expires_in: session.expires_in,
|
|
2001
|
+
token_type: "bearer",
|
|
2002
|
+
user: session.user
|
|
2003
|
+
}
|
|
2004
|
+
},
|
|
2005
|
+
error: null
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
async refreshSession(refreshToken) {
|
|
2009
|
+
const result = await this.auth.refreshSession(refreshToken);
|
|
2010
|
+
if (result.error) return { data: null, error: result.error };
|
|
2011
|
+
const session = result.data.session;
|
|
2012
|
+
return {
|
|
2013
|
+
data: {
|
|
2014
|
+
user: session.user,
|
|
2015
|
+
session: {
|
|
2016
|
+
access_token: session.access_token,
|
|
2017
|
+
refresh_token: session.refresh_token,
|
|
2018
|
+
expires_in: session.expires_in,
|
|
2019
|
+
token_type: "bearer",
|
|
2020
|
+
user: session.user
|
|
2021
|
+
}
|
|
2022
|
+
},
|
|
2023
|
+
error: null
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
async getUser() {
|
|
2027
|
+
return this.auth.getUser();
|
|
2028
|
+
}
|
|
2029
|
+
async updateUser(attributes) {
|
|
2030
|
+
return this.auth.updateUser(attributes);
|
|
2031
|
+
}
|
|
2032
|
+
async signOut(_options) {
|
|
2033
|
+
return this.auth.signOut(_options);
|
|
2034
|
+
}
|
|
2035
|
+
async register(body) {
|
|
2036
|
+
return this.auth.register(body);
|
|
2037
|
+
}
|
|
2038
|
+
async login(body) {
|
|
2039
|
+
return this.auth.login(body);
|
|
2040
|
+
}
|
|
2041
|
+
async refresh(body) {
|
|
2042
|
+
return this.auth.refresh(body);
|
|
2043
|
+
}
|
|
2044
|
+
async getMe() {
|
|
2045
|
+
return this.auth.getMe();
|
|
2046
|
+
}
|
|
2047
|
+
async updateMe(body) {
|
|
2048
|
+
return this.auth.updateMe(body);
|
|
2049
|
+
}
|
|
2050
|
+
onAuthStateChange(callback) {
|
|
2051
|
+
return this.auth.onAuthStateChange(callback);
|
|
2052
|
+
}
|
|
2053
|
+
getSession() {
|
|
2054
|
+
return this.auth.getSession();
|
|
2055
|
+
}
|
|
1307
2056
|
};
|
|
1308
2057
|
var RagableBrowserDatabaseClient = class {
|
|
1309
|
-
constructor(options) {
|
|
2058
|
+
constructor(options, ragableAuth = null) {
|
|
1310
2059
|
this.options = options;
|
|
2060
|
+
this.ragableAuth = ragableAuth;
|
|
1311
2061
|
__publicField(this, "fetchImpl");
|
|
1312
2062
|
this.fetchImpl = bindFetch(options.fetch);
|
|
1313
2063
|
}
|
|
@@ -1316,7 +2066,7 @@ var RagableBrowserDatabaseClient = class {
|
|
|
1316
2066
|
}
|
|
1317
2067
|
async query(params) {
|
|
1318
2068
|
const gid = requireAuthGroupId(this.options);
|
|
1319
|
-
const token = await resolveDatabaseAuthBearer(this.options);
|
|
2069
|
+
const token = await resolveDatabaseAuthBearer(this.options, this.ragableAuth);
|
|
1320
2070
|
const databaseInstanceId = params.databaseInstanceId?.trim() || this.options.databaseInstanceId?.trim();
|
|
1321
2071
|
if (!databaseInstanceId) {
|
|
1322
2072
|
throw new Error(
|
|
@@ -1342,7 +2092,7 @@ var RagableBrowserDatabaseClient = class {
|
|
|
1342
2092
|
})
|
|
1343
2093
|
}
|
|
1344
2094
|
);
|
|
1345
|
-
return
|
|
2095
|
+
return parseJsonOrThrow2(response);
|
|
1346
2096
|
}
|
|
1347
2097
|
baseHeaders() {
|
|
1348
2098
|
return new Headers(this.options.headers);
|
|
@@ -1357,9 +2107,6 @@ var RagableBrowserAgentsClient = class {
|
|
|
1357
2107
|
toUrl(path) {
|
|
1358
2108
|
return `${normalizeBrowserApiBase(this.options.baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
|
|
1359
2109
|
}
|
|
1360
|
-
/**
|
|
1361
|
-
* Stream agent execution as SSE (`POST /public/organizations/:orgId/agents/:agentId/chat/stream`).
|
|
1362
|
-
*/
|
|
1363
2110
|
async *chatStream(agentId, params) {
|
|
1364
2111
|
const orgId = this.options.organizationId;
|
|
1365
2112
|
const body = {
|
|
@@ -1395,16 +2142,40 @@ var RagableBrowser = class {
|
|
|
1395
2142
|
__publicField(this, "agents");
|
|
1396
2143
|
__publicField(this, "auth");
|
|
1397
2144
|
__publicField(this, "database");
|
|
2145
|
+
__publicField(this, "transport");
|
|
1398
2146
|
__publicField(this, "options");
|
|
2147
|
+
__publicField(this, "_ragableAuth");
|
|
1399
2148
|
this.options = options;
|
|
2149
|
+
this.transport = new Transport({
|
|
2150
|
+
fetch: options.fetch,
|
|
2151
|
+
headers: options.headers,
|
|
2152
|
+
...options.transport
|
|
2153
|
+
});
|
|
2154
|
+
if (options.authGroupId) {
|
|
2155
|
+
this._ragableAuth = new RagableAuth({
|
|
2156
|
+
baseUrl: normalizeBrowserApiBase(options.baseUrl),
|
|
2157
|
+
authGroupId: options.authGroupId,
|
|
2158
|
+
fetch: options.fetch,
|
|
2159
|
+
headers: options.headers,
|
|
2160
|
+
auth: options.auth
|
|
2161
|
+
});
|
|
2162
|
+
this.transport.setRefreshHandler(async () => {
|
|
2163
|
+
const session = await this._ragableAuth.singleFlightRefresh(
|
|
2164
|
+
this._ragableAuth.getCurrentSession()?.refresh_token ?? ""
|
|
2165
|
+
);
|
|
2166
|
+
return session?.access_token ?? null;
|
|
2167
|
+
});
|
|
2168
|
+
if (!options.getAccessToken) {
|
|
2169
|
+
this._ragableAuth.initialize().catch(() => {
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
} else {
|
|
2173
|
+
this._ragableAuth = null;
|
|
2174
|
+
}
|
|
1400
2175
|
this.agents = new RagableBrowserAgentsClient(options);
|
|
1401
|
-
this.auth = new RagableBrowserAuthClient(options);
|
|
1402
|
-
this.database = new RagableBrowserDatabaseClient(options);
|
|
2176
|
+
this.auth = new RagableBrowserAuthClient(options, this._ragableAuth);
|
|
2177
|
+
this.database = new RagableBrowserDatabaseClient(options, this._ragableAuth);
|
|
1403
2178
|
}
|
|
1404
|
-
/**
|
|
1405
|
-
* Supabase-style table API: `.from('items').select().eq('id', 1).single()`.
|
|
1406
|
-
* Pass `databaseInstanceId` here or set `databaseInstanceId` on the client options.
|
|
1407
|
-
*/
|
|
1408
2179
|
from(table, databaseInstanceId) {
|
|
1409
2180
|
const id = databaseInstanceId?.trim() || this.options.databaseInstanceId?.trim();
|
|
1410
2181
|
if (!id) {
|
|
@@ -1412,8 +2183,40 @@ var RagableBrowser = class {
|
|
|
1412
2183
|
"RagableBrowser.from() requires databaseInstanceId in client options or as the second argument"
|
|
1413
2184
|
);
|
|
1414
2185
|
}
|
|
1415
|
-
const
|
|
1416
|
-
|
|
2186
|
+
const gid = requireAuthGroupId(this.options);
|
|
2187
|
+
const ragableAuth = this._ragableAuth;
|
|
2188
|
+
const opts = this.options;
|
|
2189
|
+
const pgFetch = async (params) => {
|
|
2190
|
+
const token = await resolveDatabaseAuthBearer(opts, ragableAuth);
|
|
2191
|
+
const baseUrl = normalizeBrowserApiBase(opts.baseUrl);
|
|
2192
|
+
const qs = params.searchParams.toString();
|
|
2193
|
+
const url = `${baseUrl}/auth-groups/${gid}/data/rest/${params.table}${qs ? `?${qs}` : ""}`;
|
|
2194
|
+
const headers = new Headers(opts.headers);
|
|
2195
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
2196
|
+
headers.set("X-Database-Instance-Id", params.databaseInstanceId);
|
|
2197
|
+
if (params.body !== void 0) {
|
|
2198
|
+
headers.set("Content-Type", "application/json");
|
|
2199
|
+
}
|
|
2200
|
+
if (params.headers) {
|
|
2201
|
+
for (const [k, v] of Object.entries(params.headers)) {
|
|
2202
|
+
headers.set(k, v);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
if (params.idempotencyKey) {
|
|
2206
|
+
headers.set("Idempotency-Key", params.idempotencyKey);
|
|
2207
|
+
}
|
|
2208
|
+
const fetchImpl = bindFetch(opts.fetch);
|
|
2209
|
+
return fetchImpl(url, {
|
|
2210
|
+
method: params.method,
|
|
2211
|
+
headers,
|
|
2212
|
+
body: params.body !== void 0 ? JSON.stringify(params.body) : void 0,
|
|
2213
|
+
signal: params.signal
|
|
2214
|
+
});
|
|
2215
|
+
};
|
|
2216
|
+
return new PostgrestTableApi(pgFetch, id, table);
|
|
2217
|
+
}
|
|
2218
|
+
destroy() {
|
|
2219
|
+
this._ragableAuth?.destroy();
|
|
1417
2220
|
}
|
|
1418
2221
|
};
|
|
1419
2222
|
function createBrowserClient(options) {
|
|
@@ -1511,7 +2314,11 @@ function createRagableServerClient(options) {
|
|
|
1511
2314
|
}
|
|
1512
2315
|
export {
|
|
1513
2316
|
AgentsClient,
|
|
2317
|
+
AuthBroadcastChannel,
|
|
2318
|
+
CookieStorageAdapter,
|
|
1514
2319
|
DEFAULT_RAGABLE_API_BASE,
|
|
2320
|
+
LocalStorageAdapter,
|
|
2321
|
+
MemoryStorageAdapter,
|
|
1515
2322
|
PostgrestDeleteReturningBuilder,
|
|
1516
2323
|
PostgrestDeleteRootBuilder,
|
|
1517
2324
|
PostgrestInsertReturningBuilder,
|
|
@@ -1523,13 +2330,20 @@ export {
|
|
|
1523
2330
|
PostgrestUpsertReturningBuilder,
|
|
1524
2331
|
PostgrestUpsertRootBuilder,
|
|
1525
2332
|
Ragable,
|
|
2333
|
+
RagableAbortError,
|
|
2334
|
+
RagableAuth,
|
|
1526
2335
|
RagableBrowser,
|
|
1527
2336
|
RagableBrowserAgentsClient,
|
|
1528
2337
|
RagableBrowserAuthClient,
|
|
1529
2338
|
RagableBrowserDatabaseClient,
|
|
1530
2339
|
RagableError,
|
|
2340
|
+
RagableNetworkError,
|
|
1531
2341
|
RagableRequestClient,
|
|
2342
|
+
RagableSdkError,
|
|
2343
|
+
RagableTimeoutError,
|
|
2344
|
+
SessionStorageAdapter,
|
|
1532
2345
|
ShiftClient,
|
|
2346
|
+
Transport,
|
|
1533
2347
|
asPostgrestResponse,
|
|
1534
2348
|
bindFetch,
|
|
1535
2349
|
createBrowserClient,
|
|
@@ -1537,10 +2351,13 @@ export {
|
|
|
1537
2351
|
createRagPipeline,
|
|
1538
2352
|
createRagableBrowserClient,
|
|
1539
2353
|
createRagableServerClient,
|
|
2354
|
+
detectStorage,
|
|
1540
2355
|
extractErrorMessage,
|
|
1541
2356
|
formatRetrievalContext,
|
|
2357
|
+
generateIdempotencyKey,
|
|
1542
2358
|
normalizeBrowserApiBase,
|
|
1543
2359
|
parseSseDataLine,
|
|
2360
|
+
parseTransportResponse,
|
|
1544
2361
|
readSseStream
|
|
1545
2362
|
};
|
|
1546
2363
|
//# sourceMappingURL=index.mjs.map
|