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