@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
@@ -182,6 +182,8 @@ function publicEventPricingType(pricing) {
182
182
  case "ticket_types":
183
183
  case "course":
184
184
  return isFreePublicEventPriceDisplay(pricing.display) ? "free" : "paid";
185
+ case "external":
186
+ return "external";
185
187
  }
186
188
  }
187
189
  function publicEventPricingCurrency(pricing) {
@@ -193,6 +195,8 @@ function publicEventPricingCurrency(pricing) {
193
195
  return pricing.currency;
194
196
  case "course":
195
197
  return pricing.display.display.currency;
198
+ case "external":
199
+ return null;
196
200
  }
197
201
  }
198
202
  function isFreePublicEventPriceDisplay(display) {
@@ -206,6 +210,7 @@ function isFreePublicEventPriceDisplay(display) {
206
210
  case "range":
207
211
  case "mixed":
208
212
  case "unavailable":
213
+ case "external":
209
214
  return false;
210
215
  }
211
216
  }
@@ -236,6 +241,8 @@ function formatBookingPriceDisplay(display, options = {}) {
236
241
  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)}`;
237
242
  case "unavailable":
238
243
  return "Pricing unavailable";
244
+ case "external":
245
+ return display.label;
239
246
  case "course":
240
247
  return formatCoursePriceDisplayLabel(display.display, options);
241
248
  default:
@@ -259,11 +266,11 @@ function isValidTimeZone(timeZone) {
259
266
  }
260
267
  function getSafeTimeZone(timeZone) {
261
268
  const normalized = typeof timeZone === "string" ? timeZone.trim() : "";
262
- return normalized && isValidTimeZone(normalized) ? normalized : FALLBACK_TIMEZONE;
269
+ return normalized.length > 0 && isValidTimeZone(normalized) ? normalized : FALLBACK_TIMEZONE;
263
270
  }
264
271
  function parseStrictBookingTimeZone(timeZone) {
265
272
  const normalized = timeZone.trim();
266
- if (!normalized || !isValidTimeZone(normalized)) {
273
+ if (normalized.length === 0 || !isValidTimeZone(normalized)) {
267
274
  return null;
268
275
  }
269
276
  return normalized;
@@ -340,7 +347,7 @@ function parseScheduledBookingDateTime(input) {
340
347
  try {
341
348
  const startsAt = toUtcIsoDateTimeString(input.startsAt);
342
349
  const timeZone = parseStrictBookingTimeZone(input.timeZone);
343
- if (!timeZone) {
350
+ if (timeZone === null) {
344
351
  return {
345
352
  ok: false,
346
353
  field: "timeZone",
@@ -475,10 +482,10 @@ function normalizeLocationAddressText(value) {
475
482
  return null;
476
483
  }
477
484
  const trimmed = value.trim();
478
- return trimmed ? trimmed : null;
485
+ return trimmed.length > 0 ? trimmed : null;
479
486
  }
480
487
  function hasLocationCoordinates(value) {
481
- return typeof value?.lat === "number" && typeof value?.lng === "number";
488
+ return value != null && typeof value.lat === "number" && typeof value.lng === "number";
482
489
  }
483
490
  function toLocationCoordinates(value) {
484
491
  if (!hasLocationCoordinates(value)) {
@@ -26353,6 +26360,18 @@ var publicCoursesArraySchema = z43.array(publicCourseSchema);
26353
26360
  var eventPresetSchema = z44.enum(["event", "class", "workshop", "retreat", "performance"]);
26354
26361
  var eventScheduleScopeSchema = z44.enum(["recurring", "oneOff"]);
26355
26362
  var eventDestinationModeSchema = z44.enum(["cms-managed", "system-managed"]);
26363
+ var publicEventDestinationSchema = z44.discriminatedUnion("kind", [
26364
+ z44.object({
26365
+ kind: z44.literal("native"),
26366
+ mode: eventDestinationModeSchema,
26367
+ path: z44.string()
26368
+ }),
26369
+ z44.object({
26370
+ kind: z44.literal("external"),
26371
+ url: z44.string(),
26372
+ label: z44.string()
26373
+ })
26374
+ ]);
26356
26375
  var eventVenueSchema = z44.object({
26357
26376
  id: z44.string(),
26358
26377
  name: z44.string(),
@@ -26367,7 +26386,7 @@ var eventCategorySchema = z44.object({
26367
26386
  name: z44.string(),
26368
26387
  color: z44.string().nullable()
26369
26388
  });
26370
- var pricingTypeSchema = z44.enum(["free", "paid"]);
26389
+ var pricingTypeSchema = z44.enum(["free", "paid", "external"]);
26371
26390
  var accessRestrictionSchema = z44.enum(["none", "category"]);
26372
26391
  var eventPricingModeSchema = z44.enum(["single", "ticket_types"]);
26373
26392
  var publicCoursePriceDisplaySchema = z44.discriminatedUnion("kind", [
@@ -26388,6 +26407,7 @@ var publicEventPriceDisplaySchema = z44.discriminatedUnion("kind", [
26388
26407
  currency: z44.string()
26389
26408
  }),
26390
26409
  z44.object({ kind: z44.literal("unavailable"), currency: z44.string() }),
26410
+ z44.object({ kind: z44.literal("external"), label: z44.string() }),
26391
26411
  z44.object({ kind: z44.literal("course"), display: publicCoursePriceDisplaySchema })
26392
26412
  ]);
26393
26413
  var publicEventTicketTypePriceDisplaySchema = z44.union([
@@ -26466,6 +26486,10 @@ var publicEventPricingSchema = z44.discriminatedUnion("kind", [
26466
26486
  z44.object({
26467
26487
  kind: z44.literal("course"),
26468
26488
  display: z44.object({ kind: z44.literal("course"), display: publicCoursePriceDisplaySchema })
26489
+ }),
26490
+ z44.object({
26491
+ kind: z44.literal("external"),
26492
+ display: z44.object({ kind: z44.literal("external"), label: z44.string() })
26469
26493
  })
26470
26494
  ]);
26471
26495
  var publicTeamMemberSummarySchema = z44.object({
@@ -26494,7 +26518,8 @@ var publicEventSchema = z44.object({
26494
26518
  image: mediaSchema2.nullable().optional()
26495
26519
  }).optional(),
26496
26520
  slug: z44.string(),
26497
- path: z44.string(),
26521
+ path: z44.string().nullable(),
26522
+ destination: publicEventDestinationSchema,
26498
26523
  requiresRegistration: z44.boolean(),
26499
26524
  deliveryMode: z44.enum(["in_person", "online", "hybrid"]),
26500
26525
  attendanceModes: z44.array(publicEventAttendanceModeAvailabilitySchema),
@@ -26798,12 +26823,41 @@ function isEventContentType(slug) {
26798
26823
  return EVENT_CONTENT_TYPE_SLUGS.includes(slug);
26799
26824
  }
26800
26825
 
26826
+ // ../blocks/src/system/runtime/nodes/events/shared/destination.ts
26827
+ function isRecord5(value) {
26828
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26829
+ }
26830
+ function isRuntimeEventDestination(value) {
26831
+ if (!isRecord5(value) || typeof value.kind !== "string") return false;
26832
+ if (value.kind === "native") {
26833
+ return (value.mode === "cms-managed" || value.mode === "system-managed") && typeof value.path === "string";
26834
+ }
26835
+ return value.kind === "external" && typeof value.url === "string" && typeof value.label === "string";
26836
+ }
26837
+ function resolveRuntimeEventDestination(event) {
26838
+ if (isRuntimeEventDestination(event.destination)) {
26839
+ return event.destination;
26840
+ }
26841
+ return {
26842
+ kind: "native",
26843
+ mode: event.eventDestinationMode === "system-managed" ? "system-managed" : "cms-managed",
26844
+ path: typeof event.path === "string" ? event.path : ""
26845
+ };
26846
+ }
26847
+
26801
26848
  // ../blocks/src/system/runtime/nodes/events/shared/pricing.ts
26802
26849
  var DEFAULT_EVENT_CURRENCY = "USD";
26803
26850
  function resolveRuntimeEventPricing(event) {
26804
26851
  if (isPublicEventPricing(event.pricing)) {
26805
26852
  return event.pricing;
26806
26853
  }
26854
+ const destination = resolveRuntimeEventDestination(event);
26855
+ if (destination.kind === "external") {
26856
+ return {
26857
+ kind: "external",
26858
+ display: { kind: "external", label: "External registration" }
26859
+ };
26860
+ }
26807
26861
  const legacy = event;
26808
26862
  const currency = readCurrency(legacy.currency);
26809
26863
  if (event.requiresRegistration === false) {
@@ -26840,22 +26894,22 @@ function resolveRuntimeEventPricing(event) {
26840
26894
  function readCurrency(value) {
26841
26895
  return typeof value === "string" && value.trim().length > 0 ? value : DEFAULT_EVENT_CURRENCY;
26842
26896
  }
26843
- function isRecord5(value) {
26897
+ function isRecord6(value) {
26844
26898
  return typeof value === "object" && value !== null && !Array.isArray(value);
26845
26899
  }
26846
26900
  function isPublicEventPricing(value) {
26847
- if (!isRecord5(value) || typeof value.kind !== "string") return false;
26848
- return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course";
26901
+ if (!isRecord6(value) || typeof value.kind !== "string") return false;
26902
+ return value.kind === "drop_in" || value.kind === "free_registration" || value.kind === "single_paid" || value.kind === "ticket_types" || value.kind === "course" || value.kind === "external";
26849
26903
  }
26850
26904
  function isPublicEventTicketType(value) {
26851
- return isRecord5(value) && typeof value.id === "string";
26905
+ return isRecord6(value) && typeof value.id === "string";
26852
26906
  }
26853
26907
  function readTicketTypePriceDisplay(value, currency) {
26854
26908
  const display = readLegacyPriceDisplay(value, currency);
26855
- return display.kind === "course" ? { kind: "unavailable", currency } : display;
26909
+ return display.kind === "course" || display.kind === "external" ? { kind: "unavailable", currency } : display;
26856
26910
  }
26857
26911
  function readLegacyPriceDisplay(value, currency) {
26858
- if (!isRecord5(value) || typeof value.kind !== "string") {
26912
+ if (!isRecord6(value) || typeof value.kind !== "string") {
26859
26913
  return { kind: "unavailable", currency };
26860
26914
  }
26861
26915
  const displayCurrency = readCurrency(value.currency);
@@ -26894,7 +26948,7 @@ function readLegacyPriceDisplay(value, currency) {
26894
26948
  case "unavailable":
26895
26949
  return { kind: "unavailable", currency: displayCurrency };
26896
26950
  case "course":
26897
- return isRecord5(value.display) ? {
26951
+ return isRecord6(value.display) ? {
26898
26952
  kind: "course",
26899
26953
  display: readCoursePriceDisplay(value.display, displayCurrency)
26900
26954
  } : {
@@ -26981,7 +27035,7 @@ function resolveEventRequiresPayment(input) {
26981
27035
  if (!input.event) {
26982
27036
  return false;
26983
27037
  }
26984
- return publicEventPricingType(resolveRuntimeEventPricing(input.event)) !== "free";
27038
+ return publicEventPricingType(resolveRuntimeEventPricing(input.event)) === "paid";
26985
27039
  }
26986
27040
 
26987
27041
  // ../blocks/src/system/runtime/nodes/event-registration/registrationEventAdapter.ts
@@ -27178,15 +27232,16 @@ var eventRegistrationBlockRuntime = defineClientOwnedBlockRuntime({
27178
27232
  })
27179
27233
  });
27180
27234
  function normalizeEventRegistrationEventInput(value) {
27181
- if (!isRecord6(value)) {
27235
+ if (!isRecord7(value)) {
27182
27236
  return value;
27183
27237
  }
27184
27238
  return {
27185
27239
  ...value,
27240
+ destination: resolveRuntimeEventDestination(value),
27186
27241
  pricing: resolveRuntimeEventPricing(value)
27187
27242
  };
27188
27243
  }
27189
- function isRecord6(value) {
27244
+ function isRecord7(value) {
27190
27245
  return typeof value === "object" && value !== null && !Array.isArray(value);
27191
27246
  }
27192
27247
  var eventRegistrationInteractiveIslandPropsCodec = createInteractiveIslandCodec({
@@ -28455,7 +28510,7 @@ function formatCalendarTitle(viewDate, view, days, monthNames = MONTH_NAMES) {
28455
28510
  if (view === "week") {
28456
28511
  const weekStart = days[0];
28457
28512
  const weekEnd = days[6];
28458
- if (!weekStart || !weekEnd) {
28513
+ if (weekStart === void 0 || weekEnd === void 0) {
28459
28514
  return `${monthNames[viewDate.getMonth()] ?? ""} ${viewDate.getFullYear()}`;
28460
28515
  }
28461
28516
  const startMonth = monthNames[weekStart.getMonth()] ?? "";
@@ -28561,7 +28616,9 @@ function getLocalDateKey(isoTimestamp, timeZone) {
28561
28616
  const year = parts.find((part) => part.type === "year")?.value;
28562
28617
  const month = parts.find((part) => part.type === "month")?.value;
28563
28618
  const day = parts.find((part) => part.type === "day")?.value;
28564
- if (!year || !month || !day) return null;
28619
+ if (year === void 0 || year.length === 0) return null;
28620
+ if (month === void 0 || month.length === 0) return null;
28621
+ if (day === void 0 || day.length === 0) return null;
28565
28622
  return `${year}-${month}-${day}`;
28566
28623
  }
28567
28624
  var validTimeZoneCache = /* @__PURE__ */ new Map();
@@ -28818,7 +28875,7 @@ function enumerateDateKeys(startDateKey, endDateKey) {
28818
28875
  }
28819
28876
  function parseDateKey(dateKey) {
28820
28877
  const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(dateKey);
28821
- if (!match) {
28878
+ if (match === null) {
28822
28879
  throw new Error(`Invalid timetable date key: ${dateKey}`);
28823
28880
  }
28824
28881
  const [, year, month, day] = match;
@@ -28838,7 +28895,7 @@ function groupItemsByDate(items, accessors) {
28838
28895
  const dateKey = getLocalDateKey(startsAt, timeZone);
28839
28896
  if (dateKey == null) continue;
28840
28897
  const existing = grouped.get(dateKey);
28841
- if (existing) {
28898
+ if (existing !== void 0) {
28842
28899
  existing.push(item);
28843
28900
  } else {
28844
28901
  grouped.set(dateKey, [item]);
@@ -28979,16 +29036,28 @@ function groupEventsByDate(events) {
28979
29036
  function formatEventPrice(event) {
28980
29037
  return formatEventPricing(resolveRuntimeEventPricing(event), { decimals: "auto" });
28981
29038
  }
29039
+ function resolveEventHref(event) {
29040
+ const destination = resolveRuntimeEventDestination(event);
29041
+ return destination.kind === "external" ? destination.url : destination.path;
29042
+ }
29043
+ function resolveEventLinkProps(event) {
29044
+ const href = resolveEventHref(event);
29045
+ const destination = resolveRuntimeEventDestination(event);
29046
+ return destination.kind === "external" ? { href, target: "_blank", rel: "noopener noreferrer" } : { href };
29047
+ }
28982
29048
  function resolveEventCta(event, buttonText) {
28983
29049
  const { isFull: isSoldOut } = getEventAvailability(event);
28984
- const defaultButtonText = event.kind === "course_session" ? "Book course" : "Book";
29050
+ const destination = resolveRuntimeEventDestination(event);
29051
+ const isExternal = destination.kind === "external";
29052
+ const defaultButtonText = isExternal ? destination.label : event.kind === "course_session" ? "Book course" : "Book";
28985
29053
  const effectiveButtonText = buttonText ?? defaultButtonText;
28986
- if (isSoldOut) {
28987
- return { label: "Sold out", href: event.path, hidden: true };
29054
+ const href = resolveEventHref(event);
29055
+ if (!isExternal && isSoldOut) {
29056
+ return { label: "Sold out", href, external: false, hidden: true };
28988
29057
  }
28989
29058
  const enrollmentOpen = event.kind === "course_session" ? event.course?.enrollmentOpen !== false : true;
28990
29059
  const label = !enrollmentOpen ? "Registration closed" : effectiveButtonText;
28991
- return { label, href: event.path, hidden: false };
29060
+ return { label, href, external: isExternal, hidden: false };
28992
29061
  }
28993
29062
 
28994
29063
  // ../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
@@ -30426,6 +30495,8 @@ var EventCard = ({
30426
30495
  "a",
30427
30496
  {
30428
30497
  href: cta.href,
30498
+ target: cta.external ? "_blank" : void 0,
30499
+ rel: cta.external ? "noopener noreferrer" : void 0,
30429
30500
  className: `${buttonClass} event-card-cta`,
30430
30501
  children: cta.label
30431
30502
  }
@@ -30515,10 +30586,19 @@ var EventCompactRow = ({
30515
30586
  formattedTime,
30516
30587
  /* @__PURE__ */ jsx8("span", { className: "event-compact-row-action-price", children: price })
30517
30588
  ] }),
30518
- 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: [
30519
- /* @__PURE__ */ jsx8("span", { children: cta.label }),
30520
- /* @__PURE__ */ jsx8(ArrowRightIcon, { "aria-hidden": "true" })
30521
- ] })
30589
+ cta.hidden ? /* @__PURE__ */ jsx8("span", { className: "event-compact-row-status", children: cta.label }) : /* @__PURE__ */ jsxs6(
30590
+ "a",
30591
+ {
30592
+ href: cta.href,
30593
+ target: cta.external ? "_blank" : void 0,
30594
+ rel: cta.external ? "noopener noreferrer" : void 0,
30595
+ className: `${buttonClass} event-compact-row-cta`,
30596
+ children: [
30597
+ /* @__PURE__ */ jsx8("span", { children: cta.label }),
30598
+ /* @__PURE__ */ jsx8(ArrowRightIcon, { "aria-hidden": "true" })
30599
+ ]
30600
+ }
30601
+ )
30522
30602
  ] })
30523
30603
  ] })
30524
30604
  ] });
@@ -32108,7 +32188,7 @@ function inferLegacyOfferingsPreviewGroupOrder(overrides) {
32108
32188
  return keys.length >= 2 ? keys : [];
32109
32189
  }
32110
32190
  function normalizeOfferingsPreviewContentForWrite(value) {
32111
- if (!isRecord7(value)) return null;
32191
+ if (!isRecord8(value)) return null;
32112
32192
  const groupOrderValue = value.groupOrder;
32113
32193
  if (Array.isArray(groupOrderValue)) {
32114
32194
  const normalized = normalizeOfferingsPreviewGroupOrder(groupOrderValue);
@@ -32121,7 +32201,7 @@ function normalizeOfferingsPreviewContentForWrite(value) {
32121
32201
  return { ...value, groupOrder: [] };
32122
32202
  }
32123
32203
  const legacyOrder = inferLegacyOfferingsPreviewGroupOrder(
32124
- Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord7) : []
32204
+ Array.isArray(value.groupOverrides) ? value.groupOverrides.filter(isRecord8) : []
32125
32205
  );
32126
32206
  return legacyOrder.length > 0 ? { ...value, groupOrder: legacyOrder } : value;
32127
32207
  }
@@ -32175,7 +32255,7 @@ function uniqueOfferingsPreviewSourceKeys(values) {
32175
32255
  }
32176
32256
  return keys;
32177
32257
  }
32178
- function isRecord7(value) {
32258
+ function isRecord8(value) {
32179
32259
  return typeof value === "object" && value !== null && !Array.isArray(value);
32180
32260
  }
32181
32261
  function mergeOrderedKeysWithDefaults(orderedKeys, defaultKeys) {
@@ -32637,7 +32717,7 @@ function isOfferingsPreviewItemLimit(value) {
32637
32717
  return value === "1" || value === "2" || value === "3" || value === "4" || value === "5" || value === "6" || value === null;
32638
32718
  }
32639
32719
  function normalizeOfferingsPreviewGroupOverride(value) {
32640
- if (!isRecord8(value)) return [];
32720
+ if (!isRecord9(value)) return [];
32641
32721
  const override = {};
32642
32722
  if (isOfferingsPreviewSourceKey(value.sourceKey) || value.sourceKey === null) {
32643
32723
  override.sourceKey = value.sourceKey;
@@ -32669,12 +32749,12 @@ function isButtonVariant(value) {
32669
32749
  return value === "primary" || value === "secondary" || value === "outline" || value === "link" || value === null;
32670
32750
  }
32671
32751
  function normalizeCardStylesContent(value) {
32672
- return isRecord8(value) || value === null ? value : void 0;
32752
+ return isRecord9(value) || value === null ? value : void 0;
32673
32753
  }
32674
32754
  function normalizeSectionStylesContent(value) {
32675
- return isRecord8(value) || value === null ? value : void 0;
32755
+ return isRecord9(value) || value === null ? value : void 0;
32676
32756
  }
32677
- function isRecord8(value) {
32757
+ function isRecord9(value) {
32678
32758
  return typeof value === "object" && value !== null && !Array.isArray(value);
32679
32759
  }
32680
32760
 
@@ -37886,11 +37966,11 @@ function extractRouteUpdatedAt(candidate) {
37886
37966
  }
37887
37967
  function resolveRouteTargetForStage(routeIdValue, routes, stageInput) {
37888
37968
  const routeId = typeof routeIdValue === "string" ? routeIdValue : null;
37889
- if (!routeId || !routes) return null;
37969
+ if (routeId === null || routeId.length === 0 || routes === void 0) return null;
37890
37970
  const routeEntry = routes[routeId];
37891
37971
  if (!isRouteVisibleInStage(routeEntry, stageInput)) return null;
37892
37972
  const href = extractRouteHref(routeEntry, stageInput);
37893
- if (!href) return null;
37973
+ if (href === null) return null;
37894
37974
  return {
37895
37975
  routeId,
37896
37976
  href,
@@ -38042,16 +38122,16 @@ import React4 from "react";
38042
38122
  // ../blocks/src/system/data/linkedContentEntries.ts
38043
38123
  var LINKED_CONTENT_ENTRY_META_KEY = "__rbLinkedContentEntry";
38044
38124
  function getLinkedContentEntryMeta(value) {
38045
- if (!isRecord9(value)) return null;
38125
+ if (!isRecord10(value)) return null;
38046
38126
  const candidate = value[LINKED_CONTENT_ENTRY_META_KEY];
38047
38127
  return isLinkedContentEntryMeta(candidate) ? candidate : null;
38048
38128
  }
38049
38129
  function buildLinkedContentEditTarget(input) {
38050
38130
  const { entry, sourceBlockId, sourceBlockKind, itemIndex } = input;
38051
- if (!isRecord9(entry)) return null;
38131
+ if (!isRecord10(entry)) return null;
38052
38132
  const meta = getLinkedContentEntryMeta(entry);
38053
38133
  if (!meta || meta.support.kind !== "supported") return null;
38054
- const content = isRecord9(entry.content) ? entry.content : {};
38134
+ const content = isRecord10(entry.content) ? entry.content : {};
38055
38135
  return {
38056
38136
  kind: "linked-content-entry",
38057
38137
  entryId: meta.entryId,
@@ -38109,14 +38189,14 @@ function resolvePreviewLinkedEntryInlineFieldType(fieldType) {
38109
38189
  return null;
38110
38190
  }
38111
38191
  function isLinkedContentEntryMeta(value) {
38112
- if (!isRecord9(value)) return false;
38192
+ if (!isRecord10(value)) return false;
38113
38193
  if (value.kind !== "linked-content-entry") return false;
38114
38194
  if (typeof value.entryId !== "string") return false;
38115
38195
  if (typeof value.contentTypeKey !== "string") return false;
38116
38196
  if (typeof value.contentTypeLabel !== "string") return false;
38117
38197
  if (typeof value.title !== "string") return false;
38118
38198
  if (!(value.optimisticHash === null || typeof value.optimisticHash === "string")) return false;
38119
- if (!isRecord9(value.contentType)) return false;
38199
+ if (!isRecord10(value.contentType)) return false;
38120
38200
  if (!isLinkedEntryFieldSourceArray(value.fieldSources)) return false;
38121
38201
  return isLinkedContentEditSupport(value.support);
38122
38202
  }
@@ -38124,7 +38204,7 @@ function isLinkedEntryFieldSourceArray(value) {
38124
38204
  return Array.isArray(value) && value.every(isLinkedEntryFieldSource);
38125
38205
  }
38126
38206
  function isLinkedEntryFieldSource(value) {
38127
- if (!isRecord9(value)) return false;
38207
+ if (!isRecord10(value)) return false;
38128
38208
  if (typeof value.displayPath !== "string") return false;
38129
38209
  switch (value.kind) {
38130
38210
  case "content-field":
@@ -38150,7 +38230,7 @@ function isLinkedEntryInlineFieldReadOnlyReason(value) {
38150
38230
  }
38151
38231
  }
38152
38232
  function isLinkedContentEditSupport(value) {
38153
- if (!isRecord9(value) || typeof value.kind !== "string") return false;
38233
+ if (!isRecord10(value) || typeof value.kind !== "string") return false;
38154
38234
  switch (value.kind) {
38155
38235
  case "supported":
38156
38236
  return true;
@@ -38162,7 +38242,7 @@ function isLinkedContentEditSupport(value) {
38162
38242
  return false;
38163
38243
  }
38164
38244
  }
38165
- function isRecord9(value) {
38245
+ function isRecord10(value) {
38166
38246
  return typeof value === "object" && value !== null && !Array.isArray(value);
38167
38247
  }
38168
38248
 
@@ -39618,12 +39698,12 @@ function toTrimmedString(value) {
39618
39698
  return trimmed ? trimmed : void 0;
39619
39699
  }
39620
39700
  function normalizeBookingFormConfig(form2) {
39621
- if (!isRecord10(form2)) return null;
39701
+ if (!isRecord11(form2)) return null;
39622
39702
  const id = readNonEmptyString(form2.id);
39623
39703
  const name = readNonEmptyString(form2.name);
39624
39704
  if (!id || !name) return null;
39625
- const schemaSource = isRecord10(form2.schema) ? form2.schema : isRecord10(form2.schemaJson) ? form2.schemaJson : null;
39626
- const settingsSource = isRecord10(form2.settings) ? form2.settings : isRecord10(form2.settingsJson) ? form2.settingsJson : null;
39705
+ const schemaSource = isRecord11(form2.schema) ? form2.schema : isRecord11(form2.schemaJson) ? form2.schemaJson : null;
39706
+ const settingsSource = isRecord11(form2.settings) ? form2.settings : isRecord11(form2.settingsJson) ? form2.settingsJson : null;
39627
39707
  return {
39628
39708
  id,
39629
39709
  name,
@@ -39647,10 +39727,10 @@ function normalizeBookingServices(services) {
39647
39727
  if (Array.isArray(services)) {
39648
39728
  return services.filter(isBookingFormService).map(normalizeService);
39649
39729
  }
39650
- if (isRecord10(services) && "services" in services) {
39730
+ if (isRecord11(services) && "services" in services) {
39651
39731
  return Array.isArray(services.services) ? services.services.filter(isBookingFormService).map(normalizeService) : [];
39652
39732
  }
39653
- return isRecord10(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
39733
+ return isRecord11(services) ? Object.values(services).filter(isBookingFormService).map(normalizeService) : [];
39654
39734
  }
39655
39735
  function normalizeBookingSchema(value) {
39656
39736
  if (!value) return void 0;
@@ -39662,7 +39742,7 @@ function normalizeBookingSchema(value) {
39662
39742
  };
39663
39743
  }
39664
39744
  function readBookingFormField(value) {
39665
- if (!isRecord10(value)) return null;
39745
+ if (!isRecord11(value)) return null;
39666
39746
  const id = readNonEmptyString(value.id);
39667
39747
  const label = readNonEmptyString(value.label);
39668
39748
  const type = readBookingFieldType(value.type);
@@ -39708,7 +39788,7 @@ function readBookingFieldOptions(value) {
39708
39788
  return options.length > 0 ? options : void 0;
39709
39789
  }
39710
39790
  function readBookingFieldOption(value) {
39711
- if (!isRecord10(value)) return null;
39791
+ if (!isRecord11(value)) return null;
39712
39792
  const optionValue = readNonEmptyString(value.value);
39713
39793
  const label = readNonEmptyString(value.label);
39714
39794
  return optionValue && label ? { value: optionValue, label } : null;
@@ -39725,7 +39805,7 @@ function normalizeBookingSettings(value) {
39725
39805
  };
39726
39806
  }
39727
39807
  function isBookingFormService(value) {
39728
- return isRecord10(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string";
39808
+ return isRecord11(value) && typeof value.id === "string" && typeof value.siteId === "string" && typeof value.title === "string";
39729
39809
  }
39730
39810
  function readNonEmptyString(value) {
39731
39811
  if (typeof value !== "string") return null;
@@ -39743,7 +39823,7 @@ function readStringArray(value) {
39743
39823
  const strings = value.filter((item) => typeof item === "string");
39744
39824
  return strings.length > 0 ? strings : void 0;
39745
39825
  }
39746
- function isRecord10(value) {
39826
+ function isRecord11(value) {
39747
39827
  return typeof value === "object" && value !== null;
39748
39828
  }
39749
39829
 
@@ -40301,7 +40381,7 @@ function EventDetailsView({
40301
40381
  "a",
40302
40382
  {
40303
40383
  className: "rb-tile rb-tile--interactive",
40304
- href: e.path,
40384
+ ...resolveEventLinkProps(e),
40305
40385
  children: /* @__PURE__ */ jsx30(
40306
40386
  DateTimeTile,
40307
40387
  {
@@ -41985,7 +42065,7 @@ var AgendaEventCard = ({
41985
42065
  showCapacity,
41986
42066
  onClick
41987
42067
  }) => {
41988
- const eventUrl = event.path;
42068
+ const eventLinkProps = resolveEventLinkProps(event);
41989
42069
  const categoryColor = event.eventCategory?.color || "rgb(var(--tb-accent))";
41990
42070
  const price = formatEventPrice(event);
41991
42071
  const { available, isFull } = getEventAvailability(event);
@@ -42046,7 +42126,7 @@ var AgendaEventCard = ({
42046
42126
  }
42047
42127
  );
42048
42128
  }
42049
- return /* @__PURE__ */ jsx47("a", { href: eventUrl, className: "event-agenda-card", children: cardContent });
42129
+ return /* @__PURE__ */ jsx47("a", { ...eventLinkProps, className: "event-agenda-card", children: cardContent });
42050
42130
  };
42051
42131
 
42052
42132
  // ../blocks/src/system/runtime/nodes/events/shared/WeekAgendaView.tsx
@@ -42289,7 +42369,7 @@ function renderCalendarSsr(display) {
42289
42369
  dayEvents.slice(0, MAX_EVENTS_PER_DAY).map((event) => /* @__PURE__ */ jsxs35(
42290
42370
  "a",
42291
42371
  {
42292
- href: event.path,
42372
+ ...resolveEventLinkProps(event),
42293
42373
  className: "event-calendar-event",
42294
42374
  title: `${event.title} at ${formatTime(event.startsAt, { timeZone: getEventTimeZone(event) })}`,
42295
42375
  children: [
@@ -42417,7 +42497,7 @@ function renderTimetableSsr(display) {
42417
42497
  day.events.length === 0 ? /* @__PURE__ */ jsx49("p", { className: "event-timetable-day-empty", children: "No classes" }) : day.events.map((cell) => /* @__PURE__ */ jsxs35(
42418
42498
  "a",
42419
42499
  {
42420
- href: cell.event.path,
42500
+ ...resolveEventLinkProps(cell.event),
42421
42501
  className: "event-timetable-event",
42422
42502
  title: `${cell.event.title}, ${cell.timeLabel}`,
42423
42503
  style: {
@@ -43224,14 +43304,14 @@ function isDecoratedHeadingBlockKind(blockKind) {
43224
43304
  return blockKind === "block.intro" || blockKind === "block.feature-cards" || blockKind === "block.testimonials" || blockKind === "block.faq";
43225
43305
  }
43226
43306
  function readVariant(blockContent) {
43227
- if (!isRecord11(blockContent)) return void 0;
43307
+ if (!isRecord12(blockContent)) return void 0;
43228
43308
  const variant = blockContent.variant;
43229
43309
  return typeof variant === "string" ? variant : void 0;
43230
43310
  }
43231
43311
  function isFaqDecoratedVariant(variant) {
43232
43312
  return variant === "default" || variant === "question-columns";
43233
43313
  }
43234
- function isRecord11(value) {
43314
+ function isRecord12(value) {
43235
43315
  return typeof value === "object" && value !== null && !Array.isArray(value);
43236
43316
  }
43237
43317
  function buildHeadingDecorationWrapperClassName(className) {
@@ -67918,7 +67998,7 @@ function preset(id, verticalId, canonicalBehaviour, label, pluralLabel, descript
67918
67998
  }
67919
67999
 
67920
68000
  // ../api/src/utils/isRecord.ts
67921
- function isRecord12(value) {
68001
+ function isRecord13(value) {
67922
68002
  return typeof value === "object" && value !== null && !Array.isArray(value);
67923
68003
  }
67924
68004
 
@@ -68225,7 +68305,7 @@ function sanitizeMarks(marks) {
68225
68305
  if (mark.type === "bold" || mark.type === "strong" || mark.type === "italic" || mark.type === "em") {
68226
68306
  return [{ type: mark.type }];
68227
68307
  }
68228
- if (mark.type === "link" && isRecord12(mark.attrs) && typeof mark.attrs.href === "string") {
68308
+ if (mark.type === "link" && isRecord13(mark.attrs) && typeof mark.attrs.href === "string") {
68229
68309
  const attrs = { href: mark.attrs.href };
68230
68310
  if (typeof mark.attrs.target === "string" && mark.attrs.target.trim().length > 0) {
68231
68311
  attrs.target = mark.attrs.target;
@@ -68240,10 +68320,10 @@ function sanitizeMarks(marks) {
68240
68320
  return sanitized.length > 0 ? sanitized : void 0;
68241
68321
  }
68242
68322
  function unwrapRichTextValue(value) {
68243
- if (isRecord12(value) && isRecord12(value.doc)) {
68323
+ if (isRecord13(value) && isRecord13(value.doc)) {
68244
68324
  return unwrapRichTextValue(value.doc);
68245
68325
  }
68246
- if (isRecord12(value) && value.type === "doc") {
68326
+ if (isRecord13(value) && value.type === "doc") {
68247
68327
  return {
68248
68328
  type: "doc",
68249
68329
  content: Array.isArray(value.content) ? value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) : []
@@ -68252,7 +68332,7 @@ function unwrapRichTextValue(value) {
68252
68332
  return EMPTY_SITE_BANNER_BODY;
68253
68333
  }
68254
68334
  function isTipTapNodeLike(value) {
68255
- return isRecord12(value) && typeof value.type === "string";
68335
+ return isRecord13(value) && typeof value.type === "string";
68256
68336
  }
68257
68337
  function coerceTipTapNode(value) {
68258
68338
  return {
@@ -68260,9 +68340,9 @@ function coerceTipTapNode(value) {
68260
68340
  ...Array.isArray(value.content) ? { content: value.content.filter(isTipTapNodeLike).map(coerceTipTapNode) } : {},
68261
68341
  ...typeof value.text === "string" ? { text: value.text } : {},
68262
68342
  ...Array.isArray(value.marks) ? {
68263
- marks: value.marks.filter((mark) => isRecord12(mark) && typeof mark.type === "string").map((mark) => ({
68343
+ marks: value.marks.filter((mark) => isRecord13(mark) && typeof mark.type === "string").map((mark) => ({
68264
68344
  type: mark.type,
68265
- ...isRecord12(mark.attrs) ? { attrs: mark.attrs } : {}
68345
+ ...isRecord13(mark.attrs) ? { attrs: mark.attrs } : {}
68266
68346
  }))
68267
68347
  } : {}
68268
68348
  };