musora-content-services 2.30.0 → 2.30.3

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 (143) 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 +0 -0
  5. package/.github/workflows/docs.js.yml +0 -0
  6. package/.github/workflows/node.js.yml +0 -0
  7. package/.prettierignore +0 -0
  8. package/.prettierrc +0 -0
  9. package/CHANGELOG.md +11 -0
  10. package/README.md +0 -0
  11. package/babel.config.cjs +0 -0
  12. package/docs/ContentOrganization.html +0 -0
  13. package/docs/Gamification.html +0 -0
  14. package/docs/UserManagementSystem.html +0 -0
  15. package/docs/api_types.js.html +0 -0
  16. package/docs/config.js.html +0 -0
  17. package/docs/content-org_content-org.js.html +0 -0
  18. package/docs/content-org_playlists-types.js.html +0 -0
  19. package/docs/content-org_playlists.js.html +0 -0
  20. package/docs/content.js.html +0 -0
  21. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  22. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  25. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  26. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  27. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  28. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  35. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  36. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  37. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  39. package/docs/gamification_awards.js.html +0 -0
  40. package/docs/gamification_gamification.js.html +0 -0
  41. package/docs/gamification_types.js.html +0 -0
  42. package/docs/global.html +0 -0
  43. package/docs/index.html +0 -0
  44. package/docs/module-Awards.html +0 -0
  45. package/docs/module-Config.html +0 -0
  46. package/docs/module-Content-Services-V2.html +0 -0
  47. package/docs/module-Interests.html +0 -0
  48. package/docs/module-Permissions.html +0 -0
  49. package/docs/module-Playlists.html +0 -0
  50. package/docs/module-Railcontent-Services.html +0 -0
  51. package/docs/module-Sanity-Services.html +0 -0
  52. package/docs/module-Sessions.html +0 -0
  53. package/docs/module-UserActivity.html +0 -0
  54. package/docs/module-UserChat.html +0 -0
  55. package/docs/module-UserManagement.html +0 -0
  56. package/docs/module-UserNotifications.html +0 -0
  57. package/docs/module-UserProfile.html +0 -0
  58. package/docs/railcontent.js.html +0 -0
  59. package/docs/sanity.js.html +0 -0
  60. package/docs/scripts/collapse.js +0 -0
  61. package/docs/scripts/commonNav.js +0 -0
  62. package/docs/scripts/linenumber.js +0 -0
  63. package/docs/scripts/nav.js +0 -0
  64. package/docs/scripts/polyfill.js +0 -0
  65. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  66. package/docs/scripts/prettify/lang-css.js +0 -0
  67. package/docs/scripts/prettify/prettify.js +0 -0
  68. package/docs/scripts/search.js +0 -0
  69. package/docs/styles/jsdoc.css +0 -0
  70. package/docs/styles/prettify.css +0 -0
  71. package/docs/userActivity.js.html +0 -0
  72. package/docs/user_chat.js.html +0 -0
  73. package/docs/user_interests.js.html +0 -0
  74. package/docs/user_management.js.html +0 -0
  75. package/docs/user_notifications.js.html +0 -0
  76. package/docs/user_permissions.js.html +0 -0
  77. package/docs/user_profile.js.html +0 -0
  78. package/docs/user_sessions.js.html +0 -0
  79. package/docs/user_types.js.html +0 -0
  80. package/docs/user_user-management-system.js.html +0 -0
  81. package/jest.config.js +0 -0
  82. package/jsdoc.json +0 -0
  83. package/package.json +1 -1
  84. package/src/contentMetaData.js +0 -0
  85. package/src/filterBuilder.js +0 -0
  86. package/src/index.d.ts +0 -0
  87. package/src/index.js +0 -0
  88. package/src/infrastructure/http/HttpClient.ts +0 -0
  89. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  90. package/src/infrastructure/http/index.ts +0 -0
  91. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  92. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  93. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  94. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  95. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  96. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  97. package/src/lib/httpHelper.js +0 -0
  98. package/src/lib/lastUpdated.js +0 -0
  99. package/src/services/api/types.js +0 -0
  100. package/src/services/content-org/content-org.js +0 -0
  101. package/src/services/content-org/guided-courses.ts +0 -0
  102. package/src/services/contentLikes.js +0 -0
  103. package/src/services/contentProgress.js +72 -43
  104. package/src/services/dataContext.js +0 -0
  105. package/src/services/dateUtils.js +0 -0
  106. package/src/services/forum.js +0 -0
  107. package/src/services/gamification/gamification.js +0 -0
  108. package/src/services/imageSRCBuilder.js +0 -0
  109. package/src/services/imageSRCVerify.js +0 -0
  110. package/src/services/user/account.ts +0 -0
  111. package/src/services/user/chat.js +0 -0
  112. package/src/services/user/interests.js +0 -0
  113. package/src/services/user/management.js +0 -0
  114. package/src/services/user/notifications.js +0 -0
  115. package/src/services/user/permissions.js +0 -0
  116. package/src/services/user/profile.js +0 -0
  117. package/src/services/user/sessions.js +0 -0
  118. package/src/services/user/types.js +0 -0
  119. package/src/services/user/user-management-system.js +0 -0
  120. package/test/HttpClient.test.js +0 -0
  121. package/test/content.test.js +0 -0
  122. package/test/contentLikes.test.js +0 -0
  123. package/test/contentProgress.test.js +0 -0
  124. package/test/dataContext.test.js +0 -0
  125. package/test/forum.test.js +0 -0
  126. package/test/imageSRCBuilder.test.js +0 -0
  127. package/test/imageSRCVerify.test.js +0 -0
  128. package/test/lib/lastUpdated.test.js +0 -0
  129. package/test/live/contentProgressLive.test.js +0 -0
  130. package/test/live/railcontentLive.test.js +0 -0
  131. package/test/localStorageMock.js +0 -0
  132. package/test/log.js +0 -0
  133. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  134. package/test/mockData/mockData_progress_content.json +0 -0
  135. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  136. package/test/mockData/mockData_user_practices.json +0 -0
  137. package/test/notifications.test.js +0 -0
  138. package/test/progressRows.test.js +0 -0
  139. package/test/sanityQueryService.test.js +0 -0
  140. package/test/streakMessage.test.js +0 -0
  141. package/test/user/permissions.test.js +0 -0
  142. package/test/userActivity.test.js +0 -0
  143. package/tools/generate-index.cjs +0 -0
package/.coderabbit.yaml CHANGED
File without changes
package/.editorconfig CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
package/.prettierignore CHANGED
File without changes
package/.prettierrc CHANGED
File without changes
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
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.30.3](https://github.com/railroadmedia/musora-content-services/compare/v2.30.2...v2.30.3) (2025-08-04)
6
+
7
+ ### [2.30.2](https://github.com/railroadmedia/musora-content-services/compare/v2.30.1...v2.30.2) (2025-08-04)
8
+
9
+ ### [2.30.1](https://github.com/railroadmedia/musora-content-services/compare/v2.30.0...v2.30.1) (2025-08-04)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * null check on buildNavigateTo ([#397](https://github.com/railroadmedia/musora-content-services/issues/397)) ([2387877](https://github.com/railroadmedia/musora-content-services/commit/238787786fa322872f733d769547a49796fc0bf1))
15
+
5
16
  ## [2.30.0](https://github.com/railroadmedia/musora-content-services/compare/v2.28.6...v2.30.0) (2025-08-01)
6
17
 
7
18
 
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
package/docs/global.html CHANGED
File without changes
package/docs/index.html 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
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.30.0",
3
+ "version": "2.30.3",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
File without changes
package/src/index.d.ts CHANGED
File without changes
package/src/index.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
File without changes
@@ -5,9 +5,9 @@ import {
5
5
  postRecordWatchSession,
6
6
  } from './railcontent.js'
7
7
  import { DataContext, ContentProgressVersionKey } from './dataContext.js'
8
- import {fetchHierarchy} from './sanity.js'
9
- import {recordUserPractice, findIncompleteLesson} from "./userActivity";
10
- import {getNextLessonLessonParentTypes} from "../contentTypeConfig.js";
8
+ import { fetchHierarchy } from './sanity.js'
9
+ import { recordUserPractice, findIncompleteLesson } from './userActivity'
10
+ import { getNextLessonLessonParentTypes } from '../contentTypeConfig.js'
11
11
 
12
12
  const STATE_STARTED = 'started'
13
13
  const STATE_COMPLETED = 'completed'
@@ -45,22 +45,19 @@ export async function getResumeTimeSecondsByIds(contentIds) {
45
45
  return getByIds(contentIds, DATA_KEY_RESUME_TIME, 0)
46
46
  }
47
47
 
48
- export async function getNextLesson(data)
49
- {
48
+ export async function getNextLesson(data) {
50
49
  let nextLessonData = {}
51
50
 
52
51
  for (const content of data) {
53
- const children = content.children?.map(child => child.id) ?? []
52
+ const children = content.children?.map((child) => child.id) ?? []
54
53
  //only calculate nextLesson if needed, based on content type
55
54
  if (!getNextLessonLessonParentTypes.includes(content.type)) {
56
55
  nextLessonData[content.id] = null
57
-
58
56
  } else {
59
57
  //return first child if parent-content is complete or no progress
60
58
  const contentState = await getProgressState(content.id)
61
59
  if (contentState !== STATE_STARTED) {
62
60
  nextLessonData[content.id] = children[0]
63
-
64
61
  } else {
65
62
  const childrenStates = await getProgressStateByIds(children)
66
63
 
@@ -73,16 +70,23 @@ export async function getNextLesson(data)
73
70
  if (lastInteractedStatus === STATE_STARTED) {
74
71
  nextLessonData[content.id] = lastInteracted
75
72
  } else {
76
- nextLessonData[content.id] = findIncompleteLesson(childrenStates, lastInteracted, content.type)
73
+ nextLessonData[content.id] = findIncompleteLesson(
74
+ childrenStates,
75
+ lastInteracted,
76
+ content.type
77
+ )
77
78
  }
78
-
79
79
  } else if (content.type === 'guided-course' || content.type === 'song-tutorial') {
80
- nextLessonData[content.id] = findIncompleteLesson(childrenStates, lastInteracted, content.type)
80
+ nextLessonData[content.id] = findIncompleteLesson(
81
+ childrenStates,
82
+ lastInteracted,
83
+ content.type
84
+ )
81
85
  } else if (content.type === 'pack') {
82
86
  const packBundles = content.children ?? []
83
87
  const packBundleProgressData = await getNextLesson(packBundles)
84
- const parentId = await getLastInteractedOf(packBundles.map(bundle => bundle.id));
85
- nextLessonData[content.id] = packBundleProgressData[parentId];
88
+ const parentId = await getLastInteractedOf(packBundles.map((bundle) => bundle.id))
89
+ nextLessonData[content.id] = packBundleProgressData[parentId]
86
90
  }
87
91
  }
88
92
  }
@@ -90,31 +94,32 @@ export async function getNextLesson(data)
90
94
  return nextLessonData
91
95
  }
92
96
 
93
- export async function getNavigateTo(data)
94
- {
97
+ export async function getNavigateTo(data) {
95
98
  let navigateToData = {}
96
99
  const twoDepthContentTypes = ['pack'] //TODO add method when we know what it's called
97
100
  //TODO add parent hierarchy upwards as well
98
101
  // data structure is the same but instead of child{} we use parent{}
99
102
  for (const content of data) {
100
-
101
103
  //only calculate nextLesson if needed, based on content type
102
104
  if (!getNextLessonLessonParentTypes.includes(content.type) || !content.children) {
103
105
  navigateToData[content.id] = null
104
106
  } else {
105
107
  const children = new Map()
106
108
  const childrenIds = []
107
- content.children.forEach(child => {
108
- childrenIds.push(child.id)
109
- children.set(child.id, child)
110
- }
111
- )
109
+ content.children.forEach((child) => {
110
+ childrenIds.push(child.id)
111
+ children.set(child.id, child)
112
+ })
112
113
  // return first child (or grand child) if parent-content is complete or no progress
113
114
  const contentState = await getProgressState(content.id)
114
115
  if (contentState !== STATE_STARTED) {
115
116
  const firstChild = content.children[0]
116
- let lastInteractedChildNavToData = await getNavigateTo([firstChild])[firstChild.id] ?? null
117
- navigateToData[content.id] = buildNavigateTo(content.children[0], lastInteractedChildNavToData)
117
+ let lastInteractedChildNavToData =
118
+ (await getNavigateTo([firstChild])[firstChild.id]) ?? null
119
+ navigateToData[content.id] = buildNavigateTo(
120
+ content.children[0],
121
+ lastInteractedChildNavToData
122
+ )
118
123
  } else {
119
124
  const childrenStates = await getProgressStateByIds(childrenIds)
120
125
  const lastInteracted = await getLastInteractedOf(childrenIds)
@@ -132,13 +137,18 @@ export async function getNavigateTo(data)
132
137
  navigateToData[content.id] = buildNavigateTo(children.get(incompleteChild))
133
138
  } else if (twoDepthContentTypes.includes(content.type)) {
134
139
  const firstChildren = content.children ?? []
135
- const lastInteractedChildId = await getLastInteractedOf(firstChildren.map(child => child.id));
140
+ const lastInteractedChildId = await getLastInteractedOf(
141
+ firstChildren.map((child) => child.id)
142
+ )
136
143
  if (childrenStates[lastInteractedChildId] === STATE_COMPLETED) {
137
144
  // TODO: packs have an extra situation where we need to jump to the next course if all lessons in the last engaged course are completed
138
145
  }
139
146
  let lastInteractedChildNavToData = await getNavigateTo(firstChildren)
140
147
  lastInteractedChildNavToData = lastInteractedChildNavToData[lastInteractedChildId]
141
- navigateToData[content.id] = buildNavigateTo(children.get(lastInteractedChildId), lastInteractedChildNavToData);
148
+ navigateToData[content.id] = buildNavigateTo(
149
+ children.get(lastInteractedChildId),
150
+ lastInteractedChildNavToData
151
+ )
142
152
  }
143
153
  }
144
154
  }
@@ -146,13 +156,16 @@ export async function getNavigateTo(data)
146
156
  return navigateToData
147
157
  }
148
158
 
149
- function buildNavigateTo(content, child = null)
150
- {
159
+ function buildNavigateTo(content, child = null) {
160
+ if (!content) {
161
+ return null
162
+ }
163
+
151
164
  return {
152
- brand: content.brand,
165
+ brand: content.brand ?? '',
153
166
  thumbnail: content.thumbnail ?? '',
154
- id: content.id,
155
- type: content.type,
167
+ id: content.id ?? null,
168
+ type: content.type ?? '',
156
169
  child: child,
157
170
  }
158
171
  }
@@ -182,10 +195,14 @@ export async function getLastInteractedOf(contentIds) {
182
195
  export async function getProgressDateByIds(contentIds) {
183
196
  let data = await dataContext.getData()
184
197
  let progress = {}
185
- contentIds?.forEach((id) => (progress[id] = {
186
- 'last_update': data[id]?.[DATA_KEY_LAST_UPDATED_TIME] ?? 0,
187
- 'progress': data[id]?.[DATA_KEY_PROGRESS] ?? 0,
188
- 'status': data[id]?.[DATA_KEY_STATUS] ?? ''}))
198
+ contentIds?.forEach(
199
+ (id) =>
200
+ (progress[id] = {
201
+ last_update: data[id]?.[DATA_KEY_LAST_UPDATED_TIME] ?? 0,
202
+ progress: data[id]?.[DATA_KEY_PROGRESS] ?? 0,
203
+ status: data[id]?.[DATA_KEY_STATUS] ?? '',
204
+ })
205
+ )
189
206
  return progress
190
207
  }
191
208
 
@@ -245,11 +262,16 @@ export async function getAllCompleted(limit = null) {
245
262
  return ids
246
263
  }
247
264
 
248
- export async function getAllStartedOrCompleted({ limit = null, onlyIds = true, brand = null, excludedIds = [] } = {}) {
265
+ export async function getAllStartedOrCompleted({
266
+ limit = null,
267
+ onlyIds = true,
268
+ brand = null,
269
+ excludedIds = [],
270
+ } = {}) {
249
271
  const data = await dataContext.getData()
250
272
  const oneMonthAgoInSeconds = Math.floor(Date.now() / 1000) - 60 * 24 * 60 * 60 // 60 days in seconds
251
273
 
252
- const excludedSet = new Set(excludedIds.map(id => parseInt(id))) // ensure IDs are numbers
274
+ const excludedSet = new Set(excludedIds.map((id) => parseInt(id))) // ensure IDs are numbers
253
275
 
254
276
  let filtered = Object.entries(data)
255
277
  .filter(([key, item]) => {
@@ -307,13 +329,14 @@ export async function getAllStartedOrCompleted({ limit = null, onlyIds = true, b
307
329
  * const progressMap = await getStartedOrCompletedProgressOnly({ brand: 'drumeo' });
308
330
  * console.log(progressMap[123]); // => 52
309
331
  */
310
- export async function getStartedOrCompletedProgressOnly({ brand = null} = {}) {
332
+ export async function getStartedOrCompletedProgressOnly({ brand = null } = {}) {
311
333
  const data = await dataContext.getData()
312
334
  const result = {}
313
335
 
314
336
  Object.entries(data).forEach(([key, item]) => {
315
337
  const id = parseInt(key)
316
- const isRelevantStatus = item[DATA_KEY_STATUS] === STATE_STARTED || item[DATA_KEY_STATUS] === STATE_COMPLETED
338
+ const isRelevantStatus =
339
+ item[DATA_KEY_STATUS] === STATE_STARTED || item[DATA_KEY_STATUS] === STATE_COMPLETED
317
340
  const isCorrectBrand = !brand || item.b === brand
318
341
 
319
342
  if (isRelevantStatus && isCorrectBrand) {
@@ -427,7 +450,7 @@ export async function recordWatchSession(
427
450
  secondsPlayed,
428
451
  sessionId = null,
429
452
  instrumentId = null,
430
- categoryId = null,
453
+ categoryId = null
431
454
  ) {
432
455
  let mediaTypeId = getMediaTypeId(mediaType, mediaCategory)
433
456
  let updateLocalProgress = mediaTypeId === 1 || mediaTypeId === 2 //only update for video playback
@@ -438,10 +461,16 @@ export async function recordWatchSession(
438
461
  try {
439
462
  //TODO: Good enough for Alpha, Refine in reliability improvements
440
463
  sessionData[sessionId] = sessionData[sessionId] || {}
441
- const secondsSinceLastUpdate = Math.ceil(secondsPlayed - (sessionData[sessionId][contentId] ?? 0))
442
- await recordUserPractice({ content_id: contentId, duration_seconds: secondsSinceLastUpdate, instrument_id: instrumentId })
464
+ const secondsSinceLastUpdate = Math.ceil(
465
+ secondsPlayed - (sessionData[sessionId][contentId] ?? 0)
466
+ )
467
+ await recordUserPractice({
468
+ content_id: contentId,
469
+ duration_seconds: secondsSinceLastUpdate,
470
+ instrument_id: instrumentId,
471
+ })
443
472
  } catch (error) {
444
- console.error('Failed to record user practice:', error)
473
+ console.error('Failed to record user practice:', error)
445
474
  }
446
475
  sessionData[sessionId][contentId] = secondsPlayed
447
476
 
@@ -506,7 +535,7 @@ function bubbleProgress(hierarchy, contentId, localContext) {
506
535
  return localContext.data[childId]?.[DATA_KEY_PROGRESS] ?? 0
507
536
  })
508
537
  let progress = Math.round(childProgress.reduce((a, b) => a + b, 0) / childProgress.length)
509
- const brand =localContext.data[contentId]?.[DATA_KEY_BRAND] ?? null
538
+ const brand = localContext.data[contentId]?.[DATA_KEY_BRAND] ?? null
510
539
  data[DATA_KEY_PROGRESS] = progress
511
540
  data[DATA_KEY_STATUS] = progress === 100 ? STATE_COMPLETED : STATE_STARTED
512
541
  data[DATA_KEY_LAST_UPDATED_TIME] = Math.round(new Date().getTime() / 1000)
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/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
File without changes