@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
@@ -17296,10 +17296,10 @@ function resolveApiBaseUrl() {
17296
17296
  var revalidateTag = null;
17297
17297
  if (typeof window === "undefined") {
17298
17298
  try {
17299
- const nextCache = __require("next/cache");
17300
- revalidateTag = nextCache.revalidateTag;
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
- get(key) {
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 void 0;
17609
- if (Date.now() > entry.expires) {
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 void 0;
17646
+ return null;
17612
17647
  }
17613
- return entry.value;
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
- set(key, value) {
17616
- if (this.cache.size >= this.maxSize) {
17617
- const firstKey = this.cache.keys().next().value;
17618
- if (firstKey) {
17619
- this.cache.delete(firstKey);
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
- expires: Date.now() + this.ttl
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
- return this.get(key) !== void 0;
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.0";
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
- throw new RiverbankApiError(body.error);
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 Builder client. Expected format: https://dashboard.example.com/api (must include /api path)"
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
- async function cachedFetch(cacheKey, fetcher, options) {
17749
- if (cacheEnabled && !options?.force) {
17750
- const cached = cache.get(cacheKey);
17751
- if (cached !== void 0) {
17752
- return cached;
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
- let data;
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
- const response = await fetcher();
17758
- data = unwrapResponse(response);
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
- convertToTypedError(error);
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
- if (cacheEnabled) {
17763
- cache.set(cacheKey, data);
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 data;
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
- return cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 cachedFetch(cacheKey, async () => {
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 {