musora-content-services 2.115.3 → 2.116.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.116.0](https://github.com/railroadmedia/musora-content-services/compare/v2.115.3...v2.116.0) (2026-01-12)
6
+
7
+
8
+ ### Features
9
+
10
+ * **T3Ps-1324:** Rename Tiered Courses filter to Course Collections ([97efe76](https://github.com/railroadmedia/musora-content-services/commit/97efe76fff3860d769593809bea2e59fb317b528))
11
+
5
12
  ### [2.115.3](https://github.com/railroadmedia/musora-content-services/compare/v2.115.2...v2.115.3) (2026-01-12)
6
13
 
7
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.115.3",
3
+ "version": "2.116.0",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  // Metadata is taken from the 'common' element and then merged with the <brand> metadata.
2
2
  // Brand values are prioritized and will override the same property in the 'common' element.
3
3
 
4
- import {ALWAYS_VISIBLE_TABS} from "./services/sanity.js";
4
+ import { ALWAYS_VISIBLE_TABS } from './services/sanity.js'
5
5
 
6
6
  const PROGRESS_NAMES = ['All', 'In Progress', 'Completed', 'Not Started']
7
7
  const DIFFICULTY_STRINGS = ['Introductory', 'Beginner', 'Intermediate', 'Advanced', 'Expert']
@@ -9,19 +9,19 @@ const DIFFICULTY_STRINGS = ['Introductory', 'Beginner', 'Intermediate', 'Advance
9
9
  const LESSON_TYPE_FILTER = [
10
10
  {
11
11
  title: 'Single Lessons',
12
- children: ['Lessons', 'Practice Alongs', 'Live Archives', 'Student Archives']
12
+ children: ['Lessons', 'Practice Alongs', 'Live Archives', 'Student Archives'],
13
13
  },
14
14
  {
15
15
  title: 'Courses',
16
- children: ['Courses', 'Guided Courses', 'Tiered Courses']
16
+ children: ['Courses', 'Guided Courses', 'Course Collections'],
17
17
  },
18
18
  {
19
19
  title: 'Skill Packs',
20
20
  },
21
21
  {
22
22
  title: 'Entertainment',
23
- children: ['Specials', 'Documentaries', 'Shows']
24
- }
23
+ children: ['Specials', 'Documentaries', 'Shows'],
24
+ },
25
25
  ]
26
26
 
27
27
  class SortingOptions {
@@ -32,7 +32,7 @@ class SortingOptions {
32
32
  static Slug = { value: 'slug', name: 'Name: A to Z' }
33
33
  static SlugDesc = { value: '-slug', name: 'Name: Z to A' }
34
34
  static AllSortingOptions = [
35
- this.PopularityDesc,
35
+ this.PopularityDesc,
36
36
  this.Popularity,
37
37
  this.PublishedOn,
38
38
  this.PublishedOnDesc,
@@ -46,12 +46,7 @@ export class LengthFilterOptions {
46
46
  static From7To15 = { value: '420-900', name: '7 to 15 Minutes' }
47
47
  static From15To30 = { value: '901-1800', name: '15 to 30 Minutes' }
48
48
  static More30 = { value: '>1801', name: '30+ Minutes' }
49
- static AllOptions = [
50
- this.UpTo7.name,
51
- this.From7To15.name,
52
- this.From15To30.name,
53
- this.More30.name,
54
- ]
49
+ static AllOptions = [this.UpTo7.name, this.From7To15.name, this.From15To30.name, this.More30.name]
55
50
  }
56
51
 
57
52
  export class Tabs {
@@ -63,18 +58,63 @@ export class Tabs {
63
58
  static Courses = { name: 'Courses', short_name: 'Courses', value: 'type,Courses' }
64
59
  static SkillLevel = { name: 'Skill Level', short_name: 'SKILL LEVEL', is_group_by: true, value: 'difficulty_string' }
65
60
  static Genres = { name: 'Genres', short_name: 'Genres', is_group_by: true, value: 'genre' }
66
- static Completed = { name: 'Completed', short_name: 'COMPLETED', is_group_by: false, value: 'completed' }
67
- static InProgress = { name: 'In Progress', short_name: 'IN PROGRESS', is_group_by: false, value: 'in progress' }
68
- static Instructors = { name: 'Instructors', short_name: 'INSTRUCTORS', is_group_by: true, value: 'instructor' }
61
+ static Completed = {
62
+ name: 'Completed',
63
+ short_name: 'COMPLETED',
64
+ is_group_by: false,
65
+ value: 'completed',
66
+ }
67
+ static InProgress = {
68
+ name: 'In Progress',
69
+ short_name: 'IN PROGRESS',
70
+ is_group_by: false,
71
+ value: 'in progress',
72
+ }
73
+ static Instructors = {
74
+ name: 'Instructors',
75
+ short_name: 'INSTRUCTORS',
76
+ is_group_by: true,
77
+ value: 'instructor',
78
+ }
69
79
  static Lessons = { name: 'Lessons', short_name: 'LESSONS', value: '' }
70
80
  static Artists = { name: 'Artists', short_name: 'ARTISTS', is_group_by: true, value: 'artist' }
71
81
  static Songs = { name: 'Songs', short_name: 'Songs', value: '' }
72
- static Tutorials = { name: 'Tutorials', short_name: 'Tutorials', value: 'type,tutorials', cardType: 'big' }
73
- static Transcriptions = { name: 'Transcriptions', short_name: 'Transcriptions', value: 'type,transcriptions', cardType: 'small' }
74
- static SheetMusic = { name: 'Sheet Music', short_name: 'Sheet Music', value: 'type,transcriptions', cardType: 'small' }
75
- static Tabs = { name: 'Tabs', short_name: 'Tabs', value: 'type,transcriptions', cardType: 'small' }
76
- static PlayAlongs = { name: 'Play-Alongs', short_name: 'Play-Alongs', value:'type,play along', cardType: 'small' }
77
- static JamTracks = { name: 'Jam Tracks', short_name: 'Jam Tracks', value:'type,jam-track', cardType: 'small' }
82
+ static Tutorials = {
83
+ name: 'Tutorials',
84
+ short_name: 'Tutorials',
85
+ value: 'type,tutorials',
86
+ cardType: 'big',
87
+ }
88
+ static Transcriptions = {
89
+ name: 'Transcriptions',
90
+ short_name: 'Transcriptions',
91
+ value: 'type,transcriptions',
92
+ cardType: 'small',
93
+ }
94
+ static SheetMusic = {
95
+ name: 'Sheet Music',
96
+ short_name: 'Sheet Music',
97
+ value: 'type,transcriptions',
98
+ cardType: 'small',
99
+ }
100
+ static Tabs = {
101
+ name: 'Tabs',
102
+ short_name: 'Tabs',
103
+ value: 'type,transcriptions',
104
+ cardType: 'small',
105
+ }
106
+ static PlayAlongs = {
107
+ name: 'Play-Alongs',
108
+ short_name: 'Play-Alongs',
109
+ value: 'type,play along',
110
+ cardType: 'small',
111
+ }
112
+ static JamTracks = {
113
+ name: 'Jam Tracks',
114
+ short_name: 'Jam Tracks',
115
+ value: 'type,jam-track',
116
+ cardType: 'small',
117
+ }
78
118
  static RecentAll = { name: 'All', short_name: 'All' }
79
119
  static RecentIncomplete = { name: 'Incomplete', short_name: 'Incomplete' }
80
120
  static RecentCompleted = { name: 'Completed', short_name: 'Completed' }
@@ -92,15 +132,26 @@ export const TabResponseType = {
92
132
  SECTIONS: 'sections',
93
133
  CATALOG: 'catalog',
94
134
  PROGRESS_ROWS: 'progress_rows',
95
- };
135
+ }
96
136
 
97
137
  const commonMetadata = {
98
- 'lessons': {
138
+ lessons: {
99
139
  name: 'Lessons',
100
140
  filterOptions: {
101
141
  difficulty: DIFFICULTY_STRINGS,
102
142
  length: LengthFilterOptions.AllOptions,
103
- style: ['Country/Folk', 'Funk/Disco', 'Hard Rock/Metal', 'Hip-Hop/Rap/EDM', 'Holiday/Soundtrack', 'Jazz/Blues', 'Latin/World', 'Pop/Rock', 'R&B/Soul', 'Worship/Gospel'],
143
+ style: [
144
+ 'Country/Folk',
145
+ 'Funk/Disco',
146
+ 'Hard Rock/Metal',
147
+ 'Hip-Hop/Rap/EDM',
148
+ 'Holiday/Soundtrack',
149
+ 'Jazz/Blues',
150
+ 'Latin/World',
151
+ 'Pop/Rock',
152
+ 'R&B/Soul',
153
+ 'Worship/Gospel',
154
+ ],
104
155
  type: LESSON_TYPE_FILTER,
105
156
  progress: PROGRESS_NAMES,
106
157
  },
@@ -115,15 +166,32 @@ const commonMetadata = {
115
166
  Tabs.Courses,
116
167
  Tabs.SkillPacks,
117
168
  Tabs.Entertainment,
118
- Tabs.ExploreAll
169
+ Tabs.ExploreAll,
119
170
  ],
120
171
  },
121
- 'songs': {
172
+ songs: {
122
173
  name: 'Songs',
123
174
  filterOptions: {
124
175
  difficulty: DIFFICULTY_STRINGS,
125
- style: ['Blues','Christian','Classical','Country','Disco','Electronic','Folk','Funk','Hip-Hop/Rap','Holiday','Jazz','Soundtrack',
126
- 'World','Metal','Pop','R&B/Soul','Rock'],
176
+ style: [
177
+ 'Blues',
178
+ 'Christian',
179
+ 'Classical',
180
+ 'Country',
181
+ 'Disco',
182
+ 'Electronic',
183
+ 'Folk',
184
+ 'Funk',
185
+ 'Hip-Hop/Rap',
186
+ 'Holiday',
187
+ 'Jazz',
188
+ 'Soundtrack',
189
+ 'World',
190
+ 'Metal',
191
+ 'Pop',
192
+ 'R&B/Soul',
193
+ 'Rock',
194
+ ],
127
195
  type: ['Tutorials', 'Transcriptions', 'Jam Tracks'],
128
196
  progress: PROGRESS_NAMES,
129
197
  },
@@ -138,16 +206,12 @@ const commonMetadata = {
138
206
  Tabs.Transcriptions,
139
207
  Tabs.PlayAlongs,
140
208
  Tabs.JamTracks,
141
- Tabs.ExploreAll
209
+ Tabs.ExploreAll,
142
210
  ],
143
211
  },
144
- 'recent': {
212
+ recent: {
145
213
  name: 'Recent Lessons',
146
- tabs: [
147
- Tabs.RecentAll,
148
- Tabs.RecentIncomplete,
149
- Tabs.RecentCompleted
150
- ],
214
+ tabs: [Tabs.RecentAll, Tabs.RecentIncomplete, Tabs.RecentCompleted],
151
215
  },
152
216
  recommendation: {
153
217
  tabs: [
@@ -171,12 +235,23 @@ const commonMetadata = {
171
235
  }
172
236
  const contentMetadata = {
173
237
  drumeo: {
174
- 'lessons': {
238
+ lessons: {
175
239
  name: 'Lessons',
176
240
  filterOptions: {
177
241
  difficulty: DIFFICULTY_STRINGS,
178
242
  length: LengthFilterOptions.AllOptions,
179
- style: ['Country/Folk', 'Funk/Disco', 'Hard Rock/Metal', 'Hip-Hop/Rap/EDM', 'Holiday/Soundtrack', 'Jazz/Blues', 'Latin/World', 'Pop/Rock', 'R&B/Soul', 'Worship/Gospel'],
243
+ style: [
244
+ 'Country/Folk',
245
+ 'Funk/Disco',
246
+ 'Hard Rock/Metal',
247
+ 'Hip-Hop/Rap/EDM',
248
+ 'Holiday/Soundtrack',
249
+ 'Jazz/Blues',
250
+ 'Latin/World',
251
+ 'Pop/Rock',
252
+ 'R&B/Soul',
253
+ 'Worship/Gospel',
254
+ ],
180
255
  type: LESSON_TYPE_FILTER,
181
256
  progress: PROGRESS_NAMES,
182
257
  },
@@ -191,18 +266,29 @@ const contentMetadata = {
191
266
  Tabs.Courses,
192
267
  Tabs.SkillPacks,
193
268
  Tabs.Entertainment,
194
- Tabs.ExploreAll
269
+ Tabs.ExploreAll,
195
270
  ],
196
271
  },
197
272
  'songs-types': ['Tutorials', 'Transcriptions', 'Play-Alongs', 'Jam Tracks'],
198
273
  },
199
274
  pianote: {
200
- 'lessons': {
275
+ lessons: {
201
276
  name: 'Lessons',
202
277
  filterOptions: {
203
278
  difficulty: DIFFICULTY_STRINGS,
204
279
  length: LengthFilterOptions.AllOptions,
205
- style: ['Classical', 'Country/Folk', 'Funk/Disco', 'Hip-Hop/Rap/EDM', 'Holiday/Soundtrack', 'Jazz/Blues', 'Latin/World', 'Pop/Rock', 'R&B/Soul', 'Worship/Gospel'],
280
+ style: [
281
+ 'Classical',
282
+ 'Country/Folk',
283
+ 'Funk/Disco',
284
+ 'Hip-Hop/Rap/EDM',
285
+ 'Holiday/Soundtrack',
286
+ 'Jazz/Blues',
287
+ 'Latin/World',
288
+ 'Pop/Rock',
289
+ 'R&B/Soul',
290
+ 'Worship/Gospel',
291
+ ],
206
292
  type: LESSON_TYPE_FILTER,
207
293
  progress: PROGRESS_NAMES,
208
294
  },
@@ -217,7 +303,7 @@ const contentMetadata = {
217
303
  Tabs.Courses,
218
304
  Tabs.SkillPacks,
219
305
  Tabs.Entertainment,
220
- Tabs.ExploreAll
306
+ Tabs.ExploreAll,
221
307
  ],
222
308
  },
223
309
  'songs-types': ['Tutorials', 'Sheet Music', 'Play-Alongs', 'Jam Tracks'],
@@ -230,10 +316,9 @@ const contentMetadata = {
230
316
  },
231
317
  singeo: {
232
318
  'songs-types': ['Tutorials', 'Sheet Music', 'Play-Alongs', 'Jam Tracks'],
233
- }
319
+ },
234
320
  }
235
321
 
236
-
237
322
  export function processMetadata(brand, type, withFilters = false) {
238
323
  let brandMetaData = contentMetadata[brand]?.[type]
239
324
  let commonMetaData = commonMetadata[type]
@@ -269,22 +354,20 @@ export function processMetadata(brand, type, withFilters = false) {
269
354
 
270
355
  function mapSongTabNames(brandMetaData) {
271
356
  brandMetaData.tabs.forEach((tab, index) => {
272
- if (ALWAYS_VISIBLE_TABS.some(visibleTab => visibleTab.name === tab)) {
273
- return;
357
+ if (ALWAYS_VISIBLE_TABS.some((visibleTab) => visibleTab.name === tab)) {
358
+ return
274
359
  }
275
360
 
276
- const targetName = brandMetaData['filterOptions']['type'][index - 1];
361
+ const targetName = brandMetaData['filterOptions']['type'][index - 1]
277
362
 
278
363
  // Find the matching Tab by name
279
- const matchingTab = Object.values(Tabs).find(
280
- tabObj => tabObj.name === targetName
281
- );
364
+ const matchingTab = Object.values(Tabs).find((tabObj) => tabObj.name === targetName)
282
365
 
283
366
  if (matchingTab) {
284
- brandMetaData.tabs[index] = matchingTab;
367
+ brandMetaData.tabs[index] = matchingTab
285
368
  }
286
- });
287
- return brandMetaData.tabs;
369
+ })
370
+ return brandMetaData.tabs
288
371
  }
289
372
 
290
373
  /**
@@ -316,25 +399,27 @@ function transformFilters(filterOptions) {
316
399
  return Object.entries(filterOptions).map(([key, values]) => {
317
400
  // Check if values is hierarchical (array of objects with title property)
318
401
  // We check for 'title' property to distinguish from simple string arrays
319
- const isHierarchical = Array.isArray(values) &&
402
+ const isHierarchical =
403
+ Array.isArray(values) &&
320
404
  values.length > 0 &&
321
405
  typeof values[0] === 'object' &&
322
- values[0].title !== undefined;
406
+ values[0].title !== undefined
323
407
 
324
408
  if (isHierarchical) {
325
409
  // Handle hierarchical structure - nest children inside parents
326
- const items = values.map(group => ({
410
+ const items = values.map((group) => ({
327
411
  name: group.title,
328
412
  value: `${key},${group.title}`,
329
413
  // Only include isParent and items if children exist
330
- ...(group.children && group.children.length > 0 && {
331
- isParent: true,
332
- items: group.children.map(child => ({
333
- name: child,
334
- value: `${key},${child}`,
335
- }))
336
- })
337
- }));
414
+ ...(group.children &&
415
+ group.children.length > 0 && {
416
+ isParent: true,
417
+ items: group.children.map((child) => ({
418
+ name: child,
419
+ value: `${key},${child}`,
420
+ })),
421
+ }),
422
+ }))
338
423
 
339
424
  return {
340
425
  title: capitalizeFirstLetter(key),
@@ -342,20 +427,20 @@ function transformFilters(filterOptions) {
342
427
  key,
343
428
  items,
344
429
  isHierarchical: true,
345
- };
430
+ }
346
431
  } else {
347
432
  // Handle flat structure (existing behavior - no changes)
348
433
  return {
349
434
  title: capitalizeFirstLetter(key),
350
435
  type: filterTypes[key] || 'checkbox',
351
436
  key,
352
- items: values.map(value => ({
437
+ items: values.map((value) => ({
353
438
  name: value,
354
439
  value: `${key},${key === 'progress' ? value.toLowerCase() : value}`,
355
440
  })),
356
- };
441
+ }
357
442
  }
358
- });
443
+ })
359
444
  }
360
445
 
361
446
  /**
@@ -201,11 +201,7 @@ export const individualLessonsTypes = [
201
201
  ...studentArchivesLessonTypes,
202
202
  ]
203
203
 
204
- export const coursesLessonTypes = [
205
- 'course',
206
- 'tiered-course', // TODO: new content type
207
- 'guided-course',
208
- ]
204
+ export const coursesLessonTypes = ['course', 'course-collection', 'guided-course']
209
205
 
210
206
  export const skillLessonTypes = ['skill-pack']
211
207
 
@@ -218,11 +214,7 @@ export const showsLessonTypes = [
218
214
  'spotlight',
219
215
  'performance',
220
216
  ]
221
- export const entertainmentLessonTypes = [
222
- 'special',
223
- 'documentary-lesson',
224
- ...showsLessonTypes,
225
- ]
217
+ export const entertainmentLessonTypes = ['special', 'documentary-lesson', ...showsLessonTypes]
226
218
  export const collectionLessonTypes = [...coursesLessonTypes, ...showsLessonTypes]
227
219
 
228
220
  export const lessonTypesMapping = {
@@ -404,6 +396,9 @@ export let contentTypeConfig = {
404
396
  'guided-course': {
405
397
  includeChildFields: true,
406
398
  },
399
+ 'course-collection': {
400
+ individualLessonsTypes: true,
401
+ },
407
402
  course: {
408
403
  fields: [
409
404
  '"lesson_count": child_count',
@@ -33,7 +33,7 @@ import {
33
33
  SONG_TYPES_WITH_CHILDREN,
34
34
  } from '../contentTypeConfig.js'
35
35
  import { fetchSimilarItems, recommendations } from './recommendations.js'
36
- import {getSongType, processMetadata, Tabs} from '../contentMetaData.js'
36
+ import { getSongType, processMetadata, Tabs } from '../contentMetaData.js'
37
37
  import { GET } from '../infrastructure/http/HttpClient.ts'
38
38
 
39
39
  import { globalConfig } from './config.js'
@@ -55,7 +55,7 @@ const excludeFromGeneratedIndex = ['fetchRelatedByLicense']
55
55
  *
56
56
  * @type {object[]}
57
57
  */
58
- export const ALWAYS_VISIBLE_TABS = [Tabs.ForYou, Tabs.ExploreAll];
58
+ export const ALWAYS_VISIBLE_TABS = [Tabs.ForYou, Tabs.ExploreAll]
59
59
 
60
60
  /**
61
61
  * Mapping from tab names to their underlying Sanity content types.
@@ -64,13 +64,13 @@ export const ALWAYS_VISIBLE_TABS = [Tabs.ForYou, Tabs.ExploreAll];
64
64
  */
65
65
  const TAB_TO_CONTENT_TYPES = {
66
66
  'Single Lessons': individualLessonsTypes,
67
- 'Courses': coursesLessonTypes,
67
+ Courses: coursesLessonTypes,
68
68
  'Skill Packs': skillLessonTypes,
69
- 'Entertainment': entertainmentLessonTypes,
70
- 'Tutorials': tutorialsLessonTypes,
71
- 'Transcriptions': transcriptionsLessonTypes,
69
+ Entertainment: entertainmentLessonTypes,
70
+ Tutorials: tutorialsLessonTypes,
71
+ Transcriptions: transcriptionsLessonTypes,
72
72
  'Sheet Music': transcriptionsLessonTypes,
73
- 'Tabs': transcriptionsLessonTypes,
73
+ Tabs: transcriptionsLessonTypes,
74
74
  'Play-Alongs': playAlongLessonTypes,
75
75
  'Jam Tracks': jamTrackLessonTypes,
76
76
  }
@@ -2258,9 +2258,9 @@ export async function fetchContentTypeCounts(brand, pageName) {
2258
2258
  * @returns {Array} - Filtered array of tabs with content
2259
2259
  */
2260
2260
  function filterTabsByContentCounts(tabs, contentTypeCounts) {
2261
- return tabs.filter(tab => {
2262
- if (ALWAYS_VISIBLE_TABS.some(visibleTab => visibleTab.name === tab.name)) {
2263
- return true;
2261
+ return tabs.filter((tab) => {
2262
+ if (ALWAYS_VISIBLE_TABS.some((visibleTab) => visibleTab.name === tab.name)) {
2263
+ return true
2264
2264
  }
2265
2265
 
2266
2266
  const tabContentTypes = TAB_TO_CONTENT_TYPES[tab.name] || []
@@ -2359,7 +2359,7 @@ function getContentTypesForFilterName(displayName) {
2359
2359
  'Student Archives': 'student archives',
2360
2360
  Courses: 'courses',
2361
2361
  'Guided Courses': 'guided courses',
2362
- 'Tiered Courses': 'tiered courses',
2362
+ 'Course Collections': 'course collections',
2363
2363
  Specials: 'specials',
2364
2364
  Documentaries: 'documentaries',
2365
2365
  Shows: 'shows',
@@ -1,9 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(rg:*)",
5
- "Bash(npm run lint:*)"
6
- ],
7
- "deny": []
8
- }
9
- }