musora-content-services 2.189.0 → 2.190.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [2.190.0](https://github.com/railroadmedia/musora-content-services/compare/v2.189.0...v2.190.0) (2026-10-20)
6
+
7
+
8
+ ### Features
9
+
10
+ * **MU2-2024:** resolve placement copy in fetchAvailablePromotionalCampaigns ([ac69d37](https://github.com/railroadmedia/musora-content-services/commit/ac69d379a264e031b1f5bd999b6bed8c78fbfb58))
11
+
5
12
  ## [2.189.0](https://github.com/railroadmedia/musora-content-services/compare/v2.188.0...v2.189.0) (2026-10-20)
6
13
 
7
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.189.0",
3
+ "version": "2.190.0",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/index.d.ts CHANGED
@@ -222,7 +222,7 @@ import {
222
222
  fetchAvailablePromotionalCampaigns,
223
223
  fetchPromotionalCampaigns,
224
224
  fetchUserCampaignStates,
225
- resolveCampaignCopy
225
+ resolveCampaignPlaceholders
226
226
  } from './services/marketing/promotions.ts';
227
227
 
228
228
  import {
@@ -902,7 +902,7 @@ declare module 'musora-content-services' {
902
902
  resetAllAwards,
903
903
  resetAllLearningPaths,
904
904
  resetPassword,
905
- resolveCampaignCopy,
905
+ resolveCampaignPlaceholders,
906
906
  restoreComment,
907
907
  restoreItemFromPlaylist,
908
908
  restoreNotification,
package/src/index.js CHANGED
@@ -226,7 +226,7 @@ import {
226
226
  fetchAvailablePromotionalCampaigns,
227
227
  fetchPromotionalCampaigns,
228
228
  fetchUserCampaignStates,
229
- resolveCampaignCopy
229
+ resolveCampaignPlaceholders
230
230
  } from './services/marketing/promotions.ts';
231
231
 
232
232
  import {
@@ -901,7 +901,7 @@ export {
901
901
  resetAllAwards,
902
902
  resetAllLearningPaths,
903
903
  resetPassword,
904
- resolveCampaignCopy,
904
+ resolveCampaignPlaceholders,
905
905
  restoreComment,
906
906
  restoreItemFromPlaylist,
907
907
  restoreNotification,
@@ -63,6 +63,11 @@ export interface CampaignQueryOptions {
63
63
  active?: boolean
64
64
  }
65
65
 
66
+ export interface AvailableCampaignOptions extends CampaignQueryOptions {
67
+ /** Values for the `{name}` placeholders in placement copy. See `resolveCampaignPlaceholders`. */
68
+ placeholderValues?: CampaignPlaceholderValues
69
+ }
70
+
66
71
  const UNTOUCHED: UserCampaignState = { claimed: false, dismissed: false, eligible: true }
67
72
 
68
73
  // ============================================
@@ -126,12 +131,17 @@ export async function fetchPromotionalCampaigns(
126
131
  * Every running campaign the user has not already claimed or dismissed, and is eligible.
127
132
  */
128
133
  export async function fetchAvailablePromotionalCampaigns(
129
- options: CampaignQueryOptions = {}
134
+ options: AvailableCampaignOptions = {}
130
135
  ): Promise<WithUserCampaignState<PromotionalCampaign>[]> {
131
- return campaignQuery(options)
136
+ const { placeholderValues = {}, ...queryOptions } = options
137
+
138
+ return campaignQuery(queryOptions)
132
139
  .run()
133
140
  .flatMap(decorateUserCampaignState)
134
141
  .map(filterCampaignsForUser)
142
+ .map((campaigns) =>
143
+ campaigns.map((campaign) => resolveCampaignPlaceholders(campaign, placeholderValues))
144
+ )
135
145
  .recover([])
136
146
  }
137
147
 
@@ -184,23 +194,23 @@ export function decorateUserCampaignState<T extends PromotionalCampaign>(
184
194
  }
185
195
 
186
196
  // ============================================
187
- // COPY
197
+ // PLACEHOLDERS
188
198
  // ============================================
189
199
 
190
- export type CampaignCopyValues = Record<string, string>
200
+ export type CampaignPlaceholderValues = Record<string, string>
191
201
 
192
202
  const PLACEHOLDER = /\{(\w+)\}/g
193
203
 
194
204
  const COPY_FIELDS = ['title', 'body', 'disclaimer', 'cta_label'] as const
195
205
 
196
- const fillPlaceholders = (text: string, values: CampaignCopyValues) =>
206
+ const fillPlaceholders = (text: string, values: CampaignPlaceholderValues) =>
197
207
  text.replace(PLACEHOLDER, (placeholder, name: string) => values[name] ?? placeholder)
198
208
 
199
209
  const hasPlaceholder = (text: string | undefined) => !!text && new RegExp(PLACEHOLDER).test(text)
200
210
 
201
- function resolvePlacementCopy(
211
+ function resolvePlacementPlaceholders(
202
212
  placement: PromotionalCampaignPlacement,
203
- values: CampaignCopyValues
213
+ values: CampaignPlaceholderValues
204
214
  ): PromotionalCampaignPlacement {
205
215
  const resolved = { ...placement }
206
216
 
@@ -215,14 +225,14 @@ function resolvePlacementCopy(
215
225
  * Replaces `{name}` placeholders in each placement's copy with the given values. A placement left
216
226
  * with a placeholder that has no value is dropped, so raw `{name}` text never reaches the user.
217
227
  */
218
- export function resolveCampaignCopy<T extends PromotionalCampaign>(
228
+ export function resolveCampaignPlaceholders<T extends PromotionalCampaign>(
219
229
  campaign: T,
220
- values: CampaignCopyValues
230
+ values: CampaignPlaceholderValues
221
231
  ): T {
222
232
  return {
223
233
  ...campaign,
224
- placements: campaign.placements
225
- .map((placement) => resolvePlacementCopy(placement, values))
234
+ placements: (campaign.placements ?? [])
235
+ .map((placement) => resolvePlacementPlaceholders(placement, values))
226
236
  .filter((placement) => !COPY_FIELDS.some((field) => hasPlaceholder(placement[field]))),
227
237
  }
228
238
  }
@@ -1 +1 @@
1
- export const MCS_VERSION = '2.189.0'
1
+ export const MCS_VERSION = '2.190.0'