musora-content-services 2.180.0 → 2.181.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.
- package/.claude/settings.local.json +9 -47
- package/.yarn/install-state.gz +0 -0
- package/CHANGELOG.md +13 -0
- package/dist/services/api/types.js +2 -0
- package/package.json +1 -1
- package/src/index.d.ts +3 -1
- package/src/index.js +3 -1
- package/src/services/config.js +31 -0
- package/src/services/progress-row/base.js +26 -10
- package/src/services/user/streakCalculator.ts +5 -1
- package/src/version-info.js +1 -1
|
@@ -1,50 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(
|
|
5
|
-
"Bash(
|
|
6
|
-
"
|
|
7
|
-
"Bash(counselors ls *)",
|
|
8
|
-
"Bash(counselors groups *)",
|
|
9
|
-
"Bash(counselors run *)",
|
|
4
|
+
"Bash(rg:*)",
|
|
5
|
+
"Bash(npm run lint:*)",
|
|
6
|
+
"Bash(ls:*)",
|
|
10
7
|
"Bash(npm test *)",
|
|
11
|
-
"Bash(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"Read(//tmp/pr-review-v2/**)",
|
|
15
|
-
"Bash(cat /home/alesevero/railenvironment/applications/musora-content-services/AGENTS.md)",
|
|
16
|
-
"Bash(echo \"no AGENTS.md\")",
|
|
17
|
-
"Bash(echo \"exit=$?\")",
|
|
18
|
-
"Bash(git checkout *)",
|
|
19
|
-
"Skill(pr)",
|
|
20
|
-
"Skill(create-decision)",
|
|
21
|
-
"mcp__github__pull_request_read",
|
|
22
|
-
"mcp__github__push_files",
|
|
23
|
-
"Bash(git push *)",
|
|
24
|
-
"mcp__github__create_pull_request",
|
|
25
|
-
"mcp__github__search_pull_requests",
|
|
26
|
-
"Bash(git commit *)",
|
|
27
|
-
"Bash(git add *)",
|
|
28
|
-
"mcp__github__list_pull_requests",
|
|
29
|
-
"mcp__sanity__get_schema",
|
|
30
|
-
"mcp__sanity__query_documents",
|
|
31
|
-
"Bash(git rm *)",
|
|
32
|
-
"Bash(git rebase *)",
|
|
33
|
-
"Bash(git fetch *)",
|
|
34
|
-
"Bash(git --no-pager log --oneline -1)",
|
|
35
|
-
"Bash(git --no-pager log --oneline -5)",
|
|
36
|
-
"Bash(git ls-tree *)",
|
|
37
|
-
"Bash(git --no-pager log --oneline -2)",
|
|
38
|
-
"Bash(git --no-pager diff --stat HEAD~1)",
|
|
39
|
-
"Bash(git check-ignore *)",
|
|
40
|
-
"Bash(python3 -)",
|
|
41
|
-
"Bash(git --no-pager log --oneline -3)",
|
|
42
|
-
"Bash(mkdir *)",
|
|
43
|
-
"Bash(git --no-pager log --oneline origin/main..HEAD)"
|
|
44
|
-
]
|
|
8
|
+
"Bash(npx jest *)"
|
|
9
|
+
],
|
|
10
|
+
"deny": []
|
|
45
11
|
},
|
|
46
|
-
"model": "sonnet",
|
|
47
12
|
"enabledMcpjsonServers": [
|
|
13
|
+
"1password",
|
|
48
14
|
"atlassian",
|
|
49
15
|
"figma",
|
|
50
16
|
"google-workspace",
|
|
@@ -58,14 +24,10 @@
|
|
|
58
24
|
"chrome-devtools",
|
|
59
25
|
"railway",
|
|
60
26
|
"github",
|
|
61
|
-
"asana"
|
|
62
|
-
],
|
|
63
|
-
"disabledMcpjsonServers": [
|
|
27
|
+
"asana",
|
|
64
28
|
"nightwatch",
|
|
65
|
-
"1password",
|
|
66
29
|
"mysql-staging",
|
|
67
30
|
"mysql-local",
|
|
68
31
|
"mobile"
|
|
69
|
-
]
|
|
70
|
-
"effortLevel": "medium"
|
|
32
|
+
]
|
|
71
33
|
}
|
|
Binary file
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
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.181.0](https://github.com/railroadmedia/musora-content-services/compare/v2.180.0...v2.181.0) (2026-09-17)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* allow merged config ([#1018](https://github.com/railroadmedia/musora-content-services/issues/1018)) ([8d8a9aa](https://github.com/railroadmedia/musora-content-services/commit/8d8a9aaed048e83b7dfeb15a7c12fabd199721f1))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **BR-717:** expire streak cache at calendar day boundary ([1f4481c](https://github.com/railroadmedia/musora-content-services/commit/1f4481cc5a71afa990c39d5927099b093a475c83))
|
|
16
|
+
* **BR-964:** unpin deleted playlists at fetch time ([#1052](https://github.com/railroadmedia/musora-content-services/issues/1052)) ([0367143](https://github.com/railroadmedia/musora-content-services/commit/036714377469beccbceae93d138a9d2280516395))
|
|
17
|
+
|
|
5
18
|
## [2.180.0](https://github.com/railroadmedia/musora-content-services/compare/v2.179.0...v2.180.0) (2026-09-04)
|
|
6
19
|
|
|
7
20
|
|
package/package.json
CHANGED
package/src/index.d.ts
CHANGED
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
getMCSVersion,
|
|
21
21
|
globalConfig,
|
|
22
22
|
initializeEnvVar,
|
|
23
|
-
initializeService
|
|
23
|
+
initializeService,
|
|
24
|
+
updateSessionConfig
|
|
24
25
|
} from './services/config.js';
|
|
25
26
|
|
|
26
27
|
import {
|
|
@@ -917,6 +918,7 @@ declare module 'musora-content-services' {
|
|
|
917
918
|
updatePracticeGoals,
|
|
918
919
|
updatePracticeNotes,
|
|
919
920
|
updateProfileVisibility,
|
|
921
|
+
updateSessionConfig,
|
|
920
922
|
updateThread,
|
|
921
923
|
updateUserPractice,
|
|
922
924
|
upgradeSubscription,
|
package/src/index.js
CHANGED
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
getMCSVersion,
|
|
25
25
|
globalConfig,
|
|
26
26
|
initializeEnvVar,
|
|
27
|
-
initializeService
|
|
27
|
+
initializeService,
|
|
28
|
+
updateSessionConfig
|
|
28
29
|
} from './services/config.js';
|
|
29
30
|
|
|
30
31
|
import {
|
|
@@ -916,6 +917,7 @@ export {
|
|
|
916
917
|
updatePracticeGoals,
|
|
917
918
|
updatePracticeNotes,
|
|
918
919
|
updateProfileVisibility,
|
|
920
|
+
updateSessionConfig,
|
|
919
921
|
updateThread,
|
|
920
922
|
updateUserPractice,
|
|
921
923
|
upgradeSubscription,
|
package/src/services/config.js
CHANGED
|
@@ -23,9 +23,28 @@ export let globalConfig = {
|
|
|
23
23
|
*/
|
|
24
24
|
const excludeFromGeneratedIndex = []
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* @param {*} target
|
|
28
|
+
* @param {*} source
|
|
29
|
+
* @returns {*}
|
|
30
|
+
*/
|
|
31
|
+
function deepMerge(target, source) {
|
|
32
|
+
if (source === undefined) return target
|
|
33
|
+
if (typeof source !== 'object' || source === null || Array.isArray(source)) return source
|
|
34
|
+
|
|
35
|
+
const merged = { ...target }
|
|
36
|
+
for (const key of Object.keys(source)) {
|
|
37
|
+
merged[key] = deepMerge(target?.[key], source[key])
|
|
38
|
+
}
|
|
39
|
+
return merged
|
|
40
|
+
}
|
|
41
|
+
|
|
26
42
|
/**
|
|
27
43
|
* Initializes the service with the given configuration.
|
|
28
44
|
* This function must be called before using any other functions in this library.
|
|
45
|
+
* Each call replaces the prior config wholesale - pass the full config every time.
|
|
46
|
+
* For a partial update to just the session (e.g. userId resolving after boot),
|
|
47
|
+
* use {@link updateSessionConfig} instead.
|
|
29
48
|
* Automatically initializes award definitions with 24-hour cache in the background.
|
|
30
49
|
*
|
|
31
50
|
* @param {Config} config - Configuration object containing API settings.
|
|
@@ -107,6 +126,18 @@ export function initializeService(config) {
|
|
|
107
126
|
}
|
|
108
127
|
}
|
|
109
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Deep-merges partial session data (e.g. userId) into the existing sessionConfig
|
|
131
|
+
* without touching any other part of globalConfig. Use this for reactive updates
|
|
132
|
+
* (e.g. a watcher that re-syncs userId once a user resolves) instead of calling
|
|
133
|
+
* initializeService again, since initializeService replaces sessionConfig wholesale.
|
|
134
|
+
*
|
|
135
|
+
* @param {*} sessionConfig
|
|
136
|
+
*/
|
|
137
|
+
export function updateSessionConfig(sessionConfig) {
|
|
138
|
+
globalConfig.sessionConfig = deepMerge(globalConfig.sessionConfig, sessionConfig)
|
|
139
|
+
}
|
|
140
|
+
|
|
110
141
|
export function initializeEnvVar(config) {
|
|
111
142
|
globalConfig.appEnv = config.appEnv
|
|
112
143
|
}
|
|
@@ -22,7 +22,7 @@ const CACHE_EXPIRY_MS = 5 * 60 * 1000
|
|
|
22
22
|
/**
|
|
23
23
|
* Retrieves user's pinned data by brand, from localStorage or BE call.
|
|
24
24
|
* @param brand
|
|
25
|
-
* @returns {Promise<any|*|{id, type}>}
|
|
25
|
+
* @returns {Promise<any|*|{id, type}>} - only id and type
|
|
26
26
|
*/
|
|
27
27
|
async function getUserPinnedItem(brand) {
|
|
28
28
|
const key = getUserPinProgressKey()
|
|
@@ -170,7 +170,12 @@ export async function getProgressRows({ brand = 'drumeo', limit = 8 } = {}, opti
|
|
|
170
170
|
|
|
171
171
|
const [contentCardMap, playlistCards, methodCard] = await getCards(brand, limit, userPinnedItem)
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
let pinnedCard = null
|
|
174
|
+
try {
|
|
175
|
+
pinnedCard = await popPinnedItem(userPinnedItem, contentCardMap, playlistCards, methodCard, brand) // todo: catch errors in here
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('Error popping pinned item:', error)
|
|
178
|
+
}
|
|
174
179
|
|
|
175
180
|
let allResultsLength = playlistCards.length + contentCardMap.size
|
|
176
181
|
if (methodCard) {
|
|
@@ -207,7 +212,7 @@ async function getCards(brand, limit, userPinnedItem) {
|
|
|
207
212
|
* If userPinnedItem is not found, generate the pinned card from scratch.
|
|
208
213
|
*
|
|
209
214
|
**/
|
|
210
|
-
async function popPinnedItem(userPinnedItem, contentCardMap, playlistCards, methodCard) {
|
|
215
|
+
async function popPinnedItem(userPinnedItem, contentCardMap, playlistCards, methodCard, brand) {
|
|
211
216
|
if (!userPinnedItem) return null
|
|
212
217
|
const pinnedId = parseInt(userPinnedItem.id)
|
|
213
218
|
const progressType = userPinnedItem.progressType ?? userPinnedItem.type
|
|
@@ -236,13 +241,24 @@ async function popPinnedItem(userPinnedItem, contentCardMap, playlistCards, meth
|
|
|
236
241
|
if (pinnedPlaylist) {
|
|
237
242
|
item = pinnedPlaylist
|
|
238
243
|
} else {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
let playlist
|
|
245
|
+
try {
|
|
246
|
+
playlist = await fetchPlaylist(pinnedId)
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (error?.status !== 404) {
|
|
249
|
+
throw error
|
|
250
|
+
}
|
|
251
|
+
unpinProgressRow(brand)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (playlist) {
|
|
255
|
+
item = processPlaylistItem({
|
|
256
|
+
id: pinnedId,
|
|
257
|
+
playlist: playlist,
|
|
258
|
+
type: 'playlist',
|
|
259
|
+
progressTimestamp: new Date().getTime(),
|
|
260
|
+
})
|
|
261
|
+
}
|
|
246
262
|
}
|
|
247
263
|
} else if (progressType === 'method') {
|
|
248
264
|
// simply get method card and return
|
|
@@ -22,13 +22,17 @@ export interface PracticeData {
|
|
|
22
22
|
class StreakCalculator {
|
|
23
23
|
private cache: StreakData | null = null
|
|
24
24
|
async getStreakData(): Promise<StreakData> {
|
|
25
|
-
if (this.cache) {
|
|
25
|
+
if (this.cache && this.isCacheFromToday()) {
|
|
26
26
|
return this.cache
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
return await this.recalculate()
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
private isCacheFromToday(): boolean {
|
|
33
|
+
return dayjs(this.cache.calculatedAt).format('YYYY-MM-DD') === dayjs().format('YYYY-MM-DD')
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
async recalculate(): Promise<StreakData> {
|
|
33
37
|
const allPractices = await this.fetchAllPractices()
|
|
34
38
|
|
package/src/version-info.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const MCS_VERSION = '2.
|
|
1
|
+
export const MCS_VERSION = '2.181.0'
|