musora-content-services 2.95.5 → 2.96.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
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.1](https://github.com/railroadmedia/musora-content-services/compare/v2.96.0...v2.96.1) (2025-12-09)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **agi:** interface return types ([cc0cfae](https://github.com/railroadmedia/musora-content-services/commit/cc0cfae1282266567ac46a6eb072f76ffde7032e))
11
+
12
+ ## [2.96.0](https://github.com/railroadmedia/musora-content-services/compare/v2.95.5...v2.96.0) (2025-12-09)
13
+
14
+
15
+ ### Features
16
+
17
+ * handle learning path complete logic ([#628](https://github.com/railroadmedia/musora-content-services/issues/628)) ([e5266d2](https://github.com/railroadmedia/musora-content-services/commit/e5266d2219597fc295dab97cec7b7ce8ac80a705))
18
+
5
19
  ### [2.95.5](https://github.com/railroadmedia/musora-content-services/compare/v2.95.4...v2.95.5) (2025-12-08)
6
20
 
7
21
  ### [2.95.4](https://github.com/railroadmedia/musora-content-services/compare/v2.95.3...v2.95.4) (2025-12-08)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.95.5",
3
+ "version": "2.96.1",
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
@@ -52,6 +52,7 @@ import {
52
52
  getEnrichedLearningPath,
53
53
  getLearningPathLessonsByIds,
54
54
  mapContentToParent,
55
+ onContentCompletedLearningPathListener,
55
56
  resetAllLearningPaths,
56
57
  startLearningPath,
57
58
  updateDailySession
@@ -193,7 +194,9 @@ import {
193
194
  } from './services/liveTesting.ts';
194
195
 
195
196
  import {
197
+ emitContentCompleted,
196
198
  emitProgressSaved,
199
+ onContentCompleted,
197
200
  onProgressSaved
198
201
  } from './services/progress-events.js';
199
202
 
@@ -423,7 +426,7 @@ import {
423
426
  } from './services/userActivity.js';
424
427
 
425
428
  import {
426
- default as EventsAPI
429
+ default as EventsAPI
427
430
  } from './services/eventsAPI';
428
431
 
429
432
  declare module 'musora-content-services' {
@@ -468,6 +471,7 @@ declare module 'musora-content-services' {
468
471
  deleteUserActivity,
469
472
  duplicatePlaylist,
470
473
  editComment,
474
+ emitContentCompleted,
471
475
  emitProgressSaved,
472
476
  enrollUserInGuidedCourse,
473
477
  extractSanityUrl,
@@ -651,6 +655,8 @@ declare module 'musora-content-services' {
651
655
  markNotificationAsUnread,
652
656
  markThreadAsRead,
653
657
  numberOfActiveUsers,
658
+ onContentCompleted,
659
+ onContentCompletedLearningPathListener,
654
660
  onProgressSaved,
655
661
  openComment,
656
662
  otherStats,
package/src/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /*** This file was generated automatically. To recreate, please run `npm run build-index`. ***/
2
2
 
3
3
  import {
4
- default as EventsAPI
4
+ default as EventsAPI
5
5
  } from './services/eventsAPI';
6
6
 
7
7
  import {
@@ -56,6 +56,7 @@ import {
56
56
  getEnrichedLearningPath,
57
57
  getLearningPathLessonsByIds,
58
58
  mapContentToParent,
59
+ onContentCompletedLearningPathListener,
59
60
  resetAllLearningPaths,
60
61
  startLearningPath,
61
62
  updateDailySession
@@ -197,7 +198,9 @@ import {
197
198
  } from './services/liveTesting.ts';
198
199
 
199
200
  import {
201
+ emitContentCompleted,
200
202
  emitProgressSaved,
203
+ onContentCompleted,
201
204
  onProgressSaved
202
205
  } from './services/progress-events.js';
203
206
 
@@ -467,6 +470,7 @@ export {
467
470
  deleteUserActivity,
468
471
  duplicatePlaylist,
469
472
  editComment,
473
+ emitContentCompleted,
470
474
  emitProgressSaved,
471
475
  enrollUserInGuidedCourse,
472
476
  extractSanityUrl,
@@ -650,6 +654,8 @@ export {
650
654
  markNotificationAsUnread,
651
655
  markThreadAsRead,
652
656
  numberOfActiveUsers,
657
+ onContentCompleted,
658
+ onContentCompletedLearningPathListener,
653
659
  onProgressSaved,
654
660
  openComment,
655
661
  otherStats,
@@ -15,6 +15,11 @@ export interface Artist {
15
15
  lessonCount: number
16
16
  }
17
17
 
18
+ export interface Artists {
19
+ data: Artist[]
20
+ total: number
21
+ }
22
+
18
23
  /**
19
24
  * Fetch all artists with lessons available for a specific brand.
20
25
  *
@@ -29,7 +34,7 @@ export interface Artist {
29
34
  export async function fetchArtists(
30
35
  brand: Brands | string,
31
36
  options: BuildQueryOptions = { sort: 'lower(name) asc' }
32
- ): Promise<Artist[] | null> {
37
+ ): Promise<Artists> {
33
38
  const lessonFilter = await new FilterBuilder(`brand == "${brand}" && references(^._id)`, {
34
39
  bypassPermissions: true,
35
40
  }).buildFilter()
@@ -15,6 +15,11 @@ export interface Genre {
15
15
  thumbnail: string
16
16
  }
17
17
 
18
+ export interface Genres {
19
+ data: Genre[]
20
+ total: number
21
+ }
22
+
18
23
  /**
19
24
  * Fetch all genres with lessons available for a specific brand.
20
25
  *
@@ -29,7 +34,7 @@ export interface Genre {
29
34
  export async function fetchGenres(
30
35
  brand: Brands | string,
31
36
  options: BuildQueryOptions = { sort: 'lower(name) asc' }
32
- ): Promise<Genre[]> {
37
+ ): Promise<Genres> {
33
38
  const lessonFilter = await new FilterBuilder(`brand == "${brand}" && references(^._id)`, {
34
39
  bypassPermissions: true,
35
40
  }).buildFilter()
@@ -35,7 +35,7 @@ export interface Instructors {
35
35
  export async function fetchInstructors(
36
36
  brand: Brands | string,
37
37
  options: BuildQueryOptions
38
- ): Promise<Instructor[]> {
38
+ ): Promise<Instructors> {
39
39
  const lessonFilter = await new FilterBuilder(`brand == "${brand}" && references(^._id)`, {
40
40
  bypassPermissions: true,
41
41
  }).buildFilter()
@@ -12,31 +12,31 @@ import {
12
12
  getAllCompletedByIds,
13
13
  getProgressState,
14
14
  } from '../contentProgress.js'
15
- import { COLLECTION_TYPE, STATE } from "../sync/models/ContentProgress";
16
- import { SyncWriteDTO } from "../sync";
17
- import { ContentProgress } from "../sync/models";
15
+ import { COLLECTION_TYPE, STATE } from '../sync/models/ContentProgress'
16
+ import { SyncWriteDTO } from '../sync'
17
+ import { ContentProgress } from '../sync/models'
18
18
 
19
19
  const BASE_PATH: string = `/api/content-org`
20
20
  const LEARNING_PATHS_PATH = `${BASE_PATH}/v1/user/learning-paths`
21
21
 
22
22
  interface ActiveLearningPathResponse {
23
- user_id: number,
24
- brand: string,
25
- active_learning_path_id: number,
23
+ user_id: number
24
+ brand: string
25
+ active_learning_path_id: number
26
26
  }
27
27
 
28
28
  interface DailySessionResponse {
29
- user_id: number,
30
- brand: string,
29
+ user_id: number
30
+ brand: string
31
31
  user_date: string
32
- daily_session: DailySession[],
33
- active_learning_path_id: number,
34
- active_learning_path_created_at: string,
32
+ daily_session: DailySession[]
33
+ active_learning_path_id: number
34
+ active_learning_path_created_at: string
35
35
  }
36
36
 
37
37
  interface DailySession {
38
- content_ids: number[],
39
- learning_path_id: number,
38
+ content_ids: number[]
39
+ learning_path_id: number
40
40
  }
41
41
 
42
42
  /**
@@ -47,7 +47,7 @@ interface DailySession {
47
47
  export async function getDailySession(brand: string, userDate: Date) {
48
48
  const stringDate = userDate.toISOString().split('T')[0]
49
49
  const url: string = `${LEARNING_PATHS_PATH}/daily-session/get?brand=${brand}&userDate=${stringDate}`
50
- return await fetchHandler(url, 'GET', null, null) as DailySessionResponse
50
+ return (await fetchHandler(url, 'GET', null, null)) as DailySessionResponse
51
51
  }
52
52
 
53
53
  /**
@@ -64,7 +64,7 @@ export async function updateDailySession(
64
64
  const stringDate = userDate.toISOString().split('T')[0]
65
65
  const url: string = `${LEARNING_PATHS_PATH}/daily-session/create`
66
66
  const body = { brand: brand, userDate: stringDate, keepFirstLearningPath: keepFirstLearningPath }
67
- return await fetchHandler(url, 'POST', null, body) as DailySessionResponse
67
+ return (await fetchHandler(url, 'POST', null, body)) as DailySessionResponse
68
68
  }
69
69
 
70
70
  /**
@@ -73,7 +73,7 @@ export async function updateDailySession(
73
73
  */
74
74
  export async function getActivePath(brand: string) {
75
75
  const url: string = `${LEARNING_PATHS_PATH}/active-path/get?brand=${brand}`
76
- return await fetchHandler(url, 'GET', null, null) as ActiveLearningPathResponse
76
+ return (await fetchHandler(url, 'GET', null, null)) as ActiveLearningPathResponse
77
77
  }
78
78
 
79
79
  /**
@@ -84,7 +84,7 @@ export async function getActivePath(brand: string) {
84
84
  export async function startLearningPath(brand: string, learningPathId: number) {
85
85
  const url: string = `${LEARNING_PATHS_PATH}/active-path/set`
86
86
  const body = { brand: brand, learning_path_id: learningPathId }
87
- return await fetchHandler(url, 'POST', null, body) as ActiveLearningPathResponse
87
+ return (await fetchHandler(url, 'POST', null, body)) as ActiveLearningPathResponse
88
88
  }
89
89
 
90
90
  /**
@@ -227,8 +227,6 @@ export async function fetchLearningPathLessons(
227
227
  }))
228
228
  }
229
229
 
230
-
231
-
232
230
  return {
233
231
  ...learningPath,
234
232
  is_active_learning_path: isActiveLearningPath,
@@ -250,14 +248,16 @@ export async function fetchLearningPathLessons(
250
248
  * @param {number[]} contentIds The array of content IDs within the learning path
251
249
  * @returns {Promise<number[]>} Array with completed content IDs
252
250
  */
253
- export async function fetchLearningPathProgressCheckLessons(contentIds: number[]): Promise<number[]> {
251
+ export async function fetchLearningPathProgressCheckLessons(
252
+ contentIds: number[]
253
+ ): Promise<number[]> {
254
254
  let query = await getAllCompletedByIds(contentIds)
255
- let completedProgress = query.data.map(progress => progress.content_id)
256
- return contentIds.filter(contentId => completedProgress.includes(contentId))
255
+ let completedProgress = query.data.map((progress) => progress.content_id)
256
+ return contentIds.filter((contentId) => completedProgress.includes(contentId))
257
257
  }
258
258
 
259
259
  interface completeMethodIntroVideo {
260
- intro_video_response: SyncWriteDTO<ContentProgress, any> | null,
260
+ intro_video_response: SyncWriteDTO<ContentProgress, any> | null
261
261
  active_path_response: ActiveLearningPathResponse
262
262
  }
263
263
  /**
@@ -268,7 +268,10 @@ interface completeMethodIntroVideo {
268
268
  * @returns {Promise<Object|null>} response.intro_video_response - The intro video completion response or null if already completed.
269
269
  * @returns {Promise<Object>} response.active_path_response - The set active learning path response.
270
270
  */
271
- export async function completeMethodIntroVideo(introVideoId: number, brand: string): Promise<completeMethodIntroVideo> {
271
+ export async function completeMethodIntroVideo(
272
+ introVideoId: number,
273
+ brand: string
274
+ ): Promise<completeMethodIntroVideo> {
272
275
  let response = {} as completeMethodIntroVideo
273
276
 
274
277
  response.intro_video_response = await completeIfNotCompleted(introVideoId)
@@ -278,13 +281,12 @@ export async function completeMethodIntroVideo(introVideoId: number, brand: stri
278
281
 
279
282
  response.active_path_response = await startLearningPath(brand, learningPathId)
280
283
 
281
-
282
284
  return response
283
285
  }
284
286
 
285
287
  interface completeLearningPathIntroVideo {
286
- intro_video_response: SyncWriteDTO<ContentProgress, any> | null,
287
- learning_path_reset_response: SyncWriteDTO<ContentProgress, any> | null,
288
+ intro_video_response: SyncWriteDTO<ContentProgress, any> | null
289
+ learning_path_reset_response: SyncWriteDTO<ContentProgress, any> | null
288
290
  lesson_import_response: SyncWriteDTO<ContentProgress, any> | null
289
291
  }
290
292
  /**
@@ -297,7 +299,11 @@ interface completeLearningPathIntroVideo {
297
299
  * @returns {Promise<void>} response.learning_path_reset_response - The reset learning path response.
298
300
  * @returns {Promise<Object[]>} response.lesson_import_response - The responses for completing each content_id within the learning path.
299
301
  */
300
- export async function completeLearningPathIntroVideo(introVideoId: number, learningPathId: number, lessonsToImport: number[] | null) {
302
+ export async function completeLearningPathIntroVideo(
303
+ introVideoId: number,
304
+ learningPathId: number,
305
+ lessonsToImport: number[] | null
306
+ ) {
301
307
  let response = {} as completeLearningPathIntroVideo
302
308
 
303
309
  response.intro_video_response = await completeIfNotCompleted(introVideoId)
@@ -306,7 +312,6 @@ export async function completeLearningPathIntroVideo(introVideoId: number, learn
306
312
 
307
313
  if (!lessonsToImport) {
308
314
  response.learning_path_reset_response = await contentStatusReset(learningPathId, collection)
309
-
310
315
  } else {
311
316
  response.lesson_import_response = await contentsStatusCompleted(lessonsToImport, collection)
312
317
  }
@@ -314,9 +319,39 @@ export async function completeLearningPathIntroVideo(introVideoId: number, learn
314
319
  return response
315
320
  }
316
321
 
317
-
318
- async function completeIfNotCompleted(contentId: number): Promise<SyncWriteDTO<ContentProgress, any> | null> {
322
+ async function completeIfNotCompleted(
323
+ contentId: number
324
+ ): Promise<SyncWriteDTO<ContentProgress, any> | null> {
319
325
  const introVideoStatus = await getProgressState(contentId)
320
326
 
321
327
  return introVideoStatus !== 'completed' ? await contentStatusCompleted(contentId) : null
322
328
  }
329
+
330
+ export async function onContentCompletedLearningPathListener(event) {
331
+ console.log('if')
332
+ if (event?.collection?.type !== 'learning-path-v2') return
333
+ if (event.contentId !== event?.collection?.id) return
334
+ const learningPathId = event.contentId
335
+ const learningPath = await getEnrichedLearningPath(learningPathId)
336
+ console.log('LP', learningPath)
337
+ const brand = learningPath.brand
338
+ const activeLearningPath = await getActivePath(brand)
339
+ console.log('Active LP', activeLearningPath)
340
+ if (activeLearningPath.active_learning_path_id !== learningPathId) return
341
+ const method = await fetchMethodV2Structure(brand)
342
+ console.log('Method', method)
343
+ const currentIndex = method.learning_paths.findIndex((lp) => lp.id === learningPathId)
344
+ if (currentIndex === -1) {
345
+ return
346
+ }
347
+ const nextLearningPath = method.learning_paths[currentIndex + 1]
348
+ console.log('Next LP', nextLearningPath)
349
+ if (!nextLearningPath) {
350
+ return
351
+ }
352
+
353
+ await startLearningPath(brand, nextLearningPath.id)
354
+ const nextLearningPathData = await getEnrichedLearningPath(nextLearningPath.id)
355
+ console.log('Next LP Data', nextLearningPathData)
356
+ await contentStatusReset(nextLearningPathData.intro_video.id)
357
+ }