musora-content-services 2.9.6 → 2.11.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.
Files changed (122) hide show
  1. package/.coderabbit.yaml +0 -0
  2. package/.editorconfig +0 -0
  3. package/.github/pull_request_template.md +0 -0
  4. package/.github/workflows/conventional-commits.yaml +16 -0
  5. package/.github/workflows/node.js.yml +0 -0
  6. package/.prettierignore +0 -0
  7. package/.prettierrc +0 -0
  8. package/.yarnrc.yml +1 -0
  9. package/CHANGELOG.md +30 -0
  10. package/README.md +0 -0
  11. package/babel.config.cjs +0 -0
  12. package/docs/Content-Organization.html +0 -0
  13. package/docs/UserManagement.html +0 -0
  14. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  15. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  16. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  17. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  18. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  19. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  20. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  21. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  22. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  23. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  24. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  25. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  26. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  27. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  28. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  32. package/docs/global.html#User +0 -0
  33. package/docs/module-Notifications.html +0 -0
  34. package/docs/module-Session-Management.html +0 -0
  35. package/docs/module-User-Activity.html +0 -0
  36. package/docs/module-User-Management.html +0 -0
  37. package/docs/module-User-Permissions.html +0 -0
  38. package/docs/scripts/collapse.js +0 -0
  39. package/docs/scripts/commonNav.js +0 -0
  40. package/docs/scripts/linenumber.js +0 -0
  41. package/docs/scripts/nav.js +0 -0
  42. package/docs/scripts/polyfill.js +0 -0
  43. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  44. package/docs/scripts/prettify/lang-css.js +0 -0
  45. package/docs/scripts/prettify/prettify.js +0 -0
  46. package/docs/scripts/search.js +0 -0
  47. package/docs/styles/jsdoc.css +0 -0
  48. package/docs/styles/prettify.css +0 -0
  49. package/docs/types.js.html +0 -0
  50. package/docs/user_user-management.js.html +0 -0
  51. package/jest.config.js +0 -0
  52. package/jsdoc.json +0 -0
  53. package/link_mcs.sh +0 -0
  54. package/package.json +1 -1
  55. package/src/contentMetaData.js +0 -0
  56. package/src/contentTypeConfig.js +1 -1
  57. package/src/filterBuilder.js +0 -0
  58. package/src/infrastructure/http/HttpClient.ts +0 -0
  59. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  60. package/src/infrastructure/http/index.ts +0 -0
  61. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  62. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  63. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  64. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  65. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  66. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  67. package/src/lib/httpHelper.js +0 -0
  68. package/src/lib/lastUpdated.js +0 -0
  69. package/src/services/api/types.js +0 -0
  70. package/src/services/config.js +0 -0
  71. package/src/services/content-org/content-org.js +0 -0
  72. package/src/services/content-org/playlists-types.js +0 -0
  73. package/src/services/content-org/playlists.js +0 -0
  74. package/src/services/content.js +0 -0
  75. package/src/services/contentAggregator.js +0 -0
  76. package/src/services/contentLikes.js +0 -0
  77. package/src/services/contentProgress.js +2 -0
  78. package/src/services/dataContext.js +0 -0
  79. package/src/services/dateUtils.js +0 -0
  80. package/src/services/forum.js +0 -0
  81. package/src/services/gamification/awards.js +0 -0
  82. package/src/services/gamification/gamification.js +0 -0
  83. package/src/services/gamification/types.js +0 -0
  84. package/src/services/imageSRCBuilder.js +2 -0
  85. package/src/services/imageSRCVerify.js +0 -0
  86. package/src/services/railcontent.js +4 -1
  87. package/src/services/recommendations.js +0 -0
  88. package/src/services/sanity.js +8 -6
  89. package/src/services/types.js +0 -0
  90. package/src/services/user/chat.js +0 -0
  91. package/src/services/user/interests.js +0 -0
  92. package/src/services/user/management.js +0 -0
  93. package/src/services/user/permissions.js +0 -0
  94. package/src/services/user/profile.js +0 -0
  95. package/src/services/user/sessions.js +0 -0
  96. package/src/services/user/types.js +0 -0
  97. package/src/services/user/user-management-system.js +0 -0
  98. package/src/services/userActivity.js +24 -14
  99. package/test/HttpClient.test.js +0 -0
  100. package/test/content.test.js +0 -0
  101. package/test/contentLikes.test.js +0 -0
  102. package/test/contentProgress.test.js +0 -0
  103. package/test/dataContext.test.js +0 -0
  104. package/test/forum.test.js +0 -0
  105. package/test/imageSRCBuilder.test.js +0 -0
  106. package/test/imageSRCVerify.test.js +0 -0
  107. package/test/initializeTests.js +0 -0
  108. package/test/lib/lastUpdated.test.js +0 -0
  109. package/test/live/contentProgressLive.test.js +0 -0
  110. package/test/live/railcontentLive.test.js +0 -0
  111. package/test/localStorageMock.js +0 -0
  112. package/test/log.js +0 -0
  113. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  114. package/test/mockData/mockData_progress_content.json +0 -0
  115. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  116. package/test/mockData/mockData_user_practices.json +0 -0
  117. package/test/progressRows.test.js +0 -0
  118. package/test/sanityQueryService.test.js +0 -0
  119. package/test/streakMessage.test.js +0 -0
  120. package/test/user/permissions.test.js +0 -0
  121. package/test/userActivity.test.js +0 -0
  122. package/tools/generate-index.cjs +0 -0
package/.coderabbit.yaml CHANGED
File without changes
package/.editorconfig CHANGED
File without changes
File without changes
@@ -0,0 +1,16 @@
1
+ name: PR Conventional Commit Validation
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened, edited]
6
+
7
+ jobs:
8
+ validate-pr-title:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: PR Conventional Commit Validation
12
+ uses: ytanikin/pr-conventional-commits@1.4.1
13
+ with:
14
+ task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]'
15
+ custom_labels: '{"feat": "feature", "fix": "fix", "docs": "documentation", "test": "test", "ci": "CI/CD", "refactor": "refactor", "perf": "performance", "chore": "chore", "revert": "revert", "wip": "WIP"}'
16
+ add_scope_label: 'false'
File without changes
package/.prettierignore CHANGED
File without changes
package/.prettierrc CHANGED
File without changes
package/.yarnrc.yml ADDED
@@ -0,0 +1 @@
1
+ nodeLinker: node-modules
package/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
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.11.0](https://github.com/railroadmedia/musora-content-services/compare/v2.10.0...v2.11.0) (2025-06-24)
6
+
7
+
8
+ ### Features
9
+
10
+ * **MU2-698:** change extra minutes from 15 to 30 ([ccb1a96](https://github.com/railroadmedia/musora-content-services/commit/ccb1a9682c0d75a61743645434fb1ae5398ca509))
11
+ * **MU2-712:** Add live_event_start_time and live_event_end_time on fetchUpcomingEvents ([0de3f8e](https://github.com/railroadmedia/musora-content-services/commit/0de3f8ea68a2a8d29d122f6adf2d893669d3bd25))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * Duplicate practice per day in cache ([d44e0f6](https://github.com/railroadmedia/musora-content-services/commit/d44e0f6a384e78b5d515bcafc99cba30b24a535d))
17
+ * **MU2-693:** Fix cache(data versioning) on user practices ([ddc0d3b](https://github.com/railroadmedia/musora-content-services/commit/ddc0d3b3bd180c0f51241112ce7ba2094ff71c8c))
18
+ * User practice cache generation after one practice is restored ([dfb9e27](https://github.com/railroadmedia/musora-content-services/commit/dfb9e271e9efcb18b1d866935e0eb4acb7317b4b))
19
+
20
+ ## [2.10.0](https://github.com/railroadmedia/musora-content-services/compare/v2.9.5...v2.10.0) (2025-06-23)
21
+
22
+
23
+ ### Features
24
+
25
+ * add media type id ([2690e2c](https://github.com/railroadmedia/musora-content-services/commit/2690e2ccbe07474e51327bd6e5919da7fb96ad47))
26
+ * Any content type should be displayed in “Live Streams” Section if marked as livestream ([285dde9](https://github.com/railroadmedia/musora-content-services/commit/285dde92e74ee606410ff1e1b6981ec81d3e524c))
27
+ * MU2-712 Any content type should be displayed in “Live Streams” Section ([3229950](https://github.com/railroadmedia/musora-content-services/commit/3229950fdeea36e67fcdf8b352d0c3d53219821f))
28
+
29
+
30
+ ### Bug Fixes
31
+
32
+ * **BEH-547:** collection modules updates ([#300](https://github.com/railroadmedia/musora-content-services/issues/300)) ([d07002b](https://github.com/railroadmedia/musora-content-services/commit/d07002b32946d3d03dc9b8f3469863a34d2696b5))
33
+ * null check for buildImageSRC ([f841b46](https://github.com/railroadmedia/musora-content-services/commit/f841b460bd41e0405caf7f2c06dd0c057a5f9aa1))
34
+
5
35
  ### [2.9.6](https://github.com/railroadmedia/musora-content-services/compare/v2.9.5...v2.9.6) (2025-06-18)
6
36
 
7
37
 
package/README.md CHANGED
File without changes
package/babel.config.cjs CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/jest.config.js CHANGED
File without changes
package/jsdoc.json CHANGED
File without changes
package/link_mcs.sh CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.9.6",
3
+ "version": "2.11.0",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
@@ -416,7 +416,7 @@ export let contentTypeConfig = {
416
416
  '"lesson_count": coalesce(count(child[]->.child[]->), 0)',
417
417
  'xp',
418
418
  `"description": ${descriptionField}`,
419
- '"instructors": instructor[]->name',
419
+ '"instructors": instructor[]->{ "id": railcontent_id, name, "thumbnail_url": thumbnail_url.asset->url }',
420
420
  '"logo_image_url": logo_image_url.asset->url',
421
421
  'total_xp',
422
422
  `"children": child[]->{
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -326,6 +326,8 @@ function getMediaTypeId(mediaType, mediaCategory) {
326
326
  return 3
327
327
  case 'practice_play-alongs':
328
328
  return 4
329
+ case 'video_soundslice':
330
+ return 6
329
331
  default:
330
332
  return 5
331
333
  }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -43,6 +43,8 @@ export function buildImageSRC(url, options = {}) {
43
43
  // Process Sanity URL first if applicable
44
44
  if (url.includes('cdn.sanity.io')) {
45
45
  url = applySanityTransformations(url, options)
46
+ } else if (url.includes('imagedelivery.net')) {
47
+ return url
46
48
  }
47
49
 
48
50
  // Then apply Cloudflare transformations
File without changes
@@ -755,7 +755,7 @@ export async function reportComment(commentId, issue) {
755
755
  return await postDataHandler(url, data)
756
756
  }
757
757
 
758
- export async function fetchUserPractices({ currentVersion, userId } = {}) {
758
+ export async function fetchUserPractices(currentVersion = 0, { userId } = {}) {
759
759
  const params = new URLSearchParams();
760
760
  if (userId) params.append('user_id', userId);
761
761
  const query = params.toString() ? `?${params.toString()}` : '';
@@ -763,6 +763,9 @@ export async function fetchUserPractices({ currentVersion, userId } = {}) {
763
763
  const response = await fetchDataHandler(url, currentVersion);
764
764
  const { data, version } = response;
765
765
  const userPractices = data;
766
+ if(!userPractices ) {
767
+ return { data: { practices: {} }, version };
768
+ }
766
769
 
767
770
  const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
768
771
 
File without changes
@@ -388,8 +388,6 @@ export async function fetchNewReleases(
388
388
  * .catch(error => console.error(error));
389
389
  */
390
390
  export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {}) {
391
- const liveTypes = getUpcomingEventsTypes(brand)
392
- const typesString = arrayToStringRepresentation(liveTypes)
393
391
  const now = getSanityDate(new Date())
394
392
  const start = (page - 1) * limit
395
393
  const end = start + limit
@@ -407,9 +405,11 @@ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {})
407
405
  "type": _type,
408
406
  web_url_path,
409
407
  "permission_id": permission[]->railcontent_id,
408
+ live_event_start_time,
409
+ live_event_end_time,
410
410
  "isLive": live_event_start_time <= '${now}' && live_event_end_time >= '${now}'`
411
411
  const query = buildRawQuery(
412
- `_type in ${typesString} && brand == '${brand}' && published_on > '${now}' && status == 'scheduled'`,
412
+ `defined(live_event_start_time) && (!defined(live_event_end_time) || live_event_end_time >= '${now}' ) && brand == '${brand}' && published_on > '${now}' && status == 'scheduled'`,
413
413
  fields,
414
414
  {
415
415
  sortOrder: 'published_on asc',
@@ -443,7 +443,7 @@ export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
443
443
  const now = getSanityDate(new Date())
444
444
  const start = (page - 1) * limit
445
445
  const end = start + limit
446
- const query = `*[_type in [${typesString}] && brand == '${brand}' && status in ['published','scheduled'] && published_on > '${now}']{
446
+ const query = `*[_type in [${typesString}] && brand == '${brand}' && status in ['published','scheduled'] && (!defined(live_event_end_time) || live_event_end_time < '${now}' ) && published_on > '${now}']{
447
447
  "id": railcontent_id,
448
448
  title,
449
449
  "image": thumbnail.asset->url,
@@ -1504,6 +1504,7 @@ export async function fetchPackAll(railcontentId, type = 'pack') {
1504
1504
  }
1505
1505
 
1506
1506
  export async function fetchLiveEvent(brand, forcedContentId = null) {
1507
+ const LIVE_EXTRA_MINUTES = 30;
1507
1508
  //calendarIDs taken from addevent.php
1508
1509
  // TODO import instructor calendars to Sanity
1509
1510
  let defaultCalendarID = ''
@@ -1525,8 +1526,9 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1525
1526
  }
1526
1527
  let startDateTemp = new Date()
1527
1528
  let endDateTemp = new Date()
1528
- startDateTemp = new Date(startDateTemp.setMinutes(startDateTemp.getMinutes() + 15))
1529
- endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - 15))
1529
+
1530
+ startDateTemp = new Date(startDateTemp.setMinutes(startDateTemp.getMinutes() + LIVE_EXTRA_MINUTES))
1531
+ endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - LIVE_EXTRA_MINUTES))
1530
1532
 
1531
1533
  // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
1532
1534
  // this has some +- on times
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -55,7 +55,7 @@ function getIndefiniteArticle(streak) {
55
55
 
56
56
  export async function getUserPractices(userId = globalConfig.sessionConfig.userId) {
57
57
  if (userId !== globalConfig.sessionConfig.userId) {
58
- let data = await fetchUserPractices({ userId })
58
+ let data = await fetchUserPractices(0, { userId: userId })
59
59
  return data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
60
60
  } else {
61
61
  let data = await userActivityContext.getData()
@@ -376,20 +376,30 @@ export async function removeUserPractice(id) {
376
376
  export async function restoreUserPractice(id) {
377
377
  let url = `/api/user/practices/v1/practices/restore${buildQueryString([id])}`
378
378
  const response = await fetchHandler(url, 'put')
379
- if (response?.data) {
380
- await userActivityContext.updateLocal(async function (localContext) {
381
- const restoredPractice = response.data
382
- const { date } = restoredPractice
383
- if (!localContext.data[DATA_KEY_PRACTICES][date]) {
384
- localContext.data[DATA_KEY_PRACTICES][date] = []
385
- }
386
- localContext.data[DATA_KEY_PRACTICES][date].push({
387
- id: restoredPractice.id,
388
- duration_seconds: restoredPractice.duration_seconds,
379
+ if (response?.data?.length) {
380
+ const restoredPractice = response.data.find((p) => p.id === id)
381
+ if (restoredPractice) {
382
+ await userActivityContext.updateLocal(async function (localContext) {
383
+ const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
384
+ const utcDate = new Date(restoredPractice.day)
385
+ const localDate = convertToTimeZone(utcDate, userTimeZone)
386
+ const date =
387
+ localDate.getFullYear() + '-' +
388
+ String(localDate.getMonth() + 1).padStart(2, '0') + '-' +
389
+ String(localDate.getDate()).padStart(2, '0')
390
+ if (localContext.data[DATA_KEY_PRACTICES][date]) {
391
+ localContext.data[DATA_KEY_PRACTICES][date] = []
392
+ }
393
+ response.data.forEach((restoredPractice) => {
394
+ localContext.data[DATA_KEY_PRACTICES][date].push({
395
+ id: restoredPractice.id,
396
+ duration_seconds: restoredPractice.duration_seconds,
397
+ })
398
+ })
389
399
  })
390
- })
400
+ }
391
401
  }
392
- const formattedMeta = await formatPracticeMeta(response.data)
402
+ const formattedMeta = await formatPracticeMeta(response.data || [])
393
403
  const practiceDuration = formattedMeta.reduce(
394
404
  (total, practice) => total + (practice.duration || 0),
395
405
  0
@@ -603,7 +613,7 @@ function getStreaksAndMessage(practices) {
603
613
  async function getUserPracticeIds(day = new Date().toISOString().split('T')[0], userId = null) {
604
614
  let practices = {}
605
615
  if (userId !== globalConfig.sessionConfig.userId) {
606
- let data = await fetchUserPractices({ userId })
616
+ let data = await fetchUserPractices(0, { userId: userId })
607
617
  practices = data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
608
618
  } else {
609
619
  let data = await userActivityContext.getData()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/test/log.js CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes