@riverbankcms/sdk 0.7.0 → 0.7.3
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/README.md +229 -0
- package/dist/cli/index.js +42 -95
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init-docs/content/agents-section.md +50 -0
- package/dist/cli/init-docs/content/cli-reference.md +574 -0
- package/dist/cli/init-docs/content/content-management.md +384 -0
- package/dist/cli/init-docs/content/context-brand.md +125 -0
- package/dist/cli/init-docs/content/context-brief.md +77 -0
- package/dist/cli/init-docs/content/context-knowledge.md +111 -0
- package/dist/cli/init-docs/content/getting-started.md +130 -0
- package/dist/cli/init-docs/content/site-workflows-readme.md +96 -0
- package/dist/cli/init-docs/content/workflow-add-block.md +228 -0
- package/dist/cli/init-docs/content/workflow-create-page.md +193 -0
- package/dist/cli/init-docs/content/workflow-publish.md +280 -0
- package/dist/client/bookings.d.mts +2 -0
- package/dist/client/bookings.d.ts +2 -0
- package/dist/client/bookings.js +2956 -104
- package/dist/client/bookings.js.map +1 -1
- package/dist/client/bookings.mjs +2929 -70
- package/dist/client/bookings.mjs.map +1 -1
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +602 -68
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +602 -68
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +2 -2
- package/dist/client/hooks.d.ts +2 -2
- package/dist/client/rendering/client.js +3070 -259
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +3212 -395
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/spam-protection.d.mts +55 -0
- package/dist/client/spam-protection.d.ts +55 -0
- package/dist/client/spam-protection.js +2915 -0
- package/dist/client/spam-protection.js.map +1 -0
- package/dist/client/spam-protection.mjs +2893 -0
- package/dist/client/spam-protection.mjs.map +1 -0
- package/dist/client/{usePage-BiOReg0_.d.ts → usePage-BYmJCCm1.d.ts} +132 -11
- package/dist/client/{usePage-BXjk8BhD.d.mts → usePage-DZtrWajy.d.mts} +132 -11
- package/dist/server/{Layout-wBtJLTVX.d.ts → Layout-Yluyb6sK.d.ts} +1 -1
- package/dist/server/{Layout-B7cvis7r.d.mts → Layout-qWLdVm5-.d.mts} +1 -1
- package/dist/server/chunk-2IZ6S225.js +122 -0
- package/dist/server/chunk-2IZ6S225.js.map +1 -0
- package/dist/server/chunk-4CV4JOE5.js +27 -0
- package/dist/server/chunk-4CV4JOE5.js.map +1 -0
- package/dist/server/chunk-5LRR64Y6.mjs +72 -0
- package/dist/server/chunk-5LRR64Y6.mjs.map +1 -0
- package/dist/server/chunk-NBTRDLCM.js +72 -0
- package/dist/server/chunk-NBTRDLCM.js.map +1 -0
- package/dist/server/chunk-NFEGQTCC.mjs +27 -0
- package/dist/server/{chunk-7FIJSGHU.mjs → chunk-NFQLH5IA.mjs} +856 -74
- package/dist/server/chunk-NFQLH5IA.mjs.map +1 -0
- package/dist/server/chunk-PPHZV6YD.mjs +122 -0
- package/dist/server/chunk-PPHZV6YD.mjs.map +1 -0
- package/dist/server/{chunk-P7UVAMK6.js → chunk-VLXTNB2C.js} +866 -84
- package/dist/server/chunk-VLXTNB2C.js.map +1 -0
- package/dist/server/{components-CMMwDXTW.d.mts → components-DNHfSCML.d.mts} +3 -3
- package/dist/server/{components-CICSJyp_.d.ts → components-Di5ME6He.d.ts} +3 -3
- package/dist/server/components.d.mts +5 -5
- package/dist/server/components.d.ts +5 -5
- package/dist/server/components.js +1 -1
- package/dist/server/components.mjs +1 -1
- package/dist/server/config-validation.js +1 -1
- package/dist/server/config-validation.mjs +1 -1
- package/dist/server/config.js +1 -1
- package/dist/server/config.mjs +1 -1
- package/dist/server/data.d.mts +2 -2
- package/dist/server/data.d.ts +2 -2
- package/dist/server/data.js +1 -1
- package/dist/server/data.mjs +1 -1
- package/dist/server/env.d.mts +109 -0
- package/dist/server/env.d.ts +109 -0
- package/dist/server/env.js +14 -0
- package/dist/server/env.js.map +1 -0
- package/dist/server/env.mjs +14 -0
- package/dist/server/{index-DI_qlYx3.d.mts → index--Oyunk_B.d.mts} +2 -2
- package/dist/server/{index-BTwWvSBu.d.ts → index-C9Ra8dza.d.ts} +2 -2
- package/dist/server/{index-Bucs6UqG.d.mts → index-Clm3skz_.d.mts} +1 -1
- package/dist/server/{index-Cp7tJuRt.d.ts → index-DLvNddi-.d.ts} +1 -1
- package/dist/server/index.d.mts +216 -5
- package/dist/server/index.d.ts +216 -5
- package/dist/server/index.js +301 -4
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +301 -4
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/{loadContent-DmgpFcFC.d.ts → loadContent-D7LQwI0o.d.ts} +3 -3
- package/dist/server/{loadContent-C-YYUKQa.d.mts → loadContent-DVfuBLiZ.d.mts} +3 -3
- package/dist/server/{loadPage-IDGVDFBB.js → loadPage-AXNAERDS.js} +2 -2
- package/dist/server/{loadPage-IDGVDFBB.js.map → loadPage-AXNAERDS.js.map} +1 -1
- package/dist/server/{loadPage-DP3nrHBi.d.ts → loadPage-BmYJCe_V.d.ts} +2 -2
- package/dist/server/{loadPage-B8mQUUSo.d.mts → loadPage-BucnLHmE.d.mts} +2 -2
- package/dist/server/{loadPage-DNQTTRHL.mjs → loadPage-XR7ORQ2E.mjs} +2 -2
- package/dist/server/loadPage-XR7ORQ2E.mjs.map +1 -0
- package/dist/server/metadata.d.mts +4 -4
- package/dist/server/metadata.d.ts +4 -4
- package/dist/server/metadata.js +1 -1
- package/dist/server/metadata.mjs +1 -1
- package/dist/server/navigation.d.mts +2 -2
- package/dist/server/navigation.d.ts +2 -2
- package/dist/server/navigation.js +1 -1
- package/dist/server/navigation.mjs +1 -1
- package/dist/server/next/revalidate.d.mts +66 -0
- package/dist/server/next/revalidate.d.ts +66 -0
- package/dist/server/next/revalidate.js +60 -0
- package/dist/server/next/revalidate.js.map +1 -0
- package/dist/server/next/revalidate.mjs +60 -0
- package/dist/server/next/revalidate.mjs.map +1 -0
- package/dist/server/next/tags.d.mts +81 -0
- package/dist/server/next/tags.d.ts +81 -0
- package/dist/server/next/tags.js +36 -0
- package/dist/server/next/tags.js.map +1 -0
- package/dist/server/next/tags.mjs +36 -0
- package/dist/server/next/tags.mjs.map +1 -0
- package/dist/server/next.d.mts +164 -6
- package/dist/server/next.d.ts +164 -6
- package/dist/server/next.js +79 -11
- package/dist/server/next.js.map +1 -1
- package/dist/server/next.mjs +76 -8
- package/dist/server/next.mjs.map +1 -1
- package/dist/server/rendering/server.d.mts +4 -4
- package/dist/server/rendering/server.d.ts +4 -4
- package/dist/server/rendering/server.js +1 -1
- package/dist/server/rendering/server.mjs +1 -1
- package/dist/server/rendering.d.mts +7 -7
- package/dist/server/rendering.d.ts +7 -7
- package/dist/server/rendering.js +3 -3
- package/dist/server/rendering.js.map +1 -1
- package/dist/server/rendering.mjs +4 -4
- package/dist/server/routing.d.mts +3 -3
- package/dist/server/routing.d.ts +3 -3
- package/dist/server/routing.js +2 -2
- package/dist/server/routing.mjs +2 -2
- package/dist/server/server.d.mts +5 -5
- package/dist/server/server.d.ts +5 -5
- package/dist/server/server.js +5 -5
- package/dist/server/server.js.map +1 -1
- package/dist/server/server.mjs +5 -5
- package/dist/server/theme-bridge.js +1 -1
- package/dist/server/theme-bridge.mjs +1 -1
- package/dist/server/theme.js +1 -1
- package/dist/server/theme.mjs +1 -1
- package/dist/server/{types-BvcJU7zk.d.ts → types-BRQyLrQU.d.ts} +132 -11
- package/dist/server/{types-Dsu9wsUh.d.mts → types-BSV6Vc-P.d.mts} +2 -2
- package/dist/server/{types-1cLz0vnq.d.mts → types-C-LShyIg.d.mts} +132 -11
- package/dist/server/{types-CVykEqXN.d.ts → types-Dt98DeYa.d.ts} +2 -2
- package/dist/server/webhooks.d.mts +81 -0
- package/dist/server/webhooks.d.ts +81 -0
- package/dist/server/webhooks.js +12 -0
- package/dist/server/webhooks.js.map +1 -0
- package/dist/server/webhooks.mjs +12 -0
- package/dist/server/webhooks.mjs.map +1 -0
- package/package.json +29 -3
- package/dist/client/resolver-BhueZVxZ.d.mts +0 -61
- package/dist/client/resolver-BhueZVxZ.d.ts +0 -61
- package/dist/client/usePage--fGlyrgj.d.mts +0 -6439
- package/dist/client/usePage-BBcFCxOU.d.ts +0 -6297
- package/dist/client/usePage-BC8Q2E3t.d.mts +0 -6431
- package/dist/client/usePage-BTPnCuWC.d.mts +0 -6511
- package/dist/client/usePage-BafOS9UT.d.mts +0 -6512
- package/dist/client/usePage-BcjWPXvh.d.mts +0 -6388
- package/dist/client/usePage-Bnx-kA6x.d.mts +0 -6670
- package/dist/client/usePage-BvKAa3Zw.d.mts +0 -366
- package/dist/client/usePage-BvKAa3Zw.d.ts +0 -366
- package/dist/client/usePage-BydHcMYB.d.mts +0 -6297
- package/dist/client/usePage-C3ZKNwY7.d.mts +0 -6393
- package/dist/client/usePage-CE7X5NcN.d.ts +0 -6439
- package/dist/client/usePage-CHEybPMD.d.ts +0 -6429
- package/dist/client/usePage-CrKw1H6Y.d.ts +0 -6338
- package/dist/client/usePage-CyYpOJud.d.ts +0 -6388
- package/dist/client/usePage-D4fxZbRR.d.mts +0 -6429
- package/dist/client/usePage-DMI8ImsU.d.mts +0 -6338
- package/dist/client/usePage-DoPI6b8V.d.ts +0 -6511
- package/dist/client/usePage-DpRNZUtP.d.ts +0 -6431
- package/dist/client/usePage-QNWArrVO.d.ts +0 -6670
- package/dist/client/usePage-fBgPB6Oq.d.ts +0 -6512
- package/dist/client/usePage-gpVaeWDy.d.ts +0 -6393
- package/dist/server/chunk-7FIJSGHU.mjs.map +0 -1
- package/dist/server/chunk-BJTO5JO5.mjs +0 -11
- package/dist/server/chunk-DGUM43GV.js +0 -11
- package/dist/server/chunk-DGUM43GV.js.map +0 -1
- package/dist/server/chunk-P7UVAMK6.js.map +0 -1
- /package/dist/server/{chunk-BJTO5JO5.mjs.map → chunk-NFEGQTCC.mjs.map} +0 -0
- /package/dist/server/{loadPage-DNQTTRHL.mjs.map → env.mjs.map} +0 -0
package/dist/client/client.mjs
CHANGED
|
@@ -17296,10 +17296,10 @@ function resolveApiBaseUrl() {
|
|
|
17296
17296
|
var revalidateTag = null;
|
|
17297
17297
|
if (typeof window === "undefined") {
|
|
17298
17298
|
try {
|
|
17299
|
-
const
|
|
17300
|
-
|
|
17299
|
+
const dynamicRequire = new Function("modulePath", "return require(modulePath)");
|
|
17300
|
+
const nextCache = dynamicRequire("next/cache");
|
|
17301
|
+
revalidateTag = nextCache.revalidateTag ?? null;
|
|
17301
17302
|
} catch {
|
|
17302
|
-
revalidateTag = null;
|
|
17303
17303
|
}
|
|
17304
17304
|
}
|
|
17305
17305
|
var sdkVersion;
|
|
@@ -17328,8 +17328,22 @@ var ApiRequestError = class extends Error {
|
|
|
17328
17328
|
this.body = options.body;
|
|
17329
17329
|
this.cause = options.cause;
|
|
17330
17330
|
this.errorCode = options.errorCode;
|
|
17331
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
17331
17332
|
}
|
|
17332
17333
|
};
|
|
17334
|
+
function parseRetryAfterHeader(headerValue) {
|
|
17335
|
+
if (!headerValue) return void 0;
|
|
17336
|
+
if (/^\d+$/.test(headerValue)) {
|
|
17337
|
+
const seconds = parseInt(headerValue, 10);
|
|
17338
|
+
return seconds * 1e3;
|
|
17339
|
+
}
|
|
17340
|
+
const date = new Date(headerValue);
|
|
17341
|
+
if (!isNaN(date.getTime())) {
|
|
17342
|
+
const delayMs = date.getTime() - Date.now();
|
|
17343
|
+
return delayMs > 0 ? delayMs : void 0;
|
|
17344
|
+
}
|
|
17345
|
+
return void 0;
|
|
17346
|
+
}
|
|
17333
17347
|
function buildEndpointURL(baseURL, endpoint) {
|
|
17334
17348
|
return baseURL + API_ENDPOINTS[endpoint].path;
|
|
17335
17349
|
}
|
|
@@ -17546,6 +17560,7 @@ function createParsedClient(rawClient) {
|
|
|
17546
17560
|
if (!response.ok) {
|
|
17547
17561
|
const body = await parseErrorBody(response);
|
|
17548
17562
|
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
17563
|
+
const retryAfterMs = parseRetryAfterHeader(response.headers.get("retry-after"));
|
|
17549
17564
|
throw new ApiRequestError(
|
|
17550
17565
|
`Request to ${String(endpoint)} failed with status ${response.status}`,
|
|
17551
17566
|
{
|
|
@@ -17554,7 +17569,8 @@ function createParsedClient(rawClient) {
|
|
|
17554
17569
|
method: config.method,
|
|
17555
17570
|
auth,
|
|
17556
17571
|
requestId,
|
|
17557
|
-
body
|
|
17572
|
+
body,
|
|
17573
|
+
retryAfterMs
|
|
17558
17574
|
}
|
|
17559
17575
|
);
|
|
17560
17576
|
}
|
|
@@ -17602,38 +17618,117 @@ var SimpleCache = class {
|
|
|
17602
17618
|
this.cache = /* @__PURE__ */ new Map();
|
|
17603
17619
|
this.maxSize = options.maxSize ?? 100;
|
|
17604
17620
|
this.ttl = options.ttl ?? 3e5;
|
|
17621
|
+
this.staleTtl = options.staleTtl ?? 3e5;
|
|
17605
17622
|
}
|
|
17606
|
-
|
|
17623
|
+
/**
|
|
17624
|
+
* Get a fresh value (within TTL)
|
|
17625
|
+
* @returns The value if fresh, null otherwise
|
|
17626
|
+
*/
|
|
17627
|
+
getFresh(key) {
|
|
17607
17628
|
const entry = this.cache.get(key);
|
|
17608
|
-
if (!entry) return
|
|
17609
|
-
|
|
17629
|
+
if (!entry) return null;
|
|
17630
|
+
const now = Date.now();
|
|
17631
|
+
if (now <= entry.freshUntil) {
|
|
17632
|
+
return entry.value;
|
|
17633
|
+
}
|
|
17634
|
+
return null;
|
|
17635
|
+
}
|
|
17636
|
+
/**
|
|
17637
|
+
* Get a value that may be stale (past TTL but within staleTtl)
|
|
17638
|
+
* @returns Object with value and stale age, or null if expired
|
|
17639
|
+
*/
|
|
17640
|
+
getStale(key) {
|
|
17641
|
+
const entry = this.cache.get(key);
|
|
17642
|
+
if (!entry) return null;
|
|
17643
|
+
const now = Date.now();
|
|
17644
|
+
if (now > entry.staleUntil) {
|
|
17610
17645
|
this.cache.delete(key);
|
|
17611
|
-
return
|
|
17646
|
+
return null;
|
|
17612
17647
|
}
|
|
17613
|
-
|
|
17648
|
+
const staleAgeSec = now <= entry.freshUntil ? 0 : Math.floor((now - entry.freshUntil) / 1e3);
|
|
17649
|
+
return {
|
|
17650
|
+
value: entry.value,
|
|
17651
|
+
staleAgeSec
|
|
17652
|
+
};
|
|
17614
17653
|
}
|
|
17615
|
-
|
|
17616
|
-
|
|
17617
|
-
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17654
|
+
/**
|
|
17655
|
+
* Store a value with TTL and stale window
|
|
17656
|
+
*/
|
|
17657
|
+
set(key, value, options) {
|
|
17658
|
+
const ttl = options?.ttl ?? this.ttl;
|
|
17659
|
+
const staleTtl = options?.staleTtl ?? this.staleTtl;
|
|
17660
|
+
const now = Date.now();
|
|
17661
|
+
while (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
17662
|
+
this.evictOne(now);
|
|
17621
17663
|
}
|
|
17622
17664
|
this.cache.set(key, {
|
|
17623
17665
|
value,
|
|
17624
|
-
|
|
17666
|
+
createdAt: now,
|
|
17667
|
+
freshUntil: now + ttl,
|
|
17668
|
+
staleUntil: now + ttl + staleTtl
|
|
17625
17669
|
});
|
|
17626
17670
|
}
|
|
17671
|
+
/**
|
|
17672
|
+
* Evict one entry to make room for a new one
|
|
17673
|
+
* Priority: oldest stale entry, then oldest fresh entry
|
|
17674
|
+
*/
|
|
17675
|
+
evictOne(now) {
|
|
17676
|
+
let oldestStaleKey = null;
|
|
17677
|
+
let oldestStaleTime = Infinity;
|
|
17678
|
+
let oldestFreshKey = null;
|
|
17679
|
+
let oldestFreshTime = Infinity;
|
|
17680
|
+
for (const [key, entry] of this.cache) {
|
|
17681
|
+
if (now > entry.freshUntil) {
|
|
17682
|
+
if (entry.createdAt < oldestStaleTime) {
|
|
17683
|
+
oldestStaleTime = entry.createdAt;
|
|
17684
|
+
oldestStaleKey = key;
|
|
17685
|
+
}
|
|
17686
|
+
} else {
|
|
17687
|
+
if (entry.createdAt < oldestFreshTime) {
|
|
17688
|
+
oldestFreshTime = entry.createdAt;
|
|
17689
|
+
oldestFreshKey = key;
|
|
17690
|
+
}
|
|
17691
|
+
}
|
|
17692
|
+
}
|
|
17693
|
+
const keyToEvict = oldestStaleKey ?? oldestFreshKey;
|
|
17694
|
+
if (keyToEvict) {
|
|
17695
|
+
this.cache.delete(keyToEvict);
|
|
17696
|
+
}
|
|
17697
|
+
}
|
|
17698
|
+
/**
|
|
17699
|
+
* Remove all fully expired entries (past staleUntil)
|
|
17700
|
+
*/
|
|
17701
|
+
prune() {
|
|
17702
|
+
const now = Date.now();
|
|
17703
|
+
for (const [key, entry] of this.cache) {
|
|
17704
|
+
if (now > entry.staleUntil) {
|
|
17705
|
+
this.cache.delete(key);
|
|
17706
|
+
}
|
|
17707
|
+
}
|
|
17708
|
+
}
|
|
17709
|
+
/**
|
|
17710
|
+
* Clear all entries
|
|
17711
|
+
*/
|
|
17627
17712
|
clear() {
|
|
17628
17713
|
this.cache.clear();
|
|
17629
17714
|
}
|
|
17715
|
+
/**
|
|
17716
|
+
* Check if a key exists and is not fully expired
|
|
17717
|
+
*/
|
|
17630
17718
|
has(key) {
|
|
17631
|
-
|
|
17719
|
+
const entry = this.cache.get(key);
|
|
17720
|
+
if (!entry) return false;
|
|
17721
|
+
const now = Date.now();
|
|
17722
|
+
if (now > entry.staleUntil) {
|
|
17723
|
+
this.cache.delete(key);
|
|
17724
|
+
return false;
|
|
17725
|
+
}
|
|
17726
|
+
return now <= entry.freshUntil;
|
|
17632
17727
|
}
|
|
17633
17728
|
};
|
|
17634
17729
|
|
|
17635
17730
|
// src/version.ts
|
|
17636
|
-
var SDK_VERSION = "0.7.
|
|
17731
|
+
var SDK_VERSION = "0.7.3";
|
|
17637
17732
|
|
|
17638
17733
|
// src/client/error.ts
|
|
17639
17734
|
var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
@@ -17645,8 +17740,31 @@ var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
|
17645
17740
|
this.status = apiError.status;
|
|
17646
17741
|
this.fieldErrors = apiError.fieldErrors;
|
|
17647
17742
|
this.timestamp = apiError.timestamp;
|
|
17743
|
+
this.retryAfterMs = "retryAfterMs" in apiError ? apiError.retryAfterMs : void 0;
|
|
17744
|
+
this.isRetryable = this.computeRetryable();
|
|
17648
17745
|
Object.setPrototypeOf(this, _RiverbankApiError.prototype);
|
|
17649
17746
|
}
|
|
17747
|
+
/**
|
|
17748
|
+
* Compute whether this error is retryable based on HTTP status code.
|
|
17749
|
+
* - 0 (network errors - no HTTP response): retryable
|
|
17750
|
+
* - 429 (rate limit): retryable
|
|
17751
|
+
* - 5xx (server errors): retryable
|
|
17752
|
+
* - 4xx (client errors, except 429): NOT retryable
|
|
17753
|
+
*/
|
|
17754
|
+
computeRetryable() {
|
|
17755
|
+
if (this.status === 0) return true;
|
|
17756
|
+
if (this.status === 429) return true;
|
|
17757
|
+
if (this.status >= 500) return true;
|
|
17758
|
+
return false;
|
|
17759
|
+
}
|
|
17760
|
+
/**
|
|
17761
|
+
* Check if this is a network error (no HTTP response received)
|
|
17762
|
+
*
|
|
17763
|
+
* Matches: network:connection_error, network:timeout, network:dns_error
|
|
17764
|
+
*/
|
|
17765
|
+
isNetworkError() {
|
|
17766
|
+
return this.code.startsWith("network:");
|
|
17767
|
+
}
|
|
17650
17768
|
/**
|
|
17651
17769
|
* Check if this error matches a specific error code
|
|
17652
17770
|
*
|
|
@@ -17705,9 +17823,246 @@ var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
|
17705
17823
|
}
|
|
17706
17824
|
};
|
|
17707
17825
|
|
|
17826
|
+
// src/client/resilience.ts
|
|
17827
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
17828
|
+
maxAttempts: 3,
|
|
17829
|
+
baseDelayMs: 200,
|
|
17830
|
+
maxDelayMs: 2e3,
|
|
17831
|
+
jitter: "full"
|
|
17832
|
+
};
|
|
17833
|
+
var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
17834
|
+
failureThreshold: 5,
|
|
17835
|
+
resetTimeoutMs: 3e4,
|
|
17836
|
+
halfOpenMaxRequests: 2
|
|
17837
|
+
};
|
|
17838
|
+
function isTransientError(error) {
|
|
17839
|
+
if (error instanceof RiverbankApiError) {
|
|
17840
|
+
if (error.status === 0) return true;
|
|
17841
|
+
if (error.status === 429) return true;
|
|
17842
|
+
if (error.status >= 500) return true;
|
|
17843
|
+
return false;
|
|
17844
|
+
}
|
|
17845
|
+
return true;
|
|
17846
|
+
}
|
|
17847
|
+
function calculateBackoff(attempt, config) {
|
|
17848
|
+
const baseDelayMs = config.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs;
|
|
17849
|
+
const maxDelayMs = config.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs;
|
|
17850
|
+
const jitter = config.jitter ?? DEFAULT_RETRY_CONFIG.jitter;
|
|
17851
|
+
const exponential = baseDelayMs * Math.pow(2, attempt - 1);
|
|
17852
|
+
const capped = Math.min(exponential, maxDelayMs);
|
|
17853
|
+
if (jitter === "full") {
|
|
17854
|
+
return Math.random() * capped;
|
|
17855
|
+
}
|
|
17856
|
+
return capped;
|
|
17857
|
+
}
|
|
17858
|
+
var CircuitBreaker = class {
|
|
17859
|
+
constructor(config) {
|
|
17860
|
+
this.state = "closed";
|
|
17861
|
+
this.failureCount = 0;
|
|
17862
|
+
this.successCount = 0;
|
|
17863
|
+
this.openUntil = 0;
|
|
17864
|
+
this.halfOpenRequests = 0;
|
|
17865
|
+
this.config = {
|
|
17866
|
+
failureThreshold: config?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
|
|
17867
|
+
resetTimeoutMs: config?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
|
|
17868
|
+
halfOpenMaxRequests: config?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
|
|
17869
|
+
};
|
|
17870
|
+
}
|
|
17871
|
+
/**
|
|
17872
|
+
* Check if circuit is open (requests should be blocked)
|
|
17873
|
+
* Also handles automatic transition from open to half-open after timeout
|
|
17874
|
+
*/
|
|
17875
|
+
isOpen() {
|
|
17876
|
+
if (this.state === "open" && Date.now() >= this.openUntil) {
|
|
17877
|
+
this.transitionTo("half-open");
|
|
17878
|
+
}
|
|
17879
|
+
return this.state === "open";
|
|
17880
|
+
}
|
|
17881
|
+
/**
|
|
17882
|
+
* Check if a request can be attempted
|
|
17883
|
+
* - closed: always yes
|
|
17884
|
+
* - open: always no
|
|
17885
|
+
* - half-open: limited number of probes
|
|
17886
|
+
*/
|
|
17887
|
+
canAttempt() {
|
|
17888
|
+
if (this.state === "closed") return true;
|
|
17889
|
+
if (this.state === "open") return false;
|
|
17890
|
+
return this.halfOpenRequests < this.config.halfOpenMaxRequests;
|
|
17891
|
+
}
|
|
17892
|
+
/**
|
|
17893
|
+
* Increment half-open request counter (call before making request in half-open)
|
|
17894
|
+
*/
|
|
17895
|
+
incrementHalfOpenRequests() {
|
|
17896
|
+
if (this.state === "half-open") {
|
|
17897
|
+
this.halfOpenRequests++;
|
|
17898
|
+
}
|
|
17899
|
+
}
|
|
17900
|
+
/**
|
|
17901
|
+
* Record a successful request
|
|
17902
|
+
*/
|
|
17903
|
+
recordSuccess() {
|
|
17904
|
+
if (this.state === "half-open") {
|
|
17905
|
+
this.successCount++;
|
|
17906
|
+
if (this.successCount >= this.config.halfOpenMaxRequests) {
|
|
17907
|
+
this.transitionTo("closed");
|
|
17908
|
+
}
|
|
17909
|
+
} else {
|
|
17910
|
+
this.failureCount = 0;
|
|
17911
|
+
}
|
|
17912
|
+
}
|
|
17913
|
+
/**
|
|
17914
|
+
* Record a failed request
|
|
17915
|
+
* Only counts transient failures toward circuit breaker threshold
|
|
17916
|
+
*/
|
|
17917
|
+
recordFailure(error) {
|
|
17918
|
+
if (!isTransientError(error)) return;
|
|
17919
|
+
this.failureCount++;
|
|
17920
|
+
if (this.state === "half-open") {
|
|
17921
|
+
this.transitionTo("open");
|
|
17922
|
+
} else if (this.failureCount >= this.config.failureThreshold) {
|
|
17923
|
+
this.transitionTo("open");
|
|
17924
|
+
}
|
|
17925
|
+
}
|
|
17926
|
+
/**
|
|
17927
|
+
* Get current circuit state
|
|
17928
|
+
*/
|
|
17929
|
+
getState() {
|
|
17930
|
+
return {
|
|
17931
|
+
state: this.state,
|
|
17932
|
+
failureCount: this.failureCount,
|
|
17933
|
+
openUntil: this.state === "open" ? this.openUntil : void 0
|
|
17934
|
+
};
|
|
17935
|
+
}
|
|
17936
|
+
/**
|
|
17937
|
+
* Transition to a new state
|
|
17938
|
+
*/
|
|
17939
|
+
transitionTo(newState) {
|
|
17940
|
+
this.state = newState;
|
|
17941
|
+
if (newState === "open") {
|
|
17942
|
+
this.openUntil = Date.now() + this.config.resetTimeoutMs;
|
|
17943
|
+
} else if (newState === "half-open") {
|
|
17944
|
+
this.halfOpenRequests = 0;
|
|
17945
|
+
this.successCount = 0;
|
|
17946
|
+
} else if (newState === "closed") {
|
|
17947
|
+
this.failureCount = 0;
|
|
17948
|
+
this.successCount = 0;
|
|
17949
|
+
this.halfOpenRequests = 0;
|
|
17950
|
+
}
|
|
17951
|
+
}
|
|
17952
|
+
};
|
|
17953
|
+
async function fetchWithTimeoutAndRetry(fetcher, config) {
|
|
17954
|
+
const maxAttempts = config.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts;
|
|
17955
|
+
const requestTimeoutMs = config.requestTimeoutMs ?? 8e3;
|
|
17956
|
+
let lastError;
|
|
17957
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
17958
|
+
try {
|
|
17959
|
+
const controller = new AbortController();
|
|
17960
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
17961
|
+
try {
|
|
17962
|
+
const result = await fetcher(controller.signal);
|
|
17963
|
+
return result;
|
|
17964
|
+
} finally {
|
|
17965
|
+
clearTimeout(timeoutId);
|
|
17966
|
+
}
|
|
17967
|
+
} catch (error) {
|
|
17968
|
+
lastError = error;
|
|
17969
|
+
const shouldRetry = shouldRetryError(error, config.retryOn);
|
|
17970
|
+
if (!shouldRetry) {
|
|
17971
|
+
throw error;
|
|
17972
|
+
}
|
|
17973
|
+
if (attempt < maxAttempts) {
|
|
17974
|
+
const delay = getRetryDelay(error, attempt, config);
|
|
17975
|
+
await sleep(delay);
|
|
17976
|
+
}
|
|
17977
|
+
}
|
|
17978
|
+
}
|
|
17979
|
+
throw lastError;
|
|
17980
|
+
}
|
|
17981
|
+
function shouldRetryError(error, customRetryOn) {
|
|
17982
|
+
if (customRetryOn) {
|
|
17983
|
+
const statusCode = error instanceof RiverbankApiError ? error.status : void 0;
|
|
17984
|
+
return customRetryOn(error, statusCode);
|
|
17985
|
+
}
|
|
17986
|
+
return isTransientError(error);
|
|
17987
|
+
}
|
|
17988
|
+
function getRetryDelay(error, attempt, config) {
|
|
17989
|
+
if (error instanceof RiverbankApiError && error.retryAfterMs) {
|
|
17990
|
+
return error.retryAfterMs;
|
|
17991
|
+
}
|
|
17992
|
+
return calculateBackoff(attempt, config);
|
|
17993
|
+
}
|
|
17994
|
+
function sleep(ms) {
|
|
17995
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
17996
|
+
}
|
|
17997
|
+
var CircuitOpenError = class extends Error {
|
|
17998
|
+
constructor(state) {
|
|
17999
|
+
super("Circuit breaker is open");
|
|
18000
|
+
this.name = "CircuitOpenError";
|
|
18001
|
+
this.circuitState = state;
|
|
18002
|
+
}
|
|
18003
|
+
};
|
|
18004
|
+
|
|
17708
18005
|
// src/client/index.ts
|
|
18006
|
+
var prebuildModule = null;
|
|
18007
|
+
function getPrebuildModule() {
|
|
18008
|
+
if (prebuildModule !== null) return prebuildModule;
|
|
18009
|
+
if (typeof process === "undefined" || !process.versions?.node) {
|
|
18010
|
+
return null;
|
|
18011
|
+
}
|
|
18012
|
+
try {
|
|
18013
|
+
prebuildModule = __require("../prebuild/loader");
|
|
18014
|
+
return prebuildModule;
|
|
18015
|
+
} catch {
|
|
18016
|
+
return null;
|
|
18017
|
+
}
|
|
18018
|
+
}
|
|
17709
18019
|
setSdkVersion(SDK_VERSION);
|
|
18020
|
+
var DEFAULT_BROWSER_TIMEOUT_MS = 5e3;
|
|
18021
|
+
var DEFAULT_SERVER_TIMEOUT_MS = 8e3;
|
|
18022
|
+
function generateRequestId2() {
|
|
18023
|
+
return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
18024
|
+
}
|
|
18025
|
+
function isAbortError(error) {
|
|
18026
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
18027
|
+
return true;
|
|
18028
|
+
}
|
|
18029
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
18030
|
+
return true;
|
|
18031
|
+
}
|
|
18032
|
+
return false;
|
|
18033
|
+
}
|
|
18034
|
+
function getNetworkErrorCode(error) {
|
|
18035
|
+
if (error.name === "TimeoutError" || error.name === "AbortError") {
|
|
18036
|
+
return "network:timeout";
|
|
18037
|
+
}
|
|
18038
|
+
const nodeError = error;
|
|
18039
|
+
if (nodeError.code) {
|
|
18040
|
+
switch (nodeError.code) {
|
|
18041
|
+
case "ETIMEDOUT":
|
|
18042
|
+
case "ESOCKETTIMEDOUT":
|
|
18043
|
+
return "network:timeout";
|
|
18044
|
+
case "ENOTFOUND":
|
|
18045
|
+
case "EAI_AGAIN":
|
|
18046
|
+
return "network:dns_error";
|
|
18047
|
+
case "ECONNREFUSED":
|
|
18048
|
+
case "ECONNRESET":
|
|
18049
|
+
case "EPIPE":
|
|
18050
|
+
return "network:connection_error";
|
|
18051
|
+
}
|
|
18052
|
+
}
|
|
18053
|
+
const message = error.message.toLowerCase();
|
|
18054
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
18055
|
+
return "network:timeout";
|
|
18056
|
+
}
|
|
18057
|
+
if (message.includes("dns") || message.includes("getaddrinfo") || message.includes("enotfound")) {
|
|
18058
|
+
return "network:dns_error";
|
|
18059
|
+
}
|
|
18060
|
+
return "network:connection_error";
|
|
18061
|
+
}
|
|
17710
18062
|
function convertToTypedError(error) {
|
|
18063
|
+
if (isAbortError(error)) {
|
|
18064
|
+
throw error;
|
|
18065
|
+
}
|
|
17711
18066
|
if (error instanceof ApiEnvelopeError) {
|
|
17712
18067
|
throw new RiverbankApiError({
|
|
17713
18068
|
code: error.code,
|
|
@@ -17721,15 +18076,30 @@ function convertToTypedError(error) {
|
|
|
17721
18076
|
if (error instanceof ApiRequestError && error.body && typeof error.body === "object") {
|
|
17722
18077
|
const body = error.body;
|
|
17723
18078
|
if (isApiError(body)) {
|
|
17724
|
-
|
|
18079
|
+
const envelopeError = body.error;
|
|
18080
|
+
throw new RiverbankApiError({
|
|
18081
|
+
...envelopeError,
|
|
18082
|
+
retryAfterMs: error.retryAfterMs
|
|
18083
|
+
});
|
|
17725
18084
|
}
|
|
17726
18085
|
}
|
|
18086
|
+
if (error instanceof TypeError || error instanceof Error && !("status" in error)) {
|
|
18087
|
+
const networkError = error;
|
|
18088
|
+
throw new RiverbankApiError({
|
|
18089
|
+
code: getNetworkErrorCode(networkError),
|
|
18090
|
+
message: networkError.message || "Network request failed",
|
|
18091
|
+
requestId: `local-${Date.now()}`,
|
|
18092
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18093
|
+
status: 0
|
|
18094
|
+
// No HTTP response received
|
|
18095
|
+
});
|
|
18096
|
+
}
|
|
17727
18097
|
throw error;
|
|
17728
18098
|
}
|
|
17729
18099
|
function createRiverbankClient(config) {
|
|
17730
18100
|
if (!config.baseUrl) {
|
|
17731
18101
|
throw new Error(
|
|
17732
|
-
"baseUrl is required when creating a
|
|
18102
|
+
"baseUrl is required when creating a Riverbank client. Expected format: https://dashboard.example.com/api (must include /api path)"
|
|
17733
18103
|
);
|
|
17734
18104
|
}
|
|
17735
18105
|
if (!config.baseUrl.endsWith("/api")) {
|
|
@@ -17740,59 +18110,212 @@ function createRiverbankClient(config) {
|
|
|
17740
18110
|
const cacheEnabled = config.cache?.enabled ?? true;
|
|
17741
18111
|
const cacheTTL = (config.cache?.ttl ?? 300) * 1e3;
|
|
17742
18112
|
const cacheMaxSize = config.cache?.maxSize ?? 100;
|
|
18113
|
+
const resilienceEnabled = config.resilience?.enabled ?? true;
|
|
18114
|
+
const staleIfError = config.resilience?.staleIfError ?? true;
|
|
18115
|
+
const staleTtlMs = (config.resilience?.staleTtlSec ?? 300) * 1e3;
|
|
18116
|
+
const requestTimeoutMs = config.resilience?.requestTimeoutMs ?? (typeof window !== "undefined" ? DEFAULT_BROWSER_TIMEOUT_MS : DEFAULT_SERVER_TIMEOUT_MS);
|
|
18117
|
+
const retryConfig = {
|
|
18118
|
+
maxAttempts: config.resilience?.retry?.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts,
|
|
18119
|
+
baseDelayMs: config.resilience?.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
|
|
18120
|
+
maxDelayMs: config.resilience?.retry?.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs,
|
|
18121
|
+
jitter: config.resilience?.retry?.jitter ?? DEFAULT_RETRY_CONFIG.jitter,
|
|
18122
|
+
retryOn: config.resilience?.retry?.retryOn
|
|
18123
|
+
};
|
|
18124
|
+
const circuitBreakerConfig = {
|
|
18125
|
+
failureThreshold: config.resilience?.circuitBreaker?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
|
|
18126
|
+
resetTimeoutMs: config.resilience?.circuitBreaker?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
|
|
18127
|
+
halfOpenMaxRequests: config.resilience?.circuitBreaker?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
|
|
18128
|
+
};
|
|
17743
18129
|
const apiClient = createBearerAPIClient(config.apiKey, config.baseUrl);
|
|
17744
18130
|
const cache = new SimpleCache({
|
|
17745
18131
|
maxSize: cacheMaxSize,
|
|
17746
|
-
ttl: cacheTTL
|
|
18132
|
+
ttl: cacheTTL,
|
|
18133
|
+
staleTtl: staleTtlMs
|
|
17747
18134
|
});
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
18135
|
+
const circuitBreaker = new CircuitBreaker(circuitBreakerConfig);
|
|
18136
|
+
const prebuildDir = config.resilience?.prebuildDir;
|
|
18137
|
+
const prebuildMod = prebuildDir ? getPrebuildModule() : null;
|
|
18138
|
+
const prebuildLoader = prebuildMod?.canUsePrebuild() && prebuildDir ? new prebuildMod.PrebuildLoader({
|
|
18139
|
+
prebuildDir,
|
|
18140
|
+
maxPrebuildAgeSec: config.resilience?.maxPrebuildAgeSec
|
|
18141
|
+
}) : null;
|
|
18142
|
+
let lastStatus = null;
|
|
18143
|
+
let isDegraded = false;
|
|
18144
|
+
function emitStatus(source, data, details) {
|
|
18145
|
+
const status = {
|
|
18146
|
+
source,
|
|
18147
|
+
isPreview: details.isPreview,
|
|
18148
|
+
cacheKey: details.cacheKey,
|
|
18149
|
+
error: details.error,
|
|
18150
|
+
staleAgeSec: details.staleAgeSec,
|
|
18151
|
+
prebuildAgeSec: details.prebuildAgeSec,
|
|
18152
|
+
circuit: circuitBreaker.getState(),
|
|
18153
|
+
requestId: details.requestId,
|
|
18154
|
+
durationMs: details.durationMs
|
|
18155
|
+
};
|
|
18156
|
+
lastStatus = status;
|
|
18157
|
+
config.resilience?.onStatusChange?.(status);
|
|
18158
|
+
const nowDegraded = source === "stale" || source === "error";
|
|
18159
|
+
if (nowDegraded !== isDegraded) {
|
|
18160
|
+
isDegraded = nowDegraded;
|
|
18161
|
+
config.resilience?.onDegradedMode?.(nowDegraded, status);
|
|
18162
|
+
}
|
|
18163
|
+
return data;
|
|
18164
|
+
}
|
|
18165
|
+
async function resilientFetch(cacheKey, fetcher, options) {
|
|
18166
|
+
const requestId = generateRequestId2();
|
|
18167
|
+
const startTime = Date.now();
|
|
18168
|
+
const isPreview = options.preview ?? false;
|
|
18169
|
+
const statusDetails = (extra = {}) => ({
|
|
18170
|
+
requestId,
|
|
18171
|
+
cacheKey,
|
|
18172
|
+
isPreview,
|
|
18173
|
+
durationMs: Date.now() - startTime,
|
|
18174
|
+
...extra
|
|
18175
|
+
});
|
|
18176
|
+
if (cacheEnabled && !options.force) {
|
|
18177
|
+
const fresh = cache.getFresh(cacheKey);
|
|
18178
|
+
if (fresh !== null) {
|
|
18179
|
+
return emitStatus("cache", fresh, statusDetails());
|
|
17753
18180
|
}
|
|
17754
18181
|
}
|
|
17755
|
-
|
|
18182
|
+
if (resilienceEnabled && circuitBreaker.isOpen()) {
|
|
18183
|
+
if (!isPreview && staleIfError) {
|
|
18184
|
+
const stale = cache.getStale(cacheKey);
|
|
18185
|
+
if (stale) {
|
|
18186
|
+
return emitStatus("stale", stale.value, statusDetails({
|
|
18187
|
+
staleAgeSec: stale.staleAgeSec,
|
|
18188
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
18189
|
+
}));
|
|
18190
|
+
}
|
|
18191
|
+
}
|
|
18192
|
+
if (!isPreview && options.prebuildFallback) {
|
|
18193
|
+
const prebuildResult = options.prebuildFallback();
|
|
18194
|
+
if (prebuildResult) {
|
|
18195
|
+
return emitStatus("prebuild", prebuildResult.data, statusDetails({
|
|
18196
|
+
prebuildAgeSec: prebuildResult.prebuildAgeSec,
|
|
18197
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
18198
|
+
}));
|
|
18199
|
+
}
|
|
18200
|
+
}
|
|
18201
|
+
const circuitState = circuitBreaker.getState();
|
|
18202
|
+
emitStatus("error", null, statusDetails({
|
|
18203
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
18204
|
+
}));
|
|
18205
|
+
throw new CircuitOpenError(circuitState);
|
|
18206
|
+
}
|
|
17756
18207
|
try {
|
|
17757
|
-
|
|
17758
|
-
|
|
18208
|
+
let data;
|
|
18209
|
+
if (resilienceEnabled) {
|
|
18210
|
+
if (circuitBreaker.getState().state === "half-open") {
|
|
18211
|
+
circuitBreaker.incrementHalfOpenRequests();
|
|
18212
|
+
}
|
|
18213
|
+
data = await fetchWithTimeoutAndRetry(
|
|
18214
|
+
async (timeoutSignal) => {
|
|
18215
|
+
const combinedSignal = options.signal ? combineAbortSignals(timeoutSignal, options.signal) : timeoutSignal;
|
|
18216
|
+
try {
|
|
18217
|
+
const response = await fetcher(combinedSignal);
|
|
18218
|
+
return unwrapResponse(response);
|
|
18219
|
+
} catch (error) {
|
|
18220
|
+
convertToTypedError(error);
|
|
18221
|
+
}
|
|
18222
|
+
},
|
|
18223
|
+
{
|
|
18224
|
+
...retryConfig,
|
|
18225
|
+
requestTimeoutMs
|
|
18226
|
+
}
|
|
18227
|
+
);
|
|
18228
|
+
circuitBreaker.recordSuccess();
|
|
18229
|
+
} else {
|
|
18230
|
+
try {
|
|
18231
|
+
const response = await fetcher(options.signal ?? new AbortController().signal);
|
|
18232
|
+
data = unwrapResponse(response);
|
|
18233
|
+
} catch (error) {
|
|
18234
|
+
convertToTypedError(error);
|
|
18235
|
+
}
|
|
18236
|
+
}
|
|
18237
|
+
if (cacheEnabled) {
|
|
18238
|
+
cache.set(cacheKey, data);
|
|
18239
|
+
}
|
|
18240
|
+
return emitStatus("live", data, statusDetails());
|
|
17759
18241
|
} catch (error) {
|
|
17760
|
-
|
|
18242
|
+
if (resilienceEnabled && error instanceof Error) {
|
|
18243
|
+
circuitBreaker.recordFailure(error);
|
|
18244
|
+
}
|
|
18245
|
+
if (!isPreview && staleIfError && cacheEnabled) {
|
|
18246
|
+
const stale = cache.getStale(cacheKey);
|
|
18247
|
+
if (stale) {
|
|
18248
|
+
const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
18249
|
+
return emitStatus("stale", stale.value, statusDetails({
|
|
18250
|
+
staleAgeSec: stale.staleAgeSec,
|
|
18251
|
+
error: errorInfo2
|
|
18252
|
+
}));
|
|
18253
|
+
}
|
|
18254
|
+
}
|
|
18255
|
+
if (!isPreview && options.prebuildFallback) {
|
|
18256
|
+
const prebuildResult = options.prebuildFallback();
|
|
18257
|
+
if (prebuildResult) {
|
|
18258
|
+
const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
18259
|
+
return emitStatus("prebuild", prebuildResult.data, statusDetails({
|
|
18260
|
+
prebuildAgeSec: prebuildResult.prebuildAgeSec,
|
|
18261
|
+
error: errorInfo2
|
|
18262
|
+
}));
|
|
18263
|
+
}
|
|
18264
|
+
}
|
|
18265
|
+
const errorInfo = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
18266
|
+
emitStatus("error", null, statusDetails({ error: errorInfo }));
|
|
18267
|
+
throw error;
|
|
17761
18268
|
}
|
|
17762
|
-
|
|
17763
|
-
|
|
18269
|
+
}
|
|
18270
|
+
function combineAbortSignals(...signals) {
|
|
18271
|
+
const controller = new AbortController();
|
|
18272
|
+
for (const signal of signals) {
|
|
18273
|
+
if (signal.aborted) {
|
|
18274
|
+
controller.abort(signal.reason);
|
|
18275
|
+
break;
|
|
18276
|
+
}
|
|
18277
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
|
|
17764
18278
|
}
|
|
17765
|
-
return
|
|
18279
|
+
return controller.signal;
|
|
17766
18280
|
}
|
|
17767
18281
|
return {
|
|
17768
18282
|
async getSite(params) {
|
|
17769
|
-
const { slug, domain, id } = params;
|
|
18283
|
+
const { slug, domain, id, signal } = params;
|
|
17770
18284
|
if (!slug && !domain && !id) {
|
|
17771
18285
|
throw new Error(
|
|
17772
18286
|
`getSite() requires at least one identifier: slug, domain, or id. Received: ${JSON.stringify(params)}`
|
|
17773
18287
|
);
|
|
17774
18288
|
}
|
|
17775
18289
|
const cacheKey = `site:${slug || domain || id}`;
|
|
17776
|
-
|
|
18290
|
+
const siteId = id || slug || domain;
|
|
18291
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17777
18292
|
const apiParams = {};
|
|
17778
18293
|
if (params.slug) apiParams.slug = params.slug;
|
|
17779
18294
|
if (params.domain) apiParams.domain = params.domain;
|
|
17780
18295
|
if (params.id) apiParams.id = params.id;
|
|
17781
|
-
return await apiClient({ endpoint: "getSite", params: apiParams });
|
|
18296
|
+
return await apiClient({ endpoint: "getSite", params: apiParams, options: { signal: sig } });
|
|
18297
|
+
}, {
|
|
18298
|
+
signal,
|
|
18299
|
+
prebuildFallback: prebuildLoader && siteId ? () => prebuildLoader.loadSite(siteId) : void 0
|
|
17782
18300
|
});
|
|
17783
18301
|
},
|
|
17784
18302
|
async getPage(params) {
|
|
17785
|
-
const { siteId, path, preview = false } = params;
|
|
18303
|
+
const { siteId, path, preview = false, signal } = params;
|
|
17786
18304
|
const cacheKey = `page:${siteId}:${path}:${preview}`;
|
|
17787
|
-
return
|
|
17788
|
-
return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path, preview } });
|
|
18305
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
18306
|
+
return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path, preview }, options: { signal: sig } });
|
|
18307
|
+
}, {
|
|
18308
|
+
preview,
|
|
18309
|
+
signal,
|
|
18310
|
+
// Prebuild fallback only for published pages (not preview)
|
|
18311
|
+
prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadPage(siteId, path) : void 0
|
|
17789
18312
|
});
|
|
17790
18313
|
},
|
|
17791
18314
|
async getEntries(params) {
|
|
17792
|
-
const { siteId, contentType, limit, offset, order, preview = false, mode, entryIds, includeMeta } = params;
|
|
18315
|
+
const { siteId, contentType, limit, offset, order, preview = false, mode, entryIds, includeMeta, signal } = params;
|
|
17793
18316
|
const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
|
|
17794
18317
|
const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${offset ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}:${includeMeta ?? ""}`;
|
|
17795
|
-
return
|
|
18318
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17796
18319
|
let orderParam;
|
|
17797
18320
|
if (order === "newest") {
|
|
17798
18321
|
orderParam = "published_at.desc";
|
|
@@ -17814,47 +18337,52 @@ function createRiverbankClient(config) {
|
|
|
17814
18337
|
entryIds: JSON.stringify(entryIds)
|
|
17815
18338
|
}
|
|
17816
18339
|
};
|
|
17817
|
-
return await apiClient({ endpoint: "listPublishedEntries", params: apiParams });
|
|
18340
|
+
return await apiClient({ endpoint: "listPublishedEntries", params: apiParams, options: { signal: sig } });
|
|
18341
|
+
}, {
|
|
18342
|
+
preview,
|
|
18343
|
+
signal,
|
|
18344
|
+
// Prebuild fallback only for published entries (not preview, not manual mode)
|
|
18345
|
+
prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadEntries(siteId, params) : void 0
|
|
17818
18346
|
});
|
|
17819
18347
|
},
|
|
17820
18348
|
async getEntry(params) {
|
|
17821
|
-
const { siteId, contentType, slug } = params;
|
|
18349
|
+
const { siteId, contentType, slug, signal } = params;
|
|
17822
18350
|
const cacheKey = `entry:${siteId}:${contentType}:${slug}`;
|
|
17823
|
-
return
|
|
17824
|
-
return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug } });
|
|
17825
|
-
});
|
|
18351
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
18352
|
+
return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug }, options: { signal: sig } });
|
|
18353
|
+
}, { signal });
|
|
17826
18354
|
},
|
|
17827
18355
|
async getPublicFormById(params) {
|
|
17828
|
-
const { formId } = params;
|
|
18356
|
+
const { formId, signal } = params;
|
|
17829
18357
|
if (!formId) {
|
|
17830
18358
|
throw new Error("getPublicFormById() requires formId");
|
|
17831
18359
|
}
|
|
17832
18360
|
const cacheKey = `public-form:${formId}`;
|
|
17833
|
-
return
|
|
17834
|
-
return await apiClient({ endpoint: "getPublicFormById", params: { formId } });
|
|
17835
|
-
});
|
|
18361
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
18362
|
+
return await apiClient({ endpoint: "getPublicFormById", params: { formId }, options: { signal: sig } });
|
|
18363
|
+
}, { signal });
|
|
17836
18364
|
},
|
|
17837
18365
|
async getPublicBookingServices(params) {
|
|
17838
|
-
const { siteId, ids } = params;
|
|
18366
|
+
const { siteId, ids, signal } = params;
|
|
17839
18367
|
if (!siteId) {
|
|
17840
18368
|
throw new Error("getPublicBookingServices() requires siteId");
|
|
17841
18369
|
}
|
|
17842
18370
|
const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
|
|
17843
|
-
return
|
|
18371
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17844
18372
|
const apiParams = {
|
|
17845
18373
|
siteId,
|
|
17846
18374
|
...ids && { ids }
|
|
17847
18375
|
};
|
|
17848
|
-
return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams });
|
|
17849
|
-
});
|
|
18376
|
+
return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams, options: { signal: sig } });
|
|
18377
|
+
}, { signal });
|
|
17850
18378
|
},
|
|
17851
18379
|
async listPublicEvents(params) {
|
|
17852
|
-
const { siteId, limit, from, to, stage } = params;
|
|
18380
|
+
const { siteId, limit, from, to, stage, signal } = params;
|
|
17853
18381
|
if (!siteId) {
|
|
17854
18382
|
throw new Error("listPublicEvents() requires siteId");
|
|
17855
18383
|
}
|
|
17856
18384
|
const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
|
|
17857
|
-
return
|
|
18385
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17858
18386
|
const apiParams = {
|
|
17859
18387
|
siteId,
|
|
17860
18388
|
...typeof limit === "number" && { limit: String(limit) },
|
|
@@ -17862,40 +18390,46 @@ function createRiverbankClient(config) {
|
|
|
17862
18390
|
...to && { to },
|
|
17863
18391
|
...stage && { stage }
|
|
17864
18392
|
};
|
|
17865
|
-
return await apiClient({ endpoint: "listPublicEvents", params: apiParams });
|
|
17866
|
-
});
|
|
18393
|
+
return await apiClient({ endpoint: "listPublicEvents", params: apiParams, options: { signal: sig } });
|
|
18394
|
+
}, { signal });
|
|
17867
18395
|
},
|
|
17868
18396
|
async resolveEventOccurrence(params) {
|
|
17869
|
-
const { siteId, entryId, segment } = params;
|
|
18397
|
+
const { siteId, entryId, segment, signal } = params;
|
|
17870
18398
|
if (!siteId || !entryId || !segment) {
|
|
17871
18399
|
throw new Error("resolveEventOccurrence() requires siteId, entryId, and segment");
|
|
17872
18400
|
}
|
|
17873
18401
|
const cacheKey = `event-occurrence:${siteId}:${entryId}:${segment}`;
|
|
17874
|
-
return
|
|
18402
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17875
18403
|
return await apiClient({
|
|
17876
18404
|
endpoint: "resolveEventOccurrence",
|
|
17877
|
-
params: { siteId, entryId, segment }
|
|
18405
|
+
params: { siteId, entryId, segment },
|
|
18406
|
+
options: { signal: sig }
|
|
17878
18407
|
});
|
|
17879
|
-
});
|
|
18408
|
+
}, { signal });
|
|
17880
18409
|
},
|
|
17881
18410
|
async checkRedirect(params) {
|
|
17882
|
-
const { siteId, path } = params;
|
|
18411
|
+
const { siteId, path, signal } = params;
|
|
17883
18412
|
if (!siteId || !path) {
|
|
17884
18413
|
throw new Error("checkRedirect() requires siteId and path");
|
|
17885
18414
|
}
|
|
17886
18415
|
const cacheKey = `redirect:${siteId}:${path}`;
|
|
17887
|
-
return
|
|
18416
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17888
18417
|
return await apiClient({
|
|
17889
18418
|
endpoint: "checkRedirect",
|
|
17890
|
-
params: { site: siteId, path }
|
|
18419
|
+
params: { site: siteId, path },
|
|
18420
|
+
options: { signal: sig }
|
|
17891
18421
|
});
|
|
17892
|
-
});
|
|
18422
|
+
}, { signal });
|
|
17893
18423
|
},
|
|
17894
18424
|
clearCache() {
|
|
17895
18425
|
cache.clear();
|
|
18426
|
+
},
|
|
18427
|
+
getLastEmittedStatus() {
|
|
18428
|
+
return lastStatus;
|
|
18429
|
+
},
|
|
18430
|
+
getCircuitState() {
|
|
18431
|
+
return circuitBreaker.getState();
|
|
17896
18432
|
}
|
|
17897
|
-
// Cast to RiverbankClient to satisfy overloaded getEntries signature
|
|
17898
|
-
// The implementation correctly returns the right type based on includeMeta
|
|
17899
18433
|
};
|
|
17900
18434
|
}
|
|
17901
18435
|
export {
|