musora-content-services 2.96.1 → 2.96.2

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.
@@ -0,0 +1,9 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(rg:*)",
5
+ "Bash(npm run lint:*)"
6
+ ],
7
+ "deny": []
8
+ }
9
+ }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
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.96.2](https://github.com/railroadmedia/musora-content-services/compare/v2.96.1...v2.96.2) (2025-12-09)
6
+
5
7
  ### [2.96.1](https://github.com/railroadmedia/musora-content-services/compare/v2.96.0...v2.96.1) (2025-12-09)
6
8
 
7
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.96.1",
3
+ "version": "2.96.2",
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
@@ -267,7 +267,6 @@ import {
267
267
  fetchComingSoon,
268
268
  fetchCommentModContentData,
269
269
  fetchContentRows,
270
- fetchFoundation,
271
270
  fetchHierarchy,
272
271
  fetchLearningPathHierarchy,
273
272
  fetchLeaving,
@@ -275,15 +274,10 @@ import {
275
274
  fetchLessonsFeaturingThisContent,
276
275
  fetchLiveEvent,
277
276
  fetchMetadata,
278
- fetchMethod,
279
- fetchMethodChildren,
280
- fetchMethodChildrenIds,
281
- fetchMethodPreviousNextLesson,
282
277
  fetchMethodV2IntroVideo,
283
278
  fetchMethodV2Structure,
284
279
  fetchMethodV2StructureFromId,
285
280
  fetchNewReleases,
286
- fetchNextPreviousLesson,
287
281
  fetchOtherSongVersions,
288
282
  fetchOwnedContent,
289
283
  fetchPackAll,
@@ -504,7 +498,6 @@ declare module 'musora-content-services' {
504
498
  fetchEnrollmentPageMetadata,
505
499
  fetchFollowedThreads,
506
500
  fetchForumCategories,
507
- fetchFoundation,
508
501
  fetchGenreBySlug,
509
502
  fetchGenreLessons,
510
503
  fetchGenres,
@@ -527,16 +520,11 @@ declare module 'musora-content-services' {
527
520
  fetchLiveEventPollingState,
528
521
  fetchMemberships,
529
522
  fetchMetadata,
530
- fetchMethod,
531
- fetchMethodChildren,
532
- fetchMethodChildrenIds,
533
- fetchMethodPreviousNextLesson,
534
523
  fetchMethodV2IntroVideo,
535
524
  fetchMethodV2Structure,
536
525
  fetchMethodV2StructureFromId,
537
526
  fetchNewReleases,
538
527
  fetchNextContentDataForParent,
539
- fetchNextPreviousLesson,
540
528
  fetchNotificationSettings,
541
529
  fetchNotifications,
542
530
  fetchOtherSongVersions,
package/src/index.js CHANGED
@@ -271,7 +271,6 @@ import {
271
271
  fetchComingSoon,
272
272
  fetchCommentModContentData,
273
273
  fetchContentRows,
274
- fetchFoundation,
275
274
  fetchHierarchy,
276
275
  fetchLearningPathHierarchy,
277
276
  fetchLeaving,
@@ -279,15 +278,10 @@ import {
279
278
  fetchLessonsFeaturingThisContent,
280
279
  fetchLiveEvent,
281
280
  fetchMetadata,
282
- fetchMethod,
283
- fetchMethodChildren,
284
- fetchMethodChildrenIds,
285
- fetchMethodPreviousNextLesson,
286
281
  fetchMethodV2IntroVideo,
287
282
  fetchMethodV2Structure,
288
283
  fetchMethodV2StructureFromId,
289
284
  fetchNewReleases,
290
- fetchNextPreviousLesson,
291
285
  fetchOtherSongVersions,
292
286
  fetchOwnedContent,
293
287
  fetchPackAll,
@@ -503,7 +497,6 @@ export {
503
497
  fetchEnrollmentPageMetadata,
504
498
  fetchFollowedThreads,
505
499
  fetchForumCategories,
506
- fetchFoundation,
507
500
  fetchGenreBySlug,
508
501
  fetchGenreLessons,
509
502
  fetchGenres,
@@ -526,16 +519,11 @@ export {
526
519
  fetchLiveEventPollingState,
527
520
  fetchMemberships,
528
521
  fetchMetadata,
529
- fetchMethod,
530
- fetchMethodChildren,
531
- fetchMethodChildrenIds,
532
- fetchMethodPreviousNextLesson,
533
522
  fetchMethodV2IntroVideo,
534
523
  fetchMethodV2Structure,
535
524
  fetchMethodV2StructureFromId,
536
525
  fetchNewReleases,
537
526
  fetchNextContentDataForParent,
538
- fetchNextPreviousLesson,
539
527
  fetchNotificationSettings,
540
528
  fetchNotifications,
541
529
  fetchOtherSongVersions,
@@ -866,206 +866,6 @@ export async function fetchAllFilterOptions(
866
866
  return includeTabs ? { ...results, tabs, catalogName } : results
867
867
  }
868
868
 
869
- //Daniel Nov 14 2025 note - keeping this for when we migrate foundations to packs, so we know what fields to use.
870
- /**
871
- * Fetch the Foundations 2019.
872
- * @param {string} slug - The slug of the method.
873
- * @returns {Promise<Object|null>} - The fetched foundation data or null if not found.
874
- */
875
- export async function fetchFoundation(slug) {
876
- const filterParams = {}
877
- const query = await buildQuery(
878
- `_type == 'foundation' && slug.current == "${slug}"`,
879
- filterParams,
880
- getFieldsForContentType('foundation'),
881
- {
882
- sortOrder: 'published_on asc',
883
- isSingle: true,
884
- }
885
- )
886
- return fetchSanity(query, false)
887
- }
888
-
889
- /**
890
- * Fetch the Method (learning-paths) for a specific brand.
891
- * @param {string} brand - The brand for which to fetch methods.
892
- * @param {string} slug - The slug of the method.
893
- * @returns {Promise<Object|null>} - The fetched methods data or null if not found.
894
- */
895
- //todo BEH-1446 depreciated. remove all old method functions
896
- export async function fetchMethod(brand, slug) {
897
- const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
898
-
899
- const query = `*[_type == 'learning-path' && brand == "${brand}" && slug.current == "${slug}"] {
900
- "description": ${descriptionField},
901
- "instructors":instructor[]->name,
902
- published_on,
903
- "id": railcontent_id,
904
- railcontent_id,
905
- "slug": slug.current,
906
- status,
907
- title,
908
- video,
909
- length_in_seconds,
910
- parent_content_data,
911
- "breadcrumbs_data": parent_content_data[] {
912
- "id": id,
913
- "title": *[railcontent_id == ^.id][0].title,
914
- "url": *[railcontent_id == ^.id][0].web_url_path
915
- } | order(length(url)),
916
- "type": _type,
917
- "permission_id": permission_v2,
918
- "levels": child[${childrenFilter}]->
919
- {
920
- "id": railcontent_id,
921
- published_on,
922
- child_count,
923
- difficulty,
924
- difficulty_string,
925
- "thumbnail": thumbnail.asset->url,
926
- "instructor": instructor[]->{name},
927
- title,
928
- "type": _type,
929
- "description": ${descriptionField},
930
- "url": web_url_path,
931
- web_url_path,
932
- xp,
933
- total_xp
934
- }
935
- } | order(published_on asc)`
936
- return fetchSanity(query, false)
937
- }
938
-
939
- /**
940
- * Fetch the child courses for a specific method by Railcontent ID.
941
- * @param {string} railcontentId - The Railcontent ID of the current lesson.
942
- * @returns {Promise<Object|null>} - The fetched next lesson data or null if not found.
943
- */
944
- export async function fetchMethodChildren(railcontentId) {
945
- const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
946
-
947
- const query = `*[railcontent_id == ${railcontentId}]{
948
- "child_count":coalesce(count(child[${childrenFilter}]->), 0),
949
- "id": railcontent_id,
950
- "description": ${descriptionField},
951
- "thumbnail": thumbnail.asset->url,
952
- title,
953
- xp,
954
- total_xp,
955
- parent_content_data,
956
- "resources": ${resourcesField},
957
- "breadcrumbs_data": parent_content_data[] {
958
- "id": id,
959
- "title": *[railcontent_id == ^.id][0].title,
960
- "url": *[railcontent_id == ^.id][0].web_url_path
961
- } | order(length(url)),
962
- 'children': child[(${childrenFilter})]->{
963
- ${getFieldsForContentType('method')}
964
- },
965
- }[0..1]`
966
- return fetchSanity(query, true)
967
- }
968
-
969
- /**
970
- * Fetch the next lesson for a specific method by Railcontent ID.
971
- * @param {string} railcontentId - The Railcontent ID of the current lesson.
972
- * @param {string} methodId - The RailcontentID of the method
973
- * @returns {Promise<Object|null>} - object with `nextLesson` and `previousLesson` attributes
974
- * @example
975
- * fetchMethodPreviousNextLesson(241284, 241247)
976
- * .then(data => { console.log('nextLesson', data.nextLesson); console.log('prevlesson', data.prevLesson);})
977
- * .catch(error => console.error(error));
978
- */
979
- export async function fetchMethodPreviousNextLesson(railcontentId, methodId) {
980
- const sortedChildren = await fetchMethodChildrenIds(methodId)
981
- const index = sortedChildren.indexOf(Number(railcontentId))
982
- let nextId = sortedChildren[index + 1]
983
- let previousId = sortedChildren[index - 1]
984
- let ids = []
985
- if (nextId) ids.push(nextId)
986
- if (previousId) ids.push(previousId)
987
- let nextPrev = await fetchByRailContentIds(ids)
988
- const nextLesson = nextPrev.find((elem) => {
989
- return elem['id'] === nextId
990
- })
991
- const prevLesson = nextPrev.find((elem) => {
992
- return elem['id'] === previousId
993
- })
994
- return { nextLesson, prevLesson }
995
- }
996
-
997
- /**
998
- * Fetch all children of a specific method by Railcontent ID.
999
- * @param {string} railcontentId - The Railcontent ID of the method.
1000
- * @returns {Promise<Array<Object>|null>} - The fetched children data or null if not found.
1001
- */
1002
- export async function fetchMethodChildrenIds(railcontentId) {
1003
- const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
1004
-
1005
- const query = `*[ railcontent_id == ${railcontentId}]{
1006
- 'children': child[${childrenFilter}]-> {
1007
- 'id': railcontent_id,
1008
- 'type' : _type,
1009
- 'children': child[${childrenFilter}]-> {
1010
- 'id': railcontent_id,
1011
- 'type' : _type,
1012
- 'children': child[${childrenFilter}]-> {
1013
- 'id': railcontent_id,
1014
- 'type' : _type,
1015
- }
1016
- }
1017
- }
1018
- }`
1019
- let allChildren = await fetchSanity(query, false)
1020
- return getChildrenToDepth(allChildren, 4)
1021
- }
1022
-
1023
- function getChildrenToDepth(parent, depth = 1) {
1024
- let allChildrenIds = []
1025
- if (parent && parent['children'] && depth > 0) {
1026
- parent['children'].forEach((child) => {
1027
- if (!child['children']) {
1028
- allChildrenIds.push(child['id'])
1029
- }
1030
- allChildrenIds = allChildrenIds.concat(getChildrenToDepth(child, depth - 1))
1031
- })
1032
- }
1033
- return allChildrenIds
1034
- }
1035
-
1036
- /**
1037
- * Fetch the next and previous lessons for a specific lesson by Railcontent ID.
1038
- * @param {string} railcontentId - The Railcontent ID of the current lesson.
1039
- * @returns {Promise<Object|null>} - The fetched next and previous lesson data or null if found.
1040
- */
1041
- export async function fetchNextPreviousLesson(railcontentId) {
1042
- const document = await fetchLessonContent(railcontentId)
1043
- if (document.parent_content_data && document.parent_content_data.length > 0) {
1044
- const lastElement = document.parent_content_data[document.parent_content_data.length - 1]
1045
- const results = await fetchMethodPreviousNextLesson(railcontentId, lastElement.id)
1046
- return results
1047
- }
1048
- const processedData = processMetadata(document.brand, document.type, true)
1049
- let sortBy = processedData?.sortBy ?? 'published_on'
1050
- const isDesc = sortBy.startsWith('-')
1051
- sortBy = isDesc ? sortBy.substring(1) : sortBy
1052
- let sortValue = document[sortBy]
1053
- if (sortValue == null) {
1054
- sortBy = 'railcontent_id'
1055
- sortValue = document['railcontent_id']
1056
- }
1057
- const isNumeric = !isNaN(sortValue)
1058
- let prevComparison = isNumeric ? `${sortBy} <= ${sortValue}` : `${sortBy} <= "${sortValue}"`
1059
- let nextComparison = isNumeric ? `${sortBy} >= ${sortValue}` : `${sortBy} >= "${sortValue}"`
1060
- const fields = getFieldsForContentType(document.type)
1061
- const query = `{
1062
- "prevLesson": *[brand == "${document.brand}" && status == "${document.status}" && _type == "${document.type}" && ${prevComparison} && railcontent_id != ${railcontentId}] | order(${sortBy} desc){${fields}}[0...1][0],
1063
- "nextLesson": *[brand == "${document.brand}" && status == "${document.status}" && _type == "${document.type}" && ${nextComparison} && railcontent_id != ${railcontentId}] | order(${sortBy} asc){${fields}}[0...1][0]
1064
- }`
1065
-
1066
- return await fetchSanity(query, true)
1067
- }
1068
-
1069
869
  /**
1070
870
  * Fetch the next piece of content under a parent by Railcontent ID
1071
871
  * @param {int} railcontentId - The Railcontent ID of the parent content
@@ -10,7 +10,6 @@ import { fetchLessonsFeaturingThisContent } from '../src/services/sanity.js'
10
10
 
11
11
  const {
12
12
  fetchSongById,
13
- fetchArtists,
14
13
  fetchReturning,
15
14
  fetchLeaving,
16
15
  fetchComingSoon,
@@ -21,25 +20,20 @@ const {
21
20
  fetchByRailContentIds,
22
21
  fetchAll,
23
22
  fetchAllFilterOptions,
24
- fetchFoundation,
25
- fetchMethod,
26
23
  fetchRelatedLessons,
27
24
  fetchAllPacks,
28
25
  fetchPackAll,
29
26
  fetchLessonContent,
30
27
  fetchLiveEvent,
31
- fetchCoachLessons,
32
28
  fetchByReference,
33
29
  fetchScheduledReleases,
34
30
  getSortOrder,
35
31
  fetchShowsData,
36
32
  fetchMetadata,
37
- fetchNextPreviousLesson,
38
33
  fetchHierarchy,
39
34
  fetchTopLevelParentId,
40
35
  fetchOtherSongVersions,
41
36
  fetchCommentModContentData,
42
- fetchMethodPreviousNextLesson,
43
37
  fetchSanity,
44
38
  } = require('../src/services/sanity.js')
45
39
 
@@ -77,13 +71,6 @@ describe('Sanity Queries', function() {
77
71
  expect(response).toBeDefined()
78
72
  })
79
73
 
80
-
81
- test('fetchArtists', async () => {
82
- const response = await fetchArtists('drumeo')
83
- const artistNames = response.map((x) => x.name)
84
- expect(artistNames).toContain('Audioslave')
85
- }, 10000)
86
-
87
74
  test('fetchSongArtistCount', async () => {
88
75
  const response = await fetchSongArtistCount('drumeo')
89
76
  log(response)
@@ -301,13 +288,6 @@ describe('Sanity Queries', function() {
301
288
  expect(sort).toBe('published_on asc')
302
289
  })
303
290
 
304
- test('fetchMethod', async () => {
305
- const response = await fetchMethod('drumeo', 'drumeo-method')
306
- log(response)
307
- expect(response).toBeDefined()
308
- expect(response.levels.length).toBeGreaterThan(0)
309
- })
310
-
311
291
  test('fetchAll-WithProgress', async () => {
312
292
  const ids = [410213, 410215]
313
293
  let response = await fetchAll('drumeo', 'song', {
@@ -332,13 +312,6 @@ describe('Sanity Queries', function() {
332
312
  expect(response.meta.totalResults).toBe(0)
333
313
  })
334
314
 
335
- test('fetchFoundation', async () => {
336
- const response = await fetchFoundation('foundations-2019')
337
- log(response)
338
- expect(response.units.length).toBeGreaterThan(0)
339
- expect(response.type).toBe('foundation')
340
- })
341
-
342
315
  test('fetchPackAll', async () => {
343
316
  const response = await fetchPackAll(212899) //https://web-staging-one.musora.com/admin/studio/publishing/structure/pack;pack_212899%2Cinspect%3Don
344
317
  log(response)
@@ -357,35 +330,6 @@ describe('Sanity Queries', function() {
357
330
  expect(response[0].id).toBe(212899)
358
331
  })
359
332
 
360
- test('fetchCoachLessons', async () => {
361
- const response = await fetchCoachLessons('drumeo', 411493, {})
362
- expect(response.entity.length).toBeGreaterThan(0)
363
- })
364
- test('fetchCoachLessons-WithTypeFilters', async () => {
365
- const response = await fetchAllFilterOptions('drumeo', ['type,course', 'type,live'], '', '', 'coach-lessons', '', [], 31880)
366
- log(response)
367
- expect(response.meta.filterOptions.difficulty).toBeDefined()
368
- expect(response.meta.filterOptions.type).toBeDefined()
369
- expect(response.meta.filterOptions.lifestyle).toBeDefined()
370
- expect(response.meta.filterOptions.genre).toBeDefined()
371
- })
372
-
373
- test('fetchCoachLessons-WithTypeFilters-InvalidContentType', async () => {
374
- const brand = 'drumeo'
375
- const coachId = 31880
376
- const invalidContentType = 'course' // Not 'coach-lessons'
377
-
378
- await expect(fetchAllFilterOptions(brand, ['type,course', 'type,live'], '', '', invalidContentType, '', [], coachId)).rejects.toThrow(`Invalid contentType: 'course' for coachId. It must be 'coach-lessons'.`)
379
- })
380
-
381
- test('fetchCoachLessons-IncludedFields', async () => {
382
- const response = await fetchCoachLessons('drumeo', 31880, {
383
- includedFields: ['genre,Pop/Rock', 'difficulty,Beginner'],
384
- })
385
- log(response)
386
- expect(response.entity.length).toBeGreaterThan(0)
387
- })
388
-
389
333
  test('fetchAll-IncludedFields', async () => {
390
334
  let response = await fetchAll('drumeo', 'instructor', { includedFields: ['is_active'] })
391
335
  console.log(response)
@@ -488,66 +432,6 @@ describe('Sanity Queries', function() {
488
432
  expect(response).toBeDefined()
489
433
  })
490
434
 
491
- test('fetchNextPreviousLesson-Show-With-Episodes', async () => {
492
- const id = 227136
493
- const document = await fetchByRailContentId(id, 'behind-the-scenes')
494
- const response = await fetchNextPreviousLesson(id)
495
- log(response)
496
- expect(response.prevLesson).toBeDefined()
497
- expect(response.prevLesson.sort).toBeLessThanOrEqual(document.sort)
498
- expect(response.nextLesson).toBeDefined()
499
- expect(response.nextLesson.sort).toBeGreaterThanOrEqual(document.sort)
500
- })
501
-
502
- test('fetchMethodNextPreviousLesson-Last', async () => {
503
- const id = 260171
504
- const methodId = 259060
505
- const response = await fetchMethodPreviousNextLesson(id, methodId)
506
- log(response)
507
- expect(response.prevLesson).toBeDefined()
508
- expect(response.prevLesson.id).toBe(260170)
509
- expect(response.prevLesson.type).toBe('course-part')
510
- expect(response.nextLesson).not.toBeDefined()
511
- })
512
-
513
- test('fetchNextPreviousLesson-Method-Lesson', async () => {
514
- const id = 241265
515
- const response = await fetchNextPreviousLesson(id)
516
- log(response)
517
- expect(response.prevLesson).toBeDefined()
518
- expect(response.prevLesson.id).toBe(241264)
519
- expect(response.prevLesson.type).toBe('learning-path-lesson')
520
- expect(response.nextLesson).toBeDefined()
521
- expect(response.nextLesson.id).toBe(241267)
522
- expect(response.nextLesson.type).toBe('learning-path-lesson')
523
- })
524
-
525
- test('fetchNextPreviousLesson-Quick-Tips', async () => {
526
- const id = 412277
527
- const response = await fetchNextPreviousLesson(id)
528
- const document = await fetchByRailContentId(id, 'quick-tips')
529
- const documentPublishedOn = new Date(document.published_on)
530
- const prevDocumentPublishedOn = new Date(response.prevLesson.published_on)
531
- const nextDocumentPublishedOn = new Date(response.nextLesson.published_on)
532
- expect(response.prevLesson).toBeDefined()
533
- expect(prevDocumentPublishedOn.getTime()).toBeLessThan(documentPublishedOn.getTime())
534
- expect(response.nextLesson).toBeDefined()
535
- expect(documentPublishedOn.getTime()).toBeLessThan(nextDocumentPublishedOn.getTime())
536
- })
537
-
538
- test('fetchNextPreviousLesson-Song', async () => {
539
- const id = 414041
540
- const response = await fetchNextPreviousLesson(id)
541
- const document = await fetchByRailContentId(id, 'song')
542
- const documentPublishedOn = new Date(document.published_on)
543
- const prevDocumentPublishedOn = new Date(response.prevLesson.published_on)
544
- const nextDocumentPublishedOn = new Date(response.nextLesson.published_on)
545
- expect(response.prevLesson).toBeDefined()
546
- expect(prevDocumentPublishedOn.getTime()).toBeLessThanOrEqual(documentPublishedOn.getTime())
547
- expect(response.nextLesson).toBeDefined()
548
- expect(documentPublishedOn.getTime()).toBeLessThanOrEqual(nextDocumentPublishedOn.getTime())
549
- })
550
-
551
435
  test('fetchTopLevelParentId', async () => {
552
436
  let contentId = await fetchTopLevelParentId(241250)
553
437
  expect(contentId).toBe(241247)