riverbankcms 0.17.0 → 0.18.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.
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
+ import { z } from "zod";
3
+
2
4
  //#region ../core/src/result.ts
3
5
  function ok(value) {
4
6
  return {
@@ -141,7 +143,6 @@ const unsafeAsDemoLifecycleAuditEventId = (id) => id;
141
143
  const unsafeAsDemoRuntimeId = (id) => id;
142
144
  const unsafeAsDemoCapability = (value) => value;
143
145
  const unsafeAsDemoPaymentFlowId = (id) => id;
144
- const unsafeAsDemoClaimTtlHours = (value) => value;
145
146
  const unsafeAsGiftingOfferId = (id) => id;
146
147
  const unsafeAsGiftingInstrumentId = (id) => id;
147
148
  const unsafeAsBookingRequirementId = (id) => id;
@@ -392,6 +393,7 @@ const demoInstanceStatusValues = [
392
393
  "failed"
393
394
  ];
394
395
  const demoClaimStatusValues = [
396
+ "preparing",
395
397
  "active",
396
398
  "expired",
397
399
  "released"
@@ -418,7 +420,7 @@ function narrowDemoBlueprintArtifactKind(value) {
418
420
 
419
421
  //#endregion
420
422
  //#region ../core/src/demo-warm-pool-target.ts
421
- const DEFAULT_DEMO_WARM_POOL_TARGET = 0;
423
+ const DEFAULT_DEMO_WARM_POOL_TARGET = 1;
422
424
  const MAX_DEMO_WARM_POOL_TARGET = 20;
423
425
  function parseDemoWarmPoolTarget(value) {
424
426
  if (value === undefined || value === null || value.trim() === "") {
@@ -1441,6 +1443,25 @@ function isRecord$9(value) {
1441
1443
  return typeof value === "object" && value !== null && !Array.isArray(value);
1442
1444
  }
1443
1445
 
1446
+ //#endregion
1447
+ //#region ../core/src/ticket-quantity.ts
1448
+ function toPositiveTicketQuantity(value) {
1449
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value <= 0) {
1450
+ return err("invalid_quantity");
1451
+ }
1452
+ return ok(value);
1453
+ }
1454
+ /**
1455
+ * The places a booking holds: the sum of its ticket lines' quantities. The one
1456
+ * derivation for selections, persisted lines and a booking outcome's lines;
1457
+ * null when the total is not a positive whole number.
1458
+ */
1459
+ function deriveTotalTicketQuantity(lines) {
1460
+ const total = lines.reduce((sum, line) => sum + line.quantity, 0);
1461
+ const parsed = toPositiveTicketQuantity(total);
1462
+ return parsed.ok ? parsed.value : null;
1463
+ }
1464
+
1444
1465
  //#endregion
1445
1466
  //#region ../core/src/booking-flexible-balance.ts
1446
1467
  const DEFAULT_FLEXIBLE_BALANCE_STRIPE_MINIMUM_CHARGE_CENTS = 50;
@@ -3909,7 +3930,7 @@ function parsePositiveInteger(value) {
3909
3930
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : null;
3910
3931
  }
3911
3932
  function parseNewsletterListId(value) {
3912
- return parseTrimmedMatchingString(value, UUID_PATTERN);
3933
+ return parseTrimmedMatchingString(typeof value === "string" ? value.toLowerCase() : value, UUID_PATTERN);
3913
3934
  }
3914
3935
  function parseMailchimpConnectionId(value) {
3915
3936
  return parseTrimmedMatchingString(value, UUID_PATTERN);
@@ -3941,9 +3962,197 @@ function parseBrevoConfirmationTemplateId(value) {
3941
3962
  return parsePositiveInteger(value);
3942
3963
  }
3943
3964
 
3965
+ //#endregion
3966
+ //#region ../core/src/mailing-list-targeting.ts
3967
+ const DEFAULT_MAILING_LIST_TARGETING = { mode: "default" };
3968
+ function sameMailingListPlacementTargeting(left, right) {
3969
+ if (left.mode !== right.mode) return false;
3970
+ if (left.mode === "default" || right.mode === "default") return true;
3971
+ if (left.provider !== right.provider || left.selection.tag !== right.selection.tag) return false;
3972
+ if (left.provider === "mailchimp" && (right.provider !== "mailchimp" || left.audienceId !== right.audienceId)) return false;
3973
+ return sameIds(mailingListTargetingChoiceIds(left), mailingListTargetingChoiceIds(right));
3974
+ }
3975
+ function mailingListTargetingChoiceIds(targeting) {
3976
+ switch (targeting.provider) {
3977
+ case "native": return targeting.selection.tag === "general" ? [] : targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
3978
+ case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
3979
+ case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
3980
+ }
3981
+ }
3982
+ function sameIds(left, right) {
3983
+ const leftIds = new Set(left);
3984
+ const rightIds = new Set(right);
3985
+ return leftIds.size === left.length && rightIds.size === right.length && leftIds.size === rightIds.size && [...leftIds].every((id) => rightIds.has(id));
3986
+ }
3987
+ function resolveMailingListTarget(input) {
3988
+ if (input.targeting.mode === "default") {
3989
+ if (input.visitorSelection !== undefined) {
3990
+ return err({ tag: "visitor_selection_not_allowed" });
3991
+ }
3992
+ return ok(resolveDefaultTarget(input.setup));
3993
+ }
3994
+ if (input.targeting.provider !== input.setup.provider) {
3995
+ return err({
3996
+ tag: "targeting_provider_mismatch",
3997
+ setupProvider: input.setup.provider,
3998
+ targetingProvider: input.targeting.provider
3999
+ });
4000
+ }
4001
+ return resolveCustomTarget(input.targeting, input.visitorSelection);
4002
+ }
4003
+ function resolveDefaultTarget(setup) {
4004
+ switch (setup.provider) {
4005
+ case "native": return {
4006
+ provider: "native",
4007
+ audience: setup.defaultAudience.kind === "general" ? { kind: "general" } : {
4008
+ kind: "topics",
4009
+ topicIds: [setup.defaultAudience.topicId]
4010
+ }
4011
+ };
4012
+ case "brevo": return {
4013
+ provider: "brevo",
4014
+ listIds: [setup.defaultListId]
4015
+ };
4016
+ case "mailchimp": return {
4017
+ provider: "mailchimp",
4018
+ audienceId: setup.defaultAudienceId,
4019
+ interestIds: []
4020
+ };
4021
+ }
4022
+ }
4023
+ function resolveCustomTarget(targeting, visitorSelection) {
4024
+ switch (targeting.provider) {
4025
+ case "native": return resolveNativeTarget(targeting, visitorSelection);
4026
+ case "brevo": return resolveBrevoTarget(targeting, visitorSelection);
4027
+ case "mailchimp": return resolveMailchimpTarget(targeting, visitorSelection);
4028
+ }
4029
+ }
4030
+ function resolveNativeTarget(targeting, selection) {
4031
+ if (targeting.selection.tag === "general") {
4032
+ return selection === undefined ? ok({
4033
+ provider: "native",
4034
+ audience: { kind: "general" }
4035
+ }) : err({ tag: "visitor_selection_not_allowed" });
4036
+ }
4037
+ if (targeting.selection.tag === "fixed") {
4038
+ return selection === undefined ? ok({
4039
+ provider: "native",
4040
+ audience: {
4041
+ kind: "topics",
4042
+ topicIds: targeting.selection.topicIds
4043
+ }
4044
+ }) : err({ tag: "visitor_selection_not_allowed" });
4045
+ }
4046
+ if (selection === undefined) {
4047
+ return targeting.selection.offeredTopicIds.length === 1 ? ok({
4048
+ provider: "native",
4049
+ audience: {
4050
+ kind: "topics",
4051
+ topicIds: targeting.selection.offeredTopicIds
4052
+ }
4053
+ }) : err({ tag: "visitor_selection_required" });
4054
+ }
4055
+ if (selection.provider !== "native") {
4056
+ return err({ tag: "visitor_selection_provider_mismatch" });
4057
+ }
4058
+ const selected = selectedNonEmptySubset(selection.topicIds, targeting.selection.offeredTopicIds);
4059
+ return selected.ok ? ok({
4060
+ provider: "native",
4061
+ audience: {
4062
+ kind: "topics",
4063
+ topicIds: selected.value
4064
+ }
4065
+ }) : err({ tag: "visitor_selection_outside_offer" });
4066
+ }
4067
+ function resolveBrevoTarget(targeting, selection) {
4068
+ if (targeting.selection.tag === "fixed") {
4069
+ return selection === undefined ? ok({
4070
+ provider: "brevo",
4071
+ listIds: targeting.selection.listIds
4072
+ }) : err({ tag: "visitor_selection_not_allowed" });
4073
+ }
4074
+ if (selection === undefined) {
4075
+ return targeting.selection.offeredListIds.length === 1 ? ok({
4076
+ provider: "brevo",
4077
+ listIds: targeting.selection.offeredListIds
4078
+ }) : err({ tag: "visitor_selection_required" });
4079
+ }
4080
+ if (selection.provider !== "brevo") {
4081
+ return err({ tag: "visitor_selection_provider_mismatch" });
4082
+ }
4083
+ const selected = selectedNonEmptySubset(selection.listIds, targeting.selection.offeredListIds);
4084
+ return selected.ok ? ok({
4085
+ provider: "brevo",
4086
+ listIds: selected.value
4087
+ }) : err({ tag: "visitor_selection_outside_offer" });
4088
+ }
4089
+ function resolveMailchimpTarget(targeting, selection) {
4090
+ if (targeting.selection.tag === "fixed") {
4091
+ return selection === undefined ? ok({
4092
+ provider: "mailchimp",
4093
+ audienceId: targeting.audienceId,
4094
+ interestIds: targeting.selection.interestIds
4095
+ }) : err({ tag: "visitor_selection_not_allowed" });
4096
+ }
4097
+ if (selection === undefined) {
4098
+ return targeting.selection.offeredInterestIds.length === 1 ? ok({
4099
+ provider: "mailchimp",
4100
+ audienceId: targeting.audienceId,
4101
+ interestIds: targeting.selection.offeredInterestIds
4102
+ }) : err({ tag: "visitor_selection_required" });
4103
+ }
4104
+ if (selection.provider !== "mailchimp") {
4105
+ return err({ tag: "visitor_selection_provider_mismatch" });
4106
+ }
4107
+ const subset = selectedNonEmptySubset(selection.interestIds, targeting.selection.offeredInterestIds);
4108
+ return subset.ok ? ok({
4109
+ provider: "mailchimp",
4110
+ audienceId: targeting.audienceId,
4111
+ interestIds: subset.value
4112
+ }) : err({ tag: "visitor_selection_outside_offer" });
4113
+ }
4114
+ function selectedNonEmptySubset(selected, offered) {
4115
+ const normalized = [...new Set(selected)];
4116
+ const [first, ...rest] = normalized;
4117
+ if (first === undefined || normalized.some((item) => !offered.includes(item))) {
4118
+ return err("invalid_selection");
4119
+ }
4120
+ return ok([first, ...rest]);
4121
+ }
4122
+ function planMailingListChoicePresentation(targeting) {
4123
+ if (targeting.mode === "default" || targeting.selection.tag === "fixed" || targeting.selection.tag === "general") {
4124
+ return { tag: "hidden" };
4125
+ }
4126
+ switch (targeting.provider) {
4127
+ case "native": return presentVisitorSelection({
4128
+ provider: "native",
4129
+ topicIds: targeting.selection.offeredTopicIds
4130
+ }, targeting.selection.offeredTopicIds.length);
4131
+ case "brevo": return presentVisitorSelection({
4132
+ provider: "brevo",
4133
+ listIds: targeting.selection.offeredListIds
4134
+ }, targeting.selection.offeredListIds.length);
4135
+ case "mailchimp": return presentVisitorSelection({
4136
+ provider: "mailchimp",
4137
+ interestIds: targeting.selection.offeredInterestIds
4138
+ }, targeting.selection.offeredInterestIds.length);
4139
+ }
4140
+ }
4141
+ function presentVisitorSelection(selection, optionCount) {
4142
+ return optionCount === 1 ? {
4143
+ tag: "automatic",
4144
+ selection
4145
+ } : {
4146
+ tag: "visitor_choice",
4147
+ provider: selection.provider,
4148
+ optionCount
4149
+ };
4150
+ }
4151
+
3944
4152
  //#endregion
3945
4153
  //#region ../core/src/mailing-list-targeting-parser.ts
3946
4154
  function parseMailingListPlacementTargeting(value) {
4155
+ if (value == null) return ok(DEFAULT_MAILING_LIST_TARGETING);
3947
4156
  if (!isRecord$6(value)) return targetingError("shape");
3948
4157
  if (value.mode === "default") return ok({ mode: "default" });
3949
4158
  if (value.mode !== "custom") return targetingError("mode");
@@ -3959,6 +4168,7 @@ function parseMailingListVisitorSelection(value) {
3959
4168
  if (!isRecord$6(value)) return visitorSelectionError("shape");
3960
4169
  switch (value.provider) {
3961
4170
  case "native": {
4171
+ if (Object.keys(value).some((key) => key !== "provider" && key !== "topicIds")) return visitorSelectionError("shape");
3962
4172
  const topicIds = parseVisitorIds(value.topicIds, parseNewsletterListId);
3963
4173
  return topicIds === null ? visitorSelectionError("targets") : ok({
3964
4174
  provider: "native",
@@ -3983,6 +4193,13 @@ function parseMailingListVisitorSelection(value) {
3983
4193
  }
3984
4194
  }
3985
4195
  function parseNativeTargeting(selection) {
4196
+ if (selection.tag === "general") {
4197
+ return Object.keys(selection).length === 1 ? ok({
4198
+ mode: "custom",
4199
+ provider: "native",
4200
+ selection: { tag: "general" }
4201
+ }) : targetingError("selection");
4202
+ }
3986
4203
  if (selection.tag === "fixed") {
3987
4204
  const topicIds = parseNonEmptyUniqueIds(selection.topicIds, parseNewsletterListId);
3988
4205
  return topicIds.ok ? ok({
@@ -4069,25 +4286,15 @@ function parseNonEmptyUniqueIds(value, parseId) {
4069
4286
  }
4070
4287
  function parseUniqueIds(value, parseId) {
4071
4288
  if (!Array.isArray(value)) return targetingError("targets");
4072
- const parsed = [];
4073
- for (const item of value) {
4289
+ return Array.from(value).reduce((parsed, item) => {
4290
+ if (!parsed.ok) return parsed;
4074
4291
  const id = parseId(item);
4075
- if (id === null) return targetingError("targets");
4076
- if (parsed.includes(id)) return targetingError("duplicate_target");
4077
- parsed.push(id);
4078
- }
4079
- return ok(parsed);
4292
+ return id === null ? targetingError("targets") : parsed.value.includes(id) ? targetingError("duplicate_target") : ok([...parsed.value, id]);
4293
+ }, ok([]));
4080
4294
  }
4081
4295
  function parseVisitorIds(value, parseId) {
4082
- if (!Array.isArray(value) || value.length === 0) return null;
4083
- const parsed = [];
4084
- for (const item of value) {
4085
- const id = parseId(item);
4086
- if (id === null) return null;
4087
- if (parsed.includes(id)) return null;
4088
- parsed.push(id);
4089
- }
4090
- return parsed;
4296
+ const parsed = parseNonEmptyUniqueIds(value, parseId);
4297
+ return parsed.ok ? parsed.value : null;
4091
4298
  }
4092
4299
  function isRecord$6(value) {
4093
4300
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -4776,6 +4983,9 @@ function mailingListProviderName(provider) {
4776
4983
  case "brevo": return "Brevo";
4777
4984
  }
4778
4985
  }
4986
+ function sameNewsletterAudience(left, right) {
4987
+ return left.kind === right.kind && (left.kind === "general" || right.kind === "topic" && left.topicId === right.topicId);
4988
+ }
4779
4989
  const DEFAULT_MAILING_LIST_SUCCESS_MESSAGE = "Thanks — you're on the list.";
4780
4990
  /**
4781
4991
  * Seeded into a new setup so every signup form carries a preferences/unsubscribe
@@ -4907,6 +5117,15 @@ function countDefaultMailingListPlacements(placementModes) {
4907
5117
 
4908
5118
  //#endregion
4909
5119
  //#region ../core/src/mailing-list-setup-parser.ts
5120
+ const newsletterAudienceSchema = z.unknown().transform((value, ctx) => {
5121
+ const parsed = parseNewsletterAudience(value);
5122
+ if (parsed.ok) return parsed.value;
5123
+ ctx.addIssue({
5124
+ code: "custom",
5125
+ message: "Choose a valid newsletter audience."
5126
+ });
5127
+ return z.NEVER;
5128
+ });
4910
5129
  function parseMailingListGlobalForm(value) {
4911
5130
  if (!isRecord$2(value)) return formError("shape");
4912
5131
  const fields = parseGlobalFields(value.fields);
@@ -4963,51 +5182,69 @@ function parseConfiguredMailingListProvider(value) {
4963
5182
  return setupError("provider");
4964
5183
  }
4965
5184
  switch (value.provider) {
4966
- case "native": {
4967
- const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
4968
- if (defaultTopicId === null) return setupError("defaultTargetId");
4969
- const confirmation = parseToggleConfirmation(value.confirmation);
4970
- return confirmation === null ? setupError("confirmation") : ok({
4971
- provider: "native",
4972
- defaultTopicId,
4973
- confirmation
4974
- });
4975
- }
4976
- case "mailchimp": {
4977
- const connectionId = parseMailchimpConnectionId(value.connectionId);
4978
- if (connectionId === null) return setupError("connectionId");
4979
- const accountId = parseMailchimpAccountId(value.accountId);
4980
- if (accountId === null) return setupError("accountId");
4981
- const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
4982
- if (defaultAudienceId === null) return setupError("defaultTargetId");
4983
- const confirmation = parseToggleConfirmation(value.confirmation);
4984
- return confirmation === null ? setupError("confirmation") : ok({
4985
- provider: "mailchimp",
4986
- connectionId,
4987
- accountId,
4988
- defaultAudienceId,
4989
- confirmation
4990
- });
4991
- }
4992
- case "brevo": {
4993
- const connectionId = parseBrevoConnectionId(value.connectionId);
4994
- if (connectionId === null) return setupError("connectionId");
4995
- const accountId = parseBrevoAccountId(value.accountId);
4996
- if (accountId === null) return setupError("accountId");
4997
- const defaultListId = parseBrevoListId(value.defaultListId);
4998
- if (defaultListId === null) return setupError("defaultTargetId");
4999
- const confirmation = parseBrevoConfirmation(value.confirmation);
5000
- return confirmation === null ? setupError("confirmation") : ok({
5001
- provider: "brevo",
5002
- connectionId,
5003
- accountId,
5004
- defaultListId,
5005
- confirmation
5006
- });
5007
- }
5185
+ case "native": return parseNativeSetup(value);
5186
+ case "mailchimp": return parseMailchimpSetup(value);
5187
+ case "brevo": return parseBrevoSetup(value);
5008
5188
  default: return setupError("provider");
5009
5189
  }
5010
5190
  }
5191
+ function parseNativeSetup(value) {
5192
+ if ("defaultTopicId" in value) return setupError("defaultTargetId");
5193
+ const defaultAudience = parseNewsletterAudience(value.defaultAudience);
5194
+ if (!defaultAudience.ok) return setupError("defaultTargetId");
5195
+ const confirmation = parseToggleConfirmation(value.confirmation);
5196
+ return confirmation === null ? setupError("confirmation") : ok({
5197
+ provider: "native",
5198
+ defaultAudience: defaultAudience.value,
5199
+ confirmation
5200
+ });
5201
+ }
5202
+ function parseMailchimpSetup(value) {
5203
+ const connectionId = parseMailchimpConnectionId(value.connectionId);
5204
+ if (connectionId === null) return setupError("connectionId");
5205
+ const accountId = parseMailchimpAccountId(value.accountId);
5206
+ if (accountId === null) return setupError("accountId");
5207
+ const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
5208
+ if (defaultAudienceId === null) return setupError("defaultTargetId");
5209
+ const confirmation = parseToggleConfirmation(value.confirmation);
5210
+ return confirmation === null ? setupError("confirmation") : ok({
5211
+ provider: "mailchimp",
5212
+ connectionId,
5213
+ accountId,
5214
+ defaultAudienceId,
5215
+ confirmation
5216
+ });
5217
+ }
5218
+ function parseBrevoSetup(value) {
5219
+ const connectionId = parseBrevoConnectionId(value.connectionId);
5220
+ if (connectionId === null) return setupError("connectionId");
5221
+ const accountId = parseBrevoAccountId(value.accountId);
5222
+ if (accountId === null) return setupError("accountId");
5223
+ const defaultListId = parseBrevoListId(value.defaultListId);
5224
+ if (defaultListId === null) return setupError("defaultTargetId");
5225
+ const confirmation = parseBrevoConfirmation(value.confirmation);
5226
+ return confirmation === null ? setupError("confirmation") : ok({
5227
+ provider: "brevo",
5228
+ connectionId,
5229
+ accountId,
5230
+ defaultListId,
5231
+ confirmation
5232
+ });
5233
+ }
5234
+ function parseNewsletterAudience(value) {
5235
+ if (!isRecord$2(value)) return err("invalid_newsletter_audience");
5236
+ if (value.kind === "general" && Object.keys(value).length === 1) {
5237
+ return ok({ kind: "general" });
5238
+ }
5239
+ if (value.kind !== "topic" || Object.keys(value).some((key) => key !== "kind" && key !== "topicId")) {
5240
+ return err("invalid_newsletter_audience");
5241
+ }
5242
+ const topicId = parseNewsletterListId(value.topicId);
5243
+ return topicId === null ? err("invalid_newsletter_audience") : ok({
5244
+ kind: "topic",
5245
+ topicId
5246
+ });
5247
+ }
5011
5248
  function parseGlobalFields(value) {
5012
5249
  if (!isRecord$2(value) || typeof value.preset !== "string") return null;
5013
5250
  switch (value.preset) {
@@ -5088,183 +5325,108 @@ function setupError(field) {
5088
5325
  }
5089
5326
 
5090
5327
  //#endregion
5091
- //#region ../core/src/mailing-list-targeting.ts
5092
- function sameMailingListPlacementTargeting(left, right) {
5093
- if (left.mode !== right.mode) return false;
5094
- if (left.mode === "default" || right.mode === "default") return true;
5095
- if (left.provider !== right.provider || left.selection.tag !== right.selection.tag) return false;
5096
- if (left.provider === "mailchimp" && (right.provider !== "mailchimp" || left.audienceId !== right.audienceId)) return false;
5097
- return sameIds(targetingSelectionIds(left), targetingSelectionIds(right));
5098
- }
5099
- function targetingSelectionIds(targeting) {
5100
- switch (targeting.provider) {
5101
- case "native": return targeting.selection.tag === "fixed" ? targeting.selection.topicIds : targeting.selection.offeredTopicIds;
5102
- case "brevo": return targeting.selection.tag === "fixed" ? targeting.selection.listIds : targeting.selection.offeredListIds;
5103
- case "mailchimp": return targeting.selection.tag === "fixed" ? targeting.selection.interestIds : targeting.selection.offeredInterestIds;
5104
- }
5105
- }
5106
- function sameIds(left, right) {
5107
- const leftIds = new Set(left);
5108
- const rightIds = new Set(right);
5109
- return leftIds.size === left.length && rightIds.size === right.length && leftIds.size === rightIds.size && [...leftIds].every((id) => rightIds.has(id));
5110
- }
5111
- function resolveMailingListTarget(input) {
5112
- if (input.targeting.mode === "default") {
5113
- if (input.visitorSelection !== undefined) {
5114
- return err({ tag: "visitor_selection_not_allowed" });
5328
+ //#region ../core/src/mailing-list-targeting-availability.ts
5329
+ function resolveMailingListDefaultDestination(provider) {
5330
+ switch (provider.provider) {
5331
+ case "native": {
5332
+ const audience = provider.defaultAudience;
5333
+ return audience.kind === "general" ? {
5334
+ id: "general",
5335
+ name: "General mailing list"
5336
+ } : provider.topics.find((item) => item.id === audience.topicId) ?? null;
5115
5337
  }
5116
- return ok(resolveDefaultTarget(input.setup));
5117
- }
5118
- if (input.targeting.provider !== input.setup.provider) {
5119
- return err({
5120
- tag: "targeting_provider_mismatch",
5121
- setupProvider: input.setup.provider,
5122
- targetingProvider: input.targeting.provider
5123
- });
5124
- }
5125
- return resolveCustomTarget(input.targeting, input.visitorSelection);
5126
- }
5127
- function resolveDefaultTarget(setup) {
5128
- switch (setup.provider) {
5129
- case "native": return {
5130
- provider: "native",
5131
- topicIds: [setup.defaultTopicId]
5132
- };
5133
- case "brevo": return {
5134
- provider: "brevo",
5135
- listIds: [setup.defaultListId]
5136
- };
5137
- case "mailchimp": return {
5138
- provider: "mailchimp",
5139
- audienceId: setup.defaultAudienceId,
5140
- interestIds: []
5141
- };
5142
- }
5143
- }
5144
- function resolveCustomTarget(targeting, visitorSelection) {
5145
- switch (targeting.provider) {
5146
- case "native": return resolveNativeTarget(targeting, visitorSelection);
5147
- case "brevo": return resolveBrevoTarget(targeting, visitorSelection);
5148
- case "mailchimp": return resolveMailchimpTarget(targeting, visitorSelection);
5149
- }
5150
- }
5151
- function resolveNativeTarget(targeting, selection) {
5152
- if (targeting.selection.tag === "fixed") {
5153
- return selection === undefined ? ok({
5154
- provider: "native",
5155
- topicIds: targeting.selection.topicIds
5156
- }) : err({ tag: "visitor_selection_not_allowed" });
5157
- }
5158
- if (selection === undefined) {
5159
- return targeting.selection.offeredTopicIds.length === 1 ? ok({
5160
- provider: "native",
5161
- topicIds: targeting.selection.offeredTopicIds
5162
- }) : err({ tag: "visitor_selection_required" });
5163
- }
5164
- if (selection.provider !== "native") {
5165
- return err({ tag: "visitor_selection_provider_mismatch" });
5166
- }
5167
- const selected = selectedNonEmptySubset(selection.topicIds, targeting.selection.offeredTopicIds);
5168
- return selected.ok ? ok({
5169
- provider: "native",
5170
- topicIds: selected.value
5171
- }) : err({ tag: "visitor_selection_outside_offer" });
5172
- }
5173
- function resolveBrevoTarget(targeting, selection) {
5174
- if (targeting.selection.tag === "fixed") {
5175
- return selection === undefined ? ok({
5176
- provider: "brevo",
5177
- listIds: targeting.selection.listIds
5178
- }) : err({ tag: "visitor_selection_not_allowed" });
5179
- }
5180
- if (selection === undefined) {
5181
- return targeting.selection.offeredListIds.length === 1 ? ok({
5182
- provider: "brevo",
5183
- listIds: targeting.selection.offeredListIds
5184
- }) : err({ tag: "visitor_selection_required" });
5185
- }
5186
- if (selection.provider !== "brevo") {
5187
- return err({ tag: "visitor_selection_provider_mismatch" });
5188
- }
5189
- const selected = selectedNonEmptySubset(selection.listIds, targeting.selection.offeredListIds);
5190
- return selected.ok ? ok({
5191
- provider: "brevo",
5192
- listIds: selected.value
5193
- }) : err({ tag: "visitor_selection_outside_offer" });
5194
- }
5195
- function resolveMailchimpTarget(targeting, selection) {
5196
- if (targeting.selection.tag === "fixed") {
5197
- return selection === undefined ? ok({
5198
- provider: "mailchimp",
5199
- audienceId: targeting.audienceId,
5200
- interestIds: targeting.selection.interestIds
5201
- }) : err({ tag: "visitor_selection_not_allowed" });
5202
- }
5203
- if (selection === undefined) {
5204
- return targeting.selection.offeredInterestIds.length === 1 ? ok({
5205
- provider: "mailchimp",
5206
- audienceId: targeting.audienceId,
5207
- interestIds: targeting.selection.offeredInterestIds
5208
- }) : err({ tag: "visitor_selection_required" });
5338
+ case "brevo": return provider.lists.find((item) => item.id === provider.defaultListId) ?? null;
5339
+ case "mailchimp": return provider.audiences.find((item) => item.id === provider.defaultAudienceId) ?? null;
5209
5340
  }
5210
- if (selection.provider !== "mailchimp") {
5211
- return err({ tag: "visitor_selection_provider_mismatch" });
5212
- }
5213
- const subset = selectedNonEmptySubset(selection.interestIds, targeting.selection.offeredInterestIds);
5214
- return subset.ok ? ok({
5215
- provider: "mailchimp",
5216
- audienceId: targeting.audienceId,
5217
- interestIds: subset.value
5218
- }) : err({ tag: "visitor_selection_outside_offer" });
5219
5341
  }
5220
- function selectedNonEmptySubset(selected, offered) {
5221
- const normalized = [...new Set(selected)];
5222
- const [first, ...rest] = normalized;
5223
- if (first === undefined || normalized.some((item) => !offered.includes(item))) {
5224
- return err("invalid_selection");
5225
- }
5226
- return ok([first, ...rest]);
5342
+ function assessMailingListTargeting(context, targeting) {
5343
+ if (context?.tag !== "configured") return {
5344
+ tag: "unavailable",
5345
+ reason: "setup_missing",
5346
+ targeting
5347
+ };
5348
+ const catalog = context.provider;
5349
+ if (targeting.mode === "custom") return assessCustomTargeting(catalog, targeting);
5350
+ const destination = resolveMailingListDefaultDestination(catalog);
5351
+ return destination === null ? {
5352
+ tag: "unavailable",
5353
+ reason: "default_missing",
5354
+ targeting
5355
+ } : {
5356
+ tag: "available",
5357
+ value: {
5358
+ mode: "default",
5359
+ targeting,
5360
+ provider: catalog.provider,
5361
+ destination
5362
+ }
5363
+ };
5227
5364
  }
5228
- function planMailingListChoicePresentation(targeting) {
5229
- if (targeting.mode === "default" || targeting.selection.tag === "fixed") {
5230
- return { tag: "hidden" };
5231
- }
5365
+ function assessCustomTargeting(catalog, targeting) {
5232
5366
  switch (targeting.provider) {
5233
- case "native": return targeting.selection.offeredTopicIds.length === 1 ? {
5234
- tag: "automatic",
5235
- selection: {
5236
- provider: "native",
5237
- topicIds: targeting.selection.offeredTopicIds
5238
- }
5239
- } : {
5240
- tag: "visitor_choice",
5367
+ case "native": return catalog.provider === "native" ? assessMatchedChoices({
5241
5368
  provider: "native",
5242
- optionCount: targeting.selection.offeredTopicIds.length
5369
+ catalog,
5370
+ targeting
5371
+ }, catalog.topics) : {
5372
+ tag: "unavailable",
5373
+ reason: "provider_mismatch",
5374
+ targeting
5243
5375
  };
5244
- case "brevo": return targeting.selection.offeredListIds.length === 1 ? {
5245
- tag: "automatic",
5246
- selection: {
5247
- provider: "brevo",
5248
- listIds: targeting.selection.offeredListIds
5249
- }
5250
- } : {
5251
- tag: "visitor_choice",
5376
+ case "brevo": return catalog.provider === "brevo" ? assessMatchedChoices({
5252
5377
  provider: "brevo",
5253
- optionCount: targeting.selection.offeredListIds.length
5378
+ catalog,
5379
+ targeting
5380
+ }, catalog.lists) : {
5381
+ tag: "unavailable",
5382
+ reason: "provider_mismatch",
5383
+ targeting
5254
5384
  };
5255
- case "mailchimp": return targeting.selection.offeredInterestIds.length === 1 ? {
5256
- tag: "automatic",
5257
- selection: {
5385
+ case "mailchimp": {
5386
+ if (catalog.provider !== "mailchimp") return {
5387
+ tag: "unavailable",
5388
+ reason: "provider_mismatch",
5389
+ targeting
5390
+ };
5391
+ const audience = catalog.audiences.find((item) => item.id === targeting.audienceId);
5392
+ return audience === undefined ? {
5393
+ tag: "unavailable",
5394
+ reason: "audience_missing",
5395
+ value: {
5396
+ mode: "custom",
5397
+ provider: "mailchimp",
5398
+ catalog,
5399
+ targeting
5400
+ }
5401
+ } : assessMatchedChoices({
5258
5402
  provider: "mailchimp",
5259
- interestIds: targeting.selection.offeredInterestIds
5260
- }
5261
- } : {
5262
- tag: "visitor_choice",
5263
- provider: "mailchimp",
5264
- optionCount: targeting.selection.offeredInterestIds.length
5265
- };
5403
+ catalog,
5404
+ targeting,
5405
+ audience
5406
+ }, audience.interests);
5407
+ }
5266
5408
  }
5267
5409
  }
5410
+ function assessMatchedChoices(matched, choices) {
5411
+ const selected = new Set(mailingListTargetingChoiceIds(matched.targeting));
5412
+ const available = new Set(choices.map((item) => item.id));
5413
+ const missingIds = [...selected].filter((id) => !available.has(id));
5414
+ const value = {
5415
+ mode: "custom",
5416
+ ...matched,
5417
+ choices,
5418
+ selectedChoices: choices.filter((item) => selected.has(item.id))
5419
+ };
5420
+ return missingIds.length > 0 ? {
5421
+ tag: "unavailable",
5422
+ reason: "choices_missing",
5423
+ missingIds,
5424
+ value
5425
+ } : {
5426
+ tag: "available",
5427
+ value
5428
+ };
5429
+ }
5268
5430
 
5269
5431
  //#endregion
5270
5432
  //#region ../core/src/mailing-list-runtime.ts
@@ -5337,11 +5499,12 @@ function parseRuntimeProvider(value) {
5337
5499
  }
5338
5500
  }
5339
5501
  function parseNativeRuntimeProvider(value) {
5340
- const defaultTopicId = parseNewsletterListId(value.defaultTopicId);
5502
+ if ("defaultTopicId" in value) return null;
5503
+ const defaultAudience = parseNewsletterAudience(value.defaultAudience);
5341
5504
  const topics = parseNamedItems(value.topics, parseNewsletterListId);
5342
- return defaultTopicId !== null && topics !== null ? {
5505
+ return defaultAudience.ok && topics !== null ? {
5343
5506
  provider: "native",
5344
- defaultTopicId,
5507
+ defaultAudience: defaultAudience.value,
5345
5508
  topics
5346
5509
  } : null;
5347
5510
  }
@@ -5357,59 +5520,47 @@ function parseBrevoRuntimeProvider(value) {
5357
5520
  function parseMailchimpRuntimeProvider(value) {
5358
5521
  if (!Array.isArray(value.audiences)) return null;
5359
5522
  const defaultAudienceId = parseMailchimpAudienceId(value.defaultAudienceId);
5360
- if (defaultAudienceId === null) return null;
5361
- const audiences = [];
5362
- for (const audience of value.audiences) {
5363
- if (!isRecord$1(audience)) return null;
5364
- const id = parseMailchimpAudienceId(audience.id);
5365
- const name = parseName(audience.name);
5366
- if (id === null || name === null || !Array.isArray(audience.interests)) {
5367
- return null;
5368
- }
5369
- const interests = parseMailchimpInterests(audience.interests);
5370
- if (interests === null) return null;
5371
- audiences.push({
5372
- id,
5373
- name,
5374
- interests
5375
- });
5376
- }
5377
- return {
5523
+ const audiences = Array.from(value.audiences, parseMailchimpAudience);
5524
+ return defaultAudienceId !== null && audiences.every((audience) => audience !== null) ? {
5378
5525
  provider: "mailchimp",
5379
5526
  defaultAudienceId,
5380
5527
  audiences
5381
- };
5528
+ } : null;
5382
5529
  }
5383
- function parseMailchimpInterests(value) {
5384
- const interests = [];
5385
- for (const interest of value) {
5386
- if (!isRecord$1(interest)) return null;
5387
- const id = parseMailchimpInterestId(interest.id);
5388
- const name = parseName(interest.name);
5389
- const categoryName = parseName(interest.categoryName);
5390
- if (id === null || name === null || categoryName === null) return null;
5391
- interests.push({
5392
- id,
5393
- name,
5394
- categoryName
5395
- });
5396
- }
5397
- return interests;
5530
+ function parseMailchimpAudience(value) {
5531
+ if (!isRecord$1(value) || !Array.isArray(value.interests)) return null;
5532
+ const id = parseMailchimpAudienceId(value.id);
5533
+ const name = parseName(value.name);
5534
+ const interests = Array.from(value.interests, parseMailchimpInterest);
5535
+ return id !== null && name !== null && interests.every((interest) => interest !== null) ? {
5536
+ id,
5537
+ name,
5538
+ interests
5539
+ } : null;
5540
+ }
5541
+ function parseMailchimpInterest(value) {
5542
+ if (!isRecord$1(value)) return null;
5543
+ const id = parseMailchimpInterestId(value.id);
5544
+ const name = parseName(value.name);
5545
+ const categoryName = parseName(value.categoryName);
5546
+ return id !== null && name !== null && categoryName !== null ? {
5547
+ id,
5548
+ name,
5549
+ categoryName
5550
+ } : null;
5398
5551
  }
5399
5552
  function parseNamedItems(value, parseId) {
5400
5553
  if (!Array.isArray(value)) return null;
5401
- const items = [];
5402
- for (const item of value) {
5554
+ const items = Array.from(value, (item) => {
5403
5555
  if (!isRecord$1(item)) return null;
5404
5556
  const id = parseId(item.id);
5405
5557
  const name = parseName(item.name);
5406
- if (id === null || name === null) return null;
5407
- items.push({
5558
+ return id !== null && name !== null ? {
5408
5559
  id,
5409
5560
  name
5410
- });
5411
- }
5412
- return items;
5561
+ } : null;
5562
+ });
5563
+ return items.every((item) => item !== null) ? items : null;
5413
5564
  }
5414
5565
  function parseName(value) {
5415
5566
  if (typeof value !== "string") return null;
@@ -7428,4 +7579,4 @@ function mergeBag(stored, patch, options) {
7428
7579
  }
7429
7580
 
7430
7581
  //#endregion
7431
- export { parseBookingTimeZone as $, unsafeAsEventOfferingId as $t, footerNavigationLayouts as A, parseCurrencyCode as At, buildBookingUtcDateWindow as B, parsePageId as Bt, parseMailingListContentVersionId as C, isNonNegativeWholeCents as Ct, sameMailingListPlacementTargeting as D, unsafeAsNonNegativeCents as Dt, planMailingListChoicePresentation as E, toNonNegativeCents as Et, assertAllowedPublicUrl as F, parseDatedOfferingCapacityHoldAuditRef as Ft, formatLocalScheduleTime as G, unsafeAsBookingRequirementId as Gt, formatBookingDateParts as H, parseSiteId as Ht, isPresent as I, parseDatedOfferingCapacityHoldId as It, getLocalScheduleTime as J, unsafeAsDeclaredMimeType as Jt, getBookingLocalDateKey as K, unsafeAsBookingSessionId as Kt, normalizeLocationAddressText as L, parseEventSeriesId as Lt, parseMailingListPlacementTargeting as M, formatDurationUnitLabel as Mt, isCanonicalRichTextDoc as N, parseApiKeyId as Nt, DEFAULT_MAILING_LIST_SUCCESS_MESSAGE as O, unsafeAsPositiveCents as Ot, parsePublicContactDetails as P, parseCustomerId as Pt, toScheduledBookingDateTime as Q, unsafeAsEventInstanceId as Qt, toLocationCoordinates as R, parseMediaAssetId as Rt, parseMailingListBlockId as S, mapNonEmptyArray as St, parseMailingListRuntimeContext as T, toCents as Tt, formatBookingTime as U, parseUserId as Ut, formatBookingDate as V, parseRetiredCourseId as Vt, formatLocalScheduleDate as W, unsafeAsAppointmentResourceId as Wt, toBookingTimeZone as X, unsafeAsDetectedMimeType as Xt, parseUtcIsoDateTimeString as Y, unsafeAsDemoCapability as Yt, toScheduledBookingDateRange as Z, unsafeAsEntitlementId as Zt, createSecret as _, unsafeAsUploadSessionId as _n, publicEventPricingDisplay as _t, parseVerticalId as a, unsafeAsPassId as an, clampConfiguredAmount as at, stableJsonStringify as b, ok as bn, resolvePricingOptionsPriceDisplay as bt, DEFAULT_EVENT_TYPE_PRESETS as c, unsafeAsPricingOptionId as cn, omitZeroAmountFutureRows as ct, SURFACE_EVENT_PRESET_VALUES as d, unsafeAsSiteFontId as dn, resolveFlexibleBalanceInitialPaymentCents as dt, unsafeAsEventSeriesId as en, validateBookingPaymentOptionKinds as et, WHOLE_SERIES_EVENT_PRESET_VALUES as f, unsafeAsSiteId as fn, parseEventPageSurfaceSubject as ft, resolveWholeSeriesBookingPhase as g, unsafeAsStoragePath as gn, publicEventPricingCurrency as gt, orderedLiveRunSessions as h, unsafeAsStorageBucketName as hn, isPublicEventPricingOptionDisplayable as ht, canonicalizeConfiguredBookingVerticalConfig as i, unsafeAsOriginalFilename as in, addIntervalPreservingMonthAnchor as it, parseFooterCompositionV1 as j, assertNever as jt, parseMailingListProviderScope as k, formatCurrencyAmount as kt, EVENT_PRESET_VALUES as l, unsafeAsProductId as ln, splitAmountWithRemainderInFirst as lt, CANCELLED_SESSION_STATUS as m, unsafeAsStaffMemberId as mn, formatEventPricing as mt, NAVIGATION_LOCATIONS as n, unsafeAsMediaAssetId as nn, isSettleableFlexibleBalanceContractStatus as nt, parseVerticalPresetId as o, unsafeAsPositiveByteCount as on, getBookingInstalmentDepositRefundability as ot, publicReadFreshnessSeconds as p, unsafeAsSiteScopedStoragePath as pn, formatBookingPriceDisplay as pt, getLocalScheduleDate as q, unsafeAsCheckoutLineId as qt, areConfiguredBookingVerticalConfigsEqual as r, unsafeAsOccurrenceId as rn, isSettleableOrderInstalmentPlanStatus as rt, resolveSiteVerticalBookingConfig as s, unsafeAsPositiveSeconds as sn, getBookingInstalmentProtectedDepositAmountCents as st, resolveEventCatalogue as t, unsafeAsFileChecksumSha256 as tn, isLiveOrderInstalmentPlanStatus as tt, ORDINARY_EVENT_PRESET_VALUES as u, unsafeAsSafeDownloadFilename as un, resolveFlexibleBalanceDueAt as ut, exposeSecret as v, unsafeAsUserId as vn, publicEventPricingType as vt, parseMailingListSignedSourceToken as w, parseNonNegativeCents as wt, buildMailingListSignupBlockSourceRef as x, fromArray as xt, parseManagementKey as y, err as yn, resolveExactOrFreePriceDisplay as yt, selectCategoryView as z, parseMembershipId as zt };
7582
+ export { toBookingTimeZone as $, unsafeAsCheckoutLineId as $t, footerNavigationLayouts as A, parsePositiveCents as At, normalizeLocationAddressText as B, parseCustomerId as Bt, parseMailingListBlockId as C, unsafeAsUploadSessionId as Cn, publicEventPricingType as Ct, assessMailingListTargeting as D, mapNonEmptyArray as Dt, parseMailingListRuntimeContext as E, ok as En, fromArray as Et, sameMailingListPlacementTargeting as F, formatCurrencyAmount as Ft, formatBookingDateParts as G, parseMembershipId as Gt, selectCategoryView as H, parseDatedOfferingCapacityHoldId as Ht, isCanonicalRichTextDoc as I, parseCurrencyCode as It, formatLocalScheduleTime as J, parseSiteId as Jt, formatBookingTime as K, parsePageId as Kt, parsePublicContactDetails as L, assertNever as Lt, parseMailingListPlacementTargeting as M, toNonNegativeCents as Mt, DEFAULT_MAILING_LIST_TARGETING as N, unsafeAsNonNegativeCents as Nt, DEFAULT_MAILING_LIST_SUCCESS_MESSAGE as O, isNonNegativeWholeCents as Ot, planMailingListChoicePresentation as P, unsafeAsPositiveCents as Pt, parseUtcIsoDateTimeString as Q, unsafeAsBookingSessionId as Qt, assertAllowedPublicUrl as R, formatDurationUnitLabel as Rt, buildMailingListSignupBlockSourceRef as S, unsafeAsStoragePath as Sn, publicEventPricingOptions as St, parseMailingListSignedSourceToken as T, err as Tn, resolvePricingOptionsPriceDisplay as Tt, buildBookingUtcDateWindow as U, parseEventSeriesId as Ut, toLocationCoordinates as V, parseDatedOfferingCapacityHoldAuditRef as Vt, formatBookingDate as W, parseMediaAssetId as Wt, getLocalScheduleDate as X, unsafeAsAppointmentResourceId as Xt, getBookingLocalDateKey as Y, parseUserId as Yt, getLocalScheduleTime as Z, unsafeAsBookingRequirementId as Zt, resolveWholeSeriesBookingPhase as _, unsafeAsSiteFontId as _n, formatBookingPriceDisplay as _t, canonicalizeConfiguredBookingVerticalConfig as a, unsafeAsEventOfferingId as an, isSettleableFlexibleBalanceContractStatus as at, parseManagementKey as b, unsafeAsStaffMemberId as bn, publicEventPricingCurrency as bt, resolveSiteVerticalBookingConfig as c, unsafeAsMediaAssetId as cn, clampConfiguredAmount as ct, ORDINARY_EVENT_PRESET_VALUES as d, unsafeAsPassId as dn, getBookingInstalmentProtectedDepositAmountCents as dt, unsafeAsDeclaredMimeType as en, toScheduledBookingDateRange as et, SURFACE_EVENT_PRESET_VALUES as f, unsafeAsPositiveByteCount as fn, omitZeroAmountFutureRows as ft, orderedLiveRunSessions as g, unsafeAsSafeDownloadFilename as gn, parseEventPageSurfaceSubject as gt, CANCELLED_SESSION_STATUS as h, unsafeAsProductId as hn, resolveFlexibleBalanceInitialPaymentCents as ht, areConfiguredBookingVerticalConfigsEqual as i, unsafeAsEventInstanceId as in, isLiveOrderInstalmentPlanStatus as it, parseFooterCompositionV1 as j, toCents as jt, parseMailingListProviderScope as k, parseNonNegativeCents as kt, DEFAULT_EVENT_TYPE_PRESETS as l, unsafeAsOccurrenceId as ln, getBookingInstalmentDepositRefundability as lt, publicReadFreshnessSeconds as m, unsafeAsPricingOptionId as mn, resolveFlexibleBalanceDueAt as mt, EVENT_PRESET_VOCABULARY as n, unsafeAsDetectedMimeType as nn, parseBookingTimeZone as nt, parseVerticalId as o, unsafeAsEventSeriesId as on, isSettleableOrderInstalmentPlanStatus as ot, WHOLE_SERIES_EVENT_PRESET_VALUES as p, unsafeAsPositiveSeconds as pn, splitAmountWithRemainderInFirst as pt, formatLocalScheduleDate as q, parseRetiredCourseId as qt, NAVIGATION_LOCATIONS as r, unsafeAsEntitlementId as rn, validateBookingPaymentOptionKinds as rt, parseVerticalPresetId as s, unsafeAsFileChecksumSha256 as sn, addIntervalPreservingMonthAnchor as st, resolveEventCatalogue as t, unsafeAsDemoCapability as tn, toScheduledBookingDateTime as tt, EVENT_PRESET_VALUES as u, unsafeAsOriginalFilename as un, getBookingInstalmentPlanIntervalCount as ut, createSecret as v, unsafeAsSiteId as vn, formatEventPricing as vt, parseMailingListContentVersionId as w, unsafeAsUserId as wn, resolveExactOrFreePriceDisplay as wt, stableJsonStringify as x, unsafeAsStorageBucketName as xn, publicEventPricingDisplay as xt, exposeSecret as y, unsafeAsSiteScopedStoragePath as yn, isPublicEventPricingOptionDisplayable as yt, isPresent as z, parseApiKeyId as zt };