@ragable/sdk 0.5.0 → 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 +405 -175
- package/dist/index.d.ts +405 -175
- package/dist/index.js +1305 -455
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1290 -455
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23,6 +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,
|
|
28
|
+
DEFAULT_RAGABLE_API_BASE: () => DEFAULT_RAGABLE_API_BASE,
|
|
29
|
+
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
30
|
+
MemoryStorageAdapter: () => MemoryStorageAdapter,
|
|
26
31
|
PostgrestDeleteReturningBuilder: () => PostgrestDeleteReturningBuilder,
|
|
27
32
|
PostgrestDeleteRootBuilder: () => PostgrestDeleteRootBuilder,
|
|
28
33
|
PostgrestInsertReturningBuilder: () => PostgrestInsertReturningBuilder,
|
|
@@ -34,13 +39,20 @@ __export(index_exports, {
|
|
|
34
39
|
PostgrestUpsertReturningBuilder: () => PostgrestUpsertReturningBuilder,
|
|
35
40
|
PostgrestUpsertRootBuilder: () => PostgrestUpsertRootBuilder,
|
|
36
41
|
Ragable: () => Ragable,
|
|
42
|
+
RagableAbortError: () => RagableAbortError,
|
|
43
|
+
RagableAuth: () => RagableAuth,
|
|
37
44
|
RagableBrowser: () => RagableBrowser,
|
|
38
45
|
RagableBrowserAgentsClient: () => RagableBrowserAgentsClient,
|
|
39
46
|
RagableBrowserAuthClient: () => RagableBrowserAuthClient,
|
|
40
47
|
RagableBrowserDatabaseClient: () => RagableBrowserDatabaseClient,
|
|
41
48
|
RagableError: () => RagableError,
|
|
49
|
+
RagableNetworkError: () => RagableNetworkError,
|
|
42
50
|
RagableRequestClient: () => RagableRequestClient,
|
|
51
|
+
RagableSdkError: () => RagableSdkError,
|
|
52
|
+
RagableTimeoutError: () => RagableTimeoutError,
|
|
53
|
+
SessionStorageAdapter: () => SessionStorageAdapter,
|
|
43
54
|
ShiftClient: () => ShiftClient,
|
|
55
|
+
Transport: () => Transport,
|
|
44
56
|
asPostgrestResponse: () => asPostgrestResponse,
|
|
45
57
|
bindFetch: () => bindFetch,
|
|
46
58
|
createBrowserClient: () => createBrowserClient,
|
|
@@ -48,10 +60,13 @@ __export(index_exports, {
|
|
|
48
60
|
createRagPipeline: () => createRagPipeline,
|
|
49
61
|
createRagableBrowserClient: () => createRagableBrowserClient,
|
|
50
62
|
createRagableServerClient: () => createRagableServerClient,
|
|
63
|
+
detectStorage: () => detectStorage,
|
|
51
64
|
extractErrorMessage: () => extractErrorMessage,
|
|
52
65
|
formatRetrievalContext: () => formatRetrievalContext,
|
|
66
|
+
generateIdempotencyKey: () => generateIdempotencyKey,
|
|
53
67
|
normalizeBrowserApiBase: () => normalizeBrowserApiBase,
|
|
54
68
|
parseSseDataLine: () => parseSseDataLine,
|
|
69
|
+
parseTransportResponse: () => parseTransportResponse,
|
|
55
70
|
readSseStream: () => readSseStream
|
|
56
71
|
});
|
|
57
72
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -63,14 +78,50 @@ function bindFetch(custom) {
|
|
|
63
78
|
return f.call(globalThis, input, init);
|
|
64
79
|
};
|
|
65
80
|
}
|
|
66
|
-
var
|
|
81
|
+
var DEFAULT_RAGABLE_API_BASE = "https://ragable-341305259977.asia-southeast1.run.app/api";
|
|
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 {
|
|
67
89
|
constructor(message, status, body) {
|
|
68
90
|
super(message);
|
|
91
|
+
__publicField(this, "__type", "RagableError");
|
|
69
92
|
__publicField(this, "status");
|
|
70
93
|
__publicField(this, "body");
|
|
71
|
-
this
|
|
94
|
+
__publicField(this, "code");
|
|
95
|
+
__publicField(this, "details");
|
|
72
96
|
this.status = status;
|
|
73
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;
|
|
74
125
|
}
|
|
75
126
|
};
|
|
76
127
|
function extractErrorMessage(payload, fallback) {
|
|
@@ -94,7 +145,7 @@ var RagableRequestClient = class {
|
|
|
94
145
|
__publicField(this, "fetchImpl");
|
|
95
146
|
__publicField(this, "defaultHeaders");
|
|
96
147
|
this.apiKey = options.apiKey;
|
|
97
|
-
this.baseUrl = options.baseUrl ??
|
|
148
|
+
this.baseUrl = options.baseUrl ?? DEFAULT_RAGABLE_API_BASE;
|
|
98
149
|
this.fetchImpl = bindFetch(options.fetch);
|
|
99
150
|
this.defaultHeaders = options.headers;
|
|
100
151
|
}
|
|
@@ -398,42 +449,191 @@ var AgentsClient = class {
|
|
|
398
449
|
}
|
|
399
450
|
};
|
|
400
451
|
|
|
401
|
-
// src/
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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();
|
|
409
477
|
}
|
|
478
|
+
_uuidCounter++;
|
|
479
|
+
return `idk-${Date.now()}-${_uuidCounter}-${Math.random().toString(36).slice(2, 10)}`;
|
|
410
480
|
}
|
|
411
|
-
function
|
|
412
|
-
|
|
413
|
-
return `"${name}"`;
|
|
481
|
+
function requestCacheKey(req) {
|
|
482
|
+
return `${req.method}:${req.url}`;
|
|
414
483
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
|
422
574
|
);
|
|
423
575
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
+
}
|
|
436
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
|
|
437
637
|
async function asPostgrestResponse(fn) {
|
|
438
638
|
try {
|
|
439
639
|
const data = await fn();
|
|
@@ -443,28 +643,75 @@ async function asPostgrestResponse(fn) {
|
|
|
443
643
|
return { data: null, error: err };
|
|
444
644
|
}
|
|
445
645
|
}
|
|
446
|
-
function
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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;
|
|
457
702
|
}
|
|
458
703
|
var PostgrestSelectBuilder = class {
|
|
459
|
-
constructor(
|
|
460
|
-
this.
|
|
704
|
+
constructor(pgFetch, databaseInstanceId, table, columns) {
|
|
705
|
+
this.pgFetch = pgFetch;
|
|
461
706
|
this.databaseInstanceId = databaseInstanceId;
|
|
462
707
|
this.table = table;
|
|
463
708
|
this.columns = columns;
|
|
464
709
|
__publicField(this, "filters", []);
|
|
465
710
|
__publicField(this, "_limit");
|
|
711
|
+
__publicField(this, "_offset");
|
|
466
712
|
__publicField(this, "_order");
|
|
467
|
-
|
|
713
|
+
__publicField(this, "_signal");
|
|
714
|
+
addFilterMethods(this);
|
|
468
715
|
}
|
|
469
716
|
eq(column, value) {
|
|
470
717
|
this.filters.push({ op: "eq", column, value });
|
|
@@ -498,140 +745,173 @@ var PostgrestSelectBuilder = class {
|
|
|
498
745
|
this.filters.push({ op: "ilike", column, value });
|
|
499
746
|
return this;
|
|
500
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
|
+
}
|
|
501
766
|
limit(n) {
|
|
502
767
|
this._limit = n;
|
|
503
768
|
return this;
|
|
504
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
|
+
}
|
|
505
779
|
order(column, options) {
|
|
506
|
-
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;
|
|
507
789
|
return this;
|
|
508
790
|
}
|
|
509
|
-
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
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
|
+
}
|
|
513
799
|
if (this._order) {
|
|
514
|
-
|
|
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);
|
|
515
804
|
}
|
|
516
|
-
if (
|
|
517
|
-
|
|
805
|
+
if (this._limit != null) {
|
|
806
|
+
sp.set("limit", String(Math.max(0, Math.floor(this._limit))));
|
|
518
807
|
}
|
|
519
|
-
|
|
808
|
+
if (this._offset != null && this._offset > 0) {
|
|
809
|
+
sp.set("offset", String(Math.max(0, Math.floor(this._offset))));
|
|
810
|
+
}
|
|
811
|
+
return sp;
|
|
520
812
|
}
|
|
521
813
|
then(onfulfilled, onrejected) {
|
|
522
814
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
523
815
|
}
|
|
524
816
|
async executeMany() {
|
|
525
817
|
return asPostgrestResponse(async () => {
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
818
|
+
const response = await this.pgFetch({
|
|
819
|
+
method: "GET",
|
|
820
|
+
table: this.table,
|
|
821
|
+
searchParams: this.buildSearchParams(),
|
|
529
822
|
databaseInstanceId: this.databaseInstanceId,
|
|
530
|
-
|
|
531
|
-
params,
|
|
532
|
-
readOnly: true
|
|
823
|
+
signal: this._signal
|
|
533
824
|
});
|
|
534
|
-
return
|
|
825
|
+
return parsePostgRESTResponse(response);
|
|
535
826
|
});
|
|
536
827
|
}
|
|
537
828
|
async single() {
|
|
538
829
|
return asPostgrestResponse(async () => {
|
|
539
|
-
const
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
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" },
|
|
543
836
|
databaseInstanceId: this.databaseInstanceId,
|
|
544
|
-
|
|
545
|
-
params,
|
|
546
|
-
readOnly: true
|
|
837
|
+
signal: this._signal
|
|
547
838
|
});
|
|
548
|
-
|
|
549
|
-
throw new RagableError("JSON object requested, multiple (or no) rows returned", 406, {
|
|
550
|
-
code: "PGRST116"
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
return res.rows[0];
|
|
839
|
+
return parsePostgRESTResponse(response);
|
|
554
840
|
});
|
|
555
841
|
}
|
|
556
842
|
async maybeSingle() {
|
|
557
843
|
return asPostgrestResponse(async () => {
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
844
|
+
const response = await this.pgFetch({
|
|
845
|
+
method: "GET",
|
|
846
|
+
table: this.table,
|
|
847
|
+
searchParams: this.buildSearchParams(),
|
|
562
848
|
databaseInstanceId: this.databaseInstanceId,
|
|
563
|
-
|
|
564
|
-
params,
|
|
565
|
-
readOnly: true
|
|
849
|
+
signal: this._signal
|
|
566
850
|
});
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
+
);
|
|
571
858
|
}
|
|
572
|
-
return
|
|
859
|
+
return rows[0] ?? null;
|
|
573
860
|
});
|
|
574
861
|
}
|
|
575
862
|
};
|
|
576
863
|
var PostgrestInsertRootBuilder = class {
|
|
577
|
-
constructor(
|
|
578
|
-
this.
|
|
864
|
+
constructor(pgFetch, databaseInstanceId, table, rows) {
|
|
865
|
+
this.pgFetch = pgFetch;
|
|
579
866
|
this.databaseInstanceId = databaseInstanceId;
|
|
580
867
|
this.table = table;
|
|
581
868
|
this.rows = rows;
|
|
582
|
-
|
|
869
|
+
__publicField(this, "_signal");
|
|
583
870
|
}
|
|
584
|
-
/**
|
|
585
|
-
* Return inserted rows (Supabase: chain `.select()` to get data).
|
|
586
|
-
* @see https://supabase.com/docs/reference/javascript/insert
|
|
587
|
-
*/
|
|
588
871
|
select(columns = "*") {
|
|
589
872
|
return new PostgrestInsertReturningBuilder(
|
|
590
|
-
this.
|
|
873
|
+
this.pgFetch,
|
|
591
874
|
this.databaseInstanceId,
|
|
592
875
|
this.table,
|
|
593
876
|
this.rows,
|
|
594
|
-
columns
|
|
877
|
+
columns,
|
|
878
|
+
this._signal
|
|
595
879
|
);
|
|
596
880
|
}
|
|
881
|
+
abortSignal(signal) {
|
|
882
|
+
this._signal = signal;
|
|
883
|
+
return this;
|
|
884
|
+
}
|
|
597
885
|
then(onfulfilled, onrejected) {
|
|
598
886
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
599
887
|
}
|
|
600
888
|
async executeNoReturn() {
|
|
601
889
|
return asPostgrestResponse(async () => {
|
|
602
890
|
if (this.rows.length === 0) return null;
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
for (const k of keys) {
|
|
611
|
-
params.push(row[k]);
|
|
612
|
-
placeholders.push(`$${params.length}`);
|
|
613
|
-
}
|
|
614
|
-
valueGroups.push(`(${placeholders.join(", ")})`);
|
|
615
|
-
}
|
|
616
|
-
const cols = keys.map(quoteIdent).join(", ");
|
|
617
|
-
const sql = `INSERT INTO ${tbl} (${cols}) VALUES ${valueGroups.join(", ")}`;
|
|
618
|
-
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" },
|
|
619
898
|
databaseInstanceId: this.databaseInstanceId,
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
readOnly: false
|
|
899
|
+
signal: this._signal,
|
|
900
|
+
idempotencyKey: generateIdempotencyKey()
|
|
623
901
|
});
|
|
902
|
+
await parsePostgRESTResponse(response);
|
|
624
903
|
return null;
|
|
625
904
|
});
|
|
626
905
|
}
|
|
627
906
|
};
|
|
628
907
|
var PostgrestInsertReturningBuilder = class {
|
|
629
|
-
constructor(
|
|
630
|
-
this.
|
|
908
|
+
constructor(pgFetch, databaseInstanceId, table, rows, returning, _signal) {
|
|
909
|
+
this.pgFetch = pgFetch;
|
|
631
910
|
this.databaseInstanceId = databaseInstanceId;
|
|
632
911
|
this.table = table;
|
|
633
912
|
this.rows = rows;
|
|
634
913
|
this.returning = returning;
|
|
914
|
+
this._signal = _signal;
|
|
635
915
|
}
|
|
636
916
|
then(onfulfilled, onrejected) {
|
|
637
917
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
@@ -639,28 +919,22 @@ var PostgrestInsertReturningBuilder = class {
|
|
|
639
919
|
async executeMany() {
|
|
640
920
|
return asPostgrestResponse(async () => {
|
|
641
921
|
if (this.rows.length === 0) return [];
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
const valueGroups = [];
|
|
647
|
-
for (const row of this.rows) {
|
|
648
|
-
const placeholders = [];
|
|
649
|
-
for (const k of keys) {
|
|
650
|
-
params.push(row[k]);
|
|
651
|
-
placeholders.push(`$${params.length}`);
|
|
652
|
-
}
|
|
653
|
-
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);
|
|
654
926
|
}
|
|
655
|
-
const
|
|
656
|
-
|
|
657
|
-
|
|
927
|
+
const response = await this.pgFetch({
|
|
928
|
+
method: "POST",
|
|
929
|
+
table: this.table,
|
|
930
|
+
searchParams: sp,
|
|
931
|
+
body,
|
|
932
|
+
headers: { Prefer: "return=representation" },
|
|
658
933
|
databaseInstanceId: this.databaseInstanceId,
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
readOnly: false
|
|
934
|
+
signal: this._signal,
|
|
935
|
+
idempotencyKey: generateIdempotencyKey()
|
|
662
936
|
});
|
|
663
|
-
return
|
|
937
|
+
return parsePostgRESTResponse(response);
|
|
664
938
|
});
|
|
665
939
|
}
|
|
666
940
|
async single() {
|
|
@@ -669,9 +943,11 @@ var PostgrestInsertReturningBuilder = class {
|
|
|
669
943
|
if (error) throw error;
|
|
670
944
|
const rows = data ?? [];
|
|
671
945
|
if (rows.length === 0 || rows.length > 1) {
|
|
672
|
-
throw new RagableError(
|
|
673
|
-
|
|
674
|
-
|
|
946
|
+
throw new RagableError(
|
|
947
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
948
|
+
406,
|
|
949
|
+
{ code: "PGRST116" }
|
|
950
|
+
);
|
|
675
951
|
}
|
|
676
952
|
return rows[0];
|
|
677
953
|
});
|
|
@@ -682,22 +958,24 @@ var PostgrestInsertReturningBuilder = class {
|
|
|
682
958
|
if (error) throw error;
|
|
683
959
|
const rows = data ?? [];
|
|
684
960
|
if (rows.length > 1) {
|
|
685
|
-
throw new RagableError(
|
|
686
|
-
|
|
687
|
-
|
|
961
|
+
throw new RagableError(
|
|
962
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
963
|
+
406,
|
|
964
|
+
{ code: "PGRST116" }
|
|
965
|
+
);
|
|
688
966
|
}
|
|
689
967
|
return rows[0] ?? null;
|
|
690
968
|
});
|
|
691
969
|
}
|
|
692
970
|
};
|
|
693
971
|
var PostgrestUpdateRootBuilder = class {
|
|
694
|
-
constructor(
|
|
695
|
-
this.
|
|
972
|
+
constructor(pgFetch, databaseInstanceId, table, patch) {
|
|
973
|
+
this.pgFetch = pgFetch;
|
|
696
974
|
this.databaseInstanceId = databaseInstanceId;
|
|
697
975
|
this.table = table;
|
|
698
976
|
this.patch = patch;
|
|
699
977
|
__publicField(this, "filters", []);
|
|
700
|
-
|
|
978
|
+
__publicField(this, "_signal");
|
|
701
979
|
}
|
|
702
980
|
eq(column, value) {
|
|
703
981
|
this.filters.push({ op: "eq", column, value });
|
|
@@ -731,64 +1009,79 @@ var PostgrestUpdateRootBuilder = class {
|
|
|
731
1009
|
this.filters.push({ op: "ilike", column, value });
|
|
732
1010
|
return this;
|
|
733
1011
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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
|
+
}
|
|
738
1030
|
select(columns = "*") {
|
|
739
1031
|
return new PostgrestUpdateReturningBuilder(
|
|
740
|
-
this.
|
|
1032
|
+
this.pgFetch,
|
|
741
1033
|
this.databaseInstanceId,
|
|
742
1034
|
this.table,
|
|
743
1035
|
this.patch,
|
|
744
1036
|
this.filters,
|
|
745
|
-
columns
|
|
1037
|
+
columns,
|
|
1038
|
+
this._signal
|
|
746
1039
|
);
|
|
747
1040
|
}
|
|
1041
|
+
abortSignal(signal) {
|
|
1042
|
+
this._signal = signal;
|
|
1043
|
+
return this;
|
|
1044
|
+
}
|
|
748
1045
|
then(onfulfilled, onrejected) {
|
|
749
1046
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
750
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
|
+
}
|
|
751
1055
|
async executeNoReturn() {
|
|
752
1056
|
return asPostgrestResponse(async () => {
|
|
753
1057
|
const keys = Object.keys(this.patch);
|
|
754
1058
|
if (keys.length === 0) {
|
|
755
1059
|
throw new RagableError("Empty update payload", 400, null);
|
|
756
1060
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
const params = [];
|
|
766
|
-
const sets = [];
|
|
767
|
-
for (const k of keys) {
|
|
768
|
-
params.push(this.patch[k]);
|
|
769
|
-
sets.push(`${quoteIdent(k)} = $${params.length}`);
|
|
770
|
-
}
|
|
771
|
-
const { clause } = buildWhere(this.filters, params);
|
|
772
|
-
const tbl = quoteIdent(this.table);
|
|
773
|
-
const sql = `UPDATE ${tbl} SET ${sets.join(", ")}${clause}`;
|
|
774
|
-
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" },
|
|
775
1067
|
databaseInstanceId: this.databaseInstanceId,
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
readOnly: false
|
|
1068
|
+
signal: this._signal,
|
|
1069
|
+
idempotencyKey: generateIdempotencyKey()
|
|
779
1070
|
});
|
|
1071
|
+
await parsePostgRESTResponse(response);
|
|
780
1072
|
return null;
|
|
781
1073
|
});
|
|
782
1074
|
}
|
|
783
1075
|
};
|
|
784
1076
|
var PostgrestUpdateReturningBuilder = class {
|
|
785
|
-
constructor(
|
|
786
|
-
this.
|
|
1077
|
+
constructor(pgFetch, databaseInstanceId, table, patch, filters, returning, _signal) {
|
|
1078
|
+
this.pgFetch = pgFetch;
|
|
787
1079
|
this.databaseInstanceId = databaseInstanceId;
|
|
788
1080
|
this.table = table;
|
|
789
1081
|
this.patch = patch;
|
|
790
1082
|
this.filters = filters;
|
|
791
1083
|
this.returning = returning;
|
|
1084
|
+
this._signal = _signal;
|
|
792
1085
|
}
|
|
793
1086
|
then(onfulfilled, onrejected) {
|
|
794
1087
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
@@ -799,30 +1092,24 @@ var PostgrestUpdateReturningBuilder = class {
|
|
|
799
1092
|
if (keys.length === 0) {
|
|
800
1093
|
throw new RagableError("Empty update payload", 400, null);
|
|
801
1094
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
"UPDATE requires a filter (e.g. .eq('id', value)) \u2014 refusing unscoped update",
|
|
806
|
-
400,
|
|
807
|
-
null
|
|
808
|
-
);
|
|
1095
|
+
const sp = new URLSearchParams();
|
|
1096
|
+
for (const f of this.filters) {
|
|
1097
|
+
sp.append(f.column, encodeFilterValue(f.op, f.value));
|
|
809
1098
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
for (const k of keys) {
|
|
813
|
-
params.push(this.patch[k]);
|
|
814
|
-
sets.push(`${quoteIdent(k)} = $${params.length}`);
|
|
1099
|
+
if (this.returning && this.returning !== "*") {
|
|
1100
|
+
sp.set("select", this.returning);
|
|
815
1101
|
}
|
|
816
|
-
const
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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" },
|
|
820
1108
|
databaseInstanceId: this.databaseInstanceId,
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
readOnly: false
|
|
1109
|
+
signal: this._signal,
|
|
1110
|
+
idempotencyKey: generateIdempotencyKey()
|
|
824
1111
|
});
|
|
825
|
-
return
|
|
1112
|
+
return parsePostgRESTResponse(response);
|
|
826
1113
|
});
|
|
827
1114
|
}
|
|
828
1115
|
async single() {
|
|
@@ -831,9 +1118,11 @@ var PostgrestUpdateReturningBuilder = class {
|
|
|
831
1118
|
if (error) throw error;
|
|
832
1119
|
const rows = data ?? [];
|
|
833
1120
|
if (rows.length === 0 || rows.length > 1) {
|
|
834
|
-
throw new RagableError(
|
|
835
|
-
|
|
836
|
-
|
|
1121
|
+
throw new RagableError(
|
|
1122
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1123
|
+
406,
|
|
1124
|
+
{ code: "PGRST116" }
|
|
1125
|
+
);
|
|
837
1126
|
}
|
|
838
1127
|
return rows[0];
|
|
839
1128
|
});
|
|
@@ -844,21 +1133,23 @@ var PostgrestUpdateReturningBuilder = class {
|
|
|
844
1133
|
if (error) throw error;
|
|
845
1134
|
const rows = data ?? [];
|
|
846
1135
|
if (rows.length > 1) {
|
|
847
|
-
throw new RagableError(
|
|
848
|
-
|
|
849
|
-
|
|
1136
|
+
throw new RagableError(
|
|
1137
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1138
|
+
406,
|
|
1139
|
+
{ code: "PGRST116" }
|
|
1140
|
+
);
|
|
850
1141
|
}
|
|
851
1142
|
return rows[0] ?? null;
|
|
852
1143
|
});
|
|
853
1144
|
}
|
|
854
1145
|
};
|
|
855
1146
|
var PostgrestDeleteRootBuilder = class {
|
|
856
|
-
constructor(
|
|
857
|
-
this.
|
|
1147
|
+
constructor(pgFetch, databaseInstanceId, table) {
|
|
1148
|
+
this.pgFetch = pgFetch;
|
|
858
1149
|
this.databaseInstanceId = databaseInstanceId;
|
|
859
1150
|
this.table = table;
|
|
860
1151
|
__publicField(this, "filters", []);
|
|
861
|
-
|
|
1152
|
+
__publicField(this, "_signal");
|
|
862
1153
|
}
|
|
863
1154
|
eq(column, value) {
|
|
864
1155
|
this.filters.push({ op: "eq", column, value });
|
|
@@ -892,76 +1183,92 @@ var PostgrestDeleteRootBuilder = class {
|
|
|
892
1183
|
this.filters.push({ op: "ilike", column, value });
|
|
893
1184
|
return this;
|
|
894
1185
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
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
|
+
}
|
|
899
1204
|
select(columns = "*") {
|
|
900
1205
|
return new PostgrestDeleteReturningBuilder(
|
|
901
|
-
this.
|
|
1206
|
+
this.pgFetch,
|
|
902
1207
|
this.databaseInstanceId,
|
|
903
1208
|
this.table,
|
|
904
1209
|
this.filters,
|
|
905
|
-
columns
|
|
1210
|
+
columns,
|
|
1211
|
+
this._signal
|
|
906
1212
|
);
|
|
907
1213
|
}
|
|
1214
|
+
abortSignal(signal) {
|
|
1215
|
+
this._signal = signal;
|
|
1216
|
+
return this;
|
|
1217
|
+
}
|
|
908
1218
|
then(onfulfilled, onrejected) {
|
|
909
1219
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
910
1220
|
}
|
|
911
1221
|
async executeNoReturn() {
|
|
912
1222
|
return asPostgrestResponse(async () => {
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
400,
|
|
917
|
-
null
|
|
918
|
-
);
|
|
1223
|
+
const sp = new URLSearchParams();
|
|
1224
|
+
for (const f of this.filters) {
|
|
1225
|
+
sp.append(f.column, encodeFilterValue(f.op, f.value));
|
|
919
1226
|
}
|
|
920
|
-
const
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1227
|
+
const response = await this.pgFetch({
|
|
1228
|
+
method: "DELETE",
|
|
1229
|
+
table: this.table,
|
|
1230
|
+
searchParams: sp,
|
|
1231
|
+
headers: { Prefer: "return=minimal" },
|
|
925
1232
|
databaseInstanceId: this.databaseInstanceId,
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
readOnly: false
|
|
1233
|
+
signal: this._signal,
|
|
1234
|
+
idempotencyKey: generateIdempotencyKey()
|
|
929
1235
|
});
|
|
1236
|
+
await parsePostgRESTResponse(response);
|
|
930
1237
|
return null;
|
|
931
1238
|
});
|
|
932
1239
|
}
|
|
933
1240
|
};
|
|
934
1241
|
var PostgrestDeleteReturningBuilder = class {
|
|
935
|
-
constructor(
|
|
936
|
-
this.
|
|
1242
|
+
constructor(pgFetch, databaseInstanceId, table, filters, returning, _signal) {
|
|
1243
|
+
this.pgFetch = pgFetch;
|
|
937
1244
|
this.databaseInstanceId = databaseInstanceId;
|
|
938
1245
|
this.table = table;
|
|
939
1246
|
this.filters = filters;
|
|
940
1247
|
this.returning = returning;
|
|
1248
|
+
this._signal = _signal;
|
|
941
1249
|
}
|
|
942
1250
|
then(onfulfilled, onrejected) {
|
|
943
1251
|
return this.executeMany().then(onfulfilled, onrejected);
|
|
944
1252
|
}
|
|
945
1253
|
async executeMany() {
|
|
946
1254
|
return asPostgrestResponse(async () => {
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
);
|
|
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);
|
|
953
1261
|
}
|
|
954
|
-
const
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1262
|
+
const response = await this.pgFetch({
|
|
1263
|
+
method: "DELETE",
|
|
1264
|
+
table: this.table,
|
|
1265
|
+
searchParams: sp,
|
|
1266
|
+
headers: { Prefer: "return=representation" },
|
|
959
1267
|
databaseInstanceId: this.databaseInstanceId,
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
readOnly: false
|
|
1268
|
+
signal: this._signal,
|
|
1269
|
+
idempotencyKey: generateIdempotencyKey()
|
|
963
1270
|
});
|
|
964
|
-
return
|
|
1271
|
+
return parsePostgRESTResponse(response);
|
|
965
1272
|
});
|
|
966
1273
|
}
|
|
967
1274
|
async single() {
|
|
@@ -970,9 +1277,11 @@ var PostgrestDeleteReturningBuilder = class {
|
|
|
970
1277
|
if (error) throw error;
|
|
971
1278
|
const rows = data ?? [];
|
|
972
1279
|
if (rows.length === 0 || rows.length > 1) {
|
|
973
|
-
throw new RagableError(
|
|
974
|
-
|
|
975
|
-
|
|
1280
|
+
throw new RagableError(
|
|
1281
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1282
|
+
406,
|
|
1283
|
+
{ code: "PGRST116" }
|
|
1284
|
+
);
|
|
976
1285
|
}
|
|
977
1286
|
return rows[0];
|
|
978
1287
|
});
|
|
@@ -983,84 +1292,66 @@ var PostgrestDeleteReturningBuilder = class {
|
|
|
983
1292
|
if (error) throw error;
|
|
984
1293
|
const rows = data ?? [];
|
|
985
1294
|
if (rows.length > 1) {
|
|
986
|
-
throw new RagableError(
|
|
987
|
-
|
|
988
|
-
|
|
1295
|
+
throw new RagableError(
|
|
1296
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1297
|
+
406,
|
|
1298
|
+
{ code: "PGRST116" }
|
|
1299
|
+
);
|
|
989
1300
|
}
|
|
990
1301
|
return rows[0] ?? null;
|
|
991
1302
|
});
|
|
992
1303
|
}
|
|
993
1304
|
};
|
|
994
1305
|
var PostgrestUpsertRootBuilder = class {
|
|
995
|
-
constructor(
|
|
996
|
-
this.
|
|
1306
|
+
constructor(pgFetch, databaseInstanceId, table, rows, onConflict, ignoreDuplicates) {
|
|
1307
|
+
this.pgFetch = pgFetch;
|
|
997
1308
|
this.databaseInstanceId = databaseInstanceId;
|
|
998
1309
|
this.table = table;
|
|
999
1310
|
this.rows = rows;
|
|
1311
|
+
this.onConflict = onConflict;
|
|
1000
1312
|
this.ignoreDuplicates = ignoreDuplicates;
|
|
1001
|
-
__publicField(this, "
|
|
1002
|
-
assertIdent(table, "table");
|
|
1003
|
-
this.conflictCols = parseConflictColumns(onConflict);
|
|
1313
|
+
__publicField(this, "_signal");
|
|
1004
1314
|
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Return upserted rows (Supabase: `.upsert().select()`).
|
|
1007
|
-
* @see https://supabase.com/docs/reference/javascript/upsert
|
|
1008
|
-
*/
|
|
1009
1315
|
select(columns = "*") {
|
|
1010
1316
|
return new PostgrestUpsertReturningBuilder(this, columns);
|
|
1011
1317
|
}
|
|
1318
|
+
abortSignal(signal) {
|
|
1319
|
+
this._signal = signal;
|
|
1320
|
+
return this;
|
|
1321
|
+
}
|
|
1012
1322
|
then(onfulfilled, onrejected) {
|
|
1013
1323
|
return this.executeNoReturn().then(onfulfilled, onrejected);
|
|
1014
1324
|
}
|
|
1015
1325
|
async executeNoReturn() {
|
|
1016
1326
|
return asPostgrestResponse(async () => {
|
|
1017
|
-
await this.runUpsert(null);
|
|
1327
|
+
await this.runUpsert("return=minimal", null);
|
|
1018
1328
|
return null;
|
|
1019
1329
|
});
|
|
1020
1330
|
}
|
|
1021
|
-
async runUpsert(
|
|
1022
|
-
if (this.rows.length === 0)
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
const conflictQuoted = this.conflictCols.map(quoteIdent).join(", ");
|
|
1029
|
-
const params = [];
|
|
1030
|
-
const valueGroups = [];
|
|
1031
|
-
for (const row of this.rows) {
|
|
1032
|
-
const placeholders = [];
|
|
1033
|
-
for (const k of keys) {
|
|
1034
|
-
params.push(row[k]);
|
|
1035
|
-
placeholders.push(`$${params.length}`);
|
|
1036
|
-
}
|
|
1037
|
-
valueGroups.push(`(${placeholders.join(", ")})`);
|
|
1038
|
-
}
|
|
1039
|
-
const cols = keys.map(quoteIdent).join(", ");
|
|
1040
|
-
let sql = `INSERT INTO ${tbl} (${cols}) VALUES ${valueGroups.join(", ")} ON CONFLICT (${conflictQuoted})`;
|
|
1041
|
-
if (this.ignoreDuplicates) {
|
|
1042
|
-
sql += " DO NOTHING";
|
|
1043
|
-
} else {
|
|
1044
|
-
const setParts = keys.filter((k) => !this.conflictCols.includes(k)).map((k) => `${quoteIdent(k)} = EXCLUDED.${quoteIdent(k)}`);
|
|
1045
|
-
if (setParts.length === 0) {
|
|
1046
|
-
sql += " DO NOTHING";
|
|
1047
|
-
} else {
|
|
1048
|
-
sql += ` DO UPDATE SET ${setParts.join(", ")}`;
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
if (returning) {
|
|
1052
|
-
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);
|
|
1053
1338
|
}
|
|
1054
|
-
|
|
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}` },
|
|
1055
1346
|
databaseInstanceId: this.databaseInstanceId,
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
readOnly: false
|
|
1347
|
+
signal: this._signal,
|
|
1348
|
+
idempotencyKey: generateIdempotencyKey()
|
|
1059
1349
|
});
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1350
|
+
if (prefer.includes("return=minimal")) {
|
|
1351
|
+
await parsePostgRESTResponse(response);
|
|
1352
|
+
return [];
|
|
1353
|
+
}
|
|
1354
|
+
return parsePostgRESTResponse(response);
|
|
1064
1355
|
}
|
|
1065
1356
|
};
|
|
1066
1357
|
var PostgrestUpsertReturningBuilder = class {
|
|
@@ -1073,8 +1364,7 @@ var PostgrestUpsertReturningBuilder = class {
|
|
|
1073
1364
|
}
|
|
1074
1365
|
async executeMany() {
|
|
1075
1366
|
return asPostgrestResponse(async () => {
|
|
1076
|
-
|
|
1077
|
-
return res.rows;
|
|
1367
|
+
return this.root.runUpsert("return=representation", this.returning);
|
|
1078
1368
|
});
|
|
1079
1369
|
}
|
|
1080
1370
|
async single() {
|
|
@@ -1083,9 +1373,11 @@ var PostgrestUpsertReturningBuilder = class {
|
|
|
1083
1373
|
if (error) throw error;
|
|
1084
1374
|
const rows = data ?? [];
|
|
1085
1375
|
if (rows.length === 0 || rows.length > 1) {
|
|
1086
|
-
throw new RagableError(
|
|
1087
|
-
|
|
1088
|
-
|
|
1376
|
+
throw new RagableError(
|
|
1377
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1378
|
+
406,
|
|
1379
|
+
{ code: "PGRST116" }
|
|
1380
|
+
);
|
|
1089
1381
|
}
|
|
1090
1382
|
return rows[0];
|
|
1091
1383
|
});
|
|
@@ -1096,23 +1388,25 @@ var PostgrestUpsertReturningBuilder = class {
|
|
|
1096
1388
|
if (error) throw error;
|
|
1097
1389
|
const rows = data ?? [];
|
|
1098
1390
|
if (rows.length > 1) {
|
|
1099
|
-
throw new RagableError(
|
|
1100
|
-
|
|
1101
|
-
|
|
1391
|
+
throw new RagableError(
|
|
1392
|
+
"JSON object requested, multiple (or no) rows returned",
|
|
1393
|
+
406,
|
|
1394
|
+
{ code: "PGRST116" }
|
|
1395
|
+
);
|
|
1102
1396
|
}
|
|
1103
1397
|
return rows[0] ?? null;
|
|
1104
1398
|
});
|
|
1105
1399
|
}
|
|
1106
1400
|
};
|
|
1107
1401
|
var PostgrestTableApi = class {
|
|
1108
|
-
constructor(
|
|
1109
|
-
this.
|
|
1402
|
+
constructor(pgFetch, databaseInstanceId, table) {
|
|
1403
|
+
this.pgFetch = pgFetch;
|
|
1110
1404
|
this.databaseInstanceId = databaseInstanceId;
|
|
1111
1405
|
this.table = table;
|
|
1112
1406
|
}
|
|
1113
1407
|
select(columns = "*") {
|
|
1114
1408
|
return new PostgrestSelectBuilder(
|
|
1115
|
-
this.
|
|
1409
|
+
this.pgFetch,
|
|
1116
1410
|
this.databaseInstanceId,
|
|
1117
1411
|
this.table,
|
|
1118
1412
|
columns
|
|
@@ -1121,7 +1415,7 @@ var PostgrestTableApi = class {
|
|
|
1121
1415
|
insert(values) {
|
|
1122
1416
|
const rows = Array.isArray(values) ? values : [values];
|
|
1123
1417
|
return new PostgrestInsertRootBuilder(
|
|
1124
|
-
this.
|
|
1418
|
+
this.pgFetch,
|
|
1125
1419
|
this.databaseInstanceId,
|
|
1126
1420
|
this.table,
|
|
1127
1421
|
rows
|
|
@@ -1129,7 +1423,7 @@ var PostgrestTableApi = class {
|
|
|
1129
1423
|
}
|
|
1130
1424
|
update(patch) {
|
|
1131
1425
|
return new PostgrestUpdateRootBuilder(
|
|
1132
|
-
this.
|
|
1426
|
+
this.pgFetch,
|
|
1133
1427
|
this.databaseInstanceId,
|
|
1134
1428
|
this.table,
|
|
1135
1429
|
patch
|
|
@@ -1137,19 +1431,15 @@ var PostgrestTableApi = class {
|
|
|
1137
1431
|
}
|
|
1138
1432
|
delete() {
|
|
1139
1433
|
return new PostgrestDeleteRootBuilder(
|
|
1140
|
-
this.
|
|
1434
|
+
this.pgFetch,
|
|
1141
1435
|
this.databaseInstanceId,
|
|
1142
1436
|
this.table
|
|
1143
1437
|
);
|
|
1144
1438
|
}
|
|
1145
|
-
/**
|
|
1146
|
-
* `INSERT ... ON CONFLICT ... DO UPDATE` (or `DO NOTHING` with `ignoreDuplicates`).
|
|
1147
|
-
* @see https://supabase.com/docs/reference/javascript/upsert
|
|
1148
|
-
*/
|
|
1149
1439
|
upsert(values, options) {
|
|
1150
1440
|
const rows = Array.isArray(values) ? values : [values];
|
|
1151
1441
|
return new PostgrestUpsertRootBuilder(
|
|
1152
|
-
this.
|
|
1442
|
+
this.pgFetch,
|
|
1153
1443
|
this.databaseInstanceId,
|
|
1154
1444
|
this.table,
|
|
1155
1445
|
rows,
|
|
@@ -1159,42 +1449,139 @@ var PostgrestTableApi = class {
|
|
|
1159
1449
|
}
|
|
1160
1450
|
};
|
|
1161
1451
|
|
|
1162
|
-
// src/
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
"authGroupId is required for auth and database methods on the browser client"
|
|
1171
|
-
);
|
|
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
|
+
}
|
|
1172
1460
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
throw new Error(
|
|
1179
|
-
"getAccessToken is required for this call (return the end-user access JWT)"
|
|
1180
|
-
);
|
|
1461
|
+
setItem(key, value) {
|
|
1462
|
+
try {
|
|
1463
|
+
globalThis.localStorage.setItem(key, value);
|
|
1464
|
+
} catch {
|
|
1465
|
+
}
|
|
1181
1466
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1467
|
+
removeItem(key) {
|
|
1468
|
+
try {
|
|
1469
|
+
globalThis.localStorage.removeItem(key);
|
|
1470
|
+
} catch {
|
|
1471
|
+
}
|
|
1185
1472
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1473
|
+
};
|
|
1474
|
+
var SessionStorageAdapter = class {
|
|
1475
|
+
getItem(key) {
|
|
1476
|
+
try {
|
|
1477
|
+
return globalThis.sessionStorage.getItem(key);
|
|
1478
|
+
} catch {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1193
1481
|
}
|
|
1194
|
-
|
|
1482
|
+
setItem(key, value) {
|
|
1483
|
+
try {
|
|
1484
|
+
globalThis.sessionStorage.setItem(key, value);
|
|
1485
|
+
} catch {
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
removeItem(key) {
|
|
1489
|
+
try {
|
|
1490
|
+
globalThis.sessionStorage.removeItem(key);
|
|
1491
|
+
} catch {
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
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();
|
|
1195
1548
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
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();
|
|
1198
1585
|
const m = /^(\d+)([smhd])?$/.exec(s);
|
|
1199
1586
|
if (m) {
|
|
1200
1587
|
const n = Number(m[1]);
|
|
@@ -1205,147 +1592,541 @@ function parseExpiresInSeconds(expiresIn) {
|
|
|
1205
1592
|
const asNum = Number(s);
|
|
1206
1593
|
return Number.isFinite(asNum) ? asNum : 0;
|
|
1207
1594
|
}
|
|
1208
|
-
function
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
var RagableBrowserAuthClient = class {
|
|
1218
|
-
constructor(options) {
|
|
1219
|
-
this.options = options;
|
|
1220
|
-
__publicField(this, "fetchImpl");
|
|
1221
|
-
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);
|
|
1222
1602
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1603
|
+
if (!response.ok) {
|
|
1604
|
+
const message = extractErrorMessage(payload, response.statusText);
|
|
1605
|
+
throw new RagableError(message, response.status, payload);
|
|
1225
1606
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
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
|
+
}
|
|
1230
1685
|
}
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
authPrefix() {
|
|
1234
|
-
const gid = requireAuthGroupId(this.options);
|
|
1235
|
-
return `/auth-groups/${gid}/auth`;
|
|
1686
|
+
this.emit("INITIAL_SESSION", this.currentSession);
|
|
1687
|
+
return this.currentSession;
|
|
1236
1688
|
}
|
|
1237
|
-
|
|
1689
|
+
// ── Auth methods ───────────────────────────────────────────────────────────
|
|
1238
1690
|
async signUp(credentials) {
|
|
1239
1691
|
return asPostgrestResponse(async () => {
|
|
1240
1692
|
const name = typeof credentials.options?.data?.name === "string" ? credentials.options.data.name : void 0;
|
|
1241
|
-
const
|
|
1693
|
+
const raw = await this.fetchAuth("/register", "POST", {
|
|
1242
1694
|
email: credentials.email,
|
|
1243
1695
|
password: credentials.password,
|
|
1244
|
-
name
|
|
1696
|
+
...name !== void 0 ? { name } : {}
|
|
1245
1697
|
});
|
|
1246
|
-
|
|
1698
|
+
const session = this.rawToSession(raw);
|
|
1699
|
+
await this.setSessionInternal(session, "SIGNED_IN");
|
|
1700
|
+
return { user: session.user, session };
|
|
1247
1701
|
});
|
|
1248
1702
|
}
|
|
1249
|
-
/** Supabase: `signInWithPassword` */
|
|
1250
1703
|
async signInWithPassword(credentials) {
|
|
1251
1704
|
return asPostgrestResponse(async () => {
|
|
1252
|
-
const
|
|
1253
|
-
|
|
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 };
|
|
1254
1712
|
});
|
|
1255
1713
|
}
|
|
1256
|
-
|
|
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
|
+
}
|
|
1257
1724
|
async refreshSession(refreshToken) {
|
|
1258
1725
|
return asPostgrestResponse(async () => {
|
|
1259
|
-
const
|
|
1260
|
-
|
|
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;
|
|
1261
1749
|
const session = {
|
|
1262
|
-
access_token: tokens.
|
|
1263
|
-
refresh_token: tokens.
|
|
1264
|
-
expires_in:
|
|
1750
|
+
access_token: tokens.access_token,
|
|
1751
|
+
refresh_token: tokens.refresh_token,
|
|
1752
|
+
expires_in: expiresIn,
|
|
1753
|
+
expires_at: nowSeconds() + expiresIn,
|
|
1265
1754
|
token_type: "bearer",
|
|
1266
1755
|
user: me.user
|
|
1267
1756
|
};
|
|
1757
|
+
await this.setSessionInternal(session, "SIGNED_IN");
|
|
1268
1758
|
return { session, user: me.user };
|
|
1269
1759
|
});
|
|
1270
1760
|
}
|
|
1271
|
-
/** Supabase: `getUser()` — needs `getAccessToken` on the client */
|
|
1272
|
-
async getUser() {
|
|
1273
|
-
return asPostgrestResponse(() => this.getMe());
|
|
1274
|
-
}
|
|
1275
|
-
/** Supabase: `updateUser` */
|
|
1276
1761
|
async updateUser(attributes) {
|
|
1277
|
-
return asPostgrestResponse(
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
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
|
+
});
|
|
1283
1776
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
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 } } };
|
|
1290
1786
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
});
|
|
1298
|
-
return parseJsonOrThrow(response);
|
|
1787
|
+
// ── Accessors ──────────────────────────────────────────────────────────────
|
|
1788
|
+
getAccessToken() {
|
|
1789
|
+
return this.currentSession?.access_token ?? null;
|
|
1790
|
+
}
|
|
1791
|
+
getCurrentSession() {
|
|
1792
|
+
return this.currentSession;
|
|
1299
1793
|
}
|
|
1794
|
+
// ── Back-compat: raw Ragable auth methods ──────────────────────────────────
|
|
1300
1795
|
async register(body) {
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
});
|
|
1306
|
-
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;
|
|
1307
1800
|
}
|
|
1308
1801
|
async login(body) {
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
});
|
|
1314
|
-
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;
|
|
1315
1806
|
}
|
|
1316
1807
|
async refresh(body) {
|
|
1317
|
-
|
|
1318
|
-
method: "POST",
|
|
1319
|
-
headers: this.baseHeaders(true),
|
|
1320
|
-
body: JSON.stringify(body)
|
|
1321
|
-
});
|
|
1322
|
-
return parseJsonOrThrow(response);
|
|
1808
|
+
return this.fetchAuth("/refresh", "POST", body);
|
|
1323
1809
|
}
|
|
1324
1810
|
async getMe() {
|
|
1325
|
-
const token =
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
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
|
|
1331
1853
|
});
|
|
1332
1854
|
return parseJsonOrThrow(response);
|
|
1333
1855
|
}
|
|
1334
|
-
async
|
|
1335
|
-
const
|
|
1336
|
-
const headers = this.baseHeaders(true);
|
|
1856
|
+
async fetchAuthWithBearer(path, method, token, body) {
|
|
1857
|
+
const headers = this.baseHeaders(body !== void 0);
|
|
1337
1858
|
headers.set("Authorization", `Bearer ${token}`);
|
|
1338
|
-
const response = await this.fetchImpl(
|
|
1339
|
-
method
|
|
1859
|
+
const response = await this.fetchImpl(this.toUrl(path), {
|
|
1860
|
+
method,
|
|
1340
1861
|
headers,
|
|
1341
|
-
body: JSON.stringify(body)
|
|
1862
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
1342
1863
|
});
|
|
1343
1864
|
return parseJsonOrThrow(response);
|
|
1344
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
|
+
}
|
|
1345
2125
|
};
|
|
1346
2126
|
var RagableBrowserDatabaseClient = class {
|
|
1347
|
-
constructor(options) {
|
|
2127
|
+
constructor(options, ragableAuth = null) {
|
|
1348
2128
|
this.options = options;
|
|
2129
|
+
this.ragableAuth = ragableAuth;
|
|
1349
2130
|
__publicField(this, "fetchImpl");
|
|
1350
2131
|
this.fetchImpl = bindFetch(options.fetch);
|
|
1351
2132
|
}
|
|
@@ -1354,7 +2135,7 @@ var RagableBrowserDatabaseClient = class {
|
|
|
1354
2135
|
}
|
|
1355
2136
|
async query(params) {
|
|
1356
2137
|
const gid = requireAuthGroupId(this.options);
|
|
1357
|
-
const token = await
|
|
2138
|
+
const token = await resolveDatabaseAuthBearer(this.options, this.ragableAuth);
|
|
1358
2139
|
const databaseInstanceId = params.databaseInstanceId?.trim() || this.options.databaseInstanceId?.trim();
|
|
1359
2140
|
if (!databaseInstanceId) {
|
|
1360
2141
|
throw new Error(
|
|
@@ -1364,6 +2145,7 @@ var RagableBrowserDatabaseClient = class {
|
|
|
1364
2145
|
const headers = this.baseHeaders();
|
|
1365
2146
|
headers.set("Authorization", `Bearer ${token}`);
|
|
1366
2147
|
headers.set("Content-Type", "application/json");
|
|
2148
|
+
const readOnly = (this.options.dataAuth ?? "user") === "publicAnon" ? true : params.readOnly !== false;
|
|
1367
2149
|
const response = await this.fetchImpl(
|
|
1368
2150
|
this.toUrl(`/auth-groups/${gid}/data/query`),
|
|
1369
2151
|
{
|
|
@@ -1373,13 +2155,13 @@ var RagableBrowserDatabaseClient = class {
|
|
|
1373
2155
|
databaseInstanceId,
|
|
1374
2156
|
sql: params.sql,
|
|
1375
2157
|
...params.params !== void 0 ? { params: params.params } : {},
|
|
1376
|
-
|
|
2158
|
+
readOnly,
|
|
1377
2159
|
...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
|
|
1378
2160
|
...params.rowLimit !== void 0 ? { rowLimit: params.rowLimit } : {}
|
|
1379
2161
|
})
|
|
1380
2162
|
}
|
|
1381
2163
|
);
|
|
1382
|
-
return
|
|
2164
|
+
return parseJsonOrThrow2(response);
|
|
1383
2165
|
}
|
|
1384
2166
|
baseHeaders() {
|
|
1385
2167
|
return new Headers(this.options.headers);
|
|
@@ -1394,9 +2176,6 @@ var RagableBrowserAgentsClient = class {
|
|
|
1394
2176
|
toUrl(path) {
|
|
1395
2177
|
return `${normalizeBrowserApiBase(this.options.baseUrl)}${path.startsWith("/") ? path : `/${path}`}`;
|
|
1396
2178
|
}
|
|
1397
|
-
/**
|
|
1398
|
-
* Stream agent execution as SSE (`POST /public/organizations/:orgId/agents/:agentId/chat/stream`).
|
|
1399
|
-
*/
|
|
1400
2179
|
async *chatStream(agentId, params) {
|
|
1401
2180
|
const orgId = this.options.organizationId;
|
|
1402
2181
|
const body = {
|
|
@@ -1432,16 +2211,40 @@ var RagableBrowser = class {
|
|
|
1432
2211
|
__publicField(this, "agents");
|
|
1433
2212
|
__publicField(this, "auth");
|
|
1434
2213
|
__publicField(this, "database");
|
|
2214
|
+
__publicField(this, "transport");
|
|
1435
2215
|
__publicField(this, "options");
|
|
2216
|
+
__publicField(this, "_ragableAuth");
|
|
1436
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
|
+
}
|
|
1437
2244
|
this.agents = new RagableBrowserAgentsClient(options);
|
|
1438
|
-
this.auth = new RagableBrowserAuthClient(options);
|
|
1439
|
-
this.database = new RagableBrowserDatabaseClient(options);
|
|
2245
|
+
this.auth = new RagableBrowserAuthClient(options, this._ragableAuth);
|
|
2246
|
+
this.database = new RagableBrowserDatabaseClient(options, this._ragableAuth);
|
|
1440
2247
|
}
|
|
1441
|
-
/**
|
|
1442
|
-
* Supabase-style table API: `.from('items').select().eq('id', 1).single()`.
|
|
1443
|
-
* Pass `databaseInstanceId` here or set `databaseInstanceId` on the client options.
|
|
1444
|
-
*/
|
|
1445
2248
|
from(table, databaseInstanceId) {
|
|
1446
2249
|
const id = databaseInstanceId?.trim() || this.options.databaseInstanceId?.trim();
|
|
1447
2250
|
if (!id) {
|
|
@@ -1449,8 +2252,40 @@ var RagableBrowser = class {
|
|
|
1449
2252
|
"RagableBrowser.from() requires databaseInstanceId in client options or as the second argument"
|
|
1450
2253
|
);
|
|
1451
2254
|
}
|
|
1452
|
-
const
|
|
1453
|
-
|
|
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();
|
|
1454
2289
|
}
|
|
1455
2290
|
};
|
|
1456
2291
|
function createBrowserClient(options) {
|
|
@@ -1549,6 +2384,11 @@ function createRagableServerClient(options) {
|
|
|
1549
2384
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1550
2385
|
0 && (module.exports = {
|
|
1551
2386
|
AgentsClient,
|
|
2387
|
+
AuthBroadcastChannel,
|
|
2388
|
+
CookieStorageAdapter,
|
|
2389
|
+
DEFAULT_RAGABLE_API_BASE,
|
|
2390
|
+
LocalStorageAdapter,
|
|
2391
|
+
MemoryStorageAdapter,
|
|
1552
2392
|
PostgrestDeleteReturningBuilder,
|
|
1553
2393
|
PostgrestDeleteRootBuilder,
|
|
1554
2394
|
PostgrestInsertReturningBuilder,
|
|
@@ -1560,13 +2400,20 @@ function createRagableServerClient(options) {
|
|
|
1560
2400
|
PostgrestUpsertReturningBuilder,
|
|
1561
2401
|
PostgrestUpsertRootBuilder,
|
|
1562
2402
|
Ragable,
|
|
2403
|
+
RagableAbortError,
|
|
2404
|
+
RagableAuth,
|
|
1563
2405
|
RagableBrowser,
|
|
1564
2406
|
RagableBrowserAgentsClient,
|
|
1565
2407
|
RagableBrowserAuthClient,
|
|
1566
2408
|
RagableBrowserDatabaseClient,
|
|
1567
2409
|
RagableError,
|
|
2410
|
+
RagableNetworkError,
|
|
1568
2411
|
RagableRequestClient,
|
|
2412
|
+
RagableSdkError,
|
|
2413
|
+
RagableTimeoutError,
|
|
2414
|
+
SessionStorageAdapter,
|
|
1569
2415
|
ShiftClient,
|
|
2416
|
+
Transport,
|
|
1570
2417
|
asPostgrestResponse,
|
|
1571
2418
|
bindFetch,
|
|
1572
2419
|
createBrowserClient,
|
|
@@ -1574,10 +2421,13 @@ function createRagableServerClient(options) {
|
|
|
1574
2421
|
createRagPipeline,
|
|
1575
2422
|
createRagableBrowserClient,
|
|
1576
2423
|
createRagableServerClient,
|
|
2424
|
+
detectStorage,
|
|
1577
2425
|
extractErrorMessage,
|
|
1578
2426
|
formatRetrievalContext,
|
|
2427
|
+
generateIdempotencyKey,
|
|
1579
2428
|
normalizeBrowserApiBase,
|
|
1580
2429
|
parseSseDataLine,
|
|
2430
|
+
parseTransportResponse,
|
|
1581
2431
|
readSseStream
|
|
1582
2432
|
});
|
|
1583
2433
|
//# sourceMappingURL=index.js.map
|