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