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