@riverbankcms/sdk 0.90.0 → 0.91.0

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 (90) hide show
  1. package/dist/{PreviewEditorSidebar-KVXNF3VW.mjs → PreviewEditorSidebar-3TT47WCF.mjs} +3 -3
  2. package/dist/{PreviewEditorUI-J3JN5Z7T.mjs → PreviewEditorUI-ORCLLUNZ.mjs} +3 -3
  3. package/dist/SdkPreviewModeRuntime-GMCDQPZZ.mjs +8 -0
  4. package/dist/_dts/api/src/bookingConfig.d.ts +3 -0
  5. package/dist/_dts/api/src/event-listing-destination.d.ts +59 -0
  6. package/dist/_dts/api/src/index.d.ts +4 -0
  7. package/dist/_dts/api/src/navigation/dashboardSettingsNav.d.ts +0 -1
  8. package/dist/_dts/api/src/navigation.d.ts +2 -0
  9. package/dist/_dts/api/src/publicExperienceEndpoints.d.ts +3 -1
  10. package/dist/_dts/api/src/settings/permissions.d.ts +273 -0
  11. package/dist/_dts/api/src/types.d.ts +1 -0
  12. package/dist/_dts/blocks/src/system/blocks/events/shared/schemas.d.ts +60 -2
  13. package/dist/_dts/blocks/src/system/blocks/events/shared/types.d.ts +12 -1
  14. package/dist/_dts/blocks/src/system/blocks/media-text.d.ts +9 -9
  15. package/dist/_dts/blocks/src/system/blocks/team-members.d.ts +1 -1
  16. package/dist/_dts/blocks/src/system/manifest/fieldValidation/index.d.ts +17 -0
  17. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/event-registration.headless.contract.d.ts +5 -0
  18. package/dist/_dts/blocks/src/system/runtime/nodes/event-registration/eventPaymentOptions.d.ts +24 -0
  19. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/destination.d.ts +8 -0
  20. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/pricing.d.ts +3 -0
  21. package/dist/_dts/blocks/src/system/runtime/nodes/events/shared/utils.d.ts +7 -0
  22. package/dist/_dts/core/src/event-pricing.d.ts +12 -2
  23. package/dist/_dts/core/src/string.d.ts +12 -0
  24. package/dist/_dts/sdk/src/cli/sync/validation.d.ts +2 -2
  25. package/dist/_dts/sdk/src/public-api/contracts.d.ts +11 -1
  26. package/dist/_dts/sdk/src/rendering/islands/types.d.ts +11 -1
  27. package/dist/_dts/sdk/src/version.d.ts +1 -1
  28. package/dist/{chunk-GSPFFUVB.mjs → chunk-AFPMCLS7.mjs} +2 -2
  29. package/dist/{chunk-RQ3XZ4SJ.mjs → chunk-GDT2QGIL.mjs} +1 -1
  30. package/dist/{chunk-PMBB6MQ7.mjs → chunk-RQUZYSES.mjs} +345 -153
  31. package/dist/{chunk-AEOYWPE4.mjs → chunk-S3YHJWEU.mjs} +7 -7
  32. package/dist/{chunk-UG7I7S7W.mjs → chunk-U5YM4AWE.mjs} +1 -1
  33. package/dist/{chunk-BOQXMCSP.mjs → chunk-ZA2S7H34.mjs} +3 -3
  34. package/dist/cli/index.mjs +363 -437
  35. package/dist/cli/init-docs/content/workflow-add-block.md +48 -0
  36. package/dist/cli/init-docs/content/workflow-custom-block.md +10 -0
  37. package/dist/client/{accordion-GXQNWQ6O.mjs → accordion-AFX37652.mjs} +2 -2
  38. package/dist/client/{bookingForm-STLUJ36H.mjs → bookingForm-32V5QCOW.mjs} +6 -6
  39. package/dist/client/bookings.mjs +1170 -571
  40. package/dist/client/{carousel-CVRN6EPP.mjs → carousel-TJGQJ7ER.mjs} +3 -3
  41. package/dist/client/{cart-IYIBA5CL.mjs → cart-JUU2JEW4.mjs} +6 -6
  42. package/dist/client/{checkout-O3BGM4GS.mjs → checkout-RD6BTFX4.mjs} +6 -6
  43. package/dist/client/{chunk-DAS5CIER.mjs → chunk-6QZIBNGV.mjs} +119 -21
  44. package/dist/client/{chunk-VFHZLZAC.mjs → chunk-6RN62XBV.mjs} +1 -1
  45. package/dist/client/{chunk-D3ETC2SU.mjs → chunk-C4LP6BPG.mjs} +1 -1
  46. package/dist/client/{chunk-NBFRJ5NB.mjs → chunk-CKXEUWWA.mjs} +2 -2
  47. package/dist/client/{chunk-4RTFGHZN.mjs → chunk-E6VIWZUG.mjs} +6 -3
  48. package/dist/client/{chunk-Z62JYS5O.mjs → chunk-FMZZHAIR.mjs} +2 -2
  49. package/dist/client/{chunk-FHU6PFPM.mjs → chunk-ISAUXU5R.mjs} +1 -1
  50. package/dist/client/{chunk-HIHOQGIP.mjs → chunk-LJCX6V7Q.mjs} +9 -9
  51. package/dist/client/{chunk-6DPXDOIL.mjs → chunk-MLBNVN4H.mjs} +99 -26
  52. package/dist/client/{chunk-7DH35FGA.mjs → chunk-PMNAV6LO.mjs} +1 -1
  53. package/dist/client/{chunk-MFPKS6QS.mjs → chunk-RMOFBBNA.mjs} +5 -5
  54. package/dist/client/{chunk-25W2EQUN.mjs → chunk-RWYFG625.mjs} +17 -8
  55. package/dist/client/client.mjs +305 -113
  56. package/dist/client/{courseRegistration-2D3ACMJR.mjs → courseRegistration-X63QAIVV.mjs} +6 -6
  57. package/dist/client/{eventCalendar-QY4UZUFF.mjs → eventCalendar-5T24GCZL.mjs} +9 -9
  58. package/dist/client/{eventCombined-RRCDKJC2.mjs → eventCombined-AZVTF22X.mjs} +9 -9
  59. package/dist/client/{eventDetails-YO335BUT.mjs → eventDetails-7GPFNLGF.mjs} +9 -9
  60. package/dist/client/{eventListing-PXA6GFQ3.mjs → eventListing-QEUMTMUM.mjs} +9 -9
  61. package/dist/client/{eventRegistration-2ZHEDDXO.mjs → eventRegistration-P2EDKCQZ.mjs} +9 -9
  62. package/dist/client/{form-ZS6KX4U2.mjs → form-NUJ7H4IN.mjs} +3 -3
  63. package/dist/client/{headerSection-QK5SLIIB.mjs → headerSection-JB7LECPL.mjs} +3 -3
  64. package/dist/client/hooks.mjs +93 -42
  65. package/dist/client/{locationMap-GFNIUD2X.mjs → locationMap-UEYWUCSW.mjs} +3 -3
  66. package/dist/client/{newsletterForm-PGLTTCUG.mjs → newsletterForm-S2YZQ2X7.mjs} +3 -3
  67. package/dist/client/{productDetail-OPBE5ZPI.mjs → productDetail-H5MADGG7.mjs} +6 -6
  68. package/dist/client/{productList-NI72Q62Z.mjs → productList-DK7YON7H.mjs} +6 -6
  69. package/dist/client/rendering/client.mjs +262 -70
  70. package/dist/client/rendering/islands.mjs +19 -19
  71. package/dist/client/rendering.mjs +306 -114
  72. package/dist/client/{shop-3ZQPEWYI.mjs → shop-6PA7RDRJ.mjs} +6 -6
  73. package/dist/client/spam-protection.mjs +10 -10
  74. package/dist/preview-next/client/runtime.mjs +5 -5
  75. package/dist/{sdk-runtime-FSHXCWDS.mjs → sdk-runtime-JP5347OB.mjs} +6 -6
  76. package/dist/server/components.mjs +149 -69
  77. package/dist/server/config-validation.mjs +85 -34
  78. package/dist/server/config.mjs +85 -34
  79. package/dist/server/data.mjs +93 -42
  80. package/dist/server/index.mjs +1 -1
  81. package/dist/server/navigation.mjs +2 -2
  82. package/dist/server/next.mjs +170 -90
  83. package/dist/server/page-converter.mjs +77 -26
  84. package/dist/server/prebuild.mjs +1 -1
  85. package/dist/server/rendering/server.mjs +161 -81
  86. package/dist/server/rendering.mjs +165 -85
  87. package/dist/server/routing.mjs +135 -58
  88. package/dist/server/server.mjs +102 -51
  89. package/package.json +2 -2
  90. package/dist/SdkPreviewModeRuntime-UBL6MSNP.mjs +0 -8
@@ -176,6 +176,8 @@ function publicEventPricingType(pricing) {
176
176
  case "ticket_types":
177
177
  case "course":
178
178
  return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
179
+ case "external":
180
+ return "external";
179
181
  }
180
182
  }
181
183
  function publicEventPricingCurrency(pricing) {
@@ -187,6 +189,8 @@ function publicEventPricingCurrency(pricing) {
187
189
  return pricing.currency;
188
190
  case "course":
189
191
  return pricing.display.display.currency;
192
+ case "external":
193
+ return null;
190
194
  }
191
195
  }
192
196
  function publicEventScalarPrice(pricing) {
@@ -201,6 +205,7 @@ function publicEventPricingMode(pricing) {
201
205
  return "ticket_types";
202
206
  case "drop_in":
203
207
  case "course":
208
+ case "external":
204
209
  return void 0;
205
210
  }
206
211
  }
@@ -218,6 +223,7 @@ function isFreePublicEventPriceDisplay(display) {
218
223
  case "range":
219
224
  case "mixed":
220
225
  case "unavailable":
226
+ case "external":
221
227
  return false;
222
228
  }
223
229
  }
@@ -248,6 +254,8 @@ function formatBookingPriceDisplay(display, options = {}) {
248
254
  return display.paidToAmount > display.paidFromAmount ? `Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)} - ${formatCurrencyAmount(display.paidToAmount, display.currency, options)}` : `Free / ${formatCurrencyAmount(display.paidFromAmount, display.currency, options)}`;
249
255
  case "unavailable":
250
256
  return "Pricing unavailable";
257
+ case "external":
258
+ return display.label;
251
259
  case "course":
252
260
  return formatCoursePriceDisplayLabel(display.display, options);
253
261
  default:
@@ -371,10 +379,11 @@ function omitZeroAmountFutureRows(rows) {
371
379
  if (rows.length <= 1) {
372
380
  return rows;
373
381
  }
374
- const [firstRow, ...futureRows] = rows;
375
- if (!firstRow) {
382
+ const firstRow = rows[0];
383
+ if (firstRow === void 0) {
376
384
  return [];
377
385
  }
386
+ const futureRows = rows.slice(1);
378
387
  return [
379
388
  firstRow,
380
389
  ...futureRows.filter((row) => row.amountCents > 0)
@@ -419,11 +428,11 @@ function isValidTimeZone(timeZone) {
419
428
  }
420
429
  function getSafeTimeZone(timeZone) {
421
430
  const normalized = typeof timeZone === "string" ? timeZone.trim() : "";
422
- return normalized && isValidTimeZone(normalized) ? normalized : FALLBACK_TIMEZONE;
431
+ return normalized.length > 0 && isValidTimeZone(normalized) ? normalized : FALLBACK_TIMEZONE;
423
432
  }
424
433
  function parseStrictBookingTimeZone(timeZone) {
425
434
  const normalized = timeZone.trim();
426
- if (!normalized || !isValidTimeZone(normalized)) {
435
+ if (normalized.length === 0 || !isValidTimeZone(normalized)) {
427
436
  return null;
428
437
  }
429
438
  return normalized;
@@ -509,7 +518,7 @@ function parseScheduledBookingDateTime(input) {
509
518
  try {
510
519
  const startsAt = toUtcIsoDateTimeString(input.startsAt);
511
520
  const timeZone = parseStrictBookingTimeZone(input.timeZone);
512
- if (!timeZone) {
521
+ if (timeZone === null) {
513
522
  return {
514
523
  ok: false,
515
524
  field: "timeZone",
@@ -648,7 +657,7 @@ function getBookingLocalDateKey(input) {
648
657
  }
649
658
  function parseBookingLocalDate(value) {
650
659
  const match = LOCAL_DATE_PATTERN.exec(value.trim());
651
- if (!match?.groups) {
660
+ if (match === null || match.groups === void 0) {
652
661
  throw new Error(`Invalid booking date format: "${value}" (expected YYYY-MM-DD)`);
653
662
  }
654
663
  const year = Number(match.groups.year);
@@ -707,10 +716,10 @@ function normalizeLocationAddressText(value) {
707
716
  return null;
708
717
  }
709
718
  const trimmed = value.trim();
710
- return trimmed ? trimmed : null;
719
+ return trimmed.length > 0 ? trimmed : null;
711
720
  }
712
721
  function hasLocationCoordinates(value) {
713
- return typeof value?.lat === "number" && typeof value?.lng === "number";
722
+ return value != null && typeof value.lat === "number" && typeof value.lng === "number";
714
723
  }
715
724
  function toLocationCoordinates(value) {
716
725
  if (!hasLocationCoordinates(value)) {
@@ -727,6 +736,13 @@ var init_location = __esm({
727
736
  }
728
737
  });
729
738
 
739
+ // ../core/src/string.ts
740
+ var init_string = __esm({
741
+ "../core/src/string.ts"() {
742
+ "use strict";
743
+ }
744
+ });
745
+
730
746
  // ../core/src/business-address.ts
731
747
  var init_business_address = __esm({
732
748
  "../core/src/business-address.ts"() {
@@ -867,13 +883,6 @@ var init_slug = __esm({
867
883
  }
868
884
  });
869
885
 
870
- // ../core/src/string.ts
871
- var init_string = __esm({
872
- "../core/src/string.ts"() {
873
- "use strict";
874
- }
875
- });
876
-
877
886
  // ../core/src/uuid.ts
878
887
  var init_uuid = __esm({
879
888
  "../core/src/uuid.ts"() {
@@ -22943,7 +22952,7 @@ var init_schemas = __esm({
22943
22952
 
22944
22953
  // ../blocks/src/system/blocks/events/shared/schemas.ts
22945
22954
  import { z as z46 } from "zod";
22946
- var eventPresetSchema, eventScheduleScopeSchema, eventDestinationModeSchema, eventVenueSchema, eventCategorySchema, pricingTypeSchema, accessRestrictionSchema, eventPricingModeSchema, publicCoursePriceDisplaySchema, publicEventPriceDisplaySchema, publicEventTicketTypePriceDisplaySchema, publicEventAttendanceModeSchema, publicEventAttendanceModeAvailabilitySchema, publicEventTicketTypeAvailabilitySchema, publicEventTicketTypeSchema, publicEventPricingSchema, publicTeamMemberSummarySchema, publicEventSchema, publicEventsArraySchema;
22955
+ var eventPresetSchema, eventScheduleScopeSchema, eventDestinationModeSchema, publicEventDestinationSchema, eventVenueSchema, eventCategorySchema, pricingTypeSchema, accessRestrictionSchema, eventPricingModeSchema, publicCoursePriceDisplaySchema, publicEventPriceDisplaySchema, publicEventTicketTypePriceDisplaySchema, publicEventAttendanceModeSchema, publicEventAttendanceModeAvailabilitySchema, publicEventTicketTypeAvailabilitySchema, publicEventTicketTypeSchema, publicEventPricingSchema, publicTeamMemberSummarySchema, publicEventSchema, publicEventsArraySchema;
22947
22956
  var init_schemas2 = __esm({
22948
22957
  "../blocks/src/system/blocks/events/shared/schemas.ts"() {
22949
22958
  "use strict";
@@ -22954,6 +22963,18 @@ var init_schemas2 = __esm({
22954
22963
  eventPresetSchema = z46.enum(["event", "class", "workshop", "retreat", "performance"]);
22955
22964
  eventScheduleScopeSchema = z46.enum(["recurring", "oneOff"]);
22956
22965
  eventDestinationModeSchema = z46.enum(["cms-managed", "system-managed"]);
22966
+ publicEventDestinationSchema = z46.discriminatedUnion("kind", [
22967
+ z46.object({
22968
+ kind: z46.literal("native"),
22969
+ mode: eventDestinationModeSchema,
22970
+ path: z46.string()
22971
+ }),
22972
+ z46.object({
22973
+ kind: z46.literal("external"),
22974
+ url: z46.string(),
22975
+ label: z46.string()
22976
+ })
22977
+ ]);
22957
22978
  eventVenueSchema = z46.object({
22958
22979
  id: z46.string(),
22959
22980
  name: z46.string(),
@@ -22968,7 +22989,7 @@ var init_schemas2 = __esm({
22968
22989
  name: z46.string(),
22969
22990
  color: z46.string().nullable()
22970
22991
  });
22971
- pricingTypeSchema = z46.enum(["free", "paid"]);
22992
+ pricingTypeSchema = z46.enum(["free", "paid", "external"]);
22972
22993
  accessRestrictionSchema = z46.enum(["none", "category"]);
22973
22994
  eventPricingModeSchema = z46.enum(["single", "ticket_types"]);
22974
22995
  publicCoursePriceDisplaySchema = z46.discriminatedUnion("kind", [
@@ -22989,6 +23010,7 @@ var init_schemas2 = __esm({
22989
23010
  currency: z46.string()
22990
23011
  }),
22991
23012
  z46.object({ kind: z46.literal("unavailable"), currency: z46.string() }),
23013
+ z46.object({ kind: z46.literal("external"), label: z46.string() }),
22992
23014
  z46.object({ kind: z46.literal("course"), display: publicCoursePriceDisplaySchema })
22993
23015
  ]);
22994
23016
  publicEventTicketTypePriceDisplaySchema = z46.union([
@@ -23067,6 +23089,10 @@ var init_schemas2 = __esm({
23067
23089
  z46.object({
23068
23090
  kind: z46.literal("course"),
23069
23091
  display: z46.object({ kind: z46.literal("course"), display: publicCoursePriceDisplaySchema })
23092
+ }),
23093
+ z46.object({
23094
+ kind: z46.literal("external"),
23095
+ display: z46.object({ kind: z46.literal("external"), label: z46.string() })
23070
23096
  })
23071
23097
  ]);
23072
23098
  publicTeamMemberSummarySchema = z46.object({
@@ -23095,7 +23121,8 @@ var init_schemas2 = __esm({
23095
23121
  image: mediaSchema2.nullable().optional()
23096
23122
  }).optional(),
23097
23123
  slug: z46.string(),
23098
- path: z46.string(),
23124
+ path: z46.string().nullable(),
23125
+ destination: publicEventDestinationSchema,
23099
23126
  requiresRegistration: z46.boolean(),
23100
23127
  deliveryMode: z46.enum(["in_person", "online", "hybrid"]),
23101
23128
  attendanceModes: z46.array(publicEventAttendanceModeAvailabilitySchema),
@@ -23447,11 +23474,45 @@ var init_events = __esm({
23447
23474
  }
23448
23475
  });
23449
23476
 
23477
+ // ../blocks/src/system/runtime/nodes/events/shared/destination.ts
23478
+ function isRecord5(value) {
23479
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23480
+ }
23481
+ function isRuntimeEventDestination(value) {
23482
+ if (!isRecord5(value) || typeof value.kind !== "string") return false;
23483
+ if (value.kind === "native") {
23484
+ return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
23485
+ }
23486
+ return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
23487
+ }
23488
+ function resolveRuntimeEventDestination(event) {
23489
+ if (isRuntimeEventDestination(event.destination)) {
23490
+ return event.destination;
23491
+ }
23492
+ return {
23493
+ kind: "native",
23494
+ mode: event.eventDestinationMode === "system-managed" ? "system-managed" : "cms-managed",
23495
+ path: typeof event.path === "string" ? event.path : ""
23496
+ };
23497
+ }
23498
+ var init_destination = __esm({
23499
+ "../blocks/src/system/runtime/nodes/events/shared/destination.ts"() {
23500
+ "use strict";
23501
+ }
23502
+ });
23503
+
23450
23504
  // ../blocks/src/system/runtime/nodes/events/shared/pricing.ts
23451
23505
  function resolveRuntimeEventPricing(event) {
23452
23506
  if (isPublicEventPricing(event.pricing)) {
23453
23507
  return event.pricing;
23454
23508
  }
23509
+ const destination = resolveRuntimeEventDestination(event);
23510
+ if (destination.kind === "external") {
23511
+ return {
23512
+ kind: "external",
23513
+ display: { kind: "external", label: "External registration" }
23514
+ };
23515
+ }
23455
23516
  const legacy = event;
23456
23517
  const currency = readCurrency(legacy.currency);
23457
23518
  if (event.requiresRegistration === false) {
@@ -23488,22 +23549,22 @@ function resolveRuntimeEventPricing(event) {
23488
23549
  function readCurrency(value) {
23489
23550
  return typeof value === "string" && value.trim().length > 0 ? value : DEFAULT_EVENT_CURRENCY;
23490
23551
  }
23491
- function isRecord5(value) {
23552
+ function isRecord6(value) {
23492
23553
  return typeof value === "object" && value !== null && !Array.isArray(value);
23493
23554
  }
23494
23555
  function isPublicEventPricing(value) {
23495
- if (!isRecord5(value) || typeof value.kind !== "string") return false;
23496
- return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course";
23556
+ if (!isRecord6(value) || typeof value.kind !== "string") return false;
23557
+ return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
23497
23558
  }
23498
23559
  function isPublicEventTicketType(value) {
23499
- return isRecord5(value) && typeof value.id === "string";
23560
+ return isRecord6(value) && typeof value.id === "string";
23500
23561
  }
23501
23562
  function readTicketTypePriceDisplay(value, currency) {
23502
23563
  const display = readLegacyPriceDisplay(value, currency);
23503
- return display.kind === "course" ? { kind: "unavailable", currency } : display;
23564
+ return display.kind === "course" || display.kind === "external" ? { kind: "unavailable", currency } : display;
23504
23565
  }
23505
23566
  function readLegacyPriceDisplay(value, currency) {
23506
- if (!isRecord5(value) || typeof value.kind !== "string") {
23567
+ if (!isRecord6(value) || typeof value.kind !== "string") {
23507
23568
  return { kind: "unavailable", currency };
23508
23569
  }
23509
23570
  const displayCurrency = readCurrency(value.currency);
@@ -23542,7 +23603,7 @@ function readLegacyPriceDisplay(value, currency) {
23542
23603
  case "unavailable":
23543
23604
  return { kind: "unavailable", currency: displayCurrency };
23544
23605
  case "course":
23545
- return isRecord5(value.display) ? {
23606
+ return isRecord6(value.display) ? {
23546
23607
  kind: "course",
23547
23608
  display: readCoursePriceDisplay(value.display, displayCurrency)
23548
23609
  } : {
@@ -23580,6 +23641,7 @@ var DEFAULT_EVENT_CURRENCY;
23580
23641
  var init_pricing = __esm({
23581
23642
  "../blocks/src/system/runtime/nodes/events/shared/pricing.ts"() {
23582
23643
  "use strict";
23644
+ init_destination();
23583
23645
  DEFAULT_EVENT_CURRENCY = "USD";
23584
23646
  }
23585
23647
  });
@@ -23672,7 +23734,7 @@ function resolveEventRequiresPayment(input) {
23672
23734
  if (!input.event) {
23673
23735
  return false;
23674
23736
  }
23675
- return publicEventPricingType(resolveRuntimeEventPricing(input.event)) !== "free";
23737
+ return publicEventPricingType(resolveRuntimeEventPricing(input.event)) === "paid";
23676
23738
  }
23677
23739
  function resolveDiscountContext(input) {
23678
23740
  const selection = input.paymentSelection.method ? input.paymentSelection : null;
@@ -23804,15 +23866,16 @@ function buildEventRegistrationShellBodyText(stepLabels) {
23804
23866
  return "Preparing dates, tickets, and booking details.";
23805
23867
  }
23806
23868
  function normalizeEventRegistrationEventInput(value) {
23807
- if (!isRecord6(value)) {
23869
+ if (!isRecord7(value)) {
23808
23870
  return value;
23809
23871
  }
23810
23872
  return {
23811
23873
  ...value,
23874
+ destination: resolveRuntimeEventDestination(value),
23812
23875
  pricing: resolveRuntimeEventPricing(value)
23813
23876
  };
23814
23877
  }
23815
- function isRecord6(value) {
23878
+ function isRecord7(value) {
23816
23879
  return typeof value === "object" && value !== null && !Array.isArray(value);
23817
23880
  }
23818
23881
  function buildEventRegistrationInteractiveIslandProps(props2) {
@@ -23838,6 +23901,7 @@ var init_event_registration_interactive = __esm({
23838
23901
  init_event_registration_shared();
23839
23902
  init_events();
23840
23903
  init_pricing();
23904
+ init_destination();
23841
23905
  init_islands();
23842
23906
  init_runtime();
23843
23907
  init_contract_schemas();
@@ -24544,7 +24608,7 @@ function formatCalendarTitle(viewDate, view, days, monthNames = MONTH_NAMES) {
24544
24608
  if (view === "week") {
24545
24609
  const weekStart = days[0];
24546
24610
  const weekEnd = days[6];
24547
- if (!weekStart || !weekEnd) {
24611
+ if (weekStart === void 0 || weekEnd === void 0) {
24548
24612
  return `${monthNames[viewDate.getMonth()] ?? ""} ${viewDate.getFullYear()}`;
24549
24613
  }
24550
24614
  const startMonth = monthNames[weekStart.getMonth()] ?? "";
@@ -24676,7 +24740,9 @@ function getLocalDateKey(isoTimestamp, timeZone) {
24676
24740
  const year = parts.find((part) => part.type === "year")?.value;
24677
24741
  const month = parts.find((part) => part.type === "month")?.value;
24678
24742
  const day = parts.find((part) => part.type === "day")?.value;
24679
- if (!year || !month || !day) return null;
24743
+ if (year === void 0 || year.length === 0) return null;
24744
+ if (month === void 0 || month.length === 0) return null;
24745
+ if (day === void 0 || day.length === 0) return null;
24680
24746
  return `${year}-${month}-${day}`;
24681
24747
  }
24682
24748
  function isValidTimeZone2(timeZone) {
@@ -24933,7 +24999,7 @@ function enumerateDateKeys(startDateKey, endDateKey) {
24933
24999
  }
24934
25000
  function parseDateKey(dateKey) {
24935
25001
  const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateKey);
24936
- if (!match) {
25002
+ if (match === null) {
24937
25003
  throw new Error(`Invalid timetable date key: ${dateKey}`);
24938
25004
  }
24939
25005
  const [, year, month, day] = match;
@@ -24969,7 +25035,7 @@ function groupItemsByDate(items, accessors) {
24969
25035
  const dateKey = getLocalDateKey(startsAt, timeZone);
24970
25036
  if (dateKey == null) continue;
24971
25037
  const existing = grouped.get(dateKey);
24972
- if (existing) {
25038
+ if (existing !== void 0) {
24973
25039
  existing.push(item);
24974
25040
  } else {
24975
25041
  grouped.set(dateKey, [item]);
@@ -25129,16 +25195,28 @@ function formatEventPrice(event) {
25129
25195
  function formatEventPriceDisplay(pricing) {
25130
25196
  return formatEventPricing(pricing, { decimals: "auto" });
25131
25197
  }
25198
+ function resolveEventHref(event) {
25199
+ const destination = resolveRuntimeEventDestination(event);
25200
+ return destination.kind === "external" ? destination.url : destination.path;
25201
+ }
25202
+ function resolveEventLinkProps(event) {
25203
+ const href = resolveEventHref(event);
25204
+ const destination = resolveRuntimeEventDestination(event);
25205
+ return destination.kind === "external" ? { href, target: "_blank", rel: "noopener noreferrer" } : { href };
25206
+ }
25132
25207
  function resolveEventCta(event, buttonText) {
25133
25208
  const { isFull: isSoldOut } = getEventAvailability(event);
25134
- const defaultButtonText = event.kind === "course_session" ? "Book course" : "Book";
25209
+ const destination = resolveRuntimeEventDestination(event);
25210
+ const isExternal = destination.kind === "external";
25211
+ const defaultButtonText = isExternal ? destination.label : event.kind === "course_session" ? "Book course" : "Book";
25135
25212
  const effectiveButtonText = buttonText ?? defaultButtonText;
25136
- if (isSoldOut) {
25137
- return { label: "Sold out", href: event.path, hidden: true };
25213
+ const href = resolveEventHref(event);
25214
+ if (!isExternal && isSoldOut) {
25215
+ return { label: "Sold out", href, external: false, hidden: true };
25138
25216
  }
25139
25217
  const enrollmentOpen = event.kind === "course_session" ? event.course?.enrollmentOpen !== false : true;
25140
25218
  const label = !enrollmentOpen ? "Registration closed" : effectiveButtonText;
25141
- return { label, href: event.path, hidden: false };
25219
+ return { label, href, external: isExternal, hidden: false };
25142
25220
  }
25143
25221
  var COLUMN_CLASSES;
25144
25222
  var init_utils3 = __esm({
@@ -25147,6 +25225,7 @@ var init_utils3 = __esm({
25147
25225
  init_src();
25148
25226
  init_src3();
25149
25227
  init_eventCapacity();
25228
+ init_destination();
25150
25229
  init_pricing();
25151
25230
  init_src3();
25152
25231
  init_src3();
@@ -26681,6 +26760,8 @@ var init_EventCard = __esm({
26681
26760
  "a",
26682
26761
  {
26683
26762
  href: cta.href,
26763
+ target: cta.external ? "_blank" : void 0,
26764
+ rel: cta.external ? "noopener noreferrer" : void 0,
26684
26765
  className: `${buttonClass} event-card-cta`,
26685
26766
  children: cta.label
26686
26767
  }
@@ -26738,10 +26819,19 @@ var init_EventCompactRow = __esm({
26738
26819
  formattedTime,
26739
26820
  /* @__PURE__ */ jsx8("span", { className: "event-compact-row-action-price", children: price })
26740
26821
  ] }),
26741
- cta.hidden ? /* @__PURE__ */ jsx8("span", { className: "event-compact-row-status", children: cta.label }) : /* @__PURE__ */ jsxs6("a", { href: cta.href, className: `${buttonClass} event-compact-row-cta`, children: [
26742
- /* @__PURE__ */ jsx8("span", { children: cta.label }),
26743
- /* @__PURE__ */ jsx8(ArrowRightIcon, { "aria-hidden": "true" })
26744
- ] })
26822
+ cta.hidden ? /* @__PURE__ */ jsx8("span", { className: "event-compact-row-status", children: cta.label }) : /* @__PURE__ */ jsxs6(
26823
+ "a",
26824
+ {
26825
+ href: cta.href,
26826
+ target: cta.external ? "_blank" : void 0,
26827
+ rel: cta.external ? "noopener noreferrer" : void 0,
26828
+ className: `${buttonClass} event-compact-row-cta`,
26829
+ children: [
26830
+ /* @__PURE__ */ jsx8("span", { children: cta.label }),
26831
+ /* @__PURE__ */ jsx8(ArrowRightIcon, { "aria-hidden": "true" })
26832
+ ]
26833
+ }
26834
+ )
26745
26835
  ] })
26746
26836
  ] })
26747
26837
  ] });
@@ -29445,12 +29535,12 @@ function resolveBookingSiteId(siteId, form2) {
29445
29535
  return siteId || form2?.siteId || "";
29446
29536
  }
29447
29537
  function normalizeBookingFormConfig(form2) {
29448
- if (!isRecord10(form2)) return null;
29538
+ if (!isRecord11(form2)) return null;
29449
29539
  const id = readNonEmptyString(form2.id);
29450
29540
  const name = readNonEmptyString(form2.name);
29451
29541
  if (!id || !name) return null;
29452
- const schemaSource = isRecord10(form2.schema) ? form2.schema : isRecord10(form2.schemaJson) ? form2.schemaJson : null;
29453
- const settingsSource = isRecord10(form2.settings) ? form2.settings : isRecord10(form2.settingsJson) ? form2.settingsJson : null;
29542
+ const schemaSource = isRecord11(form2.schema) ? form2.schema : isRecord11(form2.schemaJson) ? form2.schemaJson : null;
29543
+ const settingsSource = isRecord11(form2.settings) ? form2.settings : isRecord11(form2.settingsJson) ? form2.settingsJson : null;
29454
29544
  return {
29455
29545
  id,
29456
29546
  name,
@@ -29474,10 +29564,10 @@ function normalizeBookingServices(services) {
29474
29564
  if (Array.isArray(services)) {
29475
29565
  return services.filter(isBookingFormService).map(normalizeService);
29476
29566
  }
29477
- if (isRecord10(services) && "services" in services) {
29567
+ if (isRecord11(services) && "services" in services) {
29478
29568
  return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
29479
29569
  }
29480
- return isRecord10(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
29570
+ return isRecord11(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
29481
29571
  }
29482
29572
  function normalizeBookingSchema(value) {
29483
29573
  if (!value) return void 0;
@@ -29489,7 +29579,7 @@ function normalizeBookingSchema(value) {
29489
29579
  };
29490
29580
  }
29491
29581
  function readBookingFormField(value) {
29492
- if (!isRecord10(value)) return null;
29582
+ if (!isRecord11(value)) return null;
29493
29583
  const id = readNonEmptyString(value.id);
29494
29584
  const label = readNonEmptyString(value.label);
29495
29585
  const type = readBookingFieldType(value.type);
@@ -29535,7 +29625,7 @@ function readBookingFieldOptions(value) {
29535
29625
  return options.length > 0 ? options : void 0;
29536
29626
  }
29537
29627
  function readBookingFieldOption(value) {
29538
- if (!isRecord10(value)) return null;
29628
+ if (!isRecord11(value)) return null;
29539
29629
  const optionValue = readNonEmptyString(value.value);
29540
29630
  const label = readNonEmptyString(value.label);
29541
29631
  return optionValue && label ? { value: optionValue, label } : null;
@@ -29552,7 +29642,7 @@ function normalizeBookingSettings(value) {
29552
29642
  };
29553
29643
  }
29554
29644
  function isBookingFormService(value) {
29555
- return isRecord10(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string";
29645
+ return isRecord11(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string";
29556
29646
  }
29557
29647
  function collectBookingCustomFields(data) {
29558
29648
  const customFields = {};
@@ -29582,7 +29672,7 @@ function readStringArray(value) {
29582
29672
  const strings = value.filter((item) => typeof item === "string");
29583
29673
  return strings.length > 0 ? strings : void 0;
29584
29674
  }
29585
- function isRecord10(value) {
29675
+ function isRecord11(value) {
29586
29676
  return typeof value === "object" && value !== null;
29587
29677
  }
29588
29678
  function getBookingServicePaymentSelectionMode(service) {
@@ -30370,7 +30460,7 @@ function EventDetailsView({
30370
30460
  "a",
30371
30461
  {
30372
30462
  className: "rb-tile rb-tile--interactive",
30373
- href: e.path,
30463
+ ...resolveEventLinkProps(e),
30374
30464
  children: /* @__PURE__ */ jsx30(
30375
30465
  DateTimeTile,
30376
30466
  {
@@ -30884,7 +30974,7 @@ var init_AgendaEventCard = __esm({
30884
30974
  showCapacity,
30885
30975
  onClick
30886
30976
  }) => {
30887
- const eventUrl = event.path;
30977
+ const eventLinkProps = resolveEventLinkProps(event);
30888
30978
  const categoryColor = event.eventCategory?.color || "rgb(var(--tb-accent))";
30889
30979
  const price = formatEventPrice(event);
30890
30980
  const { available, isFull } = getEventAvailability(event);
@@ -30945,7 +31035,7 @@ var init_AgendaEventCard = __esm({
30945
31035
  }
30946
31036
  );
30947
31037
  }
30948
- return /* @__PURE__ */ jsx47("a", { href: eventUrl, className: "event-agenda-card", children: cardContent });
31038
+ return /* @__PURE__ */ jsx47("a", { ...eventLinkProps, className: "event-agenda-card", children: cardContent });
30949
31039
  };
30950
31040
  }
30951
31041
  });
@@ -32802,7 +32892,7 @@ var init_embla_carousel_autoplay_esm = __esm({
32802
32892
  function isObject(subject) {
32803
32893
  return Object.prototype.toString.call(subject) === "[object Object]";
32804
32894
  }
32805
- function isRecord14(subject) {
32895
+ function isRecord15(subject) {
32806
32896
  return isObject(subject) || Array.isArray(subject);
32807
32897
  }
32808
32898
  function canUseDOM() {
@@ -32819,7 +32909,7 @@ function areOptionsEqual(optionsA, optionsB) {
32819
32909
  const valueA = optionsA[key];
32820
32910
  const valueB = optionsB[key];
32821
32911
  if (typeof valueA === "function") return `${valueA}` === `${valueB}`;
32822
- if (!isRecord14(valueA) || !isRecord14(valueB)) return valueA === valueB;
32912
+ if (!isRecord15(valueA) || !isRecord15(valueB)) return valueA === valueB;
32823
32913
  return areOptionsEqual(valueA, valueB);
32824
32914
  });
32825
32915
  }
@@ -35634,7 +35724,7 @@ function encodePublicProductCategorySelector2(selector) {
35634
35724
  };
35635
35725
  }
35636
35726
  }
35637
- function isRecord15(value) {
35727
+ function isRecord16(value) {
35638
35728
  return typeof value === "object" && value !== null;
35639
35729
  }
35640
35730
  function isJsonResponseContentType(contentType) {
@@ -35657,10 +35747,10 @@ async function parseJsonBody(response) {
35657
35747
  }
35658
35748
  }
35659
35749
  function getBlockApiErrorDetails(data) {
35660
- if (!isRecord15(data)) {
35750
+ if (!isRecord16(data)) {
35661
35751
  return {};
35662
35752
  }
35663
- const nestedError = isRecord15(data.error) ? data.error : void 0;
35753
+ const nestedError = isRecord16(data.error) ? data.error : void 0;
35664
35754
  const message = typeof nestedError?.message === "string" ? nestedError.message : typeof data.message === "string" ? data.message : void 0;
35665
35755
  const code = typeof nestedError?.code === "string" ? nestedError.code : typeof data.code === "string" ? data.code : void 0;
35666
35756
  return { message, code };
@@ -39170,22 +39260,22 @@ function AltchaWidget({
39170
39260
  if (typeof window === "undefined") return;
39171
39261
  let widget = null;
39172
39262
  Promise.resolve().then(() => (init_altcha(), altcha_exports)).then(() => {
39173
- if (!containerRef.current) return;
39263
+ if (containerRef.current === null) return;
39174
39264
  widget = document.createElement("altcha-widget");
39175
39265
  widget.setAttribute("challengeurl", challengeUrl);
39176
- if (auto) {
39266
+ if (auto === true) {
39177
39267
  widget.setAttribute("auto", "onload");
39178
39268
  }
39179
- if (hidelogo) {
39269
+ if (hidelogo === true) {
39180
39270
  widget.setAttribute("hidelogo", "true");
39181
39271
  }
39182
39272
  widget.setAttribute("hidefooter", "true");
39183
- if (className) {
39273
+ if (className !== void 0 && className.length > 0) {
39184
39274
  widget.className = className;
39185
39275
  }
39186
39276
  widget.addEventListener("statechange", ((event) => {
39187
39277
  const { state, payload } = event.detail;
39188
- if (state === "verified" && payload) {
39278
+ if (state === "verified" && payload !== void 0 && payload.length > 0) {
39189
39279
  onSolutionChange?.(payload);
39190
39280
  } else if (state === "error" || state === "expired") {
39191
39281
  onSolutionChange?.(null);
@@ -39196,7 +39286,7 @@ function AltchaWidget({
39196
39286
  console.error("Failed to load ALTCHA widget:", err3);
39197
39287
  });
39198
39288
  return () => {
39199
- if (widget && containerRef.current) {
39289
+ if (widget !== null && containerRef.current !== null) {
39200
39290
  containerRef.current.removeChild(widget);
39201
39291
  }
39202
39292
  };
@@ -39242,15 +39332,15 @@ var init_dist2 = __esm({
39242
39332
  // ../altcha/src/index.ts
39243
39333
  function getCmsApiUrl2() {
39244
39334
  const env = process.env.RIVERBANK_ENV;
39245
- if (env) {
39335
+ if (env !== void 0 && env.length > 0) {
39246
39336
  const envKey = `RIVERBANK_${env.toUpperCase()}_DASHBOARD_URL`;
39247
39337
  const dashboardUrl2 = process.env[envKey];
39248
- if (dashboardUrl2) {
39338
+ if (dashboardUrl2 !== void 0 && dashboardUrl2.length > 0) {
39249
39339
  return `${dashboardUrl2.replace(/\/$/, "")}/api`;
39250
39340
  }
39251
39341
  }
39252
39342
  const dashboardUrl = process.env.NEXT_PUBLIC_DASHBOARD_URL;
39253
- if (dashboardUrl) {
39343
+ if (dashboardUrl !== void 0 && dashboardUrl.length > 0) {
39254
39344
  return `${dashboardUrl.replace(/\/$/, "")}/api`;
39255
39345
  }
39256
39346
  throw new Error("Dashboard URL not configured. Set NEXT_PUBLIC_DASHBOARD_URL");
@@ -40562,8 +40652,10 @@ function buildBookingPaymentOptionViewModel(input) {
40562
40652
  totalAmountCents: input.totalCents,
40563
40653
  config: input.option.config
40564
40654
  });
40655
+ const remainingBalance = Math.max(0, input.totalCents - dueToday);
40565
40656
  const formattedTotal = formatPrice(input.totalCents, input.currency, input.locale);
40566
40657
  const formattedDueToday = formatPrice(dueToday, input.currency, input.locale);
40658
+ const formattedRemainingBalance = formatPrice(remainingBalance, input.currency, input.locale);
40567
40659
  const dueAt = resolveFlexibleBalanceDueAt({
40568
40660
  bookingStartAt: input.bookingStartAt ?? (/* @__PURE__ */ new Date()).toISOString(),
40569
40661
  dueDateRule: input.option.config.dueDateRule
@@ -40580,6 +40672,7 @@ function buildBookingPaymentOptionViewModel(input) {
40580
40672
  summaryRows: [
40581
40673
  { label: "Total", value: formattedTotal },
40582
40674
  { label: "Due today", value: formattedDueToday },
40675
+ { label: "Remaining balance", value: formattedRemainingBalance },
40583
40676
  { label: "Balance due by", value: formatScheduleDate(dueAt, input.locale) },
40584
40677
  {
40585
40678
  label: "Deposit policy",
@@ -45630,6 +45723,33 @@ function toEventDirectPaymentOption(option, mode) {
45630
45723
  );
45631
45724
  }
45632
45725
  }
45726
+ function resolveEventRegistrationDirectPaymentSelection(input) {
45727
+ if (input.selection.method !== "stripe") {
45728
+ return { kind: "not-stripe" };
45729
+ }
45730
+ const fallbackOption = input.options[0];
45731
+ if (!fallbackOption) {
45732
+ return { kind: "unavailable" };
45733
+ }
45734
+ const requestedKind = input.selection.collectionOptionKind;
45735
+ const selectedOption = requestedKind == null ? input.options.find((option) => option.kind === "upfront") : input.options.find((option) => option.kind === requestedKind);
45736
+ if (selectedOption) {
45737
+ return {
45738
+ kind: "valid",
45739
+ selection: {
45740
+ method: "stripe",
45741
+ collectionOptionKind: selectedOption.kind
45742
+ }
45743
+ };
45744
+ }
45745
+ return {
45746
+ kind: "invalid",
45747
+ fallbackSelection: {
45748
+ method: "stripe",
45749
+ collectionOptionKind: fallbackOption.kind
45750
+ }
45751
+ };
45752
+ }
45633
45753
  var DEFAULT_EVENT_DIRECT_PAYMENT_OPTION;
45634
45754
  var init_eventPaymentOptions = __esm({
45635
45755
  "../blocks/src/system/runtime/nodes/event-registration/eventPaymentOptions.ts"() {
@@ -45689,6 +45809,9 @@ function formatDirectPaymentOptionDetail(option) {
45689
45809
  balanceDueBy ? `Balance due by: ${balanceDueBy.value}` : null
45690
45810
  ].filter((part) => Boolean(part)).join(" \xB7 ");
45691
45811
  }
45812
+ function isDirectPaymentOptionSelected(option, selectionState) {
45813
+ return selectionState.kind === "valid" && selectionState.selection.collectionOptionKind === option.kind;
45814
+ }
45692
45815
  var PaymentSelectionStep;
45693
45816
  var init_PaymentSelectionStep = __esm({
45694
45817
  "../blocks/src/system/runtime/nodes/event-registration/PaymentSelectionStep.tsx"() {
@@ -45717,8 +45840,28 @@ var init_PaymentSelectionStep = __esm({
45717
45840
  }) => {
45718
45841
  const occurrenceId = occurrenceContext?.id ?? event?.registrationOccurrenceId;
45719
45842
  const selectedMethod = selectedSelection.method;
45843
+ const totalPrice = eligibility?.price ? eligibility.price * ticketsCount : 0;
45844
+ const canShowDirectPaymentOptions = Boolean(
45845
+ eligibility?.eligible && eligibility.canPayDirectly && eligibility.price
45846
+ );
45847
+ const directPaymentOptions = eligibility && canShowDirectPaymentOptions ? buildEventRegistrationPaymentOptionsViewModel({
45848
+ eventTitle: event?.title ?? "Event",
45849
+ paymentTerms: event?.paymentTerms,
45850
+ totalPriceCents: totalPrice,
45851
+ currency: eligibility.currency,
45852
+ startsAt: occurrenceContext?.startsAt ?? event?.startsAt
45853
+ }).options : [];
45854
+ const directPaymentSelectionState = resolveEventRegistrationDirectPaymentSelection({
45855
+ options: directPaymentOptions,
45856
+ selection: selectedSelection
45857
+ });
45858
+ const directPaymentFallbackSelection = directPaymentSelectionState.kind === "invalid" ? directPaymentSelectionState.fallbackSelection : null;
45859
+ const directPaymentFallbackKind = directPaymentFallbackSelection?.collectionOptionKind;
45720
45860
  const canContinue = (() => {
45721
45861
  if (!selectedMethod) return false;
45862
+ if (selectedMethod === "stripe") {
45863
+ return directPaymentSelectionState.kind === "valid";
45864
+ }
45722
45865
  if (selectedMethod === "pass") return Boolean(selectedSelection.passId);
45723
45866
  if (selectedMethod === "membership")
45724
45867
  return Boolean(selectedSelection.membershipId);
@@ -45728,6 +45871,13 @@ var init_PaymentSelectionStep = __esm({
45728
45871
  return Boolean(selectedSelection.bundleMembershipId);
45729
45872
  return true;
45730
45873
  })();
45874
+ useEffect14(() => {
45875
+ if (!directPaymentFallbackKind) return;
45876
+ onSelectionChange({
45877
+ method: "stripe",
45878
+ collectionOptionKind: directPaymentFallbackKind
45879
+ });
45880
+ }, [directPaymentFallbackKind, onSelectionChange]);
45731
45881
  const getButtonText = useCallback7(() => {
45732
45882
  if (selectedMethod === "stripe") return "Continue to Payment";
45733
45883
  if (selectedMethod === "pass_bundle" || selectedMethod === "membership_bundle") {
@@ -45827,25 +45977,20 @@ var init_PaymentSelectionStep = __esm({
45827
45977
  ] }) })
45828
45978
  ] });
45829
45979
  }
45830
- const totalPrice = eligibility.price ? eligibility.price * ticketsCount : 0;
45831
- const directPaymentOptions = buildEventRegistrationPaymentOptionsViewModel({
45832
- eventTitle: event?.title ?? "Event",
45833
- paymentTerms: event?.paymentTerms,
45834
- totalPriceCents: totalPrice,
45835
- currency: eligibility.currency,
45836
- startsAt: occurrenceContext?.startsAt ?? event?.startsAt
45837
- }).options;
45838
45980
  const isMultiTicket = areEntitlementsDisabledForTicketCount(ticketsCount);
45839
45981
  return /* @__PURE__ */ jsxs71("div", { className: "er-stack", children: [
45840
45982
  /* @__PURE__ */ jsx98("h3", { className: "er-title", children: "Payment Options" }),
45841
45983
  /* @__PURE__ */ jsxs71("div", { className: "er-payment-tiles", children: [
45842
- eligibility.canPayDirectly && eligibility.price && /* @__PURE__ */ jsx98(Fragment19, { children: directPaymentOptions.map((option) => /* @__PURE__ */ jsx98(
45984
+ canShowDirectPaymentOptions && /* @__PURE__ */ jsx98(Fragment19, { children: directPaymentOptions.map((option) => /* @__PURE__ */ jsx98(
45843
45985
  PaymentTile,
45844
45986
  {
45845
45987
  icon: /* @__PURE__ */ jsx98(CreditCard, { size: 24, "aria-hidden": "true" }),
45846
45988
  label: option.label,
45847
45989
  detail: formatDirectPaymentOptionDetail(option),
45848
- selected: selectedMethod === "stripe" && (selectedSelection.collectionOptionKind ?? "upfront") === option.kind,
45990
+ selected: isDirectPaymentOptionSelected(
45991
+ option,
45992
+ directPaymentSelectionState
45993
+ ),
45849
45994
  onClick: () => onSelectionChange({
45850
45995
  method: "stripe",
45851
45996
  collectionOptionKind: option.kind
@@ -46370,6 +46515,24 @@ import { Fragment as Fragment22, jsx as jsx106, jsxs as jsxs77 } from "react/jsx
46370
46515
  function isProductMethod(method) {
46371
46516
  return method !== null && ["pass", "membership", "pass_bundle", "membership_bundle"].includes(method);
46372
46517
  }
46518
+ function getSummaryRow(option, label) {
46519
+ return option.summaryRows.find((row) => row.label === label)?.value ?? null;
46520
+ }
46521
+ function getSelectedDirectPaymentOption(input) {
46522
+ const summaryEvent = input.wizard.selectedEvent ?? input.wizard.preSelectedEvent;
46523
+ if (!summaryEvent || input.wizard.paymentSelection.method !== "stripe") {
46524
+ return null;
46525
+ }
46526
+ const options = buildEventRegistrationPaymentOptionsViewModel({
46527
+ eventTitle: summaryEvent.title,
46528
+ paymentTerms: summaryEvent.paymentTerms,
46529
+ totalPriceCents: input.totalCents,
46530
+ currency: input.currency,
46531
+ startsAt: input.wizard.occurrenceContext?.startsAt ?? summaryEvent.startsAt
46532
+ }).options;
46533
+ const requestedKind = input.wizard.paymentSelection.collectionOptionKind ?? "upfront";
46534
+ return options.find((option) => option.kind === requestedKind) ?? options.find((option) => option.kind === "upfront") ?? options[0] ?? null;
46535
+ }
46373
46536
  function EventRegistrationSummaryBar(props2) {
46374
46537
  const { wizard } = props2;
46375
46538
  const summaryEvent = wizard.selectedEvent ?? wizard.preSelectedEvent;
@@ -46384,6 +46547,18 @@ function EventRegistrationSummaryBar(props2) {
46384
46547
  const autoAppliedDiscount = wizard.discountCode.trim().length > 0 ? null : wizard.autoAppliedDiscount;
46385
46548
  const autoAppliedDiscountCents = Math.max(0, autoAppliedDiscount?.computedAmountCents ?? 0);
46386
46549
  const discountedTotalCents = Math.max(0, subtotalCents - autoAppliedDiscountCents);
46550
+ const selectedDirectPaymentOption = getSelectedDirectPaymentOption({
46551
+ wizard,
46552
+ totalCents: discountedTotalCents,
46553
+ currency: summaryCurrency
46554
+ });
46555
+ const flexibleBalanceSummary = selectedDirectPaymentOption?.kind === "flexible_balance" ? {
46556
+ total: getSummaryRow(selectedDirectPaymentOption, "Total"),
46557
+ dueToday: getSummaryRow(selectedDirectPaymentOption, "Due today"),
46558
+ remainingBalance: getSummaryRow(selectedDirectPaymentOption, "Remaining balance"),
46559
+ balanceDueBy: getSummaryRow(selectedDirectPaymentOption, "Balance due by"),
46560
+ depositPolicy: getSummaryRow(selectedDirectPaymentOption, "Deposit policy")
46561
+ } : null;
46387
46562
  if (!summaryTitle) return null;
46388
46563
  const productMethod = isProductMethod(wizard.paymentSelection.method);
46389
46564
  return /* @__PURE__ */ jsxs77(Fragment22, { children: [
@@ -46427,6 +46602,22 @@ function EventRegistrationSummaryBar(props2) {
46427
46602
  summaryPrice > 0 && /* @__PURE__ */ jsx106("div", { className: "er-summary-bar-pricing", children: productMethod ? /* @__PURE__ */ jsxs77(Fragment22, { children: [
46428
46603
  /* @__PURE__ */ jsx106("span", { className: "er-summary-bar-price er-summary-bar-price--struck", children: formatPrice(summaryPrice * wizard.ticketsCount, summaryCurrency) }),
46429
46604
  /* @__PURE__ */ jsx106("div", { className: "er-summary-bar-included", children: wizard.paymentSelection.method === "pass" || wizard.paymentSelection.method === "pass_bundle" ? "1 pass credit" : "Included in membership" })
46605
+ ] }) : flexibleBalanceSummary ? /* @__PURE__ */ jsxs77(Fragment22, { children: [
46606
+ flexibleBalanceSummary.total ? /* @__PURE__ */ jsxs77("div", { className: "er-summary-bar-product-detail", children: [
46607
+ "Total: ",
46608
+ flexibleBalanceSummary.total
46609
+ ] }) : null,
46610
+ /* @__PURE__ */ jsx106("div", { className: "er-summary-bar-price", children: flexibleBalanceSummary.dueToday ?? formatPrice(discountedTotalCents, summaryCurrency) }),
46611
+ /* @__PURE__ */ jsx106("div", { className: "er-summary-bar-included", children: "Payable now" }),
46612
+ flexibleBalanceSummary.remainingBalance ? /* @__PURE__ */ jsxs77("div", { className: "er-summary-bar-product-detail", children: [
46613
+ "Remaining balance: ",
46614
+ flexibleBalanceSummary.remainingBalance,
46615
+ flexibleBalanceSummary.balanceDueBy ? ` due by ${flexibleBalanceSummary.balanceDueBy}` : ""
46616
+ ] }) : null,
46617
+ flexibleBalanceSummary.depositPolicy ? /* @__PURE__ */ jsxs77("div", { className: "er-summary-bar-product-detail", children: [
46618
+ "Deposit: ",
46619
+ flexibleBalanceSummary.depositPolicy
46620
+ ] }) : null
46430
46621
  ] }) : autoAppliedDiscount && autoAppliedDiscountCents > 0 ? /* @__PURE__ */ jsxs77(Fragment22, { children: [
46431
46622
  /* @__PURE__ */ jsx106("span", { className: "er-summary-bar-price er-summary-bar-price--struck", children: formatPrice(subtotalCents, summaryCurrency) }),
46432
46623
  /* @__PURE__ */ jsx106("div", { className: "er-summary-bar-price", children: formatPrice(discountedTotalCents, summaryCurrency) }),
@@ -46445,6 +46636,7 @@ var init_EventRegistrationSummaryBar = __esm({
46445
46636
  "use client";
46446
46637
  init_src();
46447
46638
  init_utils5();
46639
+ init_eventPaymentOptions();
46448
46640
  }
46449
46641
  });
46450
46642
 
@@ -70521,7 +70713,7 @@ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
70521
70713
  return keys.length >= 2 ? keys : [];
70522
70714
  }
70523
70715
  function normalizeOfferingsPreviewContentForWrite(value) {
70524
- if (!isRecord7(value)) return null;
70716
+ if (!isRecord8(value)) return null;
70525
70717
  const groupOrderValue = value.groupOrder;
70526
70718
  if (Array.isArray(groupOrderValue)) {
70527
70719
  const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
@@ -70534,7 +70726,7 @@ function normalizeOfferingsPreviewContentForWrite(value) {
70534
70726
  return { ...value, groupOrder: [] };
70535
70727
  }
70536
70728
  const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(
70537
- Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord7) : []
70729
+ Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord8) : []
70538
70730
  );
70539
70731
  return legacyOrder.length > 0 ? { ...value, groupOrder: legacyOrder } : value;
70540
70732
  }
@@ -70588,7 +70780,7 @@ function uniqueOfferingsPreviewSourceKeys(values) {
70588
70780
  }
70589
70781
  return keys;
70590
70782
  }
70591
- function isRecord7(value) {
70783
+ function isRecord8(value) {
70592
70784
  return typeof value === "object" && value !== null && !Array.isArray(value);
70593
70785
  }
70594
70786
  function mergeOrderedKeysWithDefaults(orderedKeys, defaultKeys) {
@@ -71050,7 +71242,7 @@ function isOfferingsPreviewItemLimit(value) {
71050
71242
  return value === "1" || value === "2" || value === "3" || value === "4" || value === "5" || value === "6" || value === null;
71051
71243
  }
71052
71244
  function normalizeOfferingsPreviewGroupOverride(value) {
71053
- if (!isRecord8(value)) return [];
71245
+ if (!isRecord9(value)) return [];
71054
71246
  const override = {};
71055
71247
  if (isOfferingsPreviewSourceKey(value.sourceKey) || value.sourceKey === null) {
71056
71248
  override.sourceKey = value.sourceKey;
@@ -71082,12 +71274,12 @@ function isButtonVariant(value) {
71082
71274
  return value === "primary" || value === "secondary" || value === "outline" || value === "link" || value === null;
71083
71275
  }
71084
71276
  function normalizeCardStylesContent(value) {
71085
- return isRecord8(value) || value === null ? value : void 0;
71277
+ return isRecord9(value) || value === null ? value : void 0;
71086
71278
  }
71087
71279
  function normalizeSectionStylesContent(value) {
71088
- return isRecord8(value) || value === null ? value : void 0;
71280
+ return isRecord9(value) || value === null ? value : void 0;
71089
71281
  }
71090
- function isRecord8(value) {
71282
+ function isRecord9(value) {
71091
71283
  return typeof value === "object" && value !== null && !Array.isArray(value);
71092
71284
  }
71093
71285
 
@@ -75065,11 +75257,11 @@ function extractRouteUpdatedAt(candidate) {
75065
75257
  }
75066
75258
  function resolveRouteTargetForStage(routeIdValue, routes, stageInput) {
75067
75259
  const routeId = typeof routeIdValue === "string" ? routeIdValue : null;
75068
- if (!routeId || !routes) return null;
75260
+ if (routeId === null || routeId.length === 0 || routes === void 0) return null;
75069
75261
  const routeEntry = routes[routeId];
75070
75262
  if (!isRouteVisibleInStage(routeEntry, stageInput)) return null;
75071
75263
  const href = extractRouteHref(routeEntry, stageInput);
75072
- if (!href) return null;
75264
+ if (href === null) return null;
75073
75265
  return {
75074
75266
  routeId,
75075
75267
  href,
@@ -75222,7 +75414,7 @@ import React4 from "react";
75222
75414
  init_src();
75223
75415
  var LINKED_CONTENT_ENTRY_META_KEY = "__rbLinkedContentEntry";
75224
75416
  function coerceLinkedContentEntriesPayload(payload) {
75225
- if (!isRecord9(payload)) return payload;
75417
+ if (!isRecord10(payload)) return payload;
75226
75418
  const response = payload;
75227
75419
  if (!Array.isArray(response.entries)) return payload;
75228
75420
  const contentType = parsePublicContentTypeMeta(response.contentType);
@@ -75230,16 +75422,16 @@ function coerceLinkedContentEntriesPayload(payload) {
75230
75422
  return response.entries.map((entry) => enrichLinkedContentEntry(entry, contentType));
75231
75423
  }
75232
75424
  function getLinkedContentEntryMeta(value) {
75233
- if (!isRecord9(value)) return null;
75425
+ if (!isRecord10(value)) return null;
75234
75426
  const candidate = value[LINKED_CONTENT_ENTRY_META_KEY];
75235
75427
  return isLinkedContentEntryMeta(candidate) ? candidate : null;
75236
75428
  }
75237
75429
  function buildLinkedContentEditTarget(input) {
75238
75430
  const { entry, sourceBlockId, sourceBlockKind, itemIndex } = input;
75239
- if (!isRecord9(entry)) return null;
75431
+ if (!isRecord10(entry)) return null;
75240
75432
  const meta = getLinkedContentEntryMeta(entry);
75241
75433
  if (!meta || meta.support.kind !== "supported") return null;
75242
- const content = isRecord9(entry.content) ? entry.content : {};
75434
+ const content = isRecord10(entry.content) ? entry.content : {};
75243
75435
  return {
75244
75436
  kind: "linked-content-entry",
75245
75437
  entryId: meta.entryId,
@@ -75289,12 +75481,12 @@ function resolveLinkedEntryInlineFieldEligibility(input) {
75289
75481
  };
75290
75482
  }
75291
75483
  function enrichLinkedContentEntry(entry, contentType) {
75292
- if (!isRecord9(entry)) return entry;
75484
+ if (!isRecord10(entry)) return entry;
75293
75485
  const entryId = typeof entry.id === "string" ? entry.id : null;
75294
75486
  if (!entryId) return entry;
75295
75487
  const title = typeof entry.title === "string" && entry.title.trim().length > 0 ? entry.title : entryId;
75296
75488
  const optimisticHash = typeof entry.optimisticHash === "string" && entry.optimisticHash.length > 0 ? entry.optimisticHash : null;
75297
- const content = isRecord9(entry.content) ? entry.content : {};
75489
+ const content = isRecord10(entry.content) ? entry.content : {};
75298
75490
  const meta = {
75299
75491
  kind: "linked-content-entry",
75300
75492
  entryId,
@@ -75390,7 +75582,7 @@ function capitalize(value) {
75390
75582
  return value.charAt(0).toUpperCase() + value.slice(1).toLowerCase();
75391
75583
  }
75392
75584
  function parsePublicContentTypeMeta(value) {
75393
- if (!isRecord9(value)) return null;
75585
+ if (!isRecord10(value)) return null;
75394
75586
  if (typeof value.key !== "string" || value.key.trim().length === 0) return null;
75395
75587
  return {
75396
75588
  key: value.key,
@@ -75400,7 +75592,7 @@ function parsePublicContentTypeMeta(value) {
75400
75592
  };
75401
75593
  }
75402
75594
  function parsePublicContentTypeLinkedEntryEditing(value) {
75403
- if (!isRecord9(value) || typeof value.kind !== "string") return null;
75595
+ if (!isRecord10(value) || typeof value.kind !== "string") return null;
75404
75596
  switch (value.kind) {
75405
75597
  case "generic":
75406
75598
  return { kind: "generic" };
@@ -75414,7 +75606,7 @@ function parsePublicContentTypeLinkedEntryEditing(value) {
75414
75606
  }
75415
75607
  }
75416
75608
  function parsePublicContentTypeFieldMeta(value) {
75417
- if (!isRecord9(value)) return [];
75609
+ if (!isRecord10(value)) return [];
75418
75610
  if (typeof value.id !== "string" || typeof value.type !== "string") return [];
75419
75611
  return [{
75420
75612
  id: value.id,
@@ -75429,14 +75621,14 @@ function parseStringArray(value) {
75429
75621
  return value.filter((item) => typeof item === "string");
75430
75622
  }
75431
75623
  function isLinkedContentEntryMeta(value) {
75432
- if (!isRecord9(value)) return false;
75624
+ if (!isRecord10(value)) return false;
75433
75625
  if (value.kind !== "linked-content-entry") return false;
75434
75626
  if (typeof value.entryId !== "string") return false;
75435
75627
  if (typeof value.contentTypeKey !== "string") return false;
75436
75628
  if (typeof value.contentTypeLabel !== "string") return false;
75437
75629
  if (typeof value.title !== "string") return false;
75438
75630
  if (!(value.optimisticHash === null || typeof value.optimisticHash === "string")) return false;
75439
- if (!isRecord9(value.contentType)) return false;
75631
+ if (!isRecord10(value.contentType)) return false;
75440
75632
  if (!isLinkedEntryFieldSourceArray(value.fieldSources)) return false;
75441
75633
  return isLinkedContentEditSupport(value.support);
75442
75634
  }
@@ -75444,7 +75636,7 @@ function isLinkedEntryFieldSourceArray(value) {
75444
75636
  return Array.isArray(value) && value.every(isLinkedEntryFieldSource);
75445
75637
  }
75446
75638
  function isLinkedEntryFieldSource(value) {
75447
- if (!isRecord9(value)) return false;
75639
+ if (!isRecord10(value)) return false;
75448
75640
  if (typeof value.displayPath !== "string") return false;
75449
75641
  switch (value.kind) {
75450
75642
  case "content-field":
@@ -75470,7 +75662,7 @@ function isLinkedEntryInlineFieldReadOnlyReason(value) {
75470
75662
  }
75471
75663
  }
75472
75664
  function isLinkedContentEditSupport(value) {
75473
- if (!isRecord9(value) || typeof value.kind !== "string") return false;
75665
+ if (!isRecord10(value) || typeof value.kind !== "string") return false;
75474
75666
  switch (value.kind) {
75475
75667
  case "supported":
75476
75668
  return true;
@@ -75482,7 +75674,7 @@ function isLinkedContentEditSupport(value) {
75482
75674
  return false;
75483
75675
  }
75484
75676
  }
75485
- function isRecord9(value) {
75677
+ function isRecord10(value) {
75486
75678
  return typeof value === "object" && value !== null && !Array.isArray(value);
75487
75679
  }
75488
75680
 
@@ -78131,7 +78323,7 @@ function renderCalendarSsr(display) {
78131
78323
  dayEvents.slice(0, MAX_EVENTS_PER_DAY).map((event) => /* @__PURE__ */ jsxs35(
78132
78324
  "a",
78133
78325
  {
78134
- href: event.path,
78326
+ ...resolveEventLinkProps(event),
78135
78327
  className: "event-calendar-event",
78136
78328
  title: `${event.title} at ${formatTime(event.startsAt, { timeZone: getEventTimeZone(event) })}`,
78137
78329
  children: [
@@ -78259,7 +78451,7 @@ function renderTimetableSsr(display) {
78259
78451
  day.events.length === 0 ? /* @__PURE__ */ jsx49("p", { className: "event-timetable-day-empty", children: "No classes" }) : day.events.map((cell) => /* @__PURE__ */ jsxs35(
78260
78452
  "a",
78261
78453
  {
78262
- href: cell.event.path,
78454
+ ...resolveEventLinkProps(cell.event),
78263
78455
  className: "event-timetable-event",
78264
78456
  title: `${cell.event.title}, ${cell.timeLabel}`,
78265
78457
  style: {
@@ -79047,14 +79239,14 @@ function isDecoratedHeadingBlockKind(blockKind) {
79047
79239
  return blockKind === "block.intro" || blockKind === "block.feature-cards" || blockKind === "block.testimonials" || blockKind === "block.faq";
79048
79240
  }
79049
79241
  function readVariant(blockContent) {
79050
- if (!isRecord11(blockContent)) return void 0;
79242
+ if (!isRecord12(blockContent)) return void 0;
79051
79243
  const variant = blockContent.variant;
79052
79244
  return typeof variant === "string" ? variant : void 0;
79053
79245
  }
79054
79246
  function isFaqDecoratedVariant(variant) {
79055
79247
  return variant === "default" || variant === "question-columns";
79056
79248
  }
79057
- function isRecord11(value) {
79249
+ function isRecord12(value) {
79058
79250
  return typeof value === "object" && value !== null && !Array.isArray(value);
79059
79251
  }
79060
79252
  function buildHeadingDecorationWrapperClassName(className) {
@@ -97178,7 +97370,7 @@ function preset(id, verticalId, canonicalBehaviour, label, pluralLabel, descript
97178
97370
  }
97179
97371
 
97180
97372
  // ../api/src/utils/isRecord.ts
97181
- function isRecord12(value) {
97373
+ function isRecord13(value) {
97182
97374
  return typeof value === "object" && value !== null && !Array.isArray(value);
97183
97375
  }
97184
97376
 
@@ -97485,7 +97677,7 @@ function sanitizeMarks(marks) {
97485
97677
  if (mark.type === "bold" || mark.type === "strong" || mark.type === "italic" || mark.type === "em") {
97486
97678
  return [{ type: mark.type }];
97487
97679
  }
97488
- if (mark.type === "link" && isRecord12(mark.attrs) && typeof mark.attrs.href === "string") {
97680
+ if (mark.type === "link" && isRecord13(mark.attrs) && typeof mark.attrs.href === "string") {
97489
97681
  const attrs = { href: mark.attrs.href };
97490
97682
  if (typeof mark.attrs.target === "string" && mark.attrs.target.trim().length > 0) {
97491
97683
  attrs.target = mark.attrs.target;
@@ -97500,10 +97692,10 @@ function sanitizeMarks(marks) {
97500
97692
  return sanitized.length > 0 ? sanitized : void 0;
97501
97693
  }
97502
97694
  function unwrapRichTextValue(value) {
97503
- if (isRecord12(value) && isRecord12(value.doc)) {
97695
+ if (isRecord13(value) && isRecord13(value.doc)) {
97504
97696
  return unwrapRichTextValue(value.doc);
97505
97697
  }
97506
- if (isRecord12(value) && value.type === "doc") {
97698
+ if (isRecord13(value) && value.type === "doc") {
97507
97699
  return {
97508
97700
  type: "doc",
97509
97701
  content: Array.isArray(value.content) ? value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) : []
@@ -97512,7 +97704,7 @@ function unwrapRichTextValue(value) {
97512
97704
  return EMPTY_SITE_BANNER_BODY;
97513
97705
  }
97514
97706
  function isTipTapNodeLike(value) {
97515
- return isRecord12(value) && typeof value.type === "string";
97707
+ return isRecord13(value) && typeof value.type === "string";
97516
97708
  }
97517
97709
  function coerceTipTapNode(value) {
97518
97710
  return {
@@ -97520,9 +97712,9 @@ function coerceTipTapNode(value) {
97520
97712
  ...Array.isArray(value.content) ? { content: value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) } : {},
97521
97713
  ...typeof value.text === "string" ? { text: value.text } : {},
97522
97714
  ...Array.isArray(value.marks) ? {
97523
- marks: value.marks.filter((mark) => isRecord12(mark) && typeof mark.type === "string").map((mark) => ({
97715
+ marks: value.marks.filter((mark) => isRecord13(mark) && typeof mark.type === "string").map((mark) => ({
97524
97716
  type: mark.type,
97525
- ...isRecord12(mark.attrs) ? { attrs: mark.attrs } : {}
97717
+ ...isRecord13(mark.attrs) ? { attrs: mark.attrs } : {}
97526
97718
  }))
97527
97719
  } : {}
97528
97720
  };
@@ -97712,7 +97904,7 @@ var ENTRY_REF_KEYS = [
97712
97904
  "entryId",
97713
97905
  "slug"
97714
97906
  ];
97715
- function isRecord13(value) {
97907
+ function isRecord14(value) {
97716
97908
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
97717
97909
  }
97718
97910
  function extractManualEntryRef(item) {
@@ -97720,7 +97912,7 @@ function extractManualEntryRef(item) {
97720
97912
  const trimmed = item.trim();
97721
97913
  return trimmed.length > 0 ? trimmed : null;
97722
97914
  }
97723
- if (!isRecord13(item)) return null;
97915
+ if (!isRecord14(item)) return null;
97724
97916
  for (const key of ENTRY_REF_KEYS) {
97725
97917
  const raw = item[key];
97726
97918
  if (typeof raw === "string" && raw.trim().length > 0) {
@@ -98906,7 +99098,7 @@ var SimpleCache = class {
98906
99098
  };
98907
99099
 
98908
99100
  // src/version.ts
98909
- var SDK_VERSION = "0.90.0";
99101
+ var SDK_VERSION = "0.91.0";
98910
99102
 
98911
99103
  // src/client/error.ts
98912
99104
  var RiverbankApiError = class _RiverbankApiError extends Error {