musora-content-services 1.2.2 → 1.2.4

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.
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-Config.html">Config</a><ul class='methods'><li data-type='method'><a href="module-Config.html#~initializeService">initializeService</a></li></ul></li><li><a href="module-Railcontent-Services.html">Railcontent-Services</a><ul class='methods'><li data-type='method'><a href="module-Railcontent-Services.html#.addItemToPlaylist">addItemToPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.countAssignmentsAndLessons">countAssignmentsAndLessons</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.createPlaylist">createPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deletePlaylist">deletePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deletePlaylistItem">deletePlaylistItem</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deletePlaylistLike">deletePlaylistLike</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.duplicatePlaylist">duplicatePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchAllCompletedStates">fetchAllCompletedStates</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCarouselCardData">fetchCarouselCardData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeIndexMetadata">fetchChallengeIndexMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeLessonData">fetchChallengeLessonData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeMetadata">fetchChallengeMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeUserActiveChallenges">fetchChallengeUserActiveChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedChallenges">fetchCompletedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedContent">fetchCompletedContent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedState">fetchCompletedState</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentInProgress">fetchContentInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentPageUserData">fetchContentPageUserData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchNextContentDataForParent">fetchNextContentDataForParent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchOwnedChallenges">fetchOwnedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPinnedPlaylists">fetchPinnedPlaylists</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPlaylist">fetchPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPlaylistItem">fetchPlaylistItem</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPlaylistItems">fetchPlaylistItems</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchSongsInProgress">fetchSongsInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserAward">fetchUserAward</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserBadges">fetchUserBadges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserChallengeProgress">fetchUserChallengeProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserPlaylists">fetchUserPlaylists</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.likePlaylist">likePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.pinPlaylist">pinPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesCommunityNotification">postChallengesCommunityNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesCompleteLesson">postChallengesCompleteLesson</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnroll">postChallengesEnroll</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnrollmentNotification">postChallengesEnrollmentNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesHideCompletedBanner">postChallengesHideCompletedBanner</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesLeave">postChallengesLeave</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesSetStartDate">postChallengesSetStartDate</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesUnlock">postChallengesUnlock</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.reportPlaylist">reportPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.unpinPlaylist">unpinPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.updatePlaylist">updatePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.updatePlaylistItem">updatePlaylistItem</a></li></ul></li><li><a href="module-Sanity-Services.html">Sanity-Services</a><ul class='methods'><li data-type='method'><a href="module-Sanity-Services.html#.fetchAll">fetchAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllFilterOptions">fetchAllFilterOptions</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllPacks">fetchAllPacks</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllSongs">fetchAllSongs</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtistLessons">fetchArtistLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtists">fetchArtists</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAssignments">fetchAssignments</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentId">fetchByRailContentId</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentIds">fetchByRailContentIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByReference">fetchByReference</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCatalogMetadata">fetchCatalogMetadata</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchChallengeOverview">fetchChallengeOverview</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchChildren">fetchChildren</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCoachLessons">fetchCoachLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCommentModContentData">fetchCommentModContentData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCourseOverview">fetchCourseOverview</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchFoundation">fetchFoundation</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchGenreLessons">fetchGenreLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLessonContent">fetchLessonContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMetadata">fetchMetadata</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethod">fetchMethod</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildren">fetchMethodChildren</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildrenIds">fetchMethodChildrenIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodNextLesson">fetchMethodNextLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodPreviousNextLesson">fetchMethodPreviousNextLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethods">fetchMethods</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNewReleases">fetchNewReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNextPreviousLesson">fetchNextPreviousLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackAll">fetchPackAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackChildren">fetchPackChildren</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackData">fetchPackData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchParentByRailContentId">fetchParentByRailContentId</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchParentForDownload">fetchParentForDownload</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedLessons">fetchRelatedLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedMethodLessons">fetchRelatedMethodLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedSongs">fetchRelatedSongs</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSanity">fetchSanity</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchScheduledReleases">fetchScheduledReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchShowsData">fetchShowsData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongArtistCount">fetchSongArtistCount</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongById">fetchSongById</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongCount">fetchSongCount</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongFilterOptions">fetchSongFilterOptions</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchUpcomingEvents">fetchUpcomingEvents</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchWorkouts">fetchWorkouts</a></li><li data-type='method'><a href="module-Sanity-Services.html#.jumpToContinueContent">jumpToContinueContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#~handleCustomFetchAll">handleCustomFetchAll</a></li></ul></li></ul>
32
+ <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-Config.html">Config</a><ul class='methods'><li data-type='method'><a href="module-Config.html#.initializeService">initializeService</a></li></ul></li><li><a href="module-Railcontent-Services.html">Railcontent-Services</a><ul class='methods'><li data-type='method'><a href="module-Railcontent-Services.html#.addItemToPlaylist">addItemToPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.countAssignmentsAndLessons">countAssignmentsAndLessons</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.createPlaylist">createPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deletePlaylist">deletePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deletePlaylistItem">deletePlaylistItem</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deletePlaylistLike">deletePlaylistLike</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.duplicatePlaylist">duplicatePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchAllCompletedStates">fetchAllCompletedStates</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCarouselCardData">fetchCarouselCardData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeIndexMetadata">fetchChallengeIndexMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeLessonData">fetchChallengeLessonData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeMetadata">fetchChallengeMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeUserActiveChallenges">fetchChallengeUserActiveChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedChallenges">fetchCompletedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedContent">fetchCompletedContent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedState">fetchCompletedState</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentInProgress">fetchContentInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentPageUserData">fetchContentPageUserData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchNextContentDataForParent">fetchNextContentDataForParent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchOwnedChallenges">fetchOwnedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPinnedPlaylists">fetchPinnedPlaylists</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPlaylist">fetchPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPlaylistItem">fetchPlaylistItem</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchPlaylistItems">fetchPlaylistItems</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchSongsInProgress">fetchSongsInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserAward">fetchUserAward</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserBadges">fetchUserBadges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserChallengeProgress">fetchUserChallengeProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserPlaylists">fetchUserPlaylists</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.likePlaylist">likePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.pinPlaylist">pinPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesCommunityNotification">postChallengesCommunityNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesCompleteLesson">postChallengesCompleteLesson</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnroll">postChallengesEnroll</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnrollmentNotification">postChallengesEnrollmentNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesHideCompletedBanner">postChallengesHideCompletedBanner</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesLeave">postChallengesLeave</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesSetStartDate">postChallengesSetStartDate</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesSoloNotification">postChallengesSoloNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesUnlock">postChallengesUnlock</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.reportPlaylist">reportPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.setStudentViewForUser">setStudentViewForUser</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.unpinPlaylist">unpinPlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.updatePlaylist">updatePlaylist</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.updatePlaylistItem">updatePlaylistItem</a></li></ul></li><li><a href="module-Sanity-Services.html">Sanity-Services</a><ul class='methods'><li data-type='method'><a href="module-Sanity-Services.html#.fetchAll">fetchAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllFilterOptions">fetchAllFilterOptions</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllPacks">fetchAllPacks</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtistLessons">fetchArtistLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtists">fetchArtists</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentId">fetchByRailContentId</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentIds">fetchByRailContentIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByReference">fetchByReference</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCoachLessons">fetchCoachLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCommentModContentData">fetchCommentModContentData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchFoundation">fetchFoundation</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchGenreLessons">fetchGenreLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLessonContent">fetchLessonContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMetadata">fetchMetadata</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethod">fetchMethod</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildren">fetchMethodChildren</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildrenIds">fetchMethodChildrenIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodPreviousNextLesson">fetchMethodPreviousNextLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNewReleases">fetchNewReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNextPreviousLesson">fetchNextPreviousLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackAll">fetchPackAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackData">fetchPackData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchParentForDownload">fetchParentForDownload</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedLessons">fetchRelatedLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedSongs">fetchRelatedSongs</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSanity">fetchSanity</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchScheduledReleases">fetchScheduledReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchShowsData">fetchShowsData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongArtistCount">fetchSongArtistCount</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongById">fetchSongById</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchUpcomingEvents">fetchUpcomingEvents</a></li><li data-type='method'><a href="module-Sanity-Services.html#.jumpToContinueContent">jumpToContinueContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#~handleCustomFetchAll">handleCustomFetchAll</a></li></ul></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -53,6 +53,7 @@ import {
53
53
  artistOrInstructorNameAsArray,
54
54
  assignmentsField,
55
55
  descriptionField,
56
+ resourcesField,
56
57
  contentTypeConfig,
57
58
  DEFAULT_FIELDS,
58
59
  getFieldsForContentType,
@@ -61,25 +62,25 @@ import {
61
62
  showsTypes,
62
63
  getNewReleasesTypes,
63
64
  coachLessonsTypes
64
- } from "../contentTypeConfig";
65
+ } from "../contentTypeConfig.js";
65
66
 
66
67
  import {
67
68
  processMetadata,
68
69
  typeWithSortOrder
69
- } from "../contentMetaData";
70
+ } from "../contentMetaData.js";
70
71
 
71
- import {globalConfig} from "./config";
72
+ import {globalConfig} from "./config.js";
72
73
 
73
74
  import {
74
75
  fetchAllCompletedStates,
75
76
  fetchCompletedChallenges,
76
- fetchCurrentSongComplete,
77
77
  fetchOwnedChallenges,
78
- fetchNextContentDataForParent
78
+ fetchNextContentDataForParent,
79
+ fetchHandler,
79
80
  } from './railcontent.js';
80
- import {arrayToStringRepresentation, FilterBuilder} from "../filterBuilder";
81
- import {fetchUserPermissions} from "./userPermissions";
82
- import {getAllCompleted, getAllStarted, getAllStartedOrCompleted} from "./contentProgress";
81
+ import {arrayToStringRepresentation, FilterBuilder} from "../filterBuilder.js";
82
+ import {fetchUserPermissions} from "./userPermissions.js";
83
+ import {getAllCompleted, getAllStarted, getAllStartedOrCompleted} from "./contentProgress.js";
83
84
 
84
85
  /**
85
86
  * Exported functions that are excluded from index generation.
@@ -161,10 +162,12 @@ export async function fetchPlayAlongsCount(brand) {
161
162
  * .catch(error => console.error(error));
162
163
  */
163
164
  export async function fetchRelatedSongs(brand, songId) {
165
+ const now = getSanityDate(new Date());
164
166
  const query = `
165
167
  *[_type == "song" &amp;&amp; railcontent_id == ${songId}]{
166
168
  "entity": array::unique([
167
- ...(*[_type == "song" &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id != ${songId} &amp;&amp; references(^.artist->_id)]{
169
+ ...(*[_type == "song" &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id != ${songId} &amp;&amp; references(^.artist->_id)
170
+ &amp;&amp; (status in ['published'] || (status == 'scheduled' &amp;&amp; defined(published_on) &amp;&amp; published_on >= '${now}'))]{
168
171
  "type": _type,
169
172
  "id": railcontent_id,
170
173
  "url": web_url_path,
@@ -190,7 +193,8 @@ export async function fetchRelatedSongs(brand, songId) {
190
193
  }
191
194
  ],
192
195
  }[0...10]),
193
- ...(*[_type == "song" &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id != ${songId} &amp;&amp; references(^.genre[]->_id)]{
196
+ ...(*[_type == "song" &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id != ${songId} &amp;&amp; references(^.genre[]->_id)
197
+ &amp;&amp; (status in ['published'] || (status == 'scheduled' &amp;&amp; defined(published_on) &amp;&amp; published_on >= '${now}'))]{
194
198
  "type": _type,
195
199
  "id": railcontent_id,
196
200
  "url": web_url_path,
@@ -226,107 +230,6 @@ export async function fetchRelatedSongs(brand, songId) {
226
230
  return fetchSanity(query, false);
227
231
  }
228
232
 
229
- /**
230
- * Fetch all songs for a specific brand with pagination and search options.
231
- * @param {string} brand - The brand for which to fetch songs.
232
- * @param {Object} params - Parameters for pagination, filtering, and sorting.
233
- * @param {number} [params.page=1] - The page number for pagination.
234
- * @param {number} [params.limit=10] - The number of songs per page.
235
- * @param {string} [params.searchTerm=""] - The search term to filter songs by title or artist.
236
- * @param {string} [params.sort="-published_on"] - The field to sort the songs by.
237
- * @param {Array&lt;string>} [params.includedFields=[]] - The fields to include in the query.
238
- * @param {string} [params.groupBy=""] - The field to group the results by.
239
- * @returns {Promise&lt;Object|null>} - The fetched song data or null if not found.
240
- *
241
- * @example
242
- * fetchAllSongs('drumeo', {
243
- * page: 2,
244
- * limit: 20,
245
- * searchTerm: 'rock',
246
- * sort: 'published_on',
247
- * includedFields: ['difficulty', 'style'],
248
- * groupBy: 'artist'
249
- * })
250
- * .then(result => console.log(result))
251
- * .catch(error => console.error(error));
252
- */
253
- export async function fetchAllSongs(brand, {
254
- page = 1,
255
- limit = 10,
256
- searchTerm = "",
257
- sort = "-published_on",
258
- includedFields = [],
259
- groupBy = ""
260
- }) {
261
- return fetchAll(brand, 'song', {page, limit, searchTerm, sort, includedFields, groupBy});
262
- }
263
-
264
- /**
265
- * Fetch filter options for a specific brand.
266
- *
267
- * @param {string} brand - The brand for which to fetch filter options.
268
- * @returns {Promise&lt;Object|null>} - A promise that resolves to an object containing filter options or null if not found.
269
- *
270
- * @example
271
- * fetchSongFilterOptions('drumeo')
272
- * .then(options => console.log(options))
273
- * .catch(error => console.error(error));
274
- */
275
- export async function fetchSongFilterOptions(brand) {
276
- const query = `
277
- {
278
- "difficulty": [
279
- {"type": "Introductory", "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; difficulty_string == "Introductory"]._id)},
280
- {"type": "Beginner", "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; difficulty_string == "Beginner"]._id)},
281
- {"type": "Intermediate", "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; difficulty_string == "Intermediate"]._id)},
282
- {"type": "Advanced", "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; difficulty_string == "Advanced"]._id)},
283
- {"type": "Expert", "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; difficulty_string == "Expert"]._id)}
284
- ],
285
- "genre": *[_type == 'genre' &amp;&amp; 'song' in filter_types] {
286
- "type": name,
287
- "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; references(^._id)]._id)
288
- },
289
- "instrumentless": [
290
- {"type": "Full Song Only", "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; instrumentless == false]._id)},
291
- {"type": "Instrument Removed", "count": count(*[_type == 'song' &amp;&amp; brand == '${brand}' &amp;&amp; instrumentless == true]._id)}
292
- ]
293
- }`;
294
-
295
- return fetchSanity(query, true);
296
- }
297
-
298
- /**
299
- * Fetch the total count of songs for a specific brand.
300
- * @param {string} brand - The brand for which to fetch the song count.
301
- * @returns {Promise&lt;number|null>} - The total count of songs or null if an error occurs.
302
- */
303
- export async function fetchSongCount(brand) {
304
- const query = `count(*[_type == 'song' &amp;&amp; brand == "${brand}"])`;
305
- return fetchSanity(query, true, {processNeedAccess: false});
306
- }
307
-
308
- /**
309
- * Fetch the latest workouts for a specific brand, including completion status and progress.
310
- * This function retrieves up to five of the latest workout content for a given brand, sorted in descending order by their publication date.
311
- * It also includes completion status and progress percentage for each workout by fetching additional data about user progress.
312
- *
313
- * @param {string} brand - The brand for which to fetch workouts (e.g., 'drumeo', 'pianote').
314
- * @returns {Promise&lt;Array&lt;Object>|null>} - A promise that resolves to an array of workout data objects with additional properties for completion status and progress percentage,
315
- * or null if no workouts are found.
316
- *
317
- * @example
318
- * fetchWorkouts('drumeo')
319
- * .then(workouts => console.log(workouts))
320
- * .catch(error => console.error(error));
321
- */
322
- export async function fetchWorkouts(brand) {
323
- const fields = getFieldsForContentType('workout');
324
- const query = `*[_type == 'workout' &amp;&amp; brand == '${brand}'] [0...5] {
325
- ${fields.toString()}
326
- } | order(published_on desc)[0...5]`
327
- return fetchSanity(query, true);
328
- }
329
-
330
233
  /**
331
234
  * Fetch the latest new releases for a specific brand.
332
235
  * @param {string} brand - The brand for which to fetch new releases.
@@ -338,12 +241,12 @@ export async function fetchNewReleases(brand, {page = 1, limit = 20, sort = "-pu
338
241
  const start = (page - 1) * limit;
339
242
  const end = start + limit;
340
243
  const sortOrder = getSortOrder(sort, brand);
341
- const filter = `_type in ${typesString} &amp;&amp; brand == '${brand}'`;
244
+ const filter = `_type in ${typesString} &amp;&amp; brand == '${brand}' &amp;&amp; show_in_new_feed == true`;
342
245
  const fields = `
343
246
  "id": railcontent_id,
344
247
  title,
345
248
  "image": thumbnail.asset->url,
346
- "artist_name": instructor[0]->name,
249
+ ${artistOrInstructorName()},
347
250
  "artists": instructor[]->name,
348
251
  difficulty,
349
252
  difficulty_string,
@@ -353,7 +256,7 @@ export async function fetchNewReleases(brand, {page = 1, limit = 20, sort = "-pu
353
256
  web_url_path,
354
257
  "permission_id": permission[]->railcontent_id,
355
258
  `;
356
- const filterParams = {};
259
+ const filterParams = {allowsPullSongsContent: false};
357
260
  const query = await buildQuery(
358
261
  filter,
359
262
  filterParams,
@@ -391,7 +294,7 @@ export async function fetchUpcomingEvents(brand, {page = 1, limit = 10} = {}) {
391
294
  "id": railcontent_id,
392
295
  title,
393
296
  "image": thumbnail.asset->url,
394
- "artist_name": instructor[0]->name,
297
+ ${artistOrInstructorName()},
395
298
  "artists": instructor[]->name,
396
299
  difficulty,
397
300
  difficulty_string,
@@ -439,7 +342,7 @@ export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
439
342
  "id": railcontent_id,
440
343
  title,
441
344
  "image": thumbnail.asset->url,
442
- "artist_name": instructor[0]->name,
345
+ ${artistOrInstructorName()},
443
346
  "artists": instructor[]->name,
444
347
  difficulty,
445
348
  difficulty_string,
@@ -465,10 +368,29 @@ export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
465
368
  * .catch(error => console.error(error));
466
369
  */
467
370
  export async function fetchByRailContentId(id, contentType) {
371
+ const fields = getFieldsForContentType(contentType);
372
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
373
+ const entityFieldsString = ` ${fields}
374
+ 'child_count': coalesce(count(child[${childrenFilter}]->), 0) ,
375
+ "lessons": child[${childrenFilter}]->{
376
+ "id": railcontent_id,
377
+ title,
378
+ "image": thumbnail.asset->url,
379
+ "instructors": instructor[]->name,
380
+ length_in_seconds,
381
+ },
382
+ 'length_in_seconds': coalesce(
383
+ math::sum(
384
+ select(
385
+ child[${childrenFilter}]->length_in_seconds
386
+ )
387
+ ),
388
+ length_in_seconds
389
+ ),`;
468
390
 
469
391
  const query = buildRawQuery(
470
392
  `railcontent_id == ${id} &amp;&amp; _type == '${contentType}'`,
471
- getFieldsForContentType(contentType),
393
+ entityFieldsString,
472
394
  {
473
395
  isSingle: true,
474
396
  },
@@ -495,7 +417,20 @@ export async function fetchByRailContentIds(ids, contentType = undefined) {
495
417
  const query = `*[railcontent_id in [${idsString}]]{
496
418
  ${getFieldsForContentType(contentType)}
497
419
  }`
498
- return fetchSanity(query, true);
420
+ const results = await fetchSanity(query, true);
421
+
422
+ const sortFuction = function compare(a,b){
423
+ const indexA = ids.indexOf(a['id']);
424
+ const indexB = ids.indexOf(b['id'])
425
+ if(indexA === indexB) return 0;
426
+ if(indexA > indexB) return 1;
427
+ return -1;
428
+ }
429
+
430
+ // Sort results to match the order of the input IDs
431
+ const sortedResults = results.sort(sortFuction);
432
+
433
+ return sortedResults;
499
434
  }
500
435
 
501
436
  /**
@@ -557,7 +492,7 @@ export async function fetchAll(brand, type, {
557
492
  if (customResults) {
558
493
  return customResults;
559
494
  }
560
-
495
+ console.log('rox fetch all ');
561
496
  let config = contentTypeConfig[type] ?? {};
562
497
  let additionalFields = config?.fields ?? [];
563
498
  let isGroupByOneToOne = (groupBy ? config?.relationships?.[groupBy]?.isOneToOne : false) ?? false;
@@ -565,15 +500,18 @@ export async function fetchAll(brand, type, {
565
500
  const start = (page - 1) * limit;
566
501
  const end = start + limit;
567
502
  let bypassStatusAndPublishedValidation = (type == 'instructor' || groupBy == 'artist' || groupBy == 'genre' || groupBy == 'instructor');
568
-
503
+ let bypassPermissions = bypassStatusAndPublishedValidation;
569
504
  // Construct the type filter
570
505
  let typeFilter;
571
506
 
572
- if( type === 'archives' ) {
573
- typeFilter = `&amp;&amp; status == "archived"`
507
+ if (type === 'archives') {
508
+ typeFilter = `&amp;&amp; status == "archived"`;
509
+ bypassStatusAndPublishedValidation = true;
510
+ } else if(type === 'pack'){
511
+ typeFilter = `&amp;&amp; (_type == 'pack' || _type == 'semester-pack')`;
574
512
  } else {
575
513
  typeFilter = type ? `&amp;&amp; _type == '${type}'` : "";
576
- }
514
+ }
577
515
 
578
516
  // Construct the search filter
579
517
  const searchFilter = searchTerm
@@ -622,6 +560,8 @@ export async function fetchAll(brand, type, {
622
560
  `;
623
561
  filter = `_type == '${groupBy}' &amp;&amp; count(*[${lessonsFilterWithRestrictions}]._id) > 0`;
624
562
  } else if (groupBy !== "") {
563
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
564
+
625
565
  const webUrlPath = (groupBy == 'genre') ? '/genres' : '';
626
566
  const lessonsFilter = `brand == '${brand}' &amp;&amp; ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`;
627
567
  const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter();
@@ -635,17 +575,28 @@ export async function fetchAll(brand, type, {
635
575
  'all_lessons_count': count(*[${lessonsFilterWithRestrictions}]._id),
636
576
  'lessons': *[${lessonsFilterWithRestrictions}]{
637
577
  ${fieldsString},
578
+ 'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
638
579
  ${groupBy}
639
580
  }[0...20]`;
640
581
  filter = `_type == '${groupBy}' &amp;&amp; count(*[${lessonsFilterWithRestrictions}]._id) > 0`;
641
582
  } else {
642
583
  filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
643
- entityFieldsString = fieldsString;
584
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
585
+ entityFieldsString = ` ${fieldsString},
586
+ 'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
587
+ 'length_in_seconds': coalesce(
588
+ math::sum(
589
+ select(
590
+ child[${childrenFilter}]->length_in_seconds
591
+ )
592
+ ),
593
+ length_in_seconds
594
+ ),`;
644
595
  }
645
596
 
646
597
  const filterWithRestrictions = await new FilterBuilder(filter, {
647
598
  bypassStatuses: bypassStatusAndPublishedValidation,
648
- bypassPermissions: bypassStatusAndPublishedValidation,
599
+ bypassPermissions: bypassPermissions,
649
600
  bypassPublishedDateRestriction: bypassStatusAndPublishedValidation
650
601
  }).buildFilter();
651
602
  query = buildEntityAndTotalQuery(
@@ -656,6 +607,7 @@ export async function fetchAll(brand, type, {
656
607
  start: start,
657
608
  end: end,
658
609
  });
610
+
659
611
  return fetchSanity(query, true);
660
612
  }
661
613
 
@@ -881,11 +833,11 @@ export async function fetchAllFilterOptions(
881
833
  const constructCommonFilter = (excludeFilter) => {
882
834
  const filterWithoutOption = excludeFilter ? filtersToGroq(filters, excludeFilter) : includedFieldsFilter;
883
835
  const statusFilter = ' &amp;&amp; status == "published"';
884
- const includeStatusFilter = !isAdmin &amp;&amp; !['instructor','artist','genre'].includes(contentType);
836
+ const includeStatusFilter = !isAdmin &amp;&amp; !['instructor', 'artist', 'genre'].includes(contentType);
885
837
 
886
838
  return coachId
887
- ? `brand == '${brand}' &amp;&amp; status == "published" &amp;&amp; references(*[_type=='instructor' &amp;&amp; railcontent_id == ${coachId}]._id) ${filterWithoutOption || ''}`
888
- : `_type == '${contentType}' &amp;&amp; brand == "${brand}"${includeStatusFilter ? statusFilter : ''}${style &amp;&amp; excludeFilter !== "style" ? ` &amp;&amp; '${style}' in genre[]->name` : ''}${artist &amp;&amp; excludeFilter !== "artist" ? ` &amp;&amp; artist->name == '${artist}'` : ''} ${progressFilter} ${filterWithoutOption || ''}`;
839
+ ? `brand == '${brand}' &amp;&amp; status == "published" &amp;&amp; references(*[_type=='instructor' &amp;&amp; railcontent_id == ${coachId}]._id) ${filterWithoutOption || ''} ${term ? ` &amp;&amp; (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`
840
+ : `_type == '${contentType}' &amp;&amp; brand == "${brand}"${includeStatusFilter ? statusFilter : ''}${style &amp;&amp; excludeFilter !== "style" ? ` &amp;&amp; '${style}' in genre[]->name` : ''}${artist &amp;&amp; excludeFilter !== "artist" ? ` &amp;&amp; artist->name == '${artist}'` : ''} ${progressFilter} ${filterWithoutOption || ''} ${term ? ` &amp;&amp; (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`;
889
841
  };
890
842
 
891
843
  const metaData = processMetadata(brand, contentType, true);
@@ -911,54 +863,6 @@ export async function fetchAllFilterOptions(
911
863
  return includeTabs ? {...results, tabs, catalogName} : results;
912
864
  }
913
865
 
914
-
915
- /**
916
- * Fetch children content by Railcontent ID.
917
- * @param {string} railcontentId - The Railcontent ID of the parent content.
918
- * @param {string} [contentType] - The content type the IDs to add needed fields to the response.
919
- * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched children content data or [] if not found.
920
- */
921
- export async function fetchChildren(railcontentId, contentType) {
922
- const query = `*[railcontent_id == ${railcontentId}]{
923
- title,
924
-
925
- 'children': child[]->{
926
- ${getFieldsForContentType(contentType)}
927
- },
928
- }[0..1]`;
929
- let parent = await fetchSanity(query, false);
930
- return parent['children'] ?? [];
931
- }
932
-
933
- /**
934
- *
935
- * @param railcontentId - railcontent id of the child
936
- * @returns {Promise&lt;Array&lt;string>|null>} - The fetched parent content data or [] if not found
937
- */
938
- export async function fetchParentByRailContentId(railcontentId) {
939
- const query = `*[railcontent_id == ${railcontentId}]{
940
- 'parents': array::unique([
941
- ...(*[references(^._id)]{
942
- ${getFieldsForContentType()}
943
- })
944
- ])
945
- }[0...1]`;
946
- let child = await fetchSanity(query, false);
947
- return child['parents'][0] ?? [];
948
- }
949
-
950
- /**
951
- * Fetch the Methods (learning-paths) for a specific brand.
952
- * @param {string} brand - The brand for which to fetch methods.
953
- * @returns {Promise&lt;Object|null>} - The fetched methods data or null if not found.
954
- */
955
- export async function fetchMethods(brand) {
956
- const query = `*[_type == 'learning-path' &amp;&amp; brand == '${brand}'] {
957
- ${getFieldsForContentType()}
958
- } | order(published_on asc)`
959
- return fetchSanity(query, true);
960
- }
961
-
962
866
  /**
963
867
  * Fetch the Foundations 2019.
964
868
  * @param {string} slug - The slug of the method.
@@ -985,6 +889,8 @@ export async function fetchFoundation(slug) {
985
889
  * @returns {Promise&lt;Object|null>} - The fetched methods data or null if not found.
986
890
  */
987
891
  export async function fetchMethod(brand, slug) {
892
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
893
+
988
894
  const query = `*[_type == 'learning-path' &amp;&amp; brand == "${brand}" &amp;&amp; slug.current == "${slug}"] {
989
895
  "description": ${descriptionField},
990
896
  "instructors":instructor[]->name,
@@ -996,9 +902,15 @@ export async function fetchMethod(brand, slug) {
996
902
  title,
997
903
  video,
998
904
  length_in_seconds,
905
+ parent_content_data,
906
+ "breadcrumbs_data": parent_content_data[] {
907
+ "id": id,
908
+ "title": *[railcontent_id == ^.id][0].title,
909
+ "url": *[railcontent_id == ^.id][0].web_url_path
910
+ } | order(length(url)),
999
911
  "type": _type,
1000
912
  "permission_id": permission[]->railcontent_id,
1001
- "levels": child[]->
913
+ "levels": child[${childrenFilter}]->
1002
914
  {
1003
915
  "id": railcontent_id,
1004
916
  published_on,
@@ -1025,34 +937,30 @@ export async function fetchMethod(brand, slug) {
1025
937
  * @returns {Promise&lt;Object|null>} - The fetched next lesson data or null if not found.
1026
938
  */
1027
939
  export async function fetchMethodChildren(railcontentId) {
940
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
941
+
1028
942
  const query = `*[railcontent_id == ${railcontentId}]{
1029
- child_count,
943
+ "child_count":coalesce(count(child[${childrenFilter}]->), 0),
1030
944
  "id": railcontent_id,
1031
945
  "description": ${descriptionField},
1032
946
  "thumbnail_url": thumbnail.asset->url,
1033
947
  title,
1034
948
  xp,
1035
949
  total_xp,
1036
- 'children': child[]->{
950
+ parent_content_data,
951
+ "resources": ${resourcesField},
952
+ "breadcrumbs_data": parent_content_data[] {
953
+ "id": id,
954
+ "title": *[railcontent_id == ^.id][0].title,
955
+ "url": *[railcontent_id == ^.id][0].web_url_path
956
+ } | order(length(url)),
957
+ 'children': child[(${childrenFilter})]->{
1037
958
  ${getFieldsForContentType('method')}
1038
959
  },
1039
960
  }[0..1]`;
1040
961
  return fetchSanity(query, true);
1041
962
  }
1042
963
 
1043
- /**
1044
- * Fetch the next lesson for a specific method by Railcontent ID.
1045
- * @param {string} railcontentId - The Railcontent ID of the current lesson.
1046
- * @param {string} methodId - The RailcontentID of the method
1047
- * @returns {Promise&lt;Object|null>} - The fetched next lesson data or null if not found.
1048
- */
1049
- export async function fetchMethodNextLesson(railcontentId, methodId) {
1050
- const sortedChildren = await fetchMethodChildrenIds(methodId);
1051
- const index = sortedChildren.indexOf(railcontentId);
1052
- const childIndex = sortedChildren[index + 1];
1053
- return childIndex ? await fetchByRailContentId(childIndex) : null;
1054
- }
1055
-
1056
964
  /**
1057
965
  * Fetch the next lesson for a specific method by Railcontent ID.
1058
966
  * @param {string} railcontentId - The Railcontent ID of the current lesson.
@@ -1087,14 +995,16 @@ export async function fetchMethodPreviousNextLesson(railcontentId, methodId) {
1087
995
  * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched children data or null if not found.
1088
996
  */
1089
997
  export async function fetchMethodChildrenIds(railcontentId) {
998
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
999
+
1090
1000
  const query = `*[ railcontent_id == ${railcontentId}]{
1091
- 'children': child[]-> {
1001
+ 'children': child[${childrenFilter}]-> {
1092
1002
  'id': railcontent_id,
1093
1003
  'type' : _type,
1094
- 'children': child[]-> {
1004
+ 'children': child[${childrenFilter}]-> {
1095
1005
  'id': railcontent_id,
1096
1006
  'type' : _type,
1097
- 'children': child[]-> {
1007
+ 'children': child[${childrenFilter}]-> {
1098
1008
  'id': railcontent_id,
1099
1009
  'type' : _type,
1100
1010
  }
@@ -1162,7 +1072,7 @@ export async function fetchNextPreviousLesson(railcontentId) {
1162
1072
  */
1163
1073
  export async function jumpToContinueContent(railcontentId) {
1164
1074
  const nextContent = await fetchNextContentDataForParent(railcontentId);
1165
- if (!nextContent) {
1075
+ if (!nextContent || !nextContent.id) {
1166
1076
  return null;
1167
1077
  }
1168
1078
  let next = await fetchByRailContentId(nextContent.id, nextContent.type);
@@ -1187,13 +1097,13 @@ export async function fetchLessonContent(railContentId) {
1187
1097
  const fields = `title,
1188
1098
  published_on,
1189
1099
  "type":_type,
1190
- "resources": resource,
1100
+ "resources": ${resourcesField},
1191
1101
  difficulty,
1192
1102
  difficulty_string,
1193
1103
  brand,
1194
1104
  status,
1195
1105
  soundslice,
1196
- instrumentless,
1106
+ instrumentless,
1197
1107
  railcontent_id,
1198
1108
  "id":railcontent_id,
1199
1109
  slug, artist->,
@@ -1203,7 +1113,7 @@ export async function fetchLessonContent(railContentId) {
1203
1113
  "description": description[0].children[0].text,
1204
1114
  "chapters": chapter[]{
1205
1115
  chapter_description,
1206
- chapter_timecode,
1116
+ chapter_timecode,
1207
1117
  "chapter_thumbnail_url": chapter_thumbnail_url.asset->url
1208
1118
  },
1209
1119
  "instructors":instructor[]->name,
@@ -1224,7 +1134,13 @@ export async function fetchLessonContent(railContentId) {
1224
1134
  mp3_yes_drums_no_click_url,
1225
1135
  mp3_yes_drums_yes_click_url,
1226
1136
  "permission_id": permission[]->railcontent_id,
1227
- parent_content_data,
1137
+ "parent_content_data": parent_content_data[]{
1138
+ "id": id,
1139
+ "title": *[railcontent_id == ^.id][0].title,
1140
+ "web_url_path": *[railcontent_id == ^.id][0].web_url_path,
1141
+ "slug":*[railcontent_id == ^.id][0].slug,
1142
+ "type": *[railcontent_id == ^.id][0]._type,
1143
+ },
1228
1144
  sort,
1229
1145
  xp`;
1230
1146
  const query = await buildQuery(
@@ -1250,11 +1166,12 @@ export async function fetchRelatedLessons(railContentId, brand) {
1250
1166
  const filterSongSameArtist = await new FilterBuilder(`_type=="song" &amp;&amp; _type==^._type &amp;&amp; brand == "${brand}" &amp;&amp; references(^.artist->_id) &amp;&amp; railcontent_id !=${railContentId}`).buildFilter();
1251
1167
  const filterSongSameGenre = await new FilterBuilder(`_type=="song" &amp;&amp; _type==^._type &amp;&amp; brand == "${brand}" &amp;&amp; references(^.genre[]->_id) &amp;&amp; railcontent_id !=${railContentId}`).buildFilter();
1252
1168
  const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`).buildFilter();
1169
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1253
1170
 
1254
1171
  const query = `*[railcontent_id == ${railContentId} &amp;&amp; brand == "${brand}" &amp;&amp; (!defined(permission) || references(*[_type=='permission']._id))]{
1255
1172
  _type, parent_type, railcontent_id,
1256
1173
  "related_lessons" : array::unique([
1257
- ...(*[${filterNeighbouringSiblings}][0].child[]->{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}),
1174
+ ...(*[${filterNeighbouringSiblings}][0].child[${childrenFilter}]->{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}),
1258
1175
  ...(*[${filterSongSameArtist}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}|order(published_on desc, title asc)[0...10]),
1259
1176
  ...(*[${filterSongSameGenre}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}|order(published_on desc, title asc)[0...10]),
1260
1177
  ...(*[${filterSameTypeAndSortOrder}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, sort}|order(sort asc, title asc)[0...10]),
@@ -1264,30 +1181,6 @@ export async function fetchRelatedLessons(railContentId, brand) {
1264
1181
  return fetchSanity(query, false);
1265
1182
  }
1266
1183
 
1267
- /**
1268
- * Fetch related method lessons for a specific lesson by RailContent ID and type.
1269
- * @param {string} railContentId - The RailContent ID of the current lesson.
1270
- * @param {string} brand - The current brand.
1271
- * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched related lessons
1272
- */
1273
- export async function fetchRelatedMethodLessons(railContentId, brand) {
1274
- const query = `*[railcontent_id == ${railContentId} &amp;&amp; brand == "${brand}"]{
1275
- "id":_id,
1276
- "related_lessons": *[references(^._id)][0].child[]->{
1277
- "id": railcontent_id,
1278
- "type": _type,
1279
- title,
1280
- "description": description[0].children[0].text, // Extraer texto plano
1281
- "thumbnail_url": thumbnail.asset->url,
1282
- "url": web_url_path,
1283
- difficulty,
1284
- difficulty_string,
1285
- }
1286
- }
1287
- }`
1288
- return fetchSanity(query, false);
1289
- }
1290
-
1291
1184
  /**
1292
1185
  * Fetch all packs.
1293
1186
  * @param {string} brand - The brand for which to fetch packs.
@@ -1299,7 +1192,7 @@ export async function fetchRelatedMethodLessons(railContentId, brand) {
1299
1192
  */
1300
1193
  export async function fetchAllPacks(brand, sort = "-published_on", searchTerm = "", page = 1, limit = 10) {
1301
1194
  const sortOrder = getSortOrder(sort, brand);
1302
- const filter = `_type == 'pack' &amp;&amp; brand == '${brand}' &amp;&amp; title match "${searchTerm}*"`
1195
+ const filter = `(_type == 'pack' || _type == 'semester-pack') &amp;&amp; brand == '${brand}' &amp;&amp; title match "${searchTerm}*"`
1303
1196
  const filterParams = {};
1304
1197
  const fields = getFieldsForContentType('pack');
1305
1198
  const start = (page - 1) * limit;
@@ -1324,8 +1217,8 @@ export async function fetchAllPacks(brand, sort = "-published_on", searchTerm =
1324
1217
  * @param {string} railcontentId - The Railcontent ID of the pack.
1325
1218
  * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched pack content data or null if not found.
1326
1219
  */
1327
- export async function fetchPackAll(railcontentId) {
1328
- return fetchByRailContentId(railcontentId, 'pack');
1220
+ export async function fetchPackAll(railcontentId, type = 'pack') {
1221
+ return fetchByRailContentId(railcontentId, type);
1329
1222
  }
1330
1223
 
1331
1224
  export async function fetchLiveEvent(brand) {
@@ -1348,17 +1241,20 @@ export async function fetchLiveEvent(brand) {
1348
1241
  default:
1349
1242
  break;
1350
1243
  }
1351
- let dateTemp = new Date();
1352
- dateTemp.setDate(dateTemp.getDate() - 1);
1244
+ let startDateTemp = new Date();
1245
+ let endDateTemp = new Date();
1246
+ startDateTemp= new Date (startDateTemp.setMinutes(startDateTemp.getMinutes() + 15));
1247
+ endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - 15));
1353
1248
 
1354
1249
  // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
1355
1250
  // this has some +- on times
1356
1251
  // But this query just finds the first scheduled event (sorted by start_time) that ends after now()
1357
- const query = `*[status == 'scheduled' &amp;&amp; defined(live_event_start_time) &amp;&amp; published_on > '${getSanityDate(dateTemp, false)}' &amp;&amp; live_event_end_time >= '${getSanityDate(new Date(), false)}']{
1252
+ const query = `*[status == 'scheduled' &amp;&amp; brand == '${brand}' &amp;&amp; defined(live_event_start_time) &amp;&amp; live_event_start_time &lt;= '${getSanityDate(startDateTemp, false)}' &amp;&amp; live_event_end_time >= '${getSanityDate(endDateTemp, false)}']{
1358
1253
  'slug': slug.current,
1359
1254
  'id': railcontent_id,
1360
1255
  live_event_start_time,
1361
1256
  live_event_end_time,
1257
+ live_event_youtube_id,
1362
1258
  railcontent_id,
1363
1259
  published_on,
1364
1260
  'event_coach_url' : instructor[0]->web_url_path,
@@ -1371,21 +1267,7 @@ export async function fetchLiveEvent(brand) {
1371
1267
  },
1372
1268
  'videoId': coalesce(live_event_youtube_id, video.external_id),
1373
1269
  } | order(live_event_start_time)[0...1]`;
1374
- return await fetchSanity(query, false);
1375
- }
1376
-
1377
- /**
1378
- * Fetch all children of a specific pack by Railcontent ID.
1379
- * @param {string} railcontentId - The Railcontent ID of the pack.
1380
- * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched pack children data or null if not found.
1381
- *
1382
- * @example
1383
- * fetchPackChildren('pack123')
1384
- * .then(children => console.log(children))
1385
- * .catch(error => console.error(error));
1386
- */
1387
- export async function fetchPackChildren(railcontentId) {
1388
- return fetchChildren(railcontentId, 'pack-children');
1270
+ return await fetchSanity(query, false, {processNeedAccess: false});
1389
1271
  }
1390
1272
 
1391
1273
  /**
@@ -1405,24 +1287,6 @@ export async function fetchPackData(id) {
1405
1287
  return fetchSanity(query, false);
1406
1288
  }
1407
1289
 
1408
- /**
1409
- * Fetch the data needed for the Challenge Overview screen.
1410
- * @param {string} id - The Railcontent ID of the course
1411
- * @returns {Promise&lt;Object|null>} - The challenge information and lessons or null if not found.
1412
- *
1413
- * @example
1414
- * fetchChallengeOverview('challenge123')
1415
- * .then(challenge => console.log(challenge))
1416
- * .catch(error => console.error(error));
1417
- */
1418
- export async function fetchChallengeOverview(id) {
1419
- // WIP
1420
- const query = `*[railcontent_id == ${id}]{
1421
- ${getFieldsForContentType("challenge")}
1422
- } [0...1]`;
1423
- return fetchSanity(query, false);
1424
- }
1425
-
1426
1290
  /**
1427
1291
  * Fetch the data needed for the coach screen.
1428
1292
  * @param {string} brand - The brand for which to fetch coach lessons
@@ -1455,10 +1319,11 @@ export async function fetchCoachLessons(brand, id, {
1455
1319
  ? filtersToGroq(includedFields)
1456
1320
  : "";
1457
1321
  const filter = `brand == '${brand}' ${searchFilter} ${includedFieldsFilter} &amp;&amp; references(*[_type=='instructor' &amp;&amp; railcontent_id == ${id}]._id)`;
1322
+ const filterWithRestrictions = await new FilterBuilder(filter).buildFilter();
1458
1323
 
1459
1324
  sortOrder = getSortOrder(sortOrder, brand);
1460
1325
  const query = buildEntityAndTotalQuery(
1461
- filter,
1326
+ filterWithRestrictions,
1462
1327
  fieldsString,
1463
1328
  {
1464
1329
  sortOrder: sortOrder,
@@ -1469,20 +1334,6 @@ export async function fetchCoachLessons(brand, id, {
1469
1334
  return fetchSanity(query, true);
1470
1335
  }
1471
1336
 
1472
- /**
1473
- * Fetch the data needed for the Course Overview screen.
1474
- * @param {string} id - The Railcontent ID of the course
1475
- * @returns {Promise&lt;Object|null>} - The course information and lessons or null if not found.
1476
- *
1477
- * @example
1478
- * fetchCourseOverview('course123')
1479
- * .then(course => console.log(course))
1480
- * .catch(error => console.error(error));
1481
- */
1482
- export async function fetchCourseOverview(id) {
1483
- return fetchByRailContentId(id, 'course');
1484
- }
1485
-
1486
1337
  /**
1487
1338
  * Fetch the data needed for the Course Overview screen.
1488
1339
  * @param {string} id - The Railcontent ID of the course
@@ -1585,13 +1436,13 @@ export async function fetchArtistLessons(brand, name, contentType, {
1585
1436
 
1586
1437
  // limits the results to supplied progressIds for started &amp; completed filters
1587
1438
  const progressFilter = progressIds !== undefined ? `&amp;&amp; railcontent_id in [${progressIds.join(',')}]` : "";
1588
-
1439
+ const now = getSanityDate(new Date());
1589
1440
  const query = `{
1590
1441
  "entity":
1591
1442
  *[_type == 'artist' &amp;&amp; name == '${name}']
1592
1443
  {'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
1593
1444
  'lessons_count': count(*[${addType} brand == '${brand}' &amp;&amp; references(^._id)]),
1594
- 'lessons': *[${addType} brand == '${brand}' &amp;&amp; references(^._id) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
1445
+ 'lessons': *[${addType} brand == '${brand}' &amp;&amp; references(^._id) &amp;&amp; (status in ['published'] || (status == 'scheduled' &amp;&amp; defined(published_on) &amp;&amp; published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
1595
1446
  [${start}...${end}]}
1596
1447
  |order(${sortOrder})
1597
1448
  }`;
@@ -1635,13 +1486,13 @@ export async function fetchGenreLessons(brand, name, contentType, {
1635
1486
  : "";
1636
1487
  // limits the results to supplied progressIds for started &amp; completed filters
1637
1488
  const progressFilter = progressIds !== undefined ? `&amp;&amp; railcontent_id in [${progressIds.join(',')}]` : "";
1638
-
1489
+ const now = getSanityDate(new Date());
1639
1490
  const query = `{
1640
1491
  "entity":
1641
1492
  *[_type == 'genre' &amp;&amp; name == '${name}']
1642
1493
  {'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
1643
1494
  'lessons_count': count(*[${addType} brand == '${brand}' &amp;&amp; references(^._id)]),
1644
- 'lessons': *[${addType} brand == '${brand}' &amp;&amp; references(^._id) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
1495
+ 'lessons': *[${addType} brand == '${brand}' &amp;&amp; references(^._id) &amp;&amp; (status in ['published'] || (status == 'scheduled' &amp;&amp; defined(published_on) &amp;&amp; published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
1645
1496
  [${start}...${end}]}
1646
1497
  |order(${sortOrder})
1647
1498
  }`;
@@ -1649,15 +1500,17 @@ export async function fetchGenreLessons(brand, name, contentType, {
1649
1500
  }
1650
1501
 
1651
1502
  export async function fetchTopLevelParentId(railcontentId) {
1503
+ const statusFilter = "&amp;&amp; status in ['scheduled', 'published', 'archived', 'unlisted']";
1504
+
1652
1505
  const query = `*[railcontent_id == ${railcontentId}]{
1653
1506
  railcontent_id,
1654
- 'parents': *[^._id in child[]._ref &amp;&amp; !(_id in path('drafts.**'))]{
1507
+ 'parents': *[^._id in child[]._ref ${statusFilter}]{
1655
1508
  railcontent_id,
1656
- 'parents': *[^._id in child[]._ref &amp;&amp; !(_id in path('drafts.**'))]{
1509
+ 'parents': *[^._id in child[]._ref ${statusFilter}]{
1657
1510
  railcontent_id,
1658
- 'parents': *[^._id in child[]._ref &amp;&amp; !(_id in path('drafts.**'))]{
1511
+ 'parents': *[^._id in child[]._ref ${statusFilter}]{
1659
1512
  railcontent_id,
1660
- 'parents': *[^._id in child[]._ref &amp;&amp; !(_id in path('drafts.**'))]{
1513
+ 'parents': *[^._id in child[]._ref ${statusFilter}]{
1661
1514
  railcontent_id,
1662
1515
  }
1663
1516
  }
@@ -1679,19 +1532,20 @@ export async function fetchTopLevelParentId(railcontentId) {
1679
1532
 
1680
1533
  export async function fetchHierarchy(railcontentId) {
1681
1534
  let topLevelId = await fetchTopLevelParentId(railcontentId);
1535
+ const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1682
1536
  const query = `*[railcontent_id == ${topLevelId}]{
1683
1537
  railcontent_id,
1684
1538
  'assignments': assignment[]{railcontent_id},
1685
- 'children': child[]->{
1539
+ 'children': child[${childrenFilter}]->{
1686
1540
  railcontent_id,
1687
1541
  'assignments': assignment[]{railcontent_id},
1688
- 'children': child[]->{
1542
+ 'children': child[${childrenFilter}]->{
1689
1543
  railcontent_id,
1690
1544
  'assignments': assignment[]{railcontent_id},
1691
- 'children': child[]->{
1545
+ 'children': child[${childrenFilter}]->{
1692
1546
  railcontent_id,
1693
1547
  'assignments': assignment[]{railcontent_id},
1694
- 'children': child[]->{
1548
+ 'children': child[${childrenFilter}]->{
1695
1549
  railcontent_id,
1696
1550
  }
1697
1551
  }
@@ -1701,6 +1555,7 @@ export async function fetchHierarchy(railcontentId) {
1701
1555
  let response = await fetchSanity(query, false, {processNeedAccess: false});
1702
1556
  if (!response) return null;
1703
1557
  let data = {
1558
+ topLevelId: topLevelId,
1704
1559
  parents: {},
1705
1560
  children: {}
1706
1561
  };
@@ -1734,26 +1589,6 @@ function populateHierarchyLookups(currentLevel, data, parentId) {
1734
1589
 
1735
1590
  }
1736
1591
 
1737
- /**
1738
- * Fetch assignments for content
1739
- *
1740
- * @param {integer} contentId - List of ids get data for
1741
- * @returns {Promise&lt;array|null>} - A promise that resolves to an array containing the data
1742
- */
1743
- export async function fetchAssignments(contentId) {
1744
- const fields = `"id": railcontent_id,"assignments":assignment[]{"id": railcontent_id}`;
1745
- const query = await buildQuery(`railcontent_id == ${contentId}`,
1746
- {bypassPermissions: true},
1747
- fields,
1748
- {end: 100});
1749
- let data = await fetchSanity(query, false);
1750
- let mapped = [];
1751
- data.assignments.forEach(function (content) {
1752
- mapped.push(content.id);
1753
- });
1754
- return mapped;
1755
- }
1756
-
1757
1592
  /**
1758
1593
  * Fetch data for comment mod page
1759
1594
  *
@@ -1813,17 +1648,23 @@ export async function fetchSanity(query,
1813
1648
  console.log("fetchSanity Query:", query);
1814
1649
  }
1815
1650
  const perspective = globalConfig.sanityConfig.perspective ?? 'published';
1816
- const encodedQuery = encodeURIComponent(query);
1817
1651
  const api = globalConfig.sanityConfig.useCachedAPI ? 'apicdn' : 'api';
1818
- const url = `https://${globalConfig.sanityConfig.projectId}.${api}.sanity.io/v${globalConfig.sanityConfig.version}/data/query/${globalConfig.sanityConfig.dataset}?perspective=${perspective}&amp;query=${encodedQuery}`;
1652
+ const url = `https://${globalConfig.sanityConfig.projectId}.${api}.sanity.io/v${globalConfig.sanityConfig.version}/data/query/${globalConfig.sanityConfig.dataset}?perspective=${perspective}`;
1819
1653
  const headers = {
1820
1654
  'Authorization': `Bearer ${globalConfig.sanityConfig.token}`,
1821
1655
  'Content-Type': 'application/json'
1822
1656
  };
1823
1657
 
1824
1658
  try {
1659
+ const method = 'post';
1660
+ const options = {
1661
+ method,
1662
+ headers,
1663
+ body: JSON.stringify({'query': query})
1664
+ };
1665
+
1825
1666
  let promisesResult = await Promise.all([
1826
- fetch(url, {headers}),
1667
+ fetch(url, options),
1827
1668
  processNeedAccess ? fetchUserPermissions() : null
1828
1669
  ]);
1829
1670
  const response = promisesResult[0];
@@ -1897,32 +1738,6 @@ function doesUserNeedAccessToContent(result, userPermissions, isAdmin) {
1897
1738
  return true;
1898
1739
  }
1899
1740
 
1900
- /**
1901
- * Fetch CatalogueMetadata from Sanity. This information may be duplicated in the contentTypeConfig.js.
1902
- * It's an ongoing discussion (Aug 2024), but it's been included here if necessary
1903
- *
1904
- * @param {string} contentType - name of the contentype to pull
1905
- * @returns {Promise&lt;Object|null>} - A promise that resolves to the fetched data or null if an error occurs or no results are found.
1906
- *
1907
- * @example
1908
- *
1909
- * fetchCatalogMetadata('song')
1910
- * .then(data => console.log(data))
1911
- * .catch(error => console.error(error));
1912
- */
1913
- export async function fetchCatalogMetadata(contentType) {
1914
- const query = `*[_type == 'CatalogMetadata']{
1915
- catalog_type,
1916
- brand,
1917
- groq_results,
1918
- groq_search_fields,
1919
- meta_data_groq,
1920
- modal_text,
1921
- sort_by,
1922
- }`
1923
- return fetchSanity(query, false, {processNeedAccess: false});
1924
- }
1925
-
1926
1741
  /**
1927
1742
  * Fetch shows data for a brand.
1928
1743
  *
@@ -1965,6 +1780,17 @@ export async function fetchMetadata(brand, type) {
1965
1780
  return processedData ? processedData : {};
1966
1781
  }
1967
1782
 
1783
+ export async function fetchChatAndLiveEnvent(brand, forcedId = null) {
1784
+ const liveEvent = (forcedId !== null) ? await fetchByRailContentIds([forcedId]): [await fetchLiveEvent(brand)];
1785
+ if (liveEvent.length === 0 || (liveEvent.length === 1 &amp;&amp; liveEvent[0] === undefined)) {
1786
+ return null;
1787
+ }
1788
+ let url = `/content/live-chat?brand=${brand}`;
1789
+ const chatData = await fetchHandler(url);
1790
+ const mergedData = { ...chatData, ...liveEvent[0] };
1791
+ return mergedData;
1792
+ }
1793
+
1968
1794
 
1969
1795
  //Helper Functions
1970
1796
  function arrayJoinWithQuotes(array, delimiter = ',') {
@@ -2080,6 +1906,7 @@ function getFilterOptions(option, commonFilter, contentType, brand) {
2080
1906
  case "difficulty":
2081
1907
  filterGroq = `
2082
1908
  "difficulty": [
1909
+ {"type": "All", "count": count(*[${commonFilter} &amp;&amp; difficulty_string == "All"])},
2083
1910
  {"type": "Introductory", "count": count(*[${commonFilter} &amp;&amp; difficulty_string == "Introductory"])},
2084
1911
  {"type": "Beginner", "count": count(*[${commonFilter} &amp;&amp; difficulty_string == "Beginner"])},
2085
1912
  {"type": "Intermediate", "count": count(*[${commonFilter} &amp;&amp; difficulty_string == "Intermediate" ])},
@@ -2088,7 +1915,9 @@ function getFilterOptions(option, commonFilter, contentType, brand) {
2088
1915
  ][count > 0],`;
2089
1916
  break;
2090
1917
  case "type":
2091
- const typesString = types.map(t => {return `{"type": "${t}"}`}).join(', ');
1918
+ const typesString = types.map(t => {
1919
+ return `{"type": "${t}"}`
1920
+ }).join(', ');
2092
1921
  filterGroq = `"type": [${typesString}]{type, 'count': count(*[_type == ^.type &amp;&amp; ${commonFilter}])}[count > 0],`;
2093
1922
  break;
2094
1923
  case "genre":
@@ -2178,7 +2007,7 @@ function cleanUpGroq(query) {
2178
2007
  <br class="clear">
2179
2008
 
2180
2009
  <footer>
2181
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Fri Dec 06 2024 14:20:14 GMT+0000 (Coordinated Universal Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
2010
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Mon Jan 20 2025 08:04:00 GMT+0000 (Coordinated Universal Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
2182
2011
  </footer>
2183
2012
 
2184
2013
  <script>prettyPrint();</script>