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