@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.js
CHANGED
|
@@ -17332,10 +17332,10 @@ function resolveApiBaseUrl() {
|
|
|
17332
17332
|
var revalidateTag = null;
|
|
17333
17333
|
if (typeof window === "undefined") {
|
|
17334
17334
|
try {
|
|
17335
|
-
const
|
|
17336
|
-
|
|
17335
|
+
const dynamicRequire = new Function("modulePath", "return require(modulePath)");
|
|
17336
|
+
const nextCache = dynamicRequire("next/cache");
|
|
17337
|
+
revalidateTag = nextCache.revalidateTag ?? null;
|
|
17337
17338
|
} catch {
|
|
17338
|
-
revalidateTag = null;
|
|
17339
17339
|
}
|
|
17340
17340
|
}
|
|
17341
17341
|
var sdkVersion;
|
|
@@ -17364,8 +17364,22 @@ var ApiRequestError = class extends Error {
|
|
|
17364
17364
|
this.body = options.body;
|
|
17365
17365
|
this.cause = options.cause;
|
|
17366
17366
|
this.errorCode = options.errorCode;
|
|
17367
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
17367
17368
|
}
|
|
17368
17369
|
};
|
|
17370
|
+
function parseRetryAfterHeader(headerValue) {
|
|
17371
|
+
if (!headerValue) return void 0;
|
|
17372
|
+
if (/^\d+$/.test(headerValue)) {
|
|
17373
|
+
const seconds = parseInt(headerValue, 10);
|
|
17374
|
+
return seconds * 1e3;
|
|
17375
|
+
}
|
|
17376
|
+
const date = new Date(headerValue);
|
|
17377
|
+
if (!isNaN(date.getTime())) {
|
|
17378
|
+
const delayMs = date.getTime() - Date.now();
|
|
17379
|
+
return delayMs > 0 ? delayMs : void 0;
|
|
17380
|
+
}
|
|
17381
|
+
return void 0;
|
|
17382
|
+
}
|
|
17369
17383
|
function buildEndpointURL(baseURL, endpoint) {
|
|
17370
17384
|
return baseURL + API_ENDPOINTS[endpoint].path;
|
|
17371
17385
|
}
|
|
@@ -17582,6 +17596,7 @@ function createParsedClient(rawClient) {
|
|
|
17582
17596
|
if (!response.ok) {
|
|
17583
17597
|
const body = await parseErrorBody(response);
|
|
17584
17598
|
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
17599
|
+
const retryAfterMs = parseRetryAfterHeader(response.headers.get("retry-after"));
|
|
17585
17600
|
throw new ApiRequestError(
|
|
17586
17601
|
`Request to ${String(endpoint)} failed with status ${response.status}`,
|
|
17587
17602
|
{
|
|
@@ -17590,7 +17605,8 @@ function createParsedClient(rawClient) {
|
|
|
17590
17605
|
method: config.method,
|
|
17591
17606
|
auth,
|
|
17592
17607
|
requestId,
|
|
17593
|
-
body
|
|
17608
|
+
body,
|
|
17609
|
+
retryAfterMs
|
|
17594
17610
|
}
|
|
17595
17611
|
);
|
|
17596
17612
|
}
|
|
@@ -17638,38 +17654,117 @@ var SimpleCache = class {
|
|
|
17638
17654
|
this.cache = /* @__PURE__ */ new Map();
|
|
17639
17655
|
this.maxSize = options.maxSize ?? 100;
|
|
17640
17656
|
this.ttl = options.ttl ?? 3e5;
|
|
17657
|
+
this.staleTtl = options.staleTtl ?? 3e5;
|
|
17641
17658
|
}
|
|
17642
|
-
|
|
17659
|
+
/**
|
|
17660
|
+
* Get a fresh value (within TTL)
|
|
17661
|
+
* @returns The value if fresh, null otherwise
|
|
17662
|
+
*/
|
|
17663
|
+
getFresh(key) {
|
|
17643
17664
|
const entry = this.cache.get(key);
|
|
17644
|
-
if (!entry) return
|
|
17645
|
-
|
|
17665
|
+
if (!entry) return null;
|
|
17666
|
+
const now = Date.now();
|
|
17667
|
+
if (now <= entry.freshUntil) {
|
|
17668
|
+
return entry.value;
|
|
17669
|
+
}
|
|
17670
|
+
return null;
|
|
17671
|
+
}
|
|
17672
|
+
/**
|
|
17673
|
+
* Get a value that may be stale (past TTL but within staleTtl)
|
|
17674
|
+
* @returns Object with value and stale age, or null if expired
|
|
17675
|
+
*/
|
|
17676
|
+
getStale(key) {
|
|
17677
|
+
const entry = this.cache.get(key);
|
|
17678
|
+
if (!entry) return null;
|
|
17679
|
+
const now = Date.now();
|
|
17680
|
+
if (now > entry.staleUntil) {
|
|
17646
17681
|
this.cache.delete(key);
|
|
17647
|
-
return
|
|
17682
|
+
return null;
|
|
17648
17683
|
}
|
|
17649
|
-
|
|
17684
|
+
const staleAgeSec = now <= entry.freshUntil ? 0 : Math.floor((now - entry.freshUntil) / 1e3);
|
|
17685
|
+
return {
|
|
17686
|
+
value: entry.value,
|
|
17687
|
+
staleAgeSec
|
|
17688
|
+
};
|
|
17650
17689
|
}
|
|
17651
|
-
|
|
17652
|
-
|
|
17653
|
-
|
|
17654
|
-
|
|
17655
|
-
|
|
17656
|
-
|
|
17690
|
+
/**
|
|
17691
|
+
* Store a value with TTL and stale window
|
|
17692
|
+
*/
|
|
17693
|
+
set(key, value, options) {
|
|
17694
|
+
const ttl = options?.ttl ?? this.ttl;
|
|
17695
|
+
const staleTtl = options?.staleTtl ?? this.staleTtl;
|
|
17696
|
+
const now = Date.now();
|
|
17697
|
+
while (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
17698
|
+
this.evictOne(now);
|
|
17657
17699
|
}
|
|
17658
17700
|
this.cache.set(key, {
|
|
17659
17701
|
value,
|
|
17660
|
-
|
|
17702
|
+
createdAt: now,
|
|
17703
|
+
freshUntil: now + ttl,
|
|
17704
|
+
staleUntil: now + ttl + staleTtl
|
|
17661
17705
|
});
|
|
17662
17706
|
}
|
|
17707
|
+
/**
|
|
17708
|
+
* Evict one entry to make room for a new one
|
|
17709
|
+
* Priority: oldest stale entry, then oldest fresh entry
|
|
17710
|
+
*/
|
|
17711
|
+
evictOne(now) {
|
|
17712
|
+
let oldestStaleKey = null;
|
|
17713
|
+
let oldestStaleTime = Infinity;
|
|
17714
|
+
let oldestFreshKey = null;
|
|
17715
|
+
let oldestFreshTime = Infinity;
|
|
17716
|
+
for (const [key, entry] of this.cache) {
|
|
17717
|
+
if (now > entry.freshUntil) {
|
|
17718
|
+
if (entry.createdAt < oldestStaleTime) {
|
|
17719
|
+
oldestStaleTime = entry.createdAt;
|
|
17720
|
+
oldestStaleKey = key;
|
|
17721
|
+
}
|
|
17722
|
+
} else {
|
|
17723
|
+
if (entry.createdAt < oldestFreshTime) {
|
|
17724
|
+
oldestFreshTime = entry.createdAt;
|
|
17725
|
+
oldestFreshKey = key;
|
|
17726
|
+
}
|
|
17727
|
+
}
|
|
17728
|
+
}
|
|
17729
|
+
const keyToEvict = oldestStaleKey ?? oldestFreshKey;
|
|
17730
|
+
if (keyToEvict) {
|
|
17731
|
+
this.cache.delete(keyToEvict);
|
|
17732
|
+
}
|
|
17733
|
+
}
|
|
17734
|
+
/**
|
|
17735
|
+
* Remove all fully expired entries (past staleUntil)
|
|
17736
|
+
*/
|
|
17737
|
+
prune() {
|
|
17738
|
+
const now = Date.now();
|
|
17739
|
+
for (const [key, entry] of this.cache) {
|
|
17740
|
+
if (now > entry.staleUntil) {
|
|
17741
|
+
this.cache.delete(key);
|
|
17742
|
+
}
|
|
17743
|
+
}
|
|
17744
|
+
}
|
|
17745
|
+
/**
|
|
17746
|
+
* Clear all entries
|
|
17747
|
+
*/
|
|
17663
17748
|
clear() {
|
|
17664
17749
|
this.cache.clear();
|
|
17665
17750
|
}
|
|
17751
|
+
/**
|
|
17752
|
+
* Check if a key exists and is not fully expired
|
|
17753
|
+
*/
|
|
17666
17754
|
has(key) {
|
|
17667
|
-
|
|
17755
|
+
const entry = this.cache.get(key);
|
|
17756
|
+
if (!entry) return false;
|
|
17757
|
+
const now = Date.now();
|
|
17758
|
+
if (now > entry.staleUntil) {
|
|
17759
|
+
this.cache.delete(key);
|
|
17760
|
+
return false;
|
|
17761
|
+
}
|
|
17762
|
+
return now <= entry.freshUntil;
|
|
17668
17763
|
}
|
|
17669
17764
|
};
|
|
17670
17765
|
|
|
17671
17766
|
// src/version.ts
|
|
17672
|
-
var SDK_VERSION = "0.7.
|
|
17767
|
+
var SDK_VERSION = "0.7.3";
|
|
17673
17768
|
|
|
17674
17769
|
// src/client/error.ts
|
|
17675
17770
|
var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
@@ -17681,8 +17776,31 @@ var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
|
17681
17776
|
this.status = apiError.status;
|
|
17682
17777
|
this.fieldErrors = apiError.fieldErrors;
|
|
17683
17778
|
this.timestamp = apiError.timestamp;
|
|
17779
|
+
this.retryAfterMs = "retryAfterMs" in apiError ? apiError.retryAfterMs : void 0;
|
|
17780
|
+
this.isRetryable = this.computeRetryable();
|
|
17684
17781
|
Object.setPrototypeOf(this, _RiverbankApiError.prototype);
|
|
17685
17782
|
}
|
|
17783
|
+
/**
|
|
17784
|
+
* Compute whether this error is retryable based on HTTP status code.
|
|
17785
|
+
* - 0 (network errors - no HTTP response): retryable
|
|
17786
|
+
* - 429 (rate limit): retryable
|
|
17787
|
+
* - 5xx (server errors): retryable
|
|
17788
|
+
* - 4xx (client errors, except 429): NOT retryable
|
|
17789
|
+
*/
|
|
17790
|
+
computeRetryable() {
|
|
17791
|
+
if (this.status === 0) return true;
|
|
17792
|
+
if (this.status === 429) return true;
|
|
17793
|
+
if (this.status >= 500) return true;
|
|
17794
|
+
return false;
|
|
17795
|
+
}
|
|
17796
|
+
/**
|
|
17797
|
+
* Check if this is a network error (no HTTP response received)
|
|
17798
|
+
*
|
|
17799
|
+
* Matches: network:connection_error, network:timeout, network:dns_error
|
|
17800
|
+
*/
|
|
17801
|
+
isNetworkError() {
|
|
17802
|
+
return this.code.startsWith("network:");
|
|
17803
|
+
}
|
|
17686
17804
|
/**
|
|
17687
17805
|
* Check if this error matches a specific error code
|
|
17688
17806
|
*
|
|
@@ -17741,9 +17859,246 @@ var RiverbankApiError = class _RiverbankApiError extends Error {
|
|
|
17741
17859
|
}
|
|
17742
17860
|
};
|
|
17743
17861
|
|
|
17862
|
+
// src/client/resilience.ts
|
|
17863
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
17864
|
+
maxAttempts: 3,
|
|
17865
|
+
baseDelayMs: 200,
|
|
17866
|
+
maxDelayMs: 2e3,
|
|
17867
|
+
jitter: "full"
|
|
17868
|
+
};
|
|
17869
|
+
var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
|
|
17870
|
+
failureThreshold: 5,
|
|
17871
|
+
resetTimeoutMs: 3e4,
|
|
17872
|
+
halfOpenMaxRequests: 2
|
|
17873
|
+
};
|
|
17874
|
+
function isTransientError(error) {
|
|
17875
|
+
if (error instanceof RiverbankApiError) {
|
|
17876
|
+
if (error.status === 0) return true;
|
|
17877
|
+
if (error.status === 429) return true;
|
|
17878
|
+
if (error.status >= 500) return true;
|
|
17879
|
+
return false;
|
|
17880
|
+
}
|
|
17881
|
+
return true;
|
|
17882
|
+
}
|
|
17883
|
+
function calculateBackoff(attempt, config) {
|
|
17884
|
+
const baseDelayMs = config.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs;
|
|
17885
|
+
const maxDelayMs = config.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs;
|
|
17886
|
+
const jitter = config.jitter ?? DEFAULT_RETRY_CONFIG.jitter;
|
|
17887
|
+
const exponential = baseDelayMs * Math.pow(2, attempt - 1);
|
|
17888
|
+
const capped = Math.min(exponential, maxDelayMs);
|
|
17889
|
+
if (jitter === "full") {
|
|
17890
|
+
return Math.random() * capped;
|
|
17891
|
+
}
|
|
17892
|
+
return capped;
|
|
17893
|
+
}
|
|
17894
|
+
var CircuitBreaker = class {
|
|
17895
|
+
constructor(config) {
|
|
17896
|
+
this.state = "closed";
|
|
17897
|
+
this.failureCount = 0;
|
|
17898
|
+
this.successCount = 0;
|
|
17899
|
+
this.openUntil = 0;
|
|
17900
|
+
this.halfOpenRequests = 0;
|
|
17901
|
+
this.config = {
|
|
17902
|
+
failureThreshold: config?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
|
|
17903
|
+
resetTimeoutMs: config?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
|
|
17904
|
+
halfOpenMaxRequests: config?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
|
|
17905
|
+
};
|
|
17906
|
+
}
|
|
17907
|
+
/**
|
|
17908
|
+
* Check if circuit is open (requests should be blocked)
|
|
17909
|
+
* Also handles automatic transition from open to half-open after timeout
|
|
17910
|
+
*/
|
|
17911
|
+
isOpen() {
|
|
17912
|
+
if (this.state === "open" && Date.now() >= this.openUntil) {
|
|
17913
|
+
this.transitionTo("half-open");
|
|
17914
|
+
}
|
|
17915
|
+
return this.state === "open";
|
|
17916
|
+
}
|
|
17917
|
+
/**
|
|
17918
|
+
* Check if a request can be attempted
|
|
17919
|
+
* - closed: always yes
|
|
17920
|
+
* - open: always no
|
|
17921
|
+
* - half-open: limited number of probes
|
|
17922
|
+
*/
|
|
17923
|
+
canAttempt() {
|
|
17924
|
+
if (this.state === "closed") return true;
|
|
17925
|
+
if (this.state === "open") return false;
|
|
17926
|
+
return this.halfOpenRequests < this.config.halfOpenMaxRequests;
|
|
17927
|
+
}
|
|
17928
|
+
/**
|
|
17929
|
+
* Increment half-open request counter (call before making request in half-open)
|
|
17930
|
+
*/
|
|
17931
|
+
incrementHalfOpenRequests() {
|
|
17932
|
+
if (this.state === "half-open") {
|
|
17933
|
+
this.halfOpenRequests++;
|
|
17934
|
+
}
|
|
17935
|
+
}
|
|
17936
|
+
/**
|
|
17937
|
+
* Record a successful request
|
|
17938
|
+
*/
|
|
17939
|
+
recordSuccess() {
|
|
17940
|
+
if (this.state === "half-open") {
|
|
17941
|
+
this.successCount++;
|
|
17942
|
+
if (this.successCount >= this.config.halfOpenMaxRequests) {
|
|
17943
|
+
this.transitionTo("closed");
|
|
17944
|
+
}
|
|
17945
|
+
} else {
|
|
17946
|
+
this.failureCount = 0;
|
|
17947
|
+
}
|
|
17948
|
+
}
|
|
17949
|
+
/**
|
|
17950
|
+
* Record a failed request
|
|
17951
|
+
* Only counts transient failures toward circuit breaker threshold
|
|
17952
|
+
*/
|
|
17953
|
+
recordFailure(error) {
|
|
17954
|
+
if (!isTransientError(error)) return;
|
|
17955
|
+
this.failureCount++;
|
|
17956
|
+
if (this.state === "half-open") {
|
|
17957
|
+
this.transitionTo("open");
|
|
17958
|
+
} else if (this.failureCount >= this.config.failureThreshold) {
|
|
17959
|
+
this.transitionTo("open");
|
|
17960
|
+
}
|
|
17961
|
+
}
|
|
17962
|
+
/**
|
|
17963
|
+
* Get current circuit state
|
|
17964
|
+
*/
|
|
17965
|
+
getState() {
|
|
17966
|
+
return {
|
|
17967
|
+
state: this.state,
|
|
17968
|
+
failureCount: this.failureCount,
|
|
17969
|
+
openUntil: this.state === "open" ? this.openUntil : void 0
|
|
17970
|
+
};
|
|
17971
|
+
}
|
|
17972
|
+
/**
|
|
17973
|
+
* Transition to a new state
|
|
17974
|
+
*/
|
|
17975
|
+
transitionTo(newState) {
|
|
17976
|
+
this.state = newState;
|
|
17977
|
+
if (newState === "open") {
|
|
17978
|
+
this.openUntil = Date.now() + this.config.resetTimeoutMs;
|
|
17979
|
+
} else if (newState === "half-open") {
|
|
17980
|
+
this.halfOpenRequests = 0;
|
|
17981
|
+
this.successCount = 0;
|
|
17982
|
+
} else if (newState === "closed") {
|
|
17983
|
+
this.failureCount = 0;
|
|
17984
|
+
this.successCount = 0;
|
|
17985
|
+
this.halfOpenRequests = 0;
|
|
17986
|
+
}
|
|
17987
|
+
}
|
|
17988
|
+
};
|
|
17989
|
+
async function fetchWithTimeoutAndRetry(fetcher, config) {
|
|
17990
|
+
const maxAttempts = config.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts;
|
|
17991
|
+
const requestTimeoutMs = config.requestTimeoutMs ?? 8e3;
|
|
17992
|
+
let lastError;
|
|
17993
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
17994
|
+
try {
|
|
17995
|
+
const controller = new AbortController();
|
|
17996
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
17997
|
+
try {
|
|
17998
|
+
const result = await fetcher(controller.signal);
|
|
17999
|
+
return result;
|
|
18000
|
+
} finally {
|
|
18001
|
+
clearTimeout(timeoutId);
|
|
18002
|
+
}
|
|
18003
|
+
} catch (error) {
|
|
18004
|
+
lastError = error;
|
|
18005
|
+
const shouldRetry = shouldRetryError(error, config.retryOn);
|
|
18006
|
+
if (!shouldRetry) {
|
|
18007
|
+
throw error;
|
|
18008
|
+
}
|
|
18009
|
+
if (attempt < maxAttempts) {
|
|
18010
|
+
const delay = getRetryDelay(error, attempt, config);
|
|
18011
|
+
await sleep(delay);
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
}
|
|
18015
|
+
throw lastError;
|
|
18016
|
+
}
|
|
18017
|
+
function shouldRetryError(error, customRetryOn) {
|
|
18018
|
+
if (customRetryOn) {
|
|
18019
|
+
const statusCode = error instanceof RiverbankApiError ? error.status : void 0;
|
|
18020
|
+
return customRetryOn(error, statusCode);
|
|
18021
|
+
}
|
|
18022
|
+
return isTransientError(error);
|
|
18023
|
+
}
|
|
18024
|
+
function getRetryDelay(error, attempt, config) {
|
|
18025
|
+
if (error instanceof RiverbankApiError && error.retryAfterMs) {
|
|
18026
|
+
return error.retryAfterMs;
|
|
18027
|
+
}
|
|
18028
|
+
return calculateBackoff(attempt, config);
|
|
18029
|
+
}
|
|
18030
|
+
function sleep(ms) {
|
|
18031
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
18032
|
+
}
|
|
18033
|
+
var CircuitOpenError = class extends Error {
|
|
18034
|
+
constructor(state) {
|
|
18035
|
+
super("Circuit breaker is open");
|
|
18036
|
+
this.name = "CircuitOpenError";
|
|
18037
|
+
this.circuitState = state;
|
|
18038
|
+
}
|
|
18039
|
+
};
|
|
18040
|
+
|
|
17744
18041
|
// src/client/index.ts
|
|
18042
|
+
var prebuildModule = null;
|
|
18043
|
+
function getPrebuildModule() {
|
|
18044
|
+
if (prebuildModule !== null) return prebuildModule;
|
|
18045
|
+
if (typeof process === "undefined" || !process.versions?.node) {
|
|
18046
|
+
return null;
|
|
18047
|
+
}
|
|
18048
|
+
try {
|
|
18049
|
+
prebuildModule = require("../prebuild/loader");
|
|
18050
|
+
return prebuildModule;
|
|
18051
|
+
} catch {
|
|
18052
|
+
return null;
|
|
18053
|
+
}
|
|
18054
|
+
}
|
|
17745
18055
|
setSdkVersion(SDK_VERSION);
|
|
18056
|
+
var DEFAULT_BROWSER_TIMEOUT_MS = 5e3;
|
|
18057
|
+
var DEFAULT_SERVER_TIMEOUT_MS = 8e3;
|
|
18058
|
+
function generateRequestId2() {
|
|
18059
|
+
return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
18060
|
+
}
|
|
18061
|
+
function isAbortError(error) {
|
|
18062
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
18063
|
+
return true;
|
|
18064
|
+
}
|
|
18065
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
18066
|
+
return true;
|
|
18067
|
+
}
|
|
18068
|
+
return false;
|
|
18069
|
+
}
|
|
18070
|
+
function getNetworkErrorCode(error) {
|
|
18071
|
+
if (error.name === "TimeoutError" || error.name === "AbortError") {
|
|
18072
|
+
return "network:timeout";
|
|
18073
|
+
}
|
|
18074
|
+
const nodeError = error;
|
|
18075
|
+
if (nodeError.code) {
|
|
18076
|
+
switch (nodeError.code) {
|
|
18077
|
+
case "ETIMEDOUT":
|
|
18078
|
+
case "ESOCKETTIMEDOUT":
|
|
18079
|
+
return "network:timeout";
|
|
18080
|
+
case "ENOTFOUND":
|
|
18081
|
+
case "EAI_AGAIN":
|
|
18082
|
+
return "network:dns_error";
|
|
18083
|
+
case "ECONNREFUSED":
|
|
18084
|
+
case "ECONNRESET":
|
|
18085
|
+
case "EPIPE":
|
|
18086
|
+
return "network:connection_error";
|
|
18087
|
+
}
|
|
18088
|
+
}
|
|
18089
|
+
const message = error.message.toLowerCase();
|
|
18090
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
18091
|
+
return "network:timeout";
|
|
18092
|
+
}
|
|
18093
|
+
if (message.includes("dns") || message.includes("getaddrinfo") || message.includes("enotfound")) {
|
|
18094
|
+
return "network:dns_error";
|
|
18095
|
+
}
|
|
18096
|
+
return "network:connection_error";
|
|
18097
|
+
}
|
|
17746
18098
|
function convertToTypedError(error) {
|
|
18099
|
+
if (isAbortError(error)) {
|
|
18100
|
+
throw error;
|
|
18101
|
+
}
|
|
17747
18102
|
if (error instanceof ApiEnvelopeError) {
|
|
17748
18103
|
throw new RiverbankApiError({
|
|
17749
18104
|
code: error.code,
|
|
@@ -17757,15 +18112,30 @@ function convertToTypedError(error) {
|
|
|
17757
18112
|
if (error instanceof ApiRequestError && error.body && typeof error.body === "object") {
|
|
17758
18113
|
const body = error.body;
|
|
17759
18114
|
if (isApiError(body)) {
|
|
17760
|
-
|
|
18115
|
+
const envelopeError = body.error;
|
|
18116
|
+
throw new RiverbankApiError({
|
|
18117
|
+
...envelopeError,
|
|
18118
|
+
retryAfterMs: error.retryAfterMs
|
|
18119
|
+
});
|
|
17761
18120
|
}
|
|
17762
18121
|
}
|
|
18122
|
+
if (error instanceof TypeError || error instanceof Error && !("status" in error)) {
|
|
18123
|
+
const networkError = error;
|
|
18124
|
+
throw new RiverbankApiError({
|
|
18125
|
+
code: getNetworkErrorCode(networkError),
|
|
18126
|
+
message: networkError.message || "Network request failed",
|
|
18127
|
+
requestId: `local-${Date.now()}`,
|
|
18128
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18129
|
+
status: 0
|
|
18130
|
+
// No HTTP response received
|
|
18131
|
+
});
|
|
18132
|
+
}
|
|
17763
18133
|
throw error;
|
|
17764
18134
|
}
|
|
17765
18135
|
function createRiverbankClient(config) {
|
|
17766
18136
|
if (!config.baseUrl) {
|
|
17767
18137
|
throw new Error(
|
|
17768
|
-
"baseUrl is required when creating a
|
|
18138
|
+
"baseUrl is required when creating a Riverbank client. Expected format: https://dashboard.example.com/api (must include /api path)"
|
|
17769
18139
|
);
|
|
17770
18140
|
}
|
|
17771
18141
|
if (!config.baseUrl.endsWith("/api")) {
|
|
@@ -17776,59 +18146,212 @@ function createRiverbankClient(config) {
|
|
|
17776
18146
|
const cacheEnabled = config.cache?.enabled ?? true;
|
|
17777
18147
|
const cacheTTL = (config.cache?.ttl ?? 300) * 1e3;
|
|
17778
18148
|
const cacheMaxSize = config.cache?.maxSize ?? 100;
|
|
18149
|
+
const resilienceEnabled = config.resilience?.enabled ?? true;
|
|
18150
|
+
const staleIfError = config.resilience?.staleIfError ?? true;
|
|
18151
|
+
const staleTtlMs = (config.resilience?.staleTtlSec ?? 300) * 1e3;
|
|
18152
|
+
const requestTimeoutMs = config.resilience?.requestTimeoutMs ?? (typeof window !== "undefined" ? DEFAULT_BROWSER_TIMEOUT_MS : DEFAULT_SERVER_TIMEOUT_MS);
|
|
18153
|
+
const retryConfig = {
|
|
18154
|
+
maxAttempts: config.resilience?.retry?.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts,
|
|
18155
|
+
baseDelayMs: config.resilience?.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
|
|
18156
|
+
maxDelayMs: config.resilience?.retry?.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs,
|
|
18157
|
+
jitter: config.resilience?.retry?.jitter ?? DEFAULT_RETRY_CONFIG.jitter,
|
|
18158
|
+
retryOn: config.resilience?.retry?.retryOn
|
|
18159
|
+
};
|
|
18160
|
+
const circuitBreakerConfig = {
|
|
18161
|
+
failureThreshold: config.resilience?.circuitBreaker?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
|
|
18162
|
+
resetTimeoutMs: config.resilience?.circuitBreaker?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
|
|
18163
|
+
halfOpenMaxRequests: config.resilience?.circuitBreaker?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
|
|
18164
|
+
};
|
|
17779
18165
|
const apiClient = createBearerAPIClient(config.apiKey, config.baseUrl);
|
|
17780
18166
|
const cache = new SimpleCache({
|
|
17781
18167
|
maxSize: cacheMaxSize,
|
|
17782
|
-
ttl: cacheTTL
|
|
18168
|
+
ttl: cacheTTL,
|
|
18169
|
+
staleTtl: staleTtlMs
|
|
17783
18170
|
});
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
18171
|
+
const circuitBreaker = new CircuitBreaker(circuitBreakerConfig);
|
|
18172
|
+
const prebuildDir = config.resilience?.prebuildDir;
|
|
18173
|
+
const prebuildMod = prebuildDir ? getPrebuildModule() : null;
|
|
18174
|
+
const prebuildLoader = prebuildMod?.canUsePrebuild() && prebuildDir ? new prebuildMod.PrebuildLoader({
|
|
18175
|
+
prebuildDir,
|
|
18176
|
+
maxPrebuildAgeSec: config.resilience?.maxPrebuildAgeSec
|
|
18177
|
+
}) : null;
|
|
18178
|
+
let lastStatus = null;
|
|
18179
|
+
let isDegraded = false;
|
|
18180
|
+
function emitStatus(source, data, details) {
|
|
18181
|
+
const status = {
|
|
18182
|
+
source,
|
|
18183
|
+
isPreview: details.isPreview,
|
|
18184
|
+
cacheKey: details.cacheKey,
|
|
18185
|
+
error: details.error,
|
|
18186
|
+
staleAgeSec: details.staleAgeSec,
|
|
18187
|
+
prebuildAgeSec: details.prebuildAgeSec,
|
|
18188
|
+
circuit: circuitBreaker.getState(),
|
|
18189
|
+
requestId: details.requestId,
|
|
18190
|
+
durationMs: details.durationMs
|
|
18191
|
+
};
|
|
18192
|
+
lastStatus = status;
|
|
18193
|
+
config.resilience?.onStatusChange?.(status);
|
|
18194
|
+
const nowDegraded = source === "stale" || source === "error";
|
|
18195
|
+
if (nowDegraded !== isDegraded) {
|
|
18196
|
+
isDegraded = nowDegraded;
|
|
18197
|
+
config.resilience?.onDegradedMode?.(nowDegraded, status);
|
|
18198
|
+
}
|
|
18199
|
+
return data;
|
|
18200
|
+
}
|
|
18201
|
+
async function resilientFetch(cacheKey, fetcher, options) {
|
|
18202
|
+
const requestId = generateRequestId2();
|
|
18203
|
+
const startTime = Date.now();
|
|
18204
|
+
const isPreview = options.preview ?? false;
|
|
18205
|
+
const statusDetails = (extra = {}) => ({
|
|
18206
|
+
requestId,
|
|
18207
|
+
cacheKey,
|
|
18208
|
+
isPreview,
|
|
18209
|
+
durationMs: Date.now() - startTime,
|
|
18210
|
+
...extra
|
|
18211
|
+
});
|
|
18212
|
+
if (cacheEnabled && !options.force) {
|
|
18213
|
+
const fresh = cache.getFresh(cacheKey);
|
|
18214
|
+
if (fresh !== null) {
|
|
18215
|
+
return emitStatus("cache", fresh, statusDetails());
|
|
17789
18216
|
}
|
|
17790
18217
|
}
|
|
17791
|
-
|
|
18218
|
+
if (resilienceEnabled && circuitBreaker.isOpen()) {
|
|
18219
|
+
if (!isPreview && staleIfError) {
|
|
18220
|
+
const stale = cache.getStale(cacheKey);
|
|
18221
|
+
if (stale) {
|
|
18222
|
+
return emitStatus("stale", stale.value, statusDetails({
|
|
18223
|
+
staleAgeSec: stale.staleAgeSec,
|
|
18224
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
18225
|
+
}));
|
|
18226
|
+
}
|
|
18227
|
+
}
|
|
18228
|
+
if (!isPreview && options.prebuildFallback) {
|
|
18229
|
+
const prebuildResult = options.prebuildFallback();
|
|
18230
|
+
if (prebuildResult) {
|
|
18231
|
+
return emitStatus("prebuild", prebuildResult.data, statusDetails({
|
|
18232
|
+
prebuildAgeSec: prebuildResult.prebuildAgeSec,
|
|
18233
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
18234
|
+
}));
|
|
18235
|
+
}
|
|
18236
|
+
}
|
|
18237
|
+
const circuitState = circuitBreaker.getState();
|
|
18238
|
+
emitStatus("error", null, statusDetails({
|
|
18239
|
+
error: { code: "circuit_open", message: "Circuit breaker is open" }
|
|
18240
|
+
}));
|
|
18241
|
+
throw new CircuitOpenError(circuitState);
|
|
18242
|
+
}
|
|
17792
18243
|
try {
|
|
17793
|
-
|
|
17794
|
-
|
|
18244
|
+
let data;
|
|
18245
|
+
if (resilienceEnabled) {
|
|
18246
|
+
if (circuitBreaker.getState().state === "half-open") {
|
|
18247
|
+
circuitBreaker.incrementHalfOpenRequests();
|
|
18248
|
+
}
|
|
18249
|
+
data = await fetchWithTimeoutAndRetry(
|
|
18250
|
+
async (timeoutSignal) => {
|
|
18251
|
+
const combinedSignal = options.signal ? combineAbortSignals(timeoutSignal, options.signal) : timeoutSignal;
|
|
18252
|
+
try {
|
|
18253
|
+
const response = await fetcher(combinedSignal);
|
|
18254
|
+
return unwrapResponse(response);
|
|
18255
|
+
} catch (error) {
|
|
18256
|
+
convertToTypedError(error);
|
|
18257
|
+
}
|
|
18258
|
+
},
|
|
18259
|
+
{
|
|
18260
|
+
...retryConfig,
|
|
18261
|
+
requestTimeoutMs
|
|
18262
|
+
}
|
|
18263
|
+
);
|
|
18264
|
+
circuitBreaker.recordSuccess();
|
|
18265
|
+
} else {
|
|
18266
|
+
try {
|
|
18267
|
+
const response = await fetcher(options.signal ?? new AbortController().signal);
|
|
18268
|
+
data = unwrapResponse(response);
|
|
18269
|
+
} catch (error) {
|
|
18270
|
+
convertToTypedError(error);
|
|
18271
|
+
}
|
|
18272
|
+
}
|
|
18273
|
+
if (cacheEnabled) {
|
|
18274
|
+
cache.set(cacheKey, data);
|
|
18275
|
+
}
|
|
18276
|
+
return emitStatus("live", data, statusDetails());
|
|
17795
18277
|
} catch (error) {
|
|
17796
|
-
|
|
18278
|
+
if (resilienceEnabled && error instanceof Error) {
|
|
18279
|
+
circuitBreaker.recordFailure(error);
|
|
18280
|
+
}
|
|
18281
|
+
if (!isPreview && staleIfError && cacheEnabled) {
|
|
18282
|
+
const stale = cache.getStale(cacheKey);
|
|
18283
|
+
if (stale) {
|
|
18284
|
+
const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
18285
|
+
return emitStatus("stale", stale.value, statusDetails({
|
|
18286
|
+
staleAgeSec: stale.staleAgeSec,
|
|
18287
|
+
error: errorInfo2
|
|
18288
|
+
}));
|
|
18289
|
+
}
|
|
18290
|
+
}
|
|
18291
|
+
if (!isPreview && options.prebuildFallback) {
|
|
18292
|
+
const prebuildResult = options.prebuildFallback();
|
|
18293
|
+
if (prebuildResult) {
|
|
18294
|
+
const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
18295
|
+
return emitStatus("prebuild", prebuildResult.data, statusDetails({
|
|
18296
|
+
prebuildAgeSec: prebuildResult.prebuildAgeSec,
|
|
18297
|
+
error: errorInfo2
|
|
18298
|
+
}));
|
|
18299
|
+
}
|
|
18300
|
+
}
|
|
18301
|
+
const errorInfo = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
|
|
18302
|
+
emitStatus("error", null, statusDetails({ error: errorInfo }));
|
|
18303
|
+
throw error;
|
|
17797
18304
|
}
|
|
17798
|
-
|
|
17799
|
-
|
|
18305
|
+
}
|
|
18306
|
+
function combineAbortSignals(...signals) {
|
|
18307
|
+
const controller = new AbortController();
|
|
18308
|
+
for (const signal of signals) {
|
|
18309
|
+
if (signal.aborted) {
|
|
18310
|
+
controller.abort(signal.reason);
|
|
18311
|
+
break;
|
|
18312
|
+
}
|
|
18313
|
+
signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
|
|
17800
18314
|
}
|
|
17801
|
-
return
|
|
18315
|
+
return controller.signal;
|
|
17802
18316
|
}
|
|
17803
18317
|
return {
|
|
17804
18318
|
async getSite(params) {
|
|
17805
|
-
const { slug, domain, id } = params;
|
|
18319
|
+
const { slug, domain, id, signal } = params;
|
|
17806
18320
|
if (!slug && !domain && !id) {
|
|
17807
18321
|
throw new Error(
|
|
17808
18322
|
`getSite() requires at least one identifier: slug, domain, or id. Received: ${JSON.stringify(params)}`
|
|
17809
18323
|
);
|
|
17810
18324
|
}
|
|
17811
18325
|
const cacheKey = `site:${slug || domain || id}`;
|
|
17812
|
-
|
|
18326
|
+
const siteId = id || slug || domain;
|
|
18327
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17813
18328
|
const apiParams = {};
|
|
17814
18329
|
if (params.slug) apiParams.slug = params.slug;
|
|
17815
18330
|
if (params.domain) apiParams.domain = params.domain;
|
|
17816
18331
|
if (params.id) apiParams.id = params.id;
|
|
17817
|
-
return await apiClient({ endpoint: "getSite", params: apiParams });
|
|
18332
|
+
return await apiClient({ endpoint: "getSite", params: apiParams, options: { signal: sig } });
|
|
18333
|
+
}, {
|
|
18334
|
+
signal,
|
|
18335
|
+
prebuildFallback: prebuildLoader && siteId ? () => prebuildLoader.loadSite(siteId) : void 0
|
|
17818
18336
|
});
|
|
17819
18337
|
},
|
|
17820
18338
|
async getPage(params) {
|
|
17821
|
-
const { siteId, path, preview = false } = params;
|
|
18339
|
+
const { siteId, path, preview = false, signal } = params;
|
|
17822
18340
|
const cacheKey = `page:${siteId}:${path}:${preview}`;
|
|
17823
|
-
return
|
|
17824
|
-
return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path, preview } });
|
|
18341
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
18342
|
+
return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path, preview }, options: { signal: sig } });
|
|
18343
|
+
}, {
|
|
18344
|
+
preview,
|
|
18345
|
+
signal,
|
|
18346
|
+
// Prebuild fallback only for published pages (not preview)
|
|
18347
|
+
prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadPage(siteId, path) : void 0
|
|
17825
18348
|
});
|
|
17826
18349
|
},
|
|
17827
18350
|
async getEntries(params) {
|
|
17828
|
-
const { siteId, contentType, limit, offset, order, preview = false, mode, entryIds, includeMeta } = params;
|
|
18351
|
+
const { siteId, contentType, limit, offset, order, preview = false, mode, entryIds, includeMeta, signal } = params;
|
|
17829
18352
|
const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
|
|
17830
18353
|
const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${offset ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}:${includeMeta ?? ""}`;
|
|
17831
|
-
return
|
|
18354
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17832
18355
|
let orderParam;
|
|
17833
18356
|
if (order === "newest") {
|
|
17834
18357
|
orderParam = "published_at.desc";
|
|
@@ -17850,47 +18373,52 @@ function createRiverbankClient(config) {
|
|
|
17850
18373
|
entryIds: JSON.stringify(entryIds)
|
|
17851
18374
|
}
|
|
17852
18375
|
};
|
|
17853
|
-
return await apiClient({ endpoint: "listPublishedEntries", params: apiParams });
|
|
18376
|
+
return await apiClient({ endpoint: "listPublishedEntries", params: apiParams, options: { signal: sig } });
|
|
18377
|
+
}, {
|
|
18378
|
+
preview,
|
|
18379
|
+
signal,
|
|
18380
|
+
// Prebuild fallback only for published entries (not preview, not manual mode)
|
|
18381
|
+
prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadEntries(siteId, params) : void 0
|
|
17854
18382
|
});
|
|
17855
18383
|
},
|
|
17856
18384
|
async getEntry(params) {
|
|
17857
|
-
const { siteId, contentType, slug } = params;
|
|
18385
|
+
const { siteId, contentType, slug, signal } = params;
|
|
17858
18386
|
const cacheKey = `entry:${siteId}:${contentType}:${slug}`;
|
|
17859
|
-
return
|
|
17860
|
-
return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug } });
|
|
17861
|
-
});
|
|
18387
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
18388
|
+
return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug }, options: { signal: sig } });
|
|
18389
|
+
}, { signal });
|
|
17862
18390
|
},
|
|
17863
18391
|
async getPublicFormById(params) {
|
|
17864
|
-
const { formId } = params;
|
|
18392
|
+
const { formId, signal } = params;
|
|
17865
18393
|
if (!formId) {
|
|
17866
18394
|
throw new Error("getPublicFormById() requires formId");
|
|
17867
18395
|
}
|
|
17868
18396
|
const cacheKey = `public-form:${formId}`;
|
|
17869
|
-
return
|
|
17870
|
-
return await apiClient({ endpoint: "getPublicFormById", params: { formId } });
|
|
17871
|
-
});
|
|
18397
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
18398
|
+
return await apiClient({ endpoint: "getPublicFormById", params: { formId }, options: { signal: sig } });
|
|
18399
|
+
}, { signal });
|
|
17872
18400
|
},
|
|
17873
18401
|
async getPublicBookingServices(params) {
|
|
17874
|
-
const { siteId, ids } = params;
|
|
18402
|
+
const { siteId, ids, signal } = params;
|
|
17875
18403
|
if (!siteId) {
|
|
17876
18404
|
throw new Error("getPublicBookingServices() requires siteId");
|
|
17877
18405
|
}
|
|
17878
18406
|
const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
|
|
17879
|
-
return
|
|
18407
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17880
18408
|
const apiParams = {
|
|
17881
18409
|
siteId,
|
|
17882
18410
|
...ids && { ids }
|
|
17883
18411
|
};
|
|
17884
|
-
return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams });
|
|
17885
|
-
});
|
|
18412
|
+
return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams, options: { signal: sig } });
|
|
18413
|
+
}, { signal });
|
|
17886
18414
|
},
|
|
17887
18415
|
async listPublicEvents(params) {
|
|
17888
|
-
const { siteId, limit, from, to, stage } = params;
|
|
18416
|
+
const { siteId, limit, from, to, stage, signal } = params;
|
|
17889
18417
|
if (!siteId) {
|
|
17890
18418
|
throw new Error("listPublicEvents() requires siteId");
|
|
17891
18419
|
}
|
|
17892
18420
|
const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
|
|
17893
|
-
return
|
|
18421
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17894
18422
|
const apiParams = {
|
|
17895
18423
|
siteId,
|
|
17896
18424
|
...typeof limit === "number" && { limit: String(limit) },
|
|
@@ -17898,40 +18426,46 @@ function createRiverbankClient(config) {
|
|
|
17898
18426
|
...to && { to },
|
|
17899
18427
|
...stage && { stage }
|
|
17900
18428
|
};
|
|
17901
|
-
return await apiClient({ endpoint: "listPublicEvents", params: apiParams });
|
|
17902
|
-
});
|
|
18429
|
+
return await apiClient({ endpoint: "listPublicEvents", params: apiParams, options: { signal: sig } });
|
|
18430
|
+
}, { signal });
|
|
17903
18431
|
},
|
|
17904
18432
|
async resolveEventOccurrence(params) {
|
|
17905
|
-
const { siteId, entryId, segment } = params;
|
|
18433
|
+
const { siteId, entryId, segment, signal } = params;
|
|
17906
18434
|
if (!siteId || !entryId || !segment) {
|
|
17907
18435
|
throw new Error("resolveEventOccurrence() requires siteId, entryId, and segment");
|
|
17908
18436
|
}
|
|
17909
18437
|
const cacheKey = `event-occurrence:${siteId}:${entryId}:${segment}`;
|
|
17910
|
-
return
|
|
18438
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17911
18439
|
return await apiClient({
|
|
17912
18440
|
endpoint: "resolveEventOccurrence",
|
|
17913
|
-
params: { siteId, entryId, segment }
|
|
18441
|
+
params: { siteId, entryId, segment },
|
|
18442
|
+
options: { signal: sig }
|
|
17914
18443
|
});
|
|
17915
|
-
});
|
|
18444
|
+
}, { signal });
|
|
17916
18445
|
},
|
|
17917
18446
|
async checkRedirect(params) {
|
|
17918
|
-
const { siteId, path } = params;
|
|
18447
|
+
const { siteId, path, signal } = params;
|
|
17919
18448
|
if (!siteId || !path) {
|
|
17920
18449
|
throw new Error("checkRedirect() requires siteId and path");
|
|
17921
18450
|
}
|
|
17922
18451
|
const cacheKey = `redirect:${siteId}:${path}`;
|
|
17923
|
-
return
|
|
18452
|
+
return resilientFetch(cacheKey, async (sig) => {
|
|
17924
18453
|
return await apiClient({
|
|
17925
18454
|
endpoint: "checkRedirect",
|
|
17926
|
-
params: { site: siteId, path }
|
|
18455
|
+
params: { site: siteId, path },
|
|
18456
|
+
options: { signal: sig }
|
|
17927
18457
|
});
|
|
17928
|
-
});
|
|
18458
|
+
}, { signal });
|
|
17929
18459
|
},
|
|
17930
18460
|
clearCache() {
|
|
17931
18461
|
cache.clear();
|
|
18462
|
+
},
|
|
18463
|
+
getLastEmittedStatus() {
|
|
18464
|
+
return lastStatus;
|
|
18465
|
+
},
|
|
18466
|
+
getCircuitState() {
|
|
18467
|
+
return circuitBreaker.getState();
|
|
17932
18468
|
}
|
|
17933
|
-
// Cast to RiverbankClient to satisfy overloaded getEntries signature
|
|
17934
|
-
// The implementation correctly returns the right type based on includeMeta
|
|
17935
18469
|
};
|
|
17936
18470
|
}
|
|
17937
18471
|
// Annotate the CommonJS export names for ESM import in node:
|