musora-content-services 2.184.1 → 2.185.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.
@@ -0,0 +1,35 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(rg:*)",
5
+ "Bash(npm run lint:*)",
6
+ "Bash(ls:*)",
7
+ "Bash(npm test *)",
8
+ "Bash(npx jest *)"
9
+ ],
10
+ "deny": []
11
+ },
12
+ "enabledMcpjsonServers": [
13
+ "1password",
14
+ "atlassian",
15
+ "figma",
16
+ "google-workspace",
17
+ "snowflake",
18
+ "aws",
19
+ "hex",
20
+ "sanity",
21
+ "mysql",
22
+ "slack",
23
+ "langfuse",
24
+ "chrome-devtools",
25
+ "railway",
26
+ "github",
27
+ "asana",
28
+ "nightwatch",
29
+ "mysql-staging",
30
+ "mysql-local",
31
+ "mobile",
32
+ "sentry",
33
+ "customerio"
34
+ ]
35
+ }
Binary file
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [2.185.0](https://github.com/railroadmedia/musora-content-services/compare/v2.184.1...v2.185.0) (2026-10-01)
6
+
7
+
8
+ ### Features
9
+
10
+ * **BEHLTP-380:** preroll LP intros ([#1066](https://github.com/railroadmedia/musora-content-services/issues/1066)) ([4706a1f](https://github.com/railroadmedia/musora-content-services/commit/4706a1f19d15ddfd409a85d2d8ebb1f70e134a99))
11
+
5
12
  ### [2.184.1](https://github.com/railroadmedia/musora-content-services/compare/v2.184.0...v2.184.1) (2026-09-28)
6
13
 
7
14
 
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.184.1",
3
+ "version": "2.185.0",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -549,6 +549,7 @@ export let contentTypeConfig = {
549
549
  'learning-path-v2': {
550
550
  fields: [
551
551
  `"intro_video": intro_video->{ ${getIntroVideoFields('learning-path-v2').join(', ')} }`,
552
+ `"preroll": intro_video->{ ${getPrerollFields().join(', ')} }`,
552
553
  'total_skills',
553
554
  `"resource": ${resourcesField}`,
554
555
  ],
@@ -693,6 +694,7 @@ export let contentTypeConfig = {
693
694
  "thumbnail": thumbnail.asset->url,
694
695
  length_in_seconds,
695
696
  intro_video,
697
+ "preroll": intro_video,
696
698
  child[]->{
697
699
  ${DEFAULT_FIELDS.join(',')}
698
700
  }
@@ -728,6 +730,20 @@ export function getIntroVideoFields(type) {
728
730
  return fields
729
731
  }
730
732
 
733
+ export function getPrerollFields() {
734
+ return [
735
+ `"id": railcontent_id`,
736
+ 'title',
737
+ 'brand',
738
+ `"instructor": ${instructorField}`,
739
+ `"type": _type`,
740
+ `"description": ${descriptionField}`,
741
+ `"thumbnail": thumbnail.asset->url`,
742
+ 'length_in_seconds',
743
+ 'video',
744
+ ]
745
+ }
746
+
731
747
  export const plusMembershipPermissions = 92
732
748
 
733
749
  /**
package/src/index.d.ts CHANGED
@@ -251,6 +251,11 @@ import {
251
251
  storeQuizAnswers
252
252
  } from './services/my-path/placement-quiz.ts';
253
253
 
254
+ import {
255
+ completeMyPathIntroVideo,
256
+ completePreroll
257
+ } from './services/my-path/preroll.ts';
258
+
254
259
  import {
255
260
  deleteAllNotifications,
256
261
  deleteNotification,
@@ -584,6 +589,8 @@ declare module 'musora-content-services' {
584
589
  closeComment,
585
590
  completeLearningPathIntroVideo,
586
591
  completeMethodIntroVideo,
592
+ completeMyPathIntroVideo,
593
+ completePreroll,
587
594
  confirmEmailChange,
588
595
  contentStatusCompleted,
589
596
  contentStatusCompletedMany,
package/src/index.js CHANGED
@@ -255,6 +255,11 @@ import {
255
255
  storeQuizAnswers
256
256
  } from './services/my-path/placement-quiz.ts';
257
257
 
258
+ import {
259
+ completeMyPathIntroVideo,
260
+ completePreroll
261
+ } from './services/my-path/preroll.ts';
262
+
258
263
  import {
259
264
  deleteAllNotifications,
260
265
  deleteNotification,
@@ -583,6 +588,8 @@ export {
583
588
  closeComment,
584
589
  completeLearningPathIntroVideo,
585
590
  completeMethodIntroVideo,
591
+ completeMyPathIntroVideo,
592
+ completePreroll,
586
593
  confirmEmailChange,
587
594
  contentStatusCompleted,
588
595
  contentStatusCompletedMany,
@@ -127,12 +127,12 @@ export async function addContextToContent(dataPromise, ...dataArgs) {
127
127
  * Key behaviors:
128
128
  * 1. Enriches all learning paths in a method structure
129
129
  * 2. Auto-sets collection for learning-path-v2 items when no collection specified
130
- * 3. Enriches intro videos when dataField_includeIntroVideo is true
130
+ * 3. Enriches LP preroll when dataField_includePreroll is true
131
131
  *
132
132
  * @param dataPromise - promise or method that provides sanity data
133
133
  * @param dataArgs - Arguments to pass to the dataPromise
134
134
  * @param options - Same as addContextToContent, plus:
135
- * @param options.dataField_includeIntroVideo - If true, adds progress to intro_video field where it exists
135
+ * @param options.dataField_includePreroll - If true, adds progress to preroll (and intro_video for FF) field where it exists
136
136
  *
137
137
  * @returns {Promise<Object | false>} - Enriched data or false if no data found
138
138
  *
@@ -140,7 +140,7 @@ export async function addContextToContent(dataPromise, ...dataArgs) {
140
140
  * // Enrich method structure with all learning paths
141
141
  * const method = await addContextToMethodContent(fetchMethodV2Structure, brand, {
142
142
  * dataField: 'learningPaths',
143
- * dataField_includeIntroVideo: true,
143
+ * dataField_includePreroll: true,
144
144
  * addProgressStatus: true,
145
145
  * addProgressPercentage: true,
146
146
  * })
@@ -151,7 +151,7 @@ export async function addContextToContent(dataPromise, ...dataArgs) {
151
151
  * collection: { id: lpId, type: 'learning-path-v2' },
152
152
  * dataField: 'children',
153
153
  * dataField_includeParent: true,
154
- * dataField_includeIntroVideo: true,
154
+ * dataField_includePreroll: true,
155
155
  * addProgressStatus: true,
156
156
  * })
157
157
  */
@@ -163,7 +163,7 @@ export async function addContextToLearningPaths(dataPromise, ...dataArgs) {
163
163
  const {
164
164
  dataField = null,
165
165
  dataField_includeParent = false,
166
- dataField_includeIntroVideo = false,
166
+ dataField_includePreroll = false,
167
167
  addProgressPercentage = false,
168
168
  addProgressStatus = false,
169
169
  addProgressTimestamp = false,
@@ -175,8 +175,9 @@ export async function addContextToLearningPaths(dataPromise, ...dataArgs) {
175
175
  } = options
176
176
 
177
177
  let dataFields = dataField ? [dataField] : []
178
- if (dataField_includeIntroVideo) {
179
- dataFields.push('intro_video')
178
+ if (dataField_includePreroll) {
179
+ dataFields.push('preroll')
180
+ dataFields.push('intro_video') // todo: remove intro_video things when My Path feature flag is removed.
180
181
  }
181
182
 
182
183
  const dataParam = lastArg === options ? dataArgs.slice(0, -1) : dataArgs
@@ -187,7 +188,7 @@ export async function addContextToLearningPaths(dataPromise, ...dataArgs) {
187
188
  if (!data) return false
188
189
 
189
190
  let items, recordIds
190
- [data, items, recordIds] = addRecordIdsToData(data, dataField, isDataAnArray, dataField_includeParent, dataField_includeIntroVideo) ?? []
191
+ [data, items, recordIds] = addRecordIdsToData(data, dataField, isDataAnArray, dataField_includeParent, dataField_includePreroll) ?? []
191
192
  if (items.length === 0) return data
192
193
 
193
194
  const ids = recordIds.map(item => extractFromRecordId(item).contentId)
@@ -304,7 +305,7 @@ function extractItemsFromData(data, dataField, isParentArray, includeParent) {
304
305
  return items
305
306
  }
306
307
 
307
- function addRecordIdsToData(data, dataField, isDataAnArray, includeParent, includeIntroVideo) {
308
+ function addRecordIdsToData(data, dataField, isDataAnArray, includeParent, includePreroll) {
308
309
  let items = []
309
310
  let recordIds = []
310
311
 
@@ -317,10 +318,11 @@ function addRecordIdsToData(data, dataField, isDataAnArray, includeParent, inclu
317
318
  items.push(content)
318
319
  recordIds.push(content.record_id)
319
320
  }
320
- if (includeIntroVideo && content.intro_video?.id) {
321
- content.intro_video.record_id = generateRecordId(content.intro_video.id, null)
322
- items.push(content.intro_video)
323
- recordIds.push(content.intro_video.record_id)
321
+ if (includePreroll && content.preroll?.id) {
322
+ content.preroll.record_id = generateRecordId(content.preroll.id, null)
323
+ content.intro_video.record_id = generateRecordId(content.intro_video.id, null) // for My Path feature flag, maintaining Method functionality.
324
+ items.push(content.preroll)
325
+ recordIds.push(content.preroll.record_id)
324
326
  }
325
327
  if (dataField) {
326
328
  for (const child of content[dataField] ?? []) {
@@ -9,7 +9,7 @@ interface LearningPath {
9
9
  title: string
10
10
  difficulty?: number
11
11
  branch: Branch
12
- intro_video_id?: number // only if we have intro videos
12
+ preroll_id?: number
13
13
  published_on?: string
14
14
  // ... any other content-related fields we need
15
15
  }
@@ -18,7 +18,7 @@ import {
18
18
  contentStatusReset,
19
19
  getAllCompletedByIds,
20
20
  getIdsWhereLastAccessedFromMethod,
21
- getProgressState,
21
+ getProgressState,
22
22
  resetStatus,
23
23
  } from '../contentProgress.js'
24
24
  import { COLLECTION_ID_SELF, COLLECTION_TYPE, CollectionParameter, STATE } from '../sync/models/ContentProgress'
@@ -254,7 +254,7 @@ export async function getEnrichedLearningPath(learningPathId: number) {
254
254
  {
255
255
  dataField: 'children',
256
256
  dataField_includeParent: true,
257
- dataField_includeIntroVideo: true,
257
+ dataField_includePreroll: true,
258
258
  addProgressStatus: true,
259
259
  addProgressPercentage: true,
260
260
  addProgressTimestamp: true,
@@ -286,7 +286,7 @@ export async function getEnrichedLearningPaths(learningPathIds: number[]) {
286
286
  {
287
287
  dataField: 'children',
288
288
  dataField_includeParent: true,
289
- dataField_includeIntroVideo: true,
289
+ dataField_includePreroll: true,
290
290
  addProgressStatus: true,
291
291
  addProgressPercentage: true,
292
292
  addProgressTimestamp: true,
@@ -484,6 +484,7 @@ export async function fetchLearningPathLessons(
484
484
  *
485
485
  * @param {number[]} contentIds The array of content IDs within the learning path
486
486
  * @returns {Promise<number[]>} Array with completed content IDs
487
+ * @deprecated Only use for Method behaviour. Learning path progress import is removed in My Path. Kept for backwards compatibility for Method.
487
488
  */
488
489
  export async function fetchLearningPathProgressCheckLessons(
489
490
  contentIds: number[],
@@ -505,6 +506,7 @@ interface completeMethodIntroVideo {
505
506
  * @returns {Promise<Array>} response - The response object.
506
507
  * @returns {Promise<Object|null>} response.intro_video_response - The intro video completion response or null if already completed.
507
508
  * @returns {Promise<Object>} response.active_path_response - The set active learning path response.
509
+ * @deprecated Only use for Method behaviour. For My Path, call `completeMyPathIntroVideo` instead.
508
510
  */
509
511
  export async function completeMethodIntroVideo(
510
512
  introVideoId: number | null,
@@ -563,6 +565,7 @@ interface completeLearningPathIntroVideo {
563
565
  * @returns {Promise<void>} response.learning_path_reset_response - The reset learning path response.
564
566
  * @returns {Promise<Object[]>} response.lesson_import_response - The responses for completing each content_id within the learning path.
565
567
  * @returns {Promise<Object|null>} response.update_dailies_response - The updated daily session if it was changed.
568
+ * @deprecated Only use for Method behaviour. For My Path, use `completePreroll` from `my-path/preroll.ts`.
566
569
  */
567
570
  export async function completeLearningPathIntroVideo(
568
571
  introVideoId: number,
@@ -14,6 +14,7 @@ interface PlacementQuizAnswers {
14
14
  gear: string[]
15
15
  }
16
16
 
17
+ // todo(BEHLTP-494): return recommended_content too.
17
18
  interface PlacementQuizResponse {
18
19
  user_id: string
19
20
  brand: string
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @module MyPathPreroll
3
+ */
4
+ import { contentStatusCompleted, getProgressState } from '../contentProgress.js'
5
+ import { SyncWriteDTO } from '../sync'
6
+ import { ContentProgress } from '../sync/models'
7
+ import { STATE } from '../sync/models/ContentProgress'
8
+ import { fetchQuizAnswers } from './placement-quiz'
9
+
10
+ export async function completePreroll(
11
+ prerollId: number
12
+ ): Promise<SyncWriteDTO<ContentProgress, any> | null> {
13
+ return await completeIfNotCompleted(prerollId)
14
+ }
15
+
16
+ interface completeMyPathIntroVideo {
17
+ placement_quiz: any
18
+ recommended_content: any
19
+ }
20
+
21
+ export async function completeMyPathIntroVideo(
22
+ introVideoId: number,
23
+ brand: string,
24
+ ): Promise<any> {
25
+ completeIfNotCompleted(introVideoId) // no need to wait for result
26
+
27
+ return await fetchQuizAnswers(brand)
28
+ }
29
+
30
+ async function completeIfNotCompleted(
31
+ contentId: number,
32
+ ): Promise<SyncWriteDTO<ContentProgress, any> | null> {
33
+ const status = await getProgressState(contentId)
34
+ return status !== STATE.COMPLETED ? await contentStatusCompleted(contentId) : null
35
+ }
@@ -2326,6 +2326,7 @@ export async function fetchMethodV2IntroVideo(brand) {
2326
2326
  * Fetch the structure (just ids) of the Method for a given brand.
2327
2327
  * @param brand
2328
2328
  * @returns {Promise<*|null>}
2329
+ * @deprecated Kept for backwards compatibility for Method.
2329
2330
  */
2330
2331
  export async function fetchMethodV2Structure(brand) {
2331
2332
  const _type = 'method-v2'
@@ -2338,6 +2339,7 @@ export async function fetchMethodV2Structure(brand) {
2338
2339
  status,
2339
2340
  published_on,
2340
2341
  'intro_video_id': intro_video->railcontent_id,
2342
+ 'preroll_id': intro_video->railcontent_id,
2341
2343
  'children': child[]->railcontent_id
2342
2344
  }
2343
2345
  }`
@@ -2356,6 +2358,7 @@ export async function devFetchAllLearningPathsAndIntroVideoIdsForDelete() {
2356
2358
  * Fetch the structure (just ids) of the Method of a given learning path or learning path lesson.
2357
2359
  * @param contentId
2358
2360
  * @returns {Promise<*|null>}
2361
+ * @deprecated Kept for backwards compatibility for Method.
2359
2362
  */
2360
2363
  export async function fetchMethodV2StructureFromId(contentId) {
2361
2364
  const _type = 'method-v2'
@@ -2366,6 +2369,7 @@ export async function fetchMethodV2StructureFromId(contentId) {
2366
2369
  'learning_paths': child[]->{
2367
2370
  'id': railcontent_id,
2368
2371
  'intro_video_id': intro_video->railcontent_id,
2372
+ 'preroll_id': intro_video->railcontent_id,
2369
2373
  'children': child[]->railcontent_id
2370
2374
  }
2371
2375
  }`
@@ -1 +1 @@
1
- export const MCS_VERSION = '2.184.1'
1
+ export const MCS_VERSION = '2.185.0'
@@ -1,61 +0,0 @@
1
- <component name="ProjectCodeStyleConfiguration">
2
- <code_scheme name="Project" version="173">
3
- <HTMLCodeStyleSettings>
4
- <option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
5
- </HTMLCodeStyleSettings>
6
- <JSCodeStyleSettings version="0">
7
- <option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
8
- <option name="FORCE_SEMICOLON_STYLE" value="true" />
9
- <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
10
- <option name="USE_DOUBLE_QUOTES" value="false" />
11
- <option name="FORCE_QUOTE_STYlE" value="true" />
12
- <option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
13
- <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
14
- <option name="SPACES_WITHIN_IMPORTS" value="true" />
15
- </JSCodeStyleSettings>
16
- <TypeScriptCodeStyleSettings version="0">
17
- <option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
18
- <option name="FORCE_SEMICOLON_STYLE" value="true" />
19
- <option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
20
- <option name="USE_DOUBLE_QUOTES" value="false" />
21
- <option name="FORCE_QUOTE_STYlE" value="true" />
22
- <option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
23
- <option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
24
- <option name="SPACES_WITHIN_IMPORTS" value="true" />
25
- </TypeScriptCodeStyleSettings>
26
- <VueCodeStyleSettings>
27
- <option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
28
- <option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
29
- </VueCodeStyleSettings>
30
- <codeStyleSettings language="HTML">
31
- <option name="SOFT_MARGINS" value="100" />
32
- <indentOptions>
33
- <option name="INDENT_SIZE" value="2" />
34
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
35
- <option name="TAB_SIZE" value="2" />
36
- </indentOptions>
37
- </codeStyleSettings>
38
- <codeStyleSettings language="JavaScript">
39
- <option name="SOFT_MARGINS" value="100" />
40
- <indentOptions>
41
- <option name="INDENT_SIZE" value="2" />
42
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
43
- <option name="TAB_SIZE" value="2" />
44
- </indentOptions>
45
- </codeStyleSettings>
46
- <codeStyleSettings language="TypeScript">
47
- <option name="SOFT_MARGINS" value="100" />
48
- <indentOptions>
49
- <option name="INDENT_SIZE" value="2" />
50
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
51
- <option name="TAB_SIZE" value="2" />
52
- </indentOptions>
53
- </codeStyleSettings>
54
- <codeStyleSettings language="Vue">
55
- <option name="SOFT_MARGINS" value="100" />
56
- <indentOptions>
57
- <option name="CONTINUATION_INDENT_SIZE" value="2" />
58
- </indentOptions>
59
- </codeStyleSettings>
60
- </code_scheme>
61
- </component>
@@ -1,5 +0,0 @@
1
- <component name="ProjectCodeStyleConfiguration">
2
- <state>
3
- <option name="USE_PER_PROJECT_SETTINGS" value="true" />
4
- </state>
5
- </component>
package/.idea/modules.xml DELETED
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectModuleManager">
4
- <modules>
5
- <module fileurl="file://$PROJECT_DIR$/.idea/musora-content-services.iml" filepath="$PROJECT_DIR$/.idea/musora-content-services.iml" />
6
- </modules>
7
- </component>
8
- </project>
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="WEB_MODULE" version="4">
3
- <component name="NewModuleRootManager">
4
- <content url="file://$MODULE_DIR$" />
5
- <orderEntry type="inheritedJdk" />
6
- <orderEntry type="sourceFolder" forTests="false" />
7
- </component>
8
- </module>
package/.idea/php.xml DELETED
@@ -1,19 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="MessDetectorOptionsConfiguration">
4
- <option name="transferred" value="true" />
5
- </component>
6
- <component name="PHPCSFixerOptionsConfiguration">
7
- <option name="transferred" value="true" />
8
- </component>
9
- <component name="PHPCodeSnifferOptionsConfiguration">
10
- <option name="highlightLevel" value="WARNING" />
11
- <option name="transferred" value="true" />
12
- </component>
13
- <component name="PhpStanOptionsConfiguration">
14
- <option name="transferred" value="true" />
15
- </component>
16
- <component name="PsalmOptionsConfiguration">
17
- <option name="transferred" value="true" />
18
- </component>
19
- </project>
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="PrettierConfiguration">
4
- <option name="myConfigurationMode" value="AUTOMATIC" />
5
- </component>
6
- </project>
package/.idea/vcs.xml DELETED
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="" vcs="Git" />
5
- <mapping directory="$PROJECT_DIR$" vcs="Git" />
6
- </component>
7
- </project>