@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.
Files changed (184) hide show
  1. package/README.md +229 -0
  2. package/dist/cli/index.js +42 -95
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/cli/init-docs/content/agents-section.md +50 -0
  5. package/dist/cli/init-docs/content/cli-reference.md +574 -0
  6. package/dist/cli/init-docs/content/content-management.md +384 -0
  7. package/dist/cli/init-docs/content/context-brand.md +125 -0
  8. package/dist/cli/init-docs/content/context-brief.md +77 -0
  9. package/dist/cli/init-docs/content/context-knowledge.md +111 -0
  10. package/dist/cli/init-docs/content/getting-started.md +130 -0
  11. package/dist/cli/init-docs/content/site-workflows-readme.md +96 -0
  12. package/dist/cli/init-docs/content/workflow-add-block.md +228 -0
  13. package/dist/cli/init-docs/content/workflow-create-page.md +193 -0
  14. package/dist/cli/init-docs/content/workflow-publish.md +280 -0
  15. package/dist/client/bookings.d.mts +2 -0
  16. package/dist/client/bookings.d.ts +2 -0
  17. package/dist/client/bookings.js +2956 -104
  18. package/dist/client/bookings.js.map +1 -1
  19. package/dist/client/bookings.mjs +2929 -70
  20. package/dist/client/bookings.mjs.map +1 -1
  21. package/dist/client/client.d.mts +2 -2
  22. package/dist/client/client.d.ts +2 -2
  23. package/dist/client/client.js +602 -68
  24. package/dist/client/client.js.map +1 -1
  25. package/dist/client/client.mjs +602 -68
  26. package/dist/client/client.mjs.map +1 -1
  27. package/dist/client/hooks.d.mts +2 -2
  28. package/dist/client/hooks.d.ts +2 -2
  29. package/dist/client/rendering/client.js +3070 -259
  30. package/dist/client/rendering/client.js.map +1 -1
  31. package/dist/client/rendering/client.mjs +3212 -395
  32. package/dist/client/rendering/client.mjs.map +1 -1
  33. package/dist/client/spam-protection.d.mts +55 -0
  34. package/dist/client/spam-protection.d.ts +55 -0
  35. package/dist/client/spam-protection.js +2915 -0
  36. package/dist/client/spam-protection.js.map +1 -0
  37. package/dist/client/spam-protection.mjs +2893 -0
  38. package/dist/client/spam-protection.mjs.map +1 -0
  39. package/dist/client/{usePage-BiOReg0_.d.ts → usePage-BYmJCCm1.d.ts} +132 -11
  40. package/dist/client/{usePage-BXjk8BhD.d.mts → usePage-DZtrWajy.d.mts} +132 -11
  41. package/dist/server/{Layout-wBtJLTVX.d.ts → Layout-Yluyb6sK.d.ts} +1 -1
  42. package/dist/server/{Layout-B7cvis7r.d.mts → Layout-qWLdVm5-.d.mts} +1 -1
  43. package/dist/server/chunk-2IZ6S225.js +122 -0
  44. package/dist/server/chunk-2IZ6S225.js.map +1 -0
  45. package/dist/server/chunk-4CV4JOE5.js +27 -0
  46. package/dist/server/chunk-4CV4JOE5.js.map +1 -0
  47. package/dist/server/chunk-5LRR64Y6.mjs +72 -0
  48. package/dist/server/chunk-5LRR64Y6.mjs.map +1 -0
  49. package/dist/server/chunk-NBTRDLCM.js +72 -0
  50. package/dist/server/chunk-NBTRDLCM.js.map +1 -0
  51. package/dist/server/chunk-NFEGQTCC.mjs +27 -0
  52. package/dist/server/{chunk-7FIJSGHU.mjs → chunk-NFQLH5IA.mjs} +856 -74
  53. package/dist/server/chunk-NFQLH5IA.mjs.map +1 -0
  54. package/dist/server/chunk-PPHZV6YD.mjs +122 -0
  55. package/dist/server/chunk-PPHZV6YD.mjs.map +1 -0
  56. package/dist/server/{chunk-P7UVAMK6.js → chunk-VLXTNB2C.js} +866 -84
  57. package/dist/server/chunk-VLXTNB2C.js.map +1 -0
  58. package/dist/server/{components-CMMwDXTW.d.mts → components-DNHfSCML.d.mts} +3 -3
  59. package/dist/server/{components-CICSJyp_.d.ts → components-Di5ME6He.d.ts} +3 -3
  60. package/dist/server/components.d.mts +5 -5
  61. package/dist/server/components.d.ts +5 -5
  62. package/dist/server/components.js +1 -1
  63. package/dist/server/components.mjs +1 -1
  64. package/dist/server/config-validation.js +1 -1
  65. package/dist/server/config-validation.mjs +1 -1
  66. package/dist/server/config.js +1 -1
  67. package/dist/server/config.mjs +1 -1
  68. package/dist/server/data.d.mts +2 -2
  69. package/dist/server/data.d.ts +2 -2
  70. package/dist/server/data.js +1 -1
  71. package/dist/server/data.mjs +1 -1
  72. package/dist/server/env.d.mts +109 -0
  73. package/dist/server/env.d.ts +109 -0
  74. package/dist/server/env.js +14 -0
  75. package/dist/server/env.js.map +1 -0
  76. package/dist/server/env.mjs +14 -0
  77. package/dist/server/{index-DI_qlYx3.d.mts → index--Oyunk_B.d.mts} +2 -2
  78. package/dist/server/{index-BTwWvSBu.d.ts → index-C9Ra8dza.d.ts} +2 -2
  79. package/dist/server/{index-Bucs6UqG.d.mts → index-Clm3skz_.d.mts} +1 -1
  80. package/dist/server/{index-Cp7tJuRt.d.ts → index-DLvNddi-.d.ts} +1 -1
  81. package/dist/server/index.d.mts +216 -5
  82. package/dist/server/index.d.ts +216 -5
  83. package/dist/server/index.js +301 -4
  84. package/dist/server/index.js.map +1 -1
  85. package/dist/server/index.mjs +301 -4
  86. package/dist/server/index.mjs.map +1 -1
  87. package/dist/server/{loadContent-DmgpFcFC.d.ts → loadContent-D7LQwI0o.d.ts} +3 -3
  88. package/dist/server/{loadContent-C-YYUKQa.d.mts → loadContent-DVfuBLiZ.d.mts} +3 -3
  89. package/dist/server/{loadPage-IDGVDFBB.js → loadPage-AXNAERDS.js} +2 -2
  90. package/dist/server/{loadPage-IDGVDFBB.js.map → loadPage-AXNAERDS.js.map} +1 -1
  91. package/dist/server/{loadPage-DP3nrHBi.d.ts → loadPage-BmYJCe_V.d.ts} +2 -2
  92. package/dist/server/{loadPage-B8mQUUSo.d.mts → loadPage-BucnLHmE.d.mts} +2 -2
  93. package/dist/server/{loadPage-DNQTTRHL.mjs → loadPage-XR7ORQ2E.mjs} +2 -2
  94. package/dist/server/loadPage-XR7ORQ2E.mjs.map +1 -0
  95. package/dist/server/metadata.d.mts +4 -4
  96. package/dist/server/metadata.d.ts +4 -4
  97. package/dist/server/metadata.js +1 -1
  98. package/dist/server/metadata.mjs +1 -1
  99. package/dist/server/navigation.d.mts +2 -2
  100. package/dist/server/navigation.d.ts +2 -2
  101. package/dist/server/navigation.js +1 -1
  102. package/dist/server/navigation.mjs +1 -1
  103. package/dist/server/next/revalidate.d.mts +66 -0
  104. package/dist/server/next/revalidate.d.ts +66 -0
  105. package/dist/server/next/revalidate.js +60 -0
  106. package/dist/server/next/revalidate.js.map +1 -0
  107. package/dist/server/next/revalidate.mjs +60 -0
  108. package/dist/server/next/revalidate.mjs.map +1 -0
  109. package/dist/server/next/tags.d.mts +81 -0
  110. package/dist/server/next/tags.d.ts +81 -0
  111. package/dist/server/next/tags.js +36 -0
  112. package/dist/server/next/tags.js.map +1 -0
  113. package/dist/server/next/tags.mjs +36 -0
  114. package/dist/server/next/tags.mjs.map +1 -0
  115. package/dist/server/next.d.mts +164 -6
  116. package/dist/server/next.d.ts +164 -6
  117. package/dist/server/next.js +79 -11
  118. package/dist/server/next.js.map +1 -1
  119. package/dist/server/next.mjs +76 -8
  120. package/dist/server/next.mjs.map +1 -1
  121. package/dist/server/rendering/server.d.mts +4 -4
  122. package/dist/server/rendering/server.d.ts +4 -4
  123. package/dist/server/rendering/server.js +1 -1
  124. package/dist/server/rendering/server.mjs +1 -1
  125. package/dist/server/rendering.d.mts +7 -7
  126. package/dist/server/rendering.d.ts +7 -7
  127. package/dist/server/rendering.js +3 -3
  128. package/dist/server/rendering.js.map +1 -1
  129. package/dist/server/rendering.mjs +4 -4
  130. package/dist/server/routing.d.mts +3 -3
  131. package/dist/server/routing.d.ts +3 -3
  132. package/dist/server/routing.js +2 -2
  133. package/dist/server/routing.mjs +2 -2
  134. package/dist/server/server.d.mts +5 -5
  135. package/dist/server/server.d.ts +5 -5
  136. package/dist/server/server.js +5 -5
  137. package/dist/server/server.js.map +1 -1
  138. package/dist/server/server.mjs +5 -5
  139. package/dist/server/theme-bridge.js +1 -1
  140. package/dist/server/theme-bridge.mjs +1 -1
  141. package/dist/server/theme.js +1 -1
  142. package/dist/server/theme.mjs +1 -1
  143. package/dist/server/{types-BvcJU7zk.d.ts → types-BRQyLrQU.d.ts} +132 -11
  144. package/dist/server/{types-Dsu9wsUh.d.mts → types-BSV6Vc-P.d.mts} +2 -2
  145. package/dist/server/{types-1cLz0vnq.d.mts → types-C-LShyIg.d.mts} +132 -11
  146. package/dist/server/{types-CVykEqXN.d.ts → types-Dt98DeYa.d.ts} +2 -2
  147. package/dist/server/webhooks.d.mts +81 -0
  148. package/dist/server/webhooks.d.ts +81 -0
  149. package/dist/server/webhooks.js +12 -0
  150. package/dist/server/webhooks.js.map +1 -0
  151. package/dist/server/webhooks.mjs +12 -0
  152. package/dist/server/webhooks.mjs.map +1 -0
  153. package/package.json +29 -3
  154. package/dist/client/resolver-BhueZVxZ.d.mts +0 -61
  155. package/dist/client/resolver-BhueZVxZ.d.ts +0 -61
  156. package/dist/client/usePage--fGlyrgj.d.mts +0 -6439
  157. package/dist/client/usePage-BBcFCxOU.d.ts +0 -6297
  158. package/dist/client/usePage-BC8Q2E3t.d.mts +0 -6431
  159. package/dist/client/usePage-BTPnCuWC.d.mts +0 -6511
  160. package/dist/client/usePage-BafOS9UT.d.mts +0 -6512
  161. package/dist/client/usePage-BcjWPXvh.d.mts +0 -6388
  162. package/dist/client/usePage-Bnx-kA6x.d.mts +0 -6670
  163. package/dist/client/usePage-BvKAa3Zw.d.mts +0 -366
  164. package/dist/client/usePage-BvKAa3Zw.d.ts +0 -366
  165. package/dist/client/usePage-BydHcMYB.d.mts +0 -6297
  166. package/dist/client/usePage-C3ZKNwY7.d.mts +0 -6393
  167. package/dist/client/usePage-CE7X5NcN.d.ts +0 -6439
  168. package/dist/client/usePage-CHEybPMD.d.ts +0 -6429
  169. package/dist/client/usePage-CrKw1H6Y.d.ts +0 -6338
  170. package/dist/client/usePage-CyYpOJud.d.ts +0 -6388
  171. package/dist/client/usePage-D4fxZbRR.d.mts +0 -6429
  172. package/dist/client/usePage-DMI8ImsU.d.mts +0 -6338
  173. package/dist/client/usePage-DoPI6b8V.d.ts +0 -6511
  174. package/dist/client/usePage-DpRNZUtP.d.ts +0 -6431
  175. package/dist/client/usePage-QNWArrVO.d.ts +0 -6670
  176. package/dist/client/usePage-fBgPB6Oq.d.ts +0 -6512
  177. package/dist/client/usePage-gpVaeWDy.d.ts +0 -6393
  178. package/dist/server/chunk-7FIJSGHU.mjs.map +0 -1
  179. package/dist/server/chunk-BJTO5JO5.mjs +0 -11
  180. package/dist/server/chunk-DGUM43GV.js +0 -11
  181. package/dist/server/chunk-DGUM43GV.js.map +0 -1
  182. package/dist/server/chunk-P7UVAMK6.js.map +0 -1
  183. /package/dist/server/{chunk-BJTO5JO5.mjs.map → chunk-NFEGQTCC.mjs.map} +0 -0
  184. /package/dist/server/{loadPage-DNQTTRHL.mjs.map → env.mjs.map} +0 -0
@@ -17332,10 +17332,10 @@ function resolveApiBaseUrl() {
17332
17332
  var revalidateTag = null;
17333
17333
  if (typeof window === "undefined") {
17334
17334
  try {
17335
- const nextCache = require("next/cache");
17336
- revalidateTag = nextCache.revalidateTag;
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
- get(key) {
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 void 0;
17645
- if (Date.now() > entry.expires) {
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 void 0;
17682
+ return null;
17648
17683
  }
17649
- return entry.value;
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
- set(key, value) {
17652
- if (this.cache.size >= this.maxSize) {
17653
- const firstKey = this.cache.keys().next().value;
17654
- if (firstKey) {
17655
- this.cache.delete(firstKey);
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
- expires: Date.now() + this.ttl
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
- return this.get(key) !== void 0;
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.0";
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
- throw new RiverbankApiError(body.error);
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 Builder client. Expected format: https://dashboard.example.com/api (must include /api path)"
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
- async function cachedFetch(cacheKey, fetcher, options) {
17785
- if (cacheEnabled && !options?.force) {
17786
- const cached = cache.get(cacheKey);
17787
- if (cached !== void 0) {
17788
- return cached;
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
- let data;
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
- const response = await fetcher();
17794
- data = unwrapResponse(response);
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
- convertToTypedError(error);
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
- if (cacheEnabled) {
17799
- cache.set(cacheKey, data);
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 data;
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
- return cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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: