musora-content-services 2.92.6 → 2.92.7

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.
@@ -1,61 +1,78 @@
1
- name: Sync V2 Docs to Main
1
+ name: Deploy Docs to GitHub Pages
2
2
  on:
3
3
  push:
4
- branches: [project-v2]
4
+ branches: [main, project-v2]
5
+
6
+ # Required permissions for GitHub Pages deployment
7
+ permissions:
8
+ contents: read
9
+ pages: write
10
+ id-token: write
11
+
12
+ # Allow only one concurrent deployment
13
+ concurrency:
14
+ group: "pages"
15
+ cancel-in-progress: false
5
16
 
6
17
  jobs:
7
- sync-docs:
18
+ deploy-docs:
8
19
  runs-on: ubuntu-latest
20
+ environment:
21
+ name: github-pages
22
+ url: ${{ steps.deployment.outputs.page_url }}
23
+
9
24
  steps:
25
+ - name: Checkout main branch
26
+ uses: actions/checkout@v4
27
+ with:
28
+ ref: main
29
+ path: main-content
30
+
10
31
  - name: Checkout project-v2 branch
11
32
  uses: actions/checkout@v4
12
33
  with:
13
34
  ref: project-v2
14
- path: project-v2-content
35
+ path: v2-content
15
36
 
16
- - name: Checkout main branch
17
- uses: actions/checkout@v4
37
+ - name: Setup Node.js
38
+ uses: actions/setup-node@v4
18
39
  with:
19
- ref: main
20
- path: main-content
21
- token: ${{ secrets.PROJECT_V2_DOCS_TOKEN }} #use separate token to trigger other actions
40
+ node-version: '20'
22
41
 
23
- - name: Copy docs from project-v2 to main
24
- run: |
25
- # Create the target directory if it doesn't exist
26
- mkdir -p main-content/docs/v2
27
-
28
- # Remove existing v2 docs to ensure clean copy
29
- rm -rf main-content/docs/v2/*
30
-
31
- # Copy docs from project-v2 to main/docs/v2
32
- if [ -d "project-v2-content/docs" ]; then
33
- cp -r project-v2-content/docs/* main-content/docs/v2/
34
- echo "✅ Copied docs from project-v2 to main/docs/v2"
35
- else
36
- echo "⚠️ No docs folder found in project-v2 branch"
37
- exit 1
38
- fi
39
-
40
- - name: Commit and push changes to main
41
- id: commit
42
+ - name: Install dependencies for v1
43
+ working-directory: main-content
44
+ run: npm ci
45
+
46
+ - name: Generate v1 documentation
47
+ working-directory: main-content
48
+ run: npm run doc
49
+
50
+ - name: Install dependencies for v2
51
+ working-directory: v2-content
52
+ run: npm ci
53
+
54
+ - name: Generate v2 documentation
55
+ working-directory: v2-content
56
+ run: npm run doc
57
+
58
+ - name: Combine v1 and v2 docs into deployment structure
42
59
  run: |
43
- cd main-content
44
- git config --local user.email "action@github.com"
45
- git config --local user.name "GitHub Action"
46
-
47
- # Check if there are any changes
48
- if [ -n "$(git status --porcelain)" ]; then
49
- git add docs/v2/
50
- git commit -m "🔄 Auto-sync: Update v2 docs from project-v2 branch
51
-
52
- - Synced from project-v2/docs
53
- - Triggered by commit: ${{ github.sha }}
54
- - Date: $(date)"
55
- git push
56
- echo "✅ Successfully pushed updated docs to main branch"
57
- echo "changes=true" >> $GITHUB_OUTPUT
58
- else
59
- echo "ℹ️ No changes detected in docs"
60
- echo "changes=false" >> $GITHUB_OUTPUT
61
- fi
60
+ mkdir -p _site
61
+ # Copy v1 docs to root
62
+ cp -r main-content/docs/* _site/
63
+ # Copy v2 docs to /v2/ subdirectory
64
+ mkdir -p _site/v2
65
+ cp -r v2-content/docs/* _site/v2/
66
+ echo "✅ Combined v1 (root) and v2 (/v2/) documentation"
67
+
68
+ - name: Setup GitHub Pages
69
+ uses: actions/configure-pages@v4
70
+
71
+ - name: Upload combined documentation artifact
72
+ uses: actions/upload-pages-artifact@v3
73
+ with:
74
+ path: './_site'
75
+
76
+ - name: Deploy to GitHub Pages
77
+ id: deployment
78
+ uses: actions/deploy-pages@v4
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.92.7](https://github.com/railroadmedia/musora-content-services/compare/v2.92.6...v2.92.7) (2025-12-02)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **agi:** make brand parameter less strict for now ([#603](https://github.com/railroadmedia/musora-content-services/issues/603)) ([b440b92](https://github.com/railroadmedia/musora-content-services/commit/b440b92ae1b4aab80d7e5ac5238c1e386cf09db8))
11
+
5
12
  ### [2.92.6](https://github.com/railroadmedia/musora-content-services/compare/v2.92.5...v2.92.6) (2025-12-02)
6
13
 
7
14
  ### [2.92.5](https://github.com/railroadmedia/musora-content-services/compare/v2.92.3...v2.92.5) (2025-12-02)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.92.6",
3
+ "version": "2.92.7",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
package/src/lib/brands.ts CHANGED
@@ -1,8 +1,8 @@
1
- export enum Brand {
2
- MUSORA = 'musora',
3
- DRUMEO = 'drumeo',
4
- PIANOTE = 'pianote',
5
- GUITAREO = 'guitareo',
6
- SINGEO = 'singeo',
7
- PLAYBASS = 'playbass',
1
+ export enum Brands {
2
+ Musora = 'musora',
3
+ Drumeo = 'drumeo',
4
+ Pianote = 'pianote',
5
+ Guitareo = 'guitareo',
6
+ Singeo = 'singeo',
7
+ Playbass = 'playbass',
8
8
  }
@@ -6,7 +6,7 @@ import { FilterBuilder } from '../../filterBuilder.js'
6
6
  import { buildDataAndTotalQuery } from '../../lib/sanity/query'
7
7
  import { fetchSanity, getSortOrder } from '../sanity.js'
8
8
  import { Lesson } from './content'
9
- import { Brand } from '../../lib/brands'
9
+ import { Brands } from '../../lib/brands'
10
10
 
11
11
  export interface Artist {
12
12
  slug: string
@@ -18,7 +18,7 @@ export interface Artist {
18
18
  /**
19
19
  * Fetch all artists with lessons available for a specific brand.
20
20
  *
21
- * @param {Brand} brand - The brand for which to fetch artists.
21
+ * @param {Brands|string} brand - The brand for which to fetch artists.
22
22
  * @returns {Promise<Artist[]|null>} - A promise that resolves to an array of artist objects or null if not found.
23
23
  *
24
24
  * @example
@@ -26,7 +26,7 @@ export interface Artist {
26
26
  * .then(artists => console.log(artists))
27
27
  * .catch(error => console.error(error));
28
28
  */
29
- export async function fetchArtists(brand: Brand): Promise<Artist[] | null> {
29
+ export async function fetchArtists(brand: Brands | string): Promise<Artist[] | null> {
30
30
  const filter = await new FilterBuilder(
31
31
  `_type == "song" && brand == "${brand}" && references(^._id)`,
32
32
  { bypassPermissions: true }
@@ -45,7 +45,7 @@ export async function fetchArtists(brand: Brand): Promise<Artist[] | null> {
45
45
  * Fetch a single artist by their Sanity ID.
46
46
  *
47
47
  * @param {string} slug - The name of the artist to fetch.
48
- * @param {Brand} [brand] - The brand for which to fetch the artist.
48
+ * @param {Brands|string} [brand] - The brand for which to fetch the artist.
49
49
  * @returns {Promise<Artist|null>} - A promise that resolves to an artist objects or null if not found.
50
50
  *
51
51
  * @example
@@ -53,7 +53,10 @@ export async function fetchArtists(brand: Brand): Promise<Artist[] | null> {
53
53
  * .then(artists => console.log(artists))
54
54
  * .catch(error => console.error(error));
55
55
  */
56
- export async function fetchArtistBySlug(slug: string, brand?: Brand): Promise<Artist | null> {
56
+ export async function fetchArtistBySlug(
57
+ slug: string,
58
+ brand?: Brands | string
59
+ ): Promise<Artist | null> {
57
60
  const brandFilter = brand ? `brand == "${brand}" && ` : ''
58
61
  const filter = await new FilterBuilder(`${brandFilter} _type == "song" && references(^._id)`, {
59
62
  bypassPermissions: true,
@@ -84,7 +87,7 @@ export interface LessonsByArtistResponse {
84
87
  /**
85
88
  * Fetch the artist's lessons.
86
89
  * @param {string} slug - The slug of the artist
87
- * @param {Brand} brand - The brand for which to fetch lessons.
90
+ * @param {Brands|string} brand - The brand for which to fetch lessons.
88
91
  * @param {string} contentType - The type of the lessons we need to get from the artist. If not defined, groq will get lessons from all content types
89
92
  * @param {Object} params - Parameters for sorting, searching, pagination and filtering.
90
93
  * @param {string} [params.sort="-published_on"] - The field to sort the lessons by.
@@ -102,7 +105,7 @@ export interface LessonsByArtistResponse {
102
105
  */
103
106
  export async function fetchArtistLessons(
104
107
  slug: string,
105
- brand: Brand,
108
+ brand: Brands | string,
106
109
  contentType: string,
107
110
  {
108
111
  sort = '-published_on',
@@ -6,7 +6,7 @@ import { fetchSanity, getSortOrder } from '../sanity.js'
6
6
  import { FilterBuilder } from '../../filterBuilder.js'
7
7
  import { Lesson } from './content'
8
8
  import { buildDataAndTotalQuery } from '../../lib/sanity/query'
9
- import { Brand } from '../../lib/brands'
9
+ import { Brands } from '../../lib/brands'
10
10
 
11
11
  export interface Genre {
12
12
  name: string
@@ -18,7 +18,7 @@ export interface Genre {
18
18
  /**
19
19
  * Fetch all genres with lessons available for a specific brand.
20
20
  *
21
- * @param {string} [brand] - The brand for which to fetch the genre for. Lesson count will be filtered by this brand if provided.
21
+ * @param {Brands|string} [brand] - The brand for which to fetch the genre for. Lesson count will be filtered by this brand if provided.
22
22
  * @returns {Promise<Genre[]>} - A promise that resolves to an genre object or null if not found.
23
23
  *
24
24
  * @example
@@ -26,7 +26,7 @@ export interface Genre {
26
26
  * .then(genres => console.log(genres))
27
27
  * .catch(error => console.error(error));
28
28
  */
29
- export async function fetchGenres(brand: Brand): Promise<Genre[]> {
29
+ export async function fetchGenres(brand: Brands | string): Promise<Genre[]> {
30
30
  const filter = await new FilterBuilder(`brand == "${brand}" && references(^._id)`, {
31
31
  bypassPermissions: true,
32
32
  }).buildFilter()
@@ -46,7 +46,7 @@ export async function fetchGenres(brand: Brand): Promise<Genre[]> {
46
46
  * Fetch a single genre by their slug and brand
47
47
  *
48
48
  * @param {string} slug - The slug of the genre to fetch.
49
- * @param {Brand} [brand] - The brand for which to fetch the genre. Lesson count will be filtered by this brand if provided.
49
+ * @param {Brands|string} [brand] - The brand for which to fetch the genre. Lesson count will be filtered by this brand if provided.
50
50
  * @returns {Promise<Genre[]|null>} - A promise that resolves to an genre object or null if not found.
51
51
  *
52
52
  * @example
@@ -54,7 +54,10 @@ export async function fetchGenres(brand: Brand): Promise<Genre[]> {
54
54
  * .then(genres => console.log(genres))
55
55
  * .catch(error => console.error(error));
56
56
  */
57
- export async function fetchGenreBySlug(slug: string, brand?: Brand): Promise<Genre | null> {
57
+ export async function fetchGenreBySlug(
58
+ slug: string,
59
+ brand?: Brands | string
60
+ ): Promise<Genre | null> {
58
61
  const brandFilter = brand ? `brand == "${brand}" && ` : ''
59
62
  const filter = await new FilterBuilder(`${brandFilter} references(^._id)`, {
60
63
  bypassPermissions: true,
@@ -88,7 +91,7 @@ export interface LessonsByGenreResponse {
88
91
  /**
89
92
  * Fetch the genre's lessons.
90
93
  * @param {string} slug - The slug of the genre
91
- * @param {Brand} brand - The brand for which to fetch lessons.
94
+ * @param {Brands|string} brand - The brand for which to fetch lessons.
92
95
  * @param {Object} params - Parameters for sorting, searching, pagination and filtering.
93
96
  * @param {string} [params.sort="-published_on"] - The field to sort the lessons by.
94
97
  * @param {string} [params.searchTerm=""] - The search term to filter the lessons.
@@ -105,7 +108,7 @@ export interface LessonsByGenreResponse {
105
108
  */
106
109
  export async function fetchGenreLessons(
107
110
  slug: string,
108
- brand: Brand,
111
+ brand: Brands | string,
109
112
  contentType?: string,
110
113
  {
111
114
  sort = '-published_on',
@@ -6,7 +6,7 @@ import { filtersToGroq, getFieldsForContentType } from '../../contentTypeConfig.
6
6
  import { fetchSanity, getSortOrder } from '../sanity.js'
7
7
  import { Lesson } from './content'
8
8
  import { buildDataAndTotalQuery } from '../../lib/sanity/query'
9
- import { Brand } from '../../lib/brands'
9
+ import { Brands } from '../../lib/brands'
10
10
 
11
11
  export interface Instructor {
12
12
  lessonCount: number
@@ -19,7 +19,7 @@ export interface Instructor {
19
19
  /**
20
20
  * Fetch all instructor with lessons available for a specific brand.
21
21
  *
22
- * @param {Brand} brand - The brand for which to fetch instructors.
22
+ * @param {Brands|string} brand - The brand for which to fetch instructors.
23
23
  * @returns {Promise<Instructor[]>} - A promise that resolves to an array of instructor objects.
24
24
  *
25
25
  * @example
@@ -27,7 +27,7 @@ export interface Instructor {
27
27
  * .then(instructors => console.log(instructors))
28
28
  * .catch(error => console.error(error));
29
29
  */
30
- export async function fetchInstructors(brand: Brand): Promise<Instructor[]> {
30
+ export async function fetchInstructors(brand: Brands | string): Promise<Instructor[]> {
31
31
  const filter = await new FilterBuilder(`brand == "${brand}" && references(^._id)`, {
32
32
  bypassPermissions: true,
33
33
  }).buildFilter()
@@ -46,7 +46,7 @@ export async function fetchInstructors(brand: Brand): Promise<Instructor[]> {
46
46
  * Fetch a single instructor by their name
47
47
  *
48
48
  * @param {string} slug - The slug of the instructor to fetch.
49
- * @param {Brand} [brand] - The brand for which to fetch the instructor. Lesson count will be filtered by this brand if provided.
49
+ * @param {Brands|string} [brand] - The brand for which to fetch the instructor. Lesson count will be filtered by this brand if provided.
50
50
  * @returns {Promise<Instructor[]>} - A promise that resolves to an instructor object or null if not found.
51
51
  *
52
52
  * @example
@@ -56,7 +56,7 @@ export async function fetchInstructors(brand: Brand): Promise<Instructor[]> {
56
56
  */
57
57
  export async function fetchInstructorBySlug(
58
58
  slug: string,
59
- brand?: Brand
59
+ brand?: Brands | string
60
60
  ): Promise<Instructor | null> {
61
61
  const brandFilter = brand ? `brand == "${brand}" && ` : ''
62
62
  const filter = await new FilterBuilder(`${brandFilter} references(^._id)`, {
@@ -89,8 +89,8 @@ export interface InstructorLessonsResponse {
89
89
 
90
90
  /**
91
91
  * Fetch the data needed for the instructor screen.
92
- * @param {string} brand - The brand for which to fetch instructor lessons
93
92
  * @param {string} slug - The slug of the instructor
93
+ * @param {Brands|string} brand - The brand for which to fetch instructor lessons
94
94
  *
95
95
  * @param {FetchInstructorLessonsOptions} options - Parameters for pagination, filtering and sorting.
96
96
  * @param {string} [options.sortOrder="-published_on"] - The field to sort the lessons by.
@@ -107,7 +107,7 @@ export interface InstructorLessonsResponse {
107
107
  */
108
108
  export async function fetchInstructorLessons(
109
109
  slug: string,
110
- brand: Brand,
110
+ brand: Brands | string,
111
111
  {
112
112
  sortOrder = '-published_on',
113
113
  searchTerm = '',
@@ -9,13 +9,8 @@
9
9
 
10
10
  import { HttpClient } from '../../infrastructure/http/HttpClient'
11
11
  import { globalConfig } from '../config.js'
12
- import {
13
- ReportResponse,
14
- ReportableType,
15
- IssueTypeMap,
16
- ReportIssueOption,
17
- } from './types'
18
- import {Brand} from "../../lib/brands";
12
+ import { ReportResponse, ReportableType, IssueTypeMap, ReportIssueOption } from './types'
13
+ import { Brands } from '../../lib/brands'
19
14
 
20
15
  /**
21
16
  * Parameters for submitting a report with type-safe issue values
@@ -30,7 +25,7 @@ export type ReportParams<T extends ReportableType = ReportableType> = {
30
25
  /** Details about the issue - required when issue is 'other', not sent otherwise */
31
26
  details?: string
32
27
  /** Brand context (required: drumeo, pianote, guitareo, singeo, playbass) */
33
- brand: Brand
28
+ brand: Brands | string
34
29
  }
35
30
 
36
31
  /**
@@ -73,7 +68,9 @@ export type ReportParams<T extends ReportableType = ReportableType> = {
73
68
  * brand: 'drumeo'
74
69
  * })
75
70
  */
76
- export async function report<T extends ReportableType>(params: ReportParams<T>): Promise<ReportResponse> {
71
+ export async function report<T extends ReportableType>(
72
+ params: ReportParams<T>
73
+ ): Promise<ReportResponse> {
77
74
  const httpClient = new HttpClient(globalConfig.baseUrl)
78
75
 
79
76
  // Build request body
@@ -125,7 +122,10 @@ export async function report<T extends ReportableType>(params: ReportParams<T>):
125
122
  * // { value: 'other', label: 'Other' }
126
123
  * // ]
127
124
  */
128
- export function getReportIssueOptions(type: ReportableType, isMobileApp: boolean = false): ReportIssueOption[] {
125
+ export function getReportIssueOptions(
126
+ type: ReportableType,
127
+ isMobileApp: boolean = false
128
+ ): ReportIssueOption[] {
129
129
  switch (type) {
130
130
  case 'forum_post':
131
131
  return [
@@ -147,7 +147,10 @@ export function getReportIssueOptions(type: ReportableType, isMobileApp: boolean
147
147
 
148
148
  case 'content':
149
149
  const contentOptions = [
150
- { value: 'incorrect_metadata', label: 'The lesson image, title or description is incorrect' },
150
+ {
151
+ value: 'incorrect_metadata',
152
+ label: 'The lesson image, title or description is incorrect',
153
+ },
151
154
  { value: 'video_issue', label: 'Video issue' },
152
155
  ]
153
156
 
@@ -165,7 +168,10 @@ export function getReportIssueOptions(type: ReportableType, isMobileApp: boolean
165
168
 
166
169
  case 'playlist':
167
170
  const playlistOptions = [
168
- { value: 'incorrect_metadata', label: 'The lesson image, title or description is incorrect' },
171
+ {
172
+ value: 'incorrect_metadata',
173
+ label: 'The lesson image, title or description is incorrect',
174
+ },
169
175
  { value: 'video_issue', label: 'Video issue' },
170
176
  ]
171
177
 
@@ -182,8 +188,6 @@ export function getReportIssueOptions(type: ReportableType, isMobileApp: boolean
182
188
  return playlistOptions
183
189
 
184
190
  default:
185
- return [
186
- { value: 'other', label: 'Other' },
187
- ]
191
+ return [{ value: 'other', label: 'Other' }]
188
192
  }
189
193
  }
@@ -2,7 +2,7 @@
2
2
  * @module Onboarding
3
3
  */
4
4
  import { HttpClient } from '../../infrastructure/http/HttpClient'
5
- import { Brand } from '../../lib/brands'
5
+ import { Brands } from '../../lib/brands'
6
6
  import { globalConfig } from '../config.js'
7
7
 
8
8
  export interface OnboardingSteps {
@@ -224,13 +224,13 @@ const recommendedContentCache: { [brand: string]: OnboardingRecommendedContent }
224
224
  * Fetches recommended content for onboarding based on the specified brand.
225
225
  *
226
226
  * @param {string} email - The user's email address.
227
- * @param {Brand} brand - The brand identifier.
227
+ * @param {Brands} brand - The brand identifier.
228
228
  * @returns {Promise<OnboardingRecommendedContent>} - A promise that resolves with the recommended content.
229
229
  * @throws {HttpError} - If the HTTP request fails.
230
230
  */
231
231
  export async function getOnboardingRecommendedContent(
232
232
  email: string,
233
- brand: Brand
233
+ brand: Brands
234
234
  ): Promise<OnboardingRecommendedContent> {
235
235
  // TODO: Replace with real API call when available
236
236
  if (recommendedContentCache[brand]) {