@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
@@ -1,6 +1,246 @@
1
1
  import {
2
- __require
3
- } from "./chunk-BJTO5JO5.mjs";
2
+ __esm,
3
+ __export,
4
+ __toCommonJS
5
+ } from "./chunk-NFEGQTCC.mjs";
6
+
7
+ // src/constants.ts
8
+ var PREBUILD_PAGE_SIZE, DEFAULT_MAX_PREBUILD_AGE_SEC, DEFAULT_PREBUILD_DIR;
9
+ var init_constants = __esm({
10
+ "src/constants.ts"() {
11
+ "use strict";
12
+ PREBUILD_PAGE_SIZE = 50;
13
+ DEFAULT_MAX_PREBUILD_AGE_SEC = 86400;
14
+ DEFAULT_PREBUILD_DIR = ".riverbank-cache";
15
+ }
16
+ });
17
+
18
+ // src/prebuild/loader.ts
19
+ var loader_exports = {};
20
+ __export(loader_exports, {
21
+ PrebuildLoader: () => PrebuildLoader,
22
+ canUsePrebuild: () => canUsePrebuild,
23
+ createPrebuildLoader: () => createPrebuildLoader
24
+ });
25
+ import * as fs from "fs";
26
+ import * as path from "path";
27
+ function loadManifest(prebuildDir) {
28
+ if (cachedManifest?.dir === prebuildDir) {
29
+ return cachedManifest.manifest;
30
+ }
31
+ const manifestPath = path.join(prebuildDir, "manifest.json");
32
+ if (!fs.existsSync(manifestPath)) {
33
+ return null;
34
+ }
35
+ try {
36
+ const content = fs.readFileSync(manifestPath, "utf-8");
37
+ const manifest = JSON.parse(content);
38
+ cachedManifest = { manifest, dir: prebuildDir };
39
+ return manifest;
40
+ } catch {
41
+ return null;
42
+ }
43
+ }
44
+ function loadJsonFile(prebuildDir, relativePath) {
45
+ const filePath = path.join(prebuildDir, relativePath);
46
+ if (!fs.existsSync(filePath)) {
47
+ return null;
48
+ }
49
+ try {
50
+ const content = fs.readFileSync(filePath, "utf-8");
51
+ return JSON.parse(content);
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+ function isPrebuildExpired(manifest, maxAgeSec) {
57
+ const ageMs = Date.now() - new Date(manifest.generatedAt).getTime();
58
+ return ageMs > maxAgeSec * 1e3;
59
+ }
60
+ function getPrebuildAgeSec(manifest) {
61
+ const ageMs = Date.now() - new Date(manifest.generatedAt).getTime();
62
+ return Math.floor(ageMs / 1e3);
63
+ }
64
+ function canUsePrebuild() {
65
+ if (typeof process === "undefined" || !process.versions?.node) {
66
+ return false;
67
+ }
68
+ try {
69
+ return typeof fs.existsSync === "function";
70
+ } catch {
71
+ return false;
72
+ }
73
+ }
74
+ function createPrebuildLoader(config) {
75
+ return new PrebuildLoader(config);
76
+ }
77
+ var cachedManifest, PrebuildLoader;
78
+ var init_loader = __esm({
79
+ "src/prebuild/loader.ts"() {
80
+ "use strict";
81
+ init_constants();
82
+ cachedManifest = null;
83
+ PrebuildLoader = class {
84
+ constructor(config) {
85
+ this.prebuildDir = config.prebuildDir ?? DEFAULT_PREBUILD_DIR;
86
+ this.maxPrebuildAgeSec = config.maxPrebuildAgeSec ?? DEFAULT_MAX_PREBUILD_AGE_SEC;
87
+ }
88
+ /**
89
+ * Check if prebuild is available and not expired.
90
+ */
91
+ isAvailable() {
92
+ if (!canUsePrebuild()) {
93
+ return false;
94
+ }
95
+ const manifest = loadManifest(this.prebuildDir);
96
+ if (!manifest) {
97
+ return false;
98
+ }
99
+ return !isPrebuildExpired(manifest, this.maxPrebuildAgeSec);
100
+ }
101
+ /**
102
+ * Load site data from prebuild cache.
103
+ */
104
+ loadSite(siteId) {
105
+ const manifest = loadManifest(this.prebuildDir);
106
+ if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
107
+ return null;
108
+ }
109
+ const cacheFile = loadJsonFile(this.prebuildDir, "site.json");
110
+ if (!cacheFile || cacheFile.data.site.id !== siteId) {
111
+ return null;
112
+ }
113
+ return {
114
+ data: cacheFile.data,
115
+ prebuildAgeSec: getPrebuildAgeSec(manifest)
116
+ };
117
+ }
118
+ /**
119
+ * Load page data from prebuild cache.
120
+ */
121
+ loadPage(siteId, pagePath) {
122
+ const manifest = loadManifest(this.prebuildDir);
123
+ if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
124
+ return null;
125
+ }
126
+ const cacheKey = `page:${siteId}:${pagePath}:false`;
127
+ const relativePath = manifest.keyToFile[cacheKey];
128
+ if (!relativePath) {
129
+ return null;
130
+ }
131
+ const cacheFile = loadJsonFile(this.prebuildDir, relativePath);
132
+ if (!cacheFile) {
133
+ return null;
134
+ }
135
+ return {
136
+ data: cacheFile.data,
137
+ prebuildAgeSec: getPrebuildAgeSec(manifest)
138
+ };
139
+ }
140
+ /**
141
+ * Load entries from prebuild cache with runtime filtering.
142
+ *
143
+ * The prebuild stores ALL entries for each content type.
144
+ * Filtering, ordering, and pagination are applied at runtime.
145
+ */
146
+ loadEntries(siteId, params) {
147
+ if (params.preview) {
148
+ return null;
149
+ }
150
+ if (params.mode === "manual" && params.entryIds?.length) {
151
+ return null;
152
+ }
153
+ const manifest = loadManifest(this.prebuildDir);
154
+ if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
155
+ return null;
156
+ }
157
+ const cacheKey = `entries-all:${siteId}:${params.contentType}`;
158
+ const relativePath = manifest.keyToFile[cacheKey];
159
+ if (!relativePath) {
160
+ return null;
161
+ }
162
+ const cacheFile = loadJsonFile(this.prebuildDir, relativePath);
163
+ if (!cacheFile) {
164
+ return null;
165
+ }
166
+ let entries = [...cacheFile.entries];
167
+ if (params.order === "newest") {
168
+ entries.sort((a, b) => {
169
+ const aTime = a.publishedAt ? new Date(a.publishedAt).getTime() : 0;
170
+ const bTime = b.publishedAt ? new Date(b.publishedAt).getTime() : 0;
171
+ return bTime - aTime;
172
+ });
173
+ } else if (params.order === "oldest") {
174
+ entries.sort((a, b) => {
175
+ const aTime = a.publishedAt ? new Date(a.publishedAt).getTime() : 0;
176
+ const bTime = b.publishedAt ? new Date(b.publishedAt).getTime() : 0;
177
+ return aTime - bTime;
178
+ });
179
+ } else if (params.order === "title") {
180
+ entries.sort((a, b) => (a.title || "").localeCompare(b.title || ""));
181
+ }
182
+ const total = entries.length;
183
+ const offset = params.offset ?? 0;
184
+ const limit = params.limit ?? 10;
185
+ if (offset > 0) {
186
+ entries = entries.slice(offset);
187
+ }
188
+ entries = entries.slice(0, limit);
189
+ const prebuildAgeSec = getPrebuildAgeSec(manifest);
190
+ if (params.includeMeta) {
191
+ return {
192
+ data: {
193
+ entries,
194
+ total,
195
+ hasMore: offset + entries.length < total,
196
+ limit,
197
+ offset,
198
+ totalPages: Math.ceil(total / limit),
199
+ currentPage: Math.floor(offset / limit) + 1
200
+ },
201
+ prebuildAgeSec
202
+ };
203
+ }
204
+ return {
205
+ data: { entries },
206
+ prebuildAgeSec
207
+ };
208
+ }
209
+ /**
210
+ * Load navigation data from prebuild cache.
211
+ */
212
+ loadNavigation() {
213
+ const manifest = loadManifest(this.prebuildDir);
214
+ if (!manifest || isPrebuildExpired(manifest, this.maxPrebuildAgeSec)) {
215
+ return null;
216
+ }
217
+ const cacheFile = loadJsonFile(
218
+ this.prebuildDir,
219
+ "navigation/menus.json"
220
+ );
221
+ if (!cacheFile) {
222
+ return null;
223
+ }
224
+ return {
225
+ data: cacheFile.menus,
226
+ prebuildAgeSec: getPrebuildAgeSec(manifest)
227
+ };
228
+ }
229
+ /**
230
+ * Get the manifest for inspection.
231
+ */
232
+ getManifest() {
233
+ return loadManifest(this.prebuildDir);
234
+ }
235
+ /**
236
+ * Clear the cached manifest (for testing).
237
+ */
238
+ clearCache() {
239
+ cachedManifest = null;
240
+ }
241
+ };
242
+ }
243
+ });
4
244
 
5
245
  // ../api/src/endpoints.ts
6
246
  var ENDPOINT_DEFINITIONS = {
@@ -1659,10 +1899,10 @@ function resolveApiBaseUrl() {
1659
1899
  var revalidateTag = null;
1660
1900
  if (typeof window === "undefined") {
1661
1901
  try {
1662
- const nextCache = __require("next/cache");
1663
- revalidateTag = nextCache.revalidateTag;
1902
+ const dynamicRequire = new Function("modulePath", "return require(modulePath)");
1903
+ const nextCache = dynamicRequire("next/cache");
1904
+ revalidateTag = nextCache.revalidateTag ?? null;
1664
1905
  } catch {
1665
- revalidateTag = null;
1666
1906
  }
1667
1907
  }
1668
1908
  var sdkVersion;
@@ -1691,8 +1931,22 @@ var ApiRequestError = class extends Error {
1691
1931
  this.body = options.body;
1692
1932
  this.cause = options.cause;
1693
1933
  this.errorCode = options.errorCode;
1934
+ this.retryAfterMs = options.retryAfterMs;
1694
1935
  }
1695
1936
  };
1937
+ function parseRetryAfterHeader(headerValue) {
1938
+ if (!headerValue) return void 0;
1939
+ if (/^\d+$/.test(headerValue)) {
1940
+ const seconds = parseInt(headerValue, 10);
1941
+ return seconds * 1e3;
1942
+ }
1943
+ const date = new Date(headerValue);
1944
+ if (!isNaN(date.getTime())) {
1945
+ const delayMs = date.getTime() - Date.now();
1946
+ return delayMs > 0 ? delayMs : void 0;
1947
+ }
1948
+ return void 0;
1949
+ }
1696
1950
  function buildEndpointURL2(baseURL, endpoint) {
1697
1951
  return baseURL + API_ENDPOINTS[endpoint].path;
1698
1952
  }
@@ -1909,6 +2163,7 @@ function createParsedClient(rawClient) {
1909
2163
  if (!response.ok) {
1910
2164
  const body = await parseErrorBody(response);
1911
2165
  const requestId = response.headers.get("x-request-id") ?? void 0;
2166
+ const retryAfterMs = parseRetryAfterHeader(response.headers.get("retry-after"));
1912
2167
  throw new ApiRequestError(
1913
2168
  `Request to ${String(endpoint)} failed with status ${response.status}`,
1914
2169
  {
@@ -1917,7 +2172,8 @@ function createParsedClient(rawClient) {
1917
2172
  method: config.method,
1918
2173
  auth,
1919
2174
  requestId,
1920
- body
2175
+ body,
2176
+ retryAfterMs
1921
2177
  }
1922
2178
  );
1923
2179
  }
@@ -1965,38 +2221,117 @@ var SimpleCache = class {
1965
2221
  this.cache = /* @__PURE__ */ new Map();
1966
2222
  this.maxSize = options.maxSize ?? 100;
1967
2223
  this.ttl = options.ttl ?? 3e5;
2224
+ this.staleTtl = options.staleTtl ?? 3e5;
1968
2225
  }
1969
- get(key) {
2226
+ /**
2227
+ * Get a fresh value (within TTL)
2228
+ * @returns The value if fresh, null otherwise
2229
+ */
2230
+ getFresh(key) {
1970
2231
  const entry = this.cache.get(key);
1971
- if (!entry) return void 0;
1972
- if (Date.now() > entry.expires) {
2232
+ if (!entry) return null;
2233
+ const now = Date.now();
2234
+ if (now <= entry.freshUntil) {
2235
+ return entry.value;
2236
+ }
2237
+ return null;
2238
+ }
2239
+ /**
2240
+ * Get a value that may be stale (past TTL but within staleTtl)
2241
+ * @returns Object with value and stale age, or null if expired
2242
+ */
2243
+ getStale(key) {
2244
+ const entry = this.cache.get(key);
2245
+ if (!entry) return null;
2246
+ const now = Date.now();
2247
+ if (now > entry.staleUntil) {
1973
2248
  this.cache.delete(key);
1974
- return void 0;
2249
+ return null;
1975
2250
  }
1976
- return entry.value;
2251
+ const staleAgeSec = now <= entry.freshUntil ? 0 : Math.floor((now - entry.freshUntil) / 1e3);
2252
+ return {
2253
+ value: entry.value,
2254
+ staleAgeSec
2255
+ };
1977
2256
  }
1978
- set(key, value) {
1979
- if (this.cache.size >= this.maxSize) {
1980
- const firstKey = this.cache.keys().next().value;
1981
- if (firstKey) {
1982
- this.cache.delete(firstKey);
1983
- }
2257
+ /**
2258
+ * Store a value with TTL and stale window
2259
+ */
2260
+ set(key, value, options) {
2261
+ const ttl = options?.ttl ?? this.ttl;
2262
+ const staleTtl = options?.staleTtl ?? this.staleTtl;
2263
+ const now = Date.now();
2264
+ while (this.cache.size >= this.maxSize && !this.cache.has(key)) {
2265
+ this.evictOne(now);
1984
2266
  }
1985
2267
  this.cache.set(key, {
1986
2268
  value,
1987
- expires: Date.now() + this.ttl
2269
+ createdAt: now,
2270
+ freshUntil: now + ttl,
2271
+ staleUntil: now + ttl + staleTtl
1988
2272
  });
1989
2273
  }
2274
+ /**
2275
+ * Evict one entry to make room for a new one
2276
+ * Priority: oldest stale entry, then oldest fresh entry
2277
+ */
2278
+ evictOne(now) {
2279
+ let oldestStaleKey = null;
2280
+ let oldestStaleTime = Infinity;
2281
+ let oldestFreshKey = null;
2282
+ let oldestFreshTime = Infinity;
2283
+ for (const [key, entry] of this.cache) {
2284
+ if (now > entry.freshUntil) {
2285
+ if (entry.createdAt < oldestStaleTime) {
2286
+ oldestStaleTime = entry.createdAt;
2287
+ oldestStaleKey = key;
2288
+ }
2289
+ } else {
2290
+ if (entry.createdAt < oldestFreshTime) {
2291
+ oldestFreshTime = entry.createdAt;
2292
+ oldestFreshKey = key;
2293
+ }
2294
+ }
2295
+ }
2296
+ const keyToEvict = oldestStaleKey ?? oldestFreshKey;
2297
+ if (keyToEvict) {
2298
+ this.cache.delete(keyToEvict);
2299
+ }
2300
+ }
2301
+ /**
2302
+ * Remove all fully expired entries (past staleUntil)
2303
+ */
2304
+ prune() {
2305
+ const now = Date.now();
2306
+ for (const [key, entry] of this.cache) {
2307
+ if (now > entry.staleUntil) {
2308
+ this.cache.delete(key);
2309
+ }
2310
+ }
2311
+ }
2312
+ /**
2313
+ * Clear all entries
2314
+ */
1990
2315
  clear() {
1991
2316
  this.cache.clear();
1992
2317
  }
2318
+ /**
2319
+ * Check if a key exists and is not fully expired
2320
+ */
1993
2321
  has(key) {
1994
- return this.get(key) !== void 0;
2322
+ const entry = this.cache.get(key);
2323
+ if (!entry) return false;
2324
+ const now = Date.now();
2325
+ if (now > entry.staleUntil) {
2326
+ this.cache.delete(key);
2327
+ return false;
2328
+ }
2329
+ return now <= entry.freshUntil;
1995
2330
  }
1996
2331
  };
1997
2332
 
1998
2333
  // src/version.ts
1999
- var SDK_VERSION = "0.7.0";
2334
+ var SDK_VERSION = "0.7.3";
2000
2335
 
2001
2336
  // src/client/error.ts
2002
2337
  var RiverbankApiError = class _RiverbankApiError extends Error {
@@ -2008,8 +2343,31 @@ var RiverbankApiError = class _RiverbankApiError extends Error {
2008
2343
  this.status = apiError.status;
2009
2344
  this.fieldErrors = apiError.fieldErrors;
2010
2345
  this.timestamp = apiError.timestamp;
2346
+ this.retryAfterMs = "retryAfterMs" in apiError ? apiError.retryAfterMs : void 0;
2347
+ this.isRetryable = this.computeRetryable();
2011
2348
  Object.setPrototypeOf(this, _RiverbankApiError.prototype);
2012
2349
  }
2350
+ /**
2351
+ * Compute whether this error is retryable based on HTTP status code.
2352
+ * - 0 (network errors - no HTTP response): retryable
2353
+ * - 429 (rate limit): retryable
2354
+ * - 5xx (server errors): retryable
2355
+ * - 4xx (client errors, except 429): NOT retryable
2356
+ */
2357
+ computeRetryable() {
2358
+ if (this.status === 0) return true;
2359
+ if (this.status === 429) return true;
2360
+ if (this.status >= 500) return true;
2361
+ return false;
2362
+ }
2363
+ /**
2364
+ * Check if this is a network error (no HTTP response received)
2365
+ *
2366
+ * Matches: network:connection_error, network:timeout, network:dns_error
2367
+ */
2368
+ isNetworkError() {
2369
+ return this.code.startsWith("network:");
2370
+ }
2013
2371
  /**
2014
2372
  * Check if this error matches a specific error code
2015
2373
  *
@@ -2068,9 +2426,246 @@ var RiverbankApiError = class _RiverbankApiError extends Error {
2068
2426
  }
2069
2427
  };
2070
2428
 
2429
+ // src/client/resilience.ts
2430
+ var DEFAULT_RETRY_CONFIG = {
2431
+ maxAttempts: 3,
2432
+ baseDelayMs: 200,
2433
+ maxDelayMs: 2e3,
2434
+ jitter: "full"
2435
+ };
2436
+ var DEFAULT_CIRCUIT_BREAKER_CONFIG = {
2437
+ failureThreshold: 5,
2438
+ resetTimeoutMs: 3e4,
2439
+ halfOpenMaxRequests: 2
2440
+ };
2441
+ function isTransientError(error) {
2442
+ if (error instanceof RiverbankApiError) {
2443
+ if (error.status === 0) return true;
2444
+ if (error.status === 429) return true;
2445
+ if (error.status >= 500) return true;
2446
+ return false;
2447
+ }
2448
+ return true;
2449
+ }
2450
+ function calculateBackoff(attempt, config) {
2451
+ const baseDelayMs = config.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs;
2452
+ const maxDelayMs = config.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs;
2453
+ const jitter = config.jitter ?? DEFAULT_RETRY_CONFIG.jitter;
2454
+ const exponential = baseDelayMs * Math.pow(2, attempt - 1);
2455
+ const capped = Math.min(exponential, maxDelayMs);
2456
+ if (jitter === "full") {
2457
+ return Math.random() * capped;
2458
+ }
2459
+ return capped;
2460
+ }
2461
+ var CircuitBreaker = class {
2462
+ constructor(config) {
2463
+ this.state = "closed";
2464
+ this.failureCount = 0;
2465
+ this.successCount = 0;
2466
+ this.openUntil = 0;
2467
+ this.halfOpenRequests = 0;
2468
+ this.config = {
2469
+ failureThreshold: config?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
2470
+ resetTimeoutMs: config?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
2471
+ halfOpenMaxRequests: config?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
2472
+ };
2473
+ }
2474
+ /**
2475
+ * Check if circuit is open (requests should be blocked)
2476
+ * Also handles automatic transition from open to half-open after timeout
2477
+ */
2478
+ isOpen() {
2479
+ if (this.state === "open" && Date.now() >= this.openUntil) {
2480
+ this.transitionTo("half-open");
2481
+ }
2482
+ return this.state === "open";
2483
+ }
2484
+ /**
2485
+ * Check if a request can be attempted
2486
+ * - closed: always yes
2487
+ * - open: always no
2488
+ * - half-open: limited number of probes
2489
+ */
2490
+ canAttempt() {
2491
+ if (this.state === "closed") return true;
2492
+ if (this.state === "open") return false;
2493
+ return this.halfOpenRequests < this.config.halfOpenMaxRequests;
2494
+ }
2495
+ /**
2496
+ * Increment half-open request counter (call before making request in half-open)
2497
+ */
2498
+ incrementHalfOpenRequests() {
2499
+ if (this.state === "half-open") {
2500
+ this.halfOpenRequests++;
2501
+ }
2502
+ }
2503
+ /**
2504
+ * Record a successful request
2505
+ */
2506
+ recordSuccess() {
2507
+ if (this.state === "half-open") {
2508
+ this.successCount++;
2509
+ if (this.successCount >= this.config.halfOpenMaxRequests) {
2510
+ this.transitionTo("closed");
2511
+ }
2512
+ } else {
2513
+ this.failureCount = 0;
2514
+ }
2515
+ }
2516
+ /**
2517
+ * Record a failed request
2518
+ * Only counts transient failures toward circuit breaker threshold
2519
+ */
2520
+ recordFailure(error) {
2521
+ if (!isTransientError(error)) return;
2522
+ this.failureCount++;
2523
+ if (this.state === "half-open") {
2524
+ this.transitionTo("open");
2525
+ } else if (this.failureCount >= this.config.failureThreshold) {
2526
+ this.transitionTo("open");
2527
+ }
2528
+ }
2529
+ /**
2530
+ * Get current circuit state
2531
+ */
2532
+ getState() {
2533
+ return {
2534
+ state: this.state,
2535
+ failureCount: this.failureCount,
2536
+ openUntil: this.state === "open" ? this.openUntil : void 0
2537
+ };
2538
+ }
2539
+ /**
2540
+ * Transition to a new state
2541
+ */
2542
+ transitionTo(newState) {
2543
+ this.state = newState;
2544
+ if (newState === "open") {
2545
+ this.openUntil = Date.now() + this.config.resetTimeoutMs;
2546
+ } else if (newState === "half-open") {
2547
+ this.halfOpenRequests = 0;
2548
+ this.successCount = 0;
2549
+ } else if (newState === "closed") {
2550
+ this.failureCount = 0;
2551
+ this.successCount = 0;
2552
+ this.halfOpenRequests = 0;
2553
+ }
2554
+ }
2555
+ };
2556
+ async function fetchWithTimeoutAndRetry(fetcher, config) {
2557
+ const maxAttempts = config.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts;
2558
+ const requestTimeoutMs = config.requestTimeoutMs ?? 8e3;
2559
+ let lastError;
2560
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2561
+ try {
2562
+ const controller = new AbortController();
2563
+ const timeoutId = setTimeout(() => controller.abort(), requestTimeoutMs);
2564
+ try {
2565
+ const result = await fetcher(controller.signal);
2566
+ return result;
2567
+ } finally {
2568
+ clearTimeout(timeoutId);
2569
+ }
2570
+ } catch (error) {
2571
+ lastError = error;
2572
+ const shouldRetry = shouldRetryError(error, config.retryOn);
2573
+ if (!shouldRetry) {
2574
+ throw error;
2575
+ }
2576
+ if (attempt < maxAttempts) {
2577
+ const delay = getRetryDelay(error, attempt, config);
2578
+ await sleep(delay);
2579
+ }
2580
+ }
2581
+ }
2582
+ throw lastError;
2583
+ }
2584
+ function shouldRetryError(error, customRetryOn) {
2585
+ if (customRetryOn) {
2586
+ const statusCode = error instanceof RiverbankApiError ? error.status : void 0;
2587
+ return customRetryOn(error, statusCode);
2588
+ }
2589
+ return isTransientError(error);
2590
+ }
2591
+ function getRetryDelay(error, attempt, config) {
2592
+ if (error instanceof RiverbankApiError && error.retryAfterMs) {
2593
+ return error.retryAfterMs;
2594
+ }
2595
+ return calculateBackoff(attempt, config);
2596
+ }
2597
+ function sleep(ms) {
2598
+ return new Promise((resolve) => setTimeout(resolve, ms));
2599
+ }
2600
+ var CircuitOpenError = class extends Error {
2601
+ constructor(state) {
2602
+ super("Circuit breaker is open");
2603
+ this.name = "CircuitOpenError";
2604
+ this.circuitState = state;
2605
+ }
2606
+ };
2607
+
2071
2608
  // src/client/index.ts
2609
+ var prebuildModule = null;
2610
+ function getPrebuildModule() {
2611
+ if (prebuildModule !== null) return prebuildModule;
2612
+ if (typeof process === "undefined" || !process.versions?.node) {
2613
+ return null;
2614
+ }
2615
+ try {
2616
+ prebuildModule = (init_loader(), __toCommonJS(loader_exports));
2617
+ return prebuildModule;
2618
+ } catch {
2619
+ return null;
2620
+ }
2621
+ }
2072
2622
  setSdkVersion(SDK_VERSION);
2623
+ var DEFAULT_BROWSER_TIMEOUT_MS = 5e3;
2624
+ var DEFAULT_SERVER_TIMEOUT_MS = 8e3;
2625
+ function generateRequestId2() {
2626
+ return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2627
+ }
2628
+ function isAbortError(error) {
2629
+ if (error instanceof DOMException && error.name === "AbortError") {
2630
+ return true;
2631
+ }
2632
+ if (error instanceof Error && error.name === "AbortError") {
2633
+ return true;
2634
+ }
2635
+ return false;
2636
+ }
2637
+ function getNetworkErrorCode(error) {
2638
+ if (error.name === "TimeoutError" || error.name === "AbortError") {
2639
+ return "network:timeout";
2640
+ }
2641
+ const nodeError = error;
2642
+ if (nodeError.code) {
2643
+ switch (nodeError.code) {
2644
+ case "ETIMEDOUT":
2645
+ case "ESOCKETTIMEDOUT":
2646
+ return "network:timeout";
2647
+ case "ENOTFOUND":
2648
+ case "EAI_AGAIN":
2649
+ return "network:dns_error";
2650
+ case "ECONNREFUSED":
2651
+ case "ECONNRESET":
2652
+ case "EPIPE":
2653
+ return "network:connection_error";
2654
+ }
2655
+ }
2656
+ const message = error.message.toLowerCase();
2657
+ if (message.includes("timeout") || message.includes("timed out")) {
2658
+ return "network:timeout";
2659
+ }
2660
+ if (message.includes("dns") || message.includes("getaddrinfo") || message.includes("enotfound")) {
2661
+ return "network:dns_error";
2662
+ }
2663
+ return "network:connection_error";
2664
+ }
2073
2665
  function convertToTypedError(error) {
2666
+ if (isAbortError(error)) {
2667
+ throw error;
2668
+ }
2074
2669
  if (error instanceof ApiEnvelopeError) {
2075
2670
  throw new RiverbankApiError({
2076
2671
  code: error.code,
@@ -2084,15 +2679,30 @@ function convertToTypedError(error) {
2084
2679
  if (error instanceof ApiRequestError && error.body && typeof error.body === "object") {
2085
2680
  const body = error.body;
2086
2681
  if (isApiError(body)) {
2087
- throw new RiverbankApiError(body.error);
2682
+ const envelopeError = body.error;
2683
+ throw new RiverbankApiError({
2684
+ ...envelopeError,
2685
+ retryAfterMs: error.retryAfterMs
2686
+ });
2088
2687
  }
2089
2688
  }
2689
+ if (error instanceof TypeError || error instanceof Error && !("status" in error)) {
2690
+ const networkError = error;
2691
+ throw new RiverbankApiError({
2692
+ code: getNetworkErrorCode(networkError),
2693
+ message: networkError.message || "Network request failed",
2694
+ requestId: `local-${Date.now()}`,
2695
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2696
+ status: 0
2697
+ // No HTTP response received
2698
+ });
2699
+ }
2090
2700
  throw error;
2091
2701
  }
2092
2702
  function createRiverbankClient(config) {
2093
2703
  if (!config.baseUrl) {
2094
2704
  throw new Error(
2095
- "baseUrl is required when creating a Builder client. Expected format: https://dashboard.example.com/api (must include /api path)"
2705
+ "baseUrl is required when creating a Riverbank client. Expected format: https://dashboard.example.com/api (must include /api path)"
2096
2706
  );
2097
2707
  }
2098
2708
  if (!config.baseUrl.endsWith("/api")) {
@@ -2103,59 +2713,212 @@ function createRiverbankClient(config) {
2103
2713
  const cacheEnabled = config.cache?.enabled ?? true;
2104
2714
  const cacheTTL = (config.cache?.ttl ?? 300) * 1e3;
2105
2715
  const cacheMaxSize = config.cache?.maxSize ?? 100;
2716
+ const resilienceEnabled = config.resilience?.enabled ?? true;
2717
+ const staleIfError = config.resilience?.staleIfError ?? true;
2718
+ const staleTtlMs = (config.resilience?.staleTtlSec ?? 300) * 1e3;
2719
+ const requestTimeoutMs = config.resilience?.requestTimeoutMs ?? (typeof window !== "undefined" ? DEFAULT_BROWSER_TIMEOUT_MS : DEFAULT_SERVER_TIMEOUT_MS);
2720
+ const retryConfig = {
2721
+ maxAttempts: config.resilience?.retry?.maxAttempts ?? DEFAULT_RETRY_CONFIG.maxAttempts,
2722
+ baseDelayMs: config.resilience?.retry?.baseDelayMs ?? DEFAULT_RETRY_CONFIG.baseDelayMs,
2723
+ maxDelayMs: config.resilience?.retry?.maxDelayMs ?? DEFAULT_RETRY_CONFIG.maxDelayMs,
2724
+ jitter: config.resilience?.retry?.jitter ?? DEFAULT_RETRY_CONFIG.jitter,
2725
+ retryOn: config.resilience?.retry?.retryOn
2726
+ };
2727
+ const circuitBreakerConfig = {
2728
+ failureThreshold: config.resilience?.circuitBreaker?.failureThreshold ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.failureThreshold,
2729
+ resetTimeoutMs: config.resilience?.circuitBreaker?.resetTimeoutMs ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.resetTimeoutMs,
2730
+ halfOpenMaxRequests: config.resilience?.circuitBreaker?.halfOpenMaxRequests ?? DEFAULT_CIRCUIT_BREAKER_CONFIG.halfOpenMaxRequests
2731
+ };
2106
2732
  const apiClient = createBearerAPIClient(config.apiKey, config.baseUrl);
2107
2733
  const cache = new SimpleCache({
2108
2734
  maxSize: cacheMaxSize,
2109
- ttl: cacheTTL
2735
+ ttl: cacheTTL,
2736
+ staleTtl: staleTtlMs
2110
2737
  });
2111
- async function cachedFetch(cacheKey, fetcher, options) {
2112
- if (cacheEnabled && !options?.force) {
2113
- const cached = cache.get(cacheKey);
2114
- if (cached !== void 0) {
2115
- return cached;
2738
+ const circuitBreaker = new CircuitBreaker(circuitBreakerConfig);
2739
+ const prebuildDir = config.resilience?.prebuildDir;
2740
+ const prebuildMod = prebuildDir ? getPrebuildModule() : null;
2741
+ const prebuildLoader = prebuildMod?.canUsePrebuild() && prebuildDir ? new prebuildMod.PrebuildLoader({
2742
+ prebuildDir,
2743
+ maxPrebuildAgeSec: config.resilience?.maxPrebuildAgeSec
2744
+ }) : null;
2745
+ let lastStatus = null;
2746
+ let isDegraded = false;
2747
+ function emitStatus(source, data, details) {
2748
+ const status = {
2749
+ source,
2750
+ isPreview: details.isPreview,
2751
+ cacheKey: details.cacheKey,
2752
+ error: details.error,
2753
+ staleAgeSec: details.staleAgeSec,
2754
+ prebuildAgeSec: details.prebuildAgeSec,
2755
+ circuit: circuitBreaker.getState(),
2756
+ requestId: details.requestId,
2757
+ durationMs: details.durationMs
2758
+ };
2759
+ lastStatus = status;
2760
+ config.resilience?.onStatusChange?.(status);
2761
+ const nowDegraded = source === "stale" || source === "error";
2762
+ if (nowDegraded !== isDegraded) {
2763
+ isDegraded = nowDegraded;
2764
+ config.resilience?.onDegradedMode?.(nowDegraded, status);
2765
+ }
2766
+ return data;
2767
+ }
2768
+ async function resilientFetch(cacheKey, fetcher, options) {
2769
+ const requestId = generateRequestId2();
2770
+ const startTime = Date.now();
2771
+ const isPreview = options.preview ?? false;
2772
+ const statusDetails = (extra = {}) => ({
2773
+ requestId,
2774
+ cacheKey,
2775
+ isPreview,
2776
+ durationMs: Date.now() - startTime,
2777
+ ...extra
2778
+ });
2779
+ if (cacheEnabled && !options.force) {
2780
+ const fresh = cache.getFresh(cacheKey);
2781
+ if (fresh !== null) {
2782
+ return emitStatus("cache", fresh, statusDetails());
2116
2783
  }
2117
2784
  }
2118
- let data;
2785
+ if (resilienceEnabled && circuitBreaker.isOpen()) {
2786
+ if (!isPreview && staleIfError) {
2787
+ const stale = cache.getStale(cacheKey);
2788
+ if (stale) {
2789
+ return emitStatus("stale", stale.value, statusDetails({
2790
+ staleAgeSec: stale.staleAgeSec,
2791
+ error: { code: "circuit_open", message: "Circuit breaker is open" }
2792
+ }));
2793
+ }
2794
+ }
2795
+ if (!isPreview && options.prebuildFallback) {
2796
+ const prebuildResult = options.prebuildFallback();
2797
+ if (prebuildResult) {
2798
+ return emitStatus("prebuild", prebuildResult.data, statusDetails({
2799
+ prebuildAgeSec: prebuildResult.prebuildAgeSec,
2800
+ error: { code: "circuit_open", message: "Circuit breaker is open" }
2801
+ }));
2802
+ }
2803
+ }
2804
+ const circuitState = circuitBreaker.getState();
2805
+ emitStatus("error", null, statusDetails({
2806
+ error: { code: "circuit_open", message: "Circuit breaker is open" }
2807
+ }));
2808
+ throw new CircuitOpenError(circuitState);
2809
+ }
2119
2810
  try {
2120
- const response = await fetcher();
2121
- data = unwrapResponse(response);
2811
+ let data;
2812
+ if (resilienceEnabled) {
2813
+ if (circuitBreaker.getState().state === "half-open") {
2814
+ circuitBreaker.incrementHalfOpenRequests();
2815
+ }
2816
+ data = await fetchWithTimeoutAndRetry(
2817
+ async (timeoutSignal) => {
2818
+ const combinedSignal = options.signal ? combineAbortSignals(timeoutSignal, options.signal) : timeoutSignal;
2819
+ try {
2820
+ const response = await fetcher(combinedSignal);
2821
+ return unwrapResponse(response);
2822
+ } catch (error) {
2823
+ convertToTypedError(error);
2824
+ }
2825
+ },
2826
+ {
2827
+ ...retryConfig,
2828
+ requestTimeoutMs
2829
+ }
2830
+ );
2831
+ circuitBreaker.recordSuccess();
2832
+ } else {
2833
+ try {
2834
+ const response = await fetcher(options.signal ?? new AbortController().signal);
2835
+ data = unwrapResponse(response);
2836
+ } catch (error) {
2837
+ convertToTypedError(error);
2838
+ }
2839
+ }
2840
+ if (cacheEnabled) {
2841
+ cache.set(cacheKey, data);
2842
+ }
2843
+ return emitStatus("live", data, statusDetails());
2122
2844
  } catch (error) {
2123
- convertToTypedError(error);
2845
+ if (resilienceEnabled && error instanceof Error) {
2846
+ circuitBreaker.recordFailure(error);
2847
+ }
2848
+ if (!isPreview && staleIfError && cacheEnabled) {
2849
+ const stale = cache.getStale(cacheKey);
2850
+ if (stale) {
2851
+ const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
2852
+ return emitStatus("stale", stale.value, statusDetails({
2853
+ staleAgeSec: stale.staleAgeSec,
2854
+ error: errorInfo2
2855
+ }));
2856
+ }
2857
+ }
2858
+ if (!isPreview && options.prebuildFallback) {
2859
+ const prebuildResult = options.prebuildFallback();
2860
+ if (prebuildResult) {
2861
+ const errorInfo2 = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
2862
+ return emitStatus("prebuild", prebuildResult.data, statusDetails({
2863
+ prebuildAgeSec: prebuildResult.prebuildAgeSec,
2864
+ error: errorInfo2
2865
+ }));
2866
+ }
2867
+ }
2868
+ const errorInfo = error instanceof RiverbankApiError ? { code: error.code, message: error.message } : { message: error.message };
2869
+ emitStatus("error", null, statusDetails({ error: errorInfo }));
2870
+ throw error;
2124
2871
  }
2125
- if (cacheEnabled) {
2126
- cache.set(cacheKey, data);
2872
+ }
2873
+ function combineAbortSignals(...signals) {
2874
+ const controller = new AbortController();
2875
+ for (const signal of signals) {
2876
+ if (signal.aborted) {
2877
+ controller.abort(signal.reason);
2878
+ break;
2879
+ }
2880
+ signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
2127
2881
  }
2128
- return data;
2882
+ return controller.signal;
2129
2883
  }
2130
2884
  return {
2131
2885
  async getSite(params) {
2132
- const { slug, domain, id } = params;
2886
+ const { slug, domain, id, signal } = params;
2133
2887
  if (!slug && !domain && !id) {
2134
2888
  throw new Error(
2135
2889
  `getSite() requires at least one identifier: slug, domain, or id. Received: ${JSON.stringify(params)}`
2136
2890
  );
2137
2891
  }
2138
2892
  const cacheKey = `site:${slug || domain || id}`;
2139
- return cachedFetch(cacheKey, async () => {
2893
+ const siteId = id || slug || domain;
2894
+ return resilientFetch(cacheKey, async (sig) => {
2140
2895
  const apiParams = {};
2141
2896
  if (params.slug) apiParams.slug = params.slug;
2142
2897
  if (params.domain) apiParams.domain = params.domain;
2143
2898
  if (params.id) apiParams.id = params.id;
2144
- return await apiClient({ endpoint: "getSite", params: apiParams });
2899
+ return await apiClient({ endpoint: "getSite", params: apiParams, options: { signal: sig } });
2900
+ }, {
2901
+ signal,
2902
+ prebuildFallback: prebuildLoader && siteId ? () => prebuildLoader.loadSite(siteId) : void 0
2145
2903
  });
2146
2904
  },
2147
2905
  async getPage(params) {
2148
- const { siteId, path, preview = false } = params;
2149
- const cacheKey = `page:${siteId}:${path}:${preview}`;
2150
- return cachedFetch(cacheKey, async () => {
2151
- return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path, preview } });
2906
+ const { siteId, path: path2, preview = false, signal } = params;
2907
+ const cacheKey = `page:${siteId}:${path2}:${preview}`;
2908
+ return resilientFetch(cacheKey, async (sig) => {
2909
+ return await apiClient({ endpoint: "getContentByPath", params: { siteId }, body: { path: path2, preview }, options: { signal: sig } });
2910
+ }, {
2911
+ preview,
2912
+ signal,
2913
+ // Prebuild fallback only for published pages (not preview)
2914
+ prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadPage(siteId, path2) : void 0
2152
2915
  });
2153
2916
  },
2154
2917
  async getEntries(params) {
2155
- const { siteId, contentType, limit, offset, order, preview = false, mode, entryIds, includeMeta } = params;
2918
+ const { siteId, contentType, limit, offset, order, preview = false, mode, entryIds, includeMeta, signal } = params;
2156
2919
  const entryIdsCacheKey = mode === "manual" && entryIds?.length ? entryIds.join(",") : "";
2157
2920
  const cacheKey = `entries:${siteId}:${contentType}:${limit ?? ""}:${offset ?? ""}:${order ?? ""}:${preview}:${mode ?? ""}:${entryIdsCacheKey}:${includeMeta ?? ""}`;
2158
- return cachedFetch(cacheKey, async () => {
2921
+ return resilientFetch(cacheKey, async (sig) => {
2159
2922
  let orderParam;
2160
2923
  if (order === "newest") {
2161
2924
  orderParam = "published_at.desc";
@@ -2177,47 +2940,52 @@ function createRiverbankClient(config) {
2177
2940
  entryIds: JSON.stringify(entryIds)
2178
2941
  }
2179
2942
  };
2180
- return await apiClient({ endpoint: "listPublishedEntries", params: apiParams });
2943
+ return await apiClient({ endpoint: "listPublishedEntries", params: apiParams, options: { signal: sig } });
2944
+ }, {
2945
+ preview,
2946
+ signal,
2947
+ // Prebuild fallback only for published entries (not preview, not manual mode)
2948
+ prebuildFallback: prebuildLoader && !preview ? () => prebuildLoader.loadEntries(siteId, params) : void 0
2181
2949
  });
2182
2950
  },
2183
2951
  async getEntry(params) {
2184
- const { siteId, contentType, slug } = params;
2952
+ const { siteId, contentType, slug, signal } = params;
2185
2953
  const cacheKey = `entry:${siteId}:${contentType}:${slug}`;
2186
- return cachedFetch(cacheKey, async () => {
2187
- return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug } });
2188
- });
2954
+ return resilientFetch(cacheKey, async (sig) => {
2955
+ return await apiClient({ endpoint: "getPublishedEntryPreview", params: { siteId, type: contentType, slug }, options: { signal: sig } });
2956
+ }, { signal });
2189
2957
  },
2190
2958
  async getPublicFormById(params) {
2191
- const { formId } = params;
2959
+ const { formId, signal } = params;
2192
2960
  if (!formId) {
2193
2961
  throw new Error("getPublicFormById() requires formId");
2194
2962
  }
2195
2963
  const cacheKey = `public-form:${formId}`;
2196
- return cachedFetch(cacheKey, async () => {
2197
- return await apiClient({ endpoint: "getPublicFormById", params: { formId } });
2198
- });
2964
+ return resilientFetch(cacheKey, async (sig) => {
2965
+ return await apiClient({ endpoint: "getPublicFormById", params: { formId }, options: { signal: sig } });
2966
+ }, { signal });
2199
2967
  },
2200
2968
  async getPublicBookingServices(params) {
2201
- const { siteId, ids } = params;
2969
+ const { siteId, ids, signal } = params;
2202
2970
  if (!siteId) {
2203
2971
  throw new Error("getPublicBookingServices() requires siteId");
2204
2972
  }
2205
2973
  const cacheKey = `public-booking-services:${siteId}:${ids ?? ""}`;
2206
- return cachedFetch(cacheKey, async () => {
2974
+ return resilientFetch(cacheKey, async (sig) => {
2207
2975
  const apiParams = {
2208
2976
  siteId,
2209
2977
  ...ids && { ids }
2210
2978
  };
2211
- return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams });
2212
- });
2979
+ return await apiClient({ endpoint: "getPublicBookingServices", params: apiParams, options: { signal: sig } });
2980
+ }, { signal });
2213
2981
  },
2214
2982
  async listPublicEvents(params) {
2215
- const { siteId, limit, from, to, stage } = params;
2983
+ const { siteId, limit, from, to, stage, signal } = params;
2216
2984
  if (!siteId) {
2217
2985
  throw new Error("listPublicEvents() requires siteId");
2218
2986
  }
2219
2987
  const cacheKey = `public-events:${siteId}:${limit ?? ""}:${from ?? ""}:${to ?? ""}:${stage ?? ""}`;
2220
- return cachedFetch(cacheKey, async () => {
2988
+ return resilientFetch(cacheKey, async (sig) => {
2221
2989
  const apiParams = {
2222
2990
  siteId,
2223
2991
  ...typeof limit === "number" && { limit: String(limit) },
@@ -2225,46 +2993,60 @@ function createRiverbankClient(config) {
2225
2993
  ...to && { to },
2226
2994
  ...stage && { stage }
2227
2995
  };
2228
- return await apiClient({ endpoint: "listPublicEvents", params: apiParams });
2229
- });
2996
+ return await apiClient({ endpoint: "listPublicEvents", params: apiParams, options: { signal: sig } });
2997
+ }, { signal });
2230
2998
  },
2231
2999
  async resolveEventOccurrence(params) {
2232
- const { siteId, entryId, segment } = params;
3000
+ const { siteId, entryId, segment, signal } = params;
2233
3001
  if (!siteId || !entryId || !segment) {
2234
3002
  throw new Error("resolveEventOccurrence() requires siteId, entryId, and segment");
2235
3003
  }
2236
3004
  const cacheKey = `event-occurrence:${siteId}:${entryId}:${segment}`;
2237
- return cachedFetch(cacheKey, async () => {
3005
+ return resilientFetch(cacheKey, async (sig) => {
2238
3006
  return await apiClient({
2239
3007
  endpoint: "resolveEventOccurrence",
2240
- params: { siteId, entryId, segment }
3008
+ params: { siteId, entryId, segment },
3009
+ options: { signal: sig }
2241
3010
  });
2242
- });
3011
+ }, { signal });
2243
3012
  },
2244
3013
  async checkRedirect(params) {
2245
- const { siteId, path } = params;
2246
- if (!siteId || !path) {
3014
+ const { siteId, path: path2, signal } = params;
3015
+ if (!siteId || !path2) {
2247
3016
  throw new Error("checkRedirect() requires siteId and path");
2248
3017
  }
2249
- const cacheKey = `redirect:${siteId}:${path}`;
2250
- return cachedFetch(cacheKey, async () => {
3018
+ const cacheKey = `redirect:${siteId}:${path2}`;
3019
+ return resilientFetch(cacheKey, async (sig) => {
2251
3020
  return await apiClient({
2252
3021
  endpoint: "checkRedirect",
2253
- params: { site: siteId, path }
3022
+ params: { site: siteId, path: path2 },
3023
+ options: { signal: sig }
2254
3024
  });
2255
- });
3025
+ }, { signal });
2256
3026
  },
2257
3027
  clearCache() {
2258
3028
  cache.clear();
3029
+ },
3030
+ getLastEmittedStatus() {
3031
+ return lastStatus;
3032
+ },
3033
+ getCircuitState() {
3034
+ return circuitBreaker.getState();
2259
3035
  }
2260
- // Cast to RiverbankClient to satisfy overloaded getEntries signature
2261
- // The implementation correctly returns the right type based on includeMeta
2262
3036
  };
2263
3037
  }
2264
3038
 
2265
3039
  export {
2266
3040
  API_ENDPOINTS,
2267
3041
  buildEndpointURL,
3042
+ SDK_VERSION,
3043
+ PREBUILD_PAGE_SIZE,
3044
+ DEFAULT_PREBUILD_DIR,
3045
+ init_constants,
3046
+ canUsePrebuild,
3047
+ PrebuildLoader,
3048
+ createPrebuildLoader,
3049
+ init_loader,
2268
3050
  createRiverbankClient
2269
3051
  };
2270
- //# sourceMappingURL=chunk-7FIJSGHU.mjs.map
3052
+ //# sourceMappingURL=chunk-NFQLH5IA.mjs.map