musora-content-services 2.90.0 → 2.92.6

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 (177) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/package.json +11 -3
  3. package/src/index.d.ts +9 -31
  4. package/src/index.js +12 -34
  5. package/src/services/content-org/learning-paths.ts +33 -3
  6. package/src/services/contentAggregator.js +2 -2
  7. package/src/services/contentLikes.js +6 -39
  8. package/src/services/contentProgress.js +181 -479
  9. package/src/services/dataContext.js +0 -2
  10. package/src/services/progress-row/method-card.js +2 -1
  11. package/src/services/railcontent.js +12 -135
  12. package/src/services/sentry/.indexignore +0 -0
  13. package/src/services/sentry/index.ts +23 -0
  14. package/src/services/sync/.indexignore +0 -0
  15. package/src/services/sync/adapters/factory.ts +26 -0
  16. package/src/services/sync/adapters/lokijs.ts +1 -0
  17. package/src/services/sync/adapters/sqlite.ts +1 -0
  18. package/src/services/sync/concurrency-safety.ts +4 -0
  19. package/src/services/sync/context/index.ts +43 -0
  20. package/src/services/sync/context/providers/base.ts +4 -0
  21. package/src/services/sync/context/providers/connectivity.ts +14 -0
  22. package/src/services/sync/context/providers/durability.ts +5 -0
  23. package/src/services/sync/context/providers/index.ts +5 -0
  24. package/src/services/sync/context/providers/session.ts +8 -0
  25. package/src/services/sync/context/providers/tabs.ts +18 -0
  26. package/src/services/sync/context/providers/visibility.ts +14 -0
  27. package/src/services/sync/database/factory.ts +10 -0
  28. package/src/services/sync/errors/boundary.ts +45 -0
  29. package/src/services/sync/errors/index.ts +49 -0
  30. package/src/services/sync/fetch.ts +313 -0
  31. package/src/services/sync/index.ts +80 -0
  32. package/src/services/sync/manager.ts +139 -0
  33. package/src/services/sync/models/Base.ts +47 -0
  34. package/src/services/sync/models/ContentLike.ts +16 -0
  35. package/src/services/sync/models/ContentProgress.ts +69 -0
  36. package/src/services/sync/models/Practice.ts +72 -0
  37. package/src/services/sync/models/PracticeDayNote.ts +23 -0
  38. package/src/services/sync/models/index.ts +4 -0
  39. package/src/services/sync/repositories/base.ts +247 -0
  40. package/src/services/sync/repositories/content-likes.ts +26 -0
  41. package/src/services/sync/repositories/content-progress.ts +160 -0
  42. package/src/services/sync/repositories/index.ts +4 -0
  43. package/src/services/sync/repositories/practice-day-notes.ts +4 -0
  44. package/src/services/sync/repositories/practices.ts +52 -0
  45. package/src/services/sync/repository-proxy.ts +48 -0
  46. package/src/services/sync/resolver.ts +84 -0
  47. package/src/services/sync/retry.ts +88 -0
  48. package/src/services/sync/run-scope.ts +30 -0
  49. package/src/services/sync/schema/index.ts +66 -0
  50. package/src/services/sync/serializers/index.ts +2 -0
  51. package/src/services/sync/serializers/model.ts +32 -0
  52. package/src/services/sync/serializers/raw.ts +21 -0
  53. package/src/services/sync/store/index.ts +779 -0
  54. package/src/services/sync/store/push-coalescer.ts +57 -0
  55. package/src/services/sync/store-configs.ts +41 -0
  56. package/src/services/sync/strategies/base.ts +21 -0
  57. package/src/services/sync/strategies/index.ts +12 -0
  58. package/src/services/sync/strategies/initial.ts +11 -0
  59. package/src/services/sync/strategies/polling.ts +54 -0
  60. package/src/services/sync/telemetry/index.ts +140 -0
  61. package/src/services/sync/telemetry/sampling.ts +91 -0
  62. package/src/services/sync/utils/event-emitter.ts +24 -0
  63. package/src/services/sync/utils/index.ts +1 -0
  64. package/src/services/sync/utils/throttle.ts +93 -0
  65. package/src/services/sync/utils/timers.ts +9 -0
  66. package/src/services/userActivity.js +83 -148
  67. package/test/contentProgress.test.js +6 -39
  68. package/test/live/contentProgressLive.test.js +2 -31
  69. package/tools/generate-index.cjs +10 -4
  70. package/babel.config.cjs +0 -3
  71. package/docs/Content.html +0 -269
  72. package/docs/ContentOrganization.html +0 -245
  73. package/docs/Forums.html +0 -269
  74. package/docs/Gamification.html +0 -245
  75. package/docs/TestUser.html +0 -260
  76. package/docs/UserManagementSystem.html +0 -317
  77. package/docs/api_types.js.html +0 -97
  78. package/docs/config.js.html +0 -140
  79. package/docs/content-org_content-org.js.html +0 -76
  80. package/docs/content-org_guided-courses.ts.html +0 -110
  81. package/docs/content-org_learning-paths.ts.html +0 -379
  82. package/docs/content-org_playlists-types.js.html +0 -128
  83. package/docs/content-org_playlists.js.html +0 -440
  84. package/docs/content.js.html +0 -603
  85. package/docs/content_artist.ts.html +0 -206
  86. package/docs/content_content.ts.html +0 -77
  87. package/docs/content_genre.ts.html +0 -209
  88. package/docs/content_instructor.ts.html +0 -206
  89. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  90. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  91. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  92. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  93. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  94. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  95. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  96. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  97. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  98. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -978
  99. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  100. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  101. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  102. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  103. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -1049
  104. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  105. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  106. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  107. package/docs/forums_categories.ts.html +0 -156
  108. package/docs/forums_discussions.js.html +0 -95
  109. package/docs/forums_forum.js.html +0 -95
  110. package/docs/forums_forums.ts.html +0 -160
  111. package/docs/forums_posts.ts.html +0 -284
  112. package/docs/forums_threads.ts.html +0 -284
  113. package/docs/gamification_awards.js.html +0 -165
  114. package/docs/gamification_awards.ts.html +0 -195
  115. package/docs/gamification_gamification.js.html +0 -76
  116. package/docs/gamification_types.js.html +0 -80
  117. package/docs/global.html +0 -6019
  118. package/docs/index.html +0 -167
  119. package/docs/liveTesting.ts.html +0 -103
  120. package/docs/module-Accounts.html +0 -2283
  121. package/docs/module-Artist.html +0 -993
  122. package/docs/module-Awards.html +0 -836
  123. package/docs/module-Categories.html +0 -711
  124. package/docs/module-Config.html +0 -431
  125. package/docs/module-Content-Services-V2.html +0 -2998
  126. package/docs/module-ForumCategories.html +0 -687
  127. package/docs/module-ForumDiscussions.html +0 -370
  128. package/docs/module-Forums.html +0 -16599
  129. package/docs/module-Genre.html +0 -981
  130. package/docs/module-GuidedCourses.html +0 -108
  131. package/docs/module-Instructor.html +0 -929
  132. package/docs/module-Interests.html +0 -1066
  133. package/docs/module-LearningPaths.html +0 -2298
  134. package/docs/module-Onboarding.html +0 -882
  135. package/docs/module-Payments.html +0 -392
  136. package/docs/module-Permissions.html +0 -406
  137. package/docs/module-Playlists.html +0 -3030
  138. package/docs/module-ProgressRow.html +0 -108
  139. package/docs/module-Railcontent-Services.html +0 -6735
  140. package/docs/module-Sanity-Services.html +0 -8244
  141. package/docs/module-Sessions.html +0 -575
  142. package/docs/module-Threads.html +0 -1119
  143. package/docs/module-UserActivity.html +0 -4580
  144. package/docs/module-UserChat.html +0 -410
  145. package/docs/module-UserManagement.html +0 -1932
  146. package/docs/module-UserMemberships.html +0 -829
  147. package/docs/module-UserNotifications.html +0 -2595
  148. package/docs/module-UserProfile.html +0 -370
  149. package/docs/progress-row_method-card.js.html +0 -183
  150. package/docs/railcontent.js.html +0 -847
  151. package/docs/sanity.js.html +0 -2322
  152. package/docs/scripts/collapse.js +0 -39
  153. package/docs/scripts/commonNav.js +0 -28
  154. package/docs/scripts/linenumber.js +0 -25
  155. package/docs/scripts/nav.js +0 -12
  156. package/docs/scripts/polyfill.js +0 -4
  157. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -202
  158. package/docs/scripts/prettify/lang-css.js +0 -2
  159. package/docs/scripts/prettify/prettify.js +0 -28
  160. package/docs/scripts/search.js +0 -99
  161. package/docs/styles/jsdoc.css +0 -776
  162. package/docs/styles/prettify.css +0 -80
  163. package/docs/userActivity.js.html +0 -1577
  164. package/docs/user_account.ts.html +0 -265
  165. package/docs/user_chat.js.html +0 -98
  166. package/docs/user_interests.js.html +0 -150
  167. package/docs/user_management.js.html +0 -258
  168. package/docs/user_memberships.js.html +0 -144
  169. package/docs/user_memberships.ts.html +0 -292
  170. package/docs/user_notifications.js.html +0 -374
  171. package/docs/user_onboarding.ts.html +0 -325
  172. package/docs/user_payments.ts.html +0 -146
  173. package/docs/user_permissions.js.html +0 -110
  174. package/docs/user_profile.js.html +0 -115
  175. package/docs/user_sessions.js.html +0 -170
  176. package/docs/user_types.js.html +0 -224
  177. package/docs/user_user-management-system.js.html +0 -79
@@ -8,8 +8,6 @@ import { globalConfig } from './config.js'
8
8
  const excludeFromGeneratedIndex = []
9
9
 
10
10
  //These constants need to match MWP UserDataVersionKeyEnum enum
11
- export const ContentLikesVersionKey = 0
12
- export const ContentProgressVersionKey = 1
13
11
  export const UserActivityVersionKey = 2
14
12
  export const PollingStateVersionKey = 3
15
13
 
@@ -18,7 +18,7 @@ export async function getMethodCard(brand) {
18
18
  const introVideo = await fetchMethodV2IntroVideo(brand)
19
19
  const introVideoProgressState = await getProgressState(introVideo?.id)
20
20
  //resetAllLearningPaths()
21
- if (introVideoProgressState != 'completed') {
21
+ if (introVideoProgressState !== 'completed') {
22
22
  //startLearningPath('drumeo', 422533)
23
23
  const timestamp = Math.floor(Date.now() / 1000)
24
24
  return {
@@ -107,5 +107,6 @@ function getMethodActionCTA(item) {
107
107
  brand: item.brand,
108
108
  id: item.id,
109
109
  slug: item.slug,
110
+ parent_id: item.parent_id,
110
111
  }
111
112
  }
@@ -10,13 +10,6 @@ import { fetchJSONHandler } from '../lib/httpHelper.js'
10
10
  * @type {string[]}
11
11
  */
12
12
  const excludeFromGeneratedIndex = [
13
- 'fetchUserLikes',
14
- 'postContentLiked',
15
- 'postContentUnliked',
16
- 'postRecordWatchSession',
17
- 'postContentStarted',
18
- 'postContentComplete',
19
- 'postContentReset',
20
13
  'fetchUserPermissionsData',
21
14
  ]
22
15
 
@@ -275,10 +268,6 @@ export async function fetchUserPermissionsData() {
275
268
  return (await fetchHandler(url, 'get')) ?? []
276
269
  }
277
270
 
278
- async function fetchDataHandler(url, dataVersion, method = 'get') {
279
- return fetchHandler(url, method, dataVersion)
280
- }
281
-
282
271
  async function postDataHandler(url, data) {
283
272
  return fetchHandler(url, 'post', null, data)
284
273
  }
@@ -297,27 +286,7 @@ async function deleteDataHandler(url, data) {
297
286
 
298
287
  export async function fetchLikeCount(contendId) {
299
288
  const url = `/api/content/v1/content/like_count/${contendId}`
300
- return await fetchDataHandler(url)
301
- }
302
-
303
- export async function fetchUserLikes(currentVersion) {
304
- let url = `/api/content/v1/user/likes`
305
- return fetchDataHandler(url, currentVersion)
306
- }
307
-
308
- export async function postContentLiked(contentId) {
309
- let url = `/api/content/v1/user/likes/${contentId}`
310
- return await postDataHandler(url)
311
- }
312
-
313
- export async function postContentUnliked(contentId) {
314
- let url = `/api/content/v1/user/likes/${contentId}`
315
- return await deleteDataHandler(url)
316
- }
317
-
318
- export async function fetchContentProgress(currentVersion) {
319
- let url = `/content/user/progress/all`
320
- return fetchDataHandler(url, currentVersion)
289
+ return await fetchHandler(url)
321
290
  }
322
291
 
323
292
  export async function postPlaylistContentEngaged(playlistItemId) {
@@ -325,25 +294,6 @@ export async function postPlaylistContentEngaged(playlistItemId) {
325
294
  return postDataHandler(url)
326
295
  }
327
296
 
328
- export async function postRecordWatchSession(
329
- contentId,
330
- mediaTypeId,
331
- mediaLengthSeconds,
332
- currentSeconds,
333
- secondsPlayed,
334
- sessionId
335
- ) {
336
- let url = `/railtracker/v2/media-playback-session`
337
- return postDataHandler(url, {
338
- content_id: contentId,
339
- media_type_id: mediaTypeId,
340
- media_length_seconds: mediaLengthSeconds,
341
- current_second: currentSeconds,
342
- seconds_played: secondsPlayed,
343
- session_id: sessionId,
344
- })
345
- }
346
-
347
297
  /**
348
298
  * Fetch the user's best award for this challenge
349
299
  *
@@ -378,58 +328,6 @@ export async function fetchUserBadges(brand = null) {
378
328
  return await fetchHandler(url, 'get')
379
329
  }
380
330
 
381
- /**
382
- * complete a content's progress for a given user
383
- * @param contentId
384
- * @param collection {object|null} - the collection context of the progress. null is normal content progress
385
- * @param collection.type - the type of collection. options: ["learning-path"]
386
- * @param collection.id - the content_id of collection.
387
- * @returns {Promise<any|string|null>}
388
- */
389
- export async function postContentComplete(contentId, collection = null) {
390
- let url = `/api/content/v1/user/progress/complete/${contentId}`
391
- const body = {collection: collection}
392
- return postDataHandler(url, body)
393
- }
394
-
395
- /**
396
- * start the user's progress on a content
397
- * @param contentId
398
- * @param collection {object|null} - the collection context of the progress. null is normal content progress
399
- * @param collection.type - the type of collection. options: ["learning-path"]
400
- * @param collection.id - the content_id of collection.
401
- * @returns {Promise<any|string|null>}
402
- */
403
- export async function postContentStart(contentId, collection = null) {
404
- let url = `/api/content/v1/user/progress/start/${contentId}`
405
- const body = {collection: collection}
406
- return postDataHandler(url, body)
407
- }
408
-
409
- /**
410
- * resets the user's progress on a content
411
- * @param contentId
412
- * @param collection {object|null} - the collection context of the progress. null is normal content progress
413
- * @param collection.type - the type of collection. options: ["learning-path"]
414
- * @param collection.id - the content_id of collection.
415
- * @returns {Promise<any|string|null>}
416
- */
417
- export async function postContentReset(contentId, collection = null) {
418
- let url = `/api/content/v1/user/progress/reset/${contentId}`
419
- const body = {collection: collection}
420
- return postDataHandler(url, body)
421
- }
422
-
423
- /**
424
- * restores the user's progress on a content
425
- * @param contentId
426
- * @returns {Promise<any|string|null>}
427
- */
428
- export async function postContentRestore(contentId) {
429
- let url = `/api/content/v1/user/progress/restore/${contentId}`
430
- return postDataHandler(url)
431
- }
432
-
433
331
  /**
434
332
  * Set a user's StudentView Flag
435
333
  *
@@ -624,24 +522,21 @@ export async function fetchComment(commentId) {
624
522
  return comment.parent ? comment.parent : comment
625
523
  }
626
524
 
627
- export async function fetchUserPractices(currentVersion = 0, { userId } = {}) {
628
- const params = new URLSearchParams()
629
- if (userId) params.append('user_id', userId)
630
- const query = params.toString() ? `?${params.toString()}` : ''
631
- const url = `/api/user/practices/v1/practices${query}`
632
- const response = await fetchDataHandler(url, currentVersion)
633
- const { data, version } = response
525
+ export async function fetchUserPractices(userId) {
526
+ const url = `/api/user/practices/v1/practices?user_id=${userId}`
527
+ const response = await fetchHandler(url)
528
+ const { data } = response
634
529
  const userPractices = data
635
530
  if (!userPractices) {
636
- return { data: { practices: {} }, version }
531
+ return {}
637
532
  }
638
533
 
639
534
  const formattedPractices = userPractices.reduce((acc, practice) => {
640
- if (!acc[practice.day]) {
641
- acc[practice.day] = []
535
+ if (!acc[practice.date]) {
536
+ acc[practice.date] = []
642
537
  }
643
538
 
644
- acc[practice.day].push({
539
+ acc[practice.date].push({
645
540
  id: practice.id,
646
541
  duration_seconds: practice.duration_seconds,
647
542
  })
@@ -649,29 +544,11 @@ export async function fetchUserPractices(currentVersion = 0, { userId } = {}) {
649
544
  return acc
650
545
  }, {})
651
546
 
652
- return {
653
- data: {
654
- practices: formattedPractices,
655
- },
656
- version,
657
- }
547
+ return formattedPractices
658
548
  }
659
549
 
660
- export async function logUserPractice(practiceDetails) {
661
- const url = `/api/user/practices/v1/practices`
662
- return await fetchHandler(url, 'POST', null, practiceDetails)
663
- }
664
- export async function fetchUserPracticeMeta(practiceIds, userId = null) {
665
- if (practiceIds.length == 0) {
666
- return []
667
- }
668
- const params = new URLSearchParams()
669
- practiceIds.forEach((id) => params.append('practice_ids[]', id))
670
-
671
- if (userId !== null) {
672
- params.append('user_id', userId)
673
- }
674
- const url = `/api/user/practices/v1/practices?${params.toString()}`
550
+ export async function fetchUserPracticeMeta(day, userId) {
551
+ const url = `/api/user/practices/v1/practices?user_id=${userId}&date=${date}`
675
552
  return await fetchHandler(url, 'GET', null)
676
553
  }
677
554
 
File without changes
@@ -0,0 +1,23 @@
1
+ import * as Sentry from "@sentry/browser";
2
+
3
+ type SentryBrowserInitOptions = NonNullable<Parameters<typeof Sentry.init>[0]>;
4
+
5
+ type TracesSampler = SentryBrowserInitOptions['tracesSampler'];
6
+ type BeforeSend = SentryBrowserInitOptions['beforeSend'];
7
+ type BeforeSendTransaction = SentryBrowserInitOptions['beforeSendTransaction'];
8
+
9
+ // Compose multiple handlers of the same type into one.
10
+ // Stops at first handler that returns a non-undefined value.
11
+
12
+ export function composeHandlers<H extends TracesSampler>(...handlers: H[]): H;
13
+ export function composeHandlers<H extends BeforeSend>(...handlers: H[]): H;
14
+ export function composeHandlers<H extends BeforeSendTransaction>(...handlers: H[]): H;
15
+ export function composeHandlers<H extends (...args: any[]) => any>(...handlers: H[]): H {
16
+ return ((...args: Parameters<H>) => {
17
+ for (const handler of handlers) {
18
+ const res = handler(...args);
19
+ if (res !== undefined) return res;
20
+ }
21
+ return args[0]
22
+ }) as H;
23
+ }
File without changes
@@ -0,0 +1,26 @@
1
+ import schema from '../schema'
2
+
3
+ import type SQLiteAdapter from '@nozbe/watermelondb/adapters/sqlite'
4
+ import type LokiJSAdapter from '@nozbe/watermelondb/adapters/lokijs'
5
+
6
+ export type DatabaseAdapter = SQLiteAdapter | LokiJSAdapter
7
+
8
+ type SQLiteAdapterOptions = ConstructorParameters<typeof SQLiteAdapter>[0]
9
+ type LokiJSAdapterOptions = ConstructorParameters<typeof LokiJSAdapter>[0]
10
+
11
+ type DatabaseAdapterOptions = SQLiteAdapterOptions & LokiJSAdapterOptions
12
+
13
+ export default function syncAdapterFactory<T extends DatabaseAdapter>(
14
+ AdapterClass: new (options: DatabaseAdapterOptions) => T,
15
+ namespace: string,
16
+ opts: Omit<DatabaseAdapterOptions, 'schema' | 'migrations'>
17
+ ): () => T {
18
+ const options = {
19
+ ...opts,
20
+ dbName: `sync:${namespace}`,
21
+ schema,
22
+ migrations: undefined
23
+ }
24
+
25
+ return () => new AdapterClass(options)
26
+ }
@@ -0,0 +1 @@
1
+ export { default } from '@nozbe/watermelondb/adapters/lokijs'
@@ -0,0 +1 @@
1
+ export { default } from '@nozbe/watermelondb/adapters/sqlite'
@@ -0,0 +1,4 @@
1
+ import type SyncContext from "./context"
2
+ import type SyncStore from "./store"
3
+
4
+ export type SyncConcurrencySafetyMechanism = (context: SyncContext, stores: SyncStore[]) => () => void
@@ -0,0 +1,43 @@
1
+ import type {
2
+ BaseSessionProvider,
3
+ BaseConnectivityProvider,
4
+ BaseVisibilityProvider,
5
+ BaseTabsProvider,
6
+ BaseDurabilityProvider,
7
+ } from './providers'
8
+
9
+ type Providers = {
10
+ session: BaseSessionProvider
11
+ connectivity: BaseConnectivityProvider
12
+ visibility: BaseVisibilityProvider
13
+ tabs: BaseTabsProvider
14
+ durability: BaseDurabilityProvider
15
+ }
16
+
17
+ export default class SyncContext {
18
+ constructor(private providers: Providers) {}
19
+
20
+ start() {
21
+ Object.values(this.providers).forEach((p) => p.start())
22
+ }
23
+
24
+ stop() {
25
+ Object.values(this.providers).forEach((p) => p.stop())
26
+ }
27
+
28
+ get session() {
29
+ return this.providers.session
30
+ }
31
+ get connectivity() {
32
+ return this.providers.connectivity
33
+ }
34
+ get visibility() {
35
+ return this.providers.visibility
36
+ }
37
+ get tabs() {
38
+ return this.providers.tabs
39
+ }
40
+ get durability() {
41
+ return this.providers.durability
42
+ }
43
+ }
@@ -0,0 +1,4 @@
1
+ export default class BaseContextProvider {
2
+ start() {}
3
+ stop() {}
4
+ }
@@ -0,0 +1,14 @@
1
+ import EventEmitter from "../../utils/event-emitter";
2
+ import BaseContextProvider from "./base";
3
+
4
+ export default abstract class BaseConnectivityProvider extends BaseContextProvider {
5
+ private emitter = new EventEmitter<{ change: [boolean] }>()
6
+
7
+ abstract getValue(): boolean
8
+
9
+ subscribe(listener: (value: boolean) => void) {
10
+ return this.emitter.on('change', listener)
11
+ }
12
+
13
+ protected notifyListeners = () => this.emitter.emit('change', this.getValue())
14
+ }
@@ -0,0 +1,5 @@
1
+ import BaseContextProvider from "./base";
2
+
3
+ export default abstract class BaseDurabilityProvider extends BaseContextProvider {
4
+ abstract getValue(): boolean
5
+ }
@@ -0,0 +1,5 @@
1
+ export { default as BaseSessionProvider } from './session'
2
+ export { default as BaseConnectivityProvider } from './connectivity'
3
+ export { default as BaseVisibilityProvider } from './visibility'
4
+ export { default as BaseTabsProvider, NullTabsProvider } from './tabs'
5
+ export { default as BaseDurabilityProvider } from './durability'
@@ -0,0 +1,8 @@
1
+ import BaseContextProvider from "./base";
2
+
3
+ export default abstract class BaseSessionProvider extends BaseContextProvider {
4
+ abstract getClientId(): string
5
+ getSessionId(): string | null {
6
+ return null
7
+ }
8
+ }
@@ -0,0 +1,18 @@
1
+ import BaseContextProvider from "./base";
2
+
3
+ export default abstract class BaseTabsProvider extends BaseContextProvider {
4
+ abstract hasOtherTabs(): boolean
5
+ abstract broadcast<T>(name: string, payload: T): void
6
+ abstract subscribe<T>(name: string, callback: (payload: T) => void): () => void
7
+ }
8
+
9
+ export class NullTabsProvider extends BaseTabsProvider {
10
+ hasOtherTabs() {
11
+ return false
12
+ }
13
+
14
+ broadcast() {}
15
+ subscribe() {
16
+ return () => {}
17
+ }
18
+ }
@@ -0,0 +1,14 @@
1
+ import EventEmitter from "../../utils/event-emitter";
2
+ import BaseContextProvider from "./base";
3
+
4
+ export default abstract class BaseVisibilityProvider extends BaseContextProvider {
5
+ private emitter = new EventEmitter<{ change: [boolean] }>()
6
+
7
+ abstract getValue(): boolean
8
+
9
+ subscribe(listener: (value: boolean) => void) {
10
+ return this.emitter.on('change', listener)
11
+ }
12
+
13
+ protected notifyListeners = () => this.emitter.emit('change', this.getValue())
14
+ }
@@ -0,0 +1,10 @@
1
+ import type { DatabaseAdapter } from '../adapters/factory'
2
+ import { Database, } from '@nozbe/watermelondb'
3
+ import * as modelClasses from '../models'
4
+
5
+ export default function syncDatabaseFactory(adapter: () => DatabaseAdapter) {
6
+ return () => new Database({
7
+ adapter: adapter(),
8
+ modelClasses: Object.values(modelClasses)
9
+ })
10
+ }
@@ -0,0 +1,45 @@
1
+ import { SyncTelemetry } from "../telemetry/index";
2
+ import { SyncError, SyncUnexpectedError } from "./index";
3
+
4
+ /**
5
+ * Safely executes a function within a "sync boundary" — ensuring that
6
+ * any thrown or rejected errors (even from asynchronous code) are caught,
7
+ * wrapped, and reported via SyncManager telemetry.
8
+ *
9
+ * This is especially useful for code that runs "out of band" — meaning
10
+ * it's not directly part of the main sync pipeline, and errors might
11
+ * otherwise not be decorated/reported how we like (like in an generic
12
+ * app-wide global error handler).
13
+ *
14
+ * - Automatically catches both synchronous and asynchronous errors.
15
+ * - Wraps unknown errors in `SyncUnexpectedError`, including optional `context`.
16
+ * - Reports all handled errors through `SyncManager.telemetry.capture`.
17
+ *
18
+ * @param fn The function to run inside the error boundary.
19
+ * @param context Optional contextual details to include in captured errors.
20
+ * @returns The result of `fn`, or a promise that resolves to it.
21
+ */
22
+
23
+ export function inBoundary<T, TContext extends Record<string, any>>(fn: (context: TContext) => T, context?: TContext): T;
24
+ export function inBoundary<T, TContext extends Record<string, any>>(fn: (context: TContext) => Promise<T>, context?: TContext): Promise<T>;
25
+ export function inBoundary<T, TContext extends Record<string, any>>(fn: (context: TContext) => T | Promise<T>, context?: TContext): T | Promise<T> {
26
+ try {
27
+ const result = fn(context || ({} as TContext));
28
+
29
+ if (result instanceof Promise) {
30
+ return result.catch((err: unknown) => {
31
+ const wrapped = err instanceof SyncError ? err : new SyncUnexpectedError((err as Error).message, context);
32
+ SyncTelemetry.getInstance().capture(wrapped)
33
+
34
+ throw wrapped;
35
+ });
36
+ }
37
+
38
+ return result;
39
+ } catch (err: unknown) {
40
+ const wrapped = err instanceof SyncError ? err : new SyncUnexpectedError((err as Error).message, context);
41
+ SyncTelemetry.getInstance().capture(wrapped);
42
+
43
+ throw wrapped;
44
+ }
45
+ }
@@ -0,0 +1,49 @@
1
+ import SyncStore from "../store"
2
+
3
+ type ErrorDetails = Record<string, unknown>
4
+ type Without<TRecord, T extends string> = {
5
+ [K in T]?: never
6
+ } & TRecord
7
+
8
+ export class SyncError extends Error {
9
+ private _reported = false
10
+ readonly details?: ErrorDetails
11
+
12
+ constructor(message: string, details?: ErrorDetails) {
13
+ super(message)
14
+ this.name = 'SyncError'
15
+ Object.setPrototypeOf(this, new.target.prototype)
16
+
17
+ this.details = details
18
+ }
19
+
20
+ markReported() {
21
+ this._reported = true
22
+ }
23
+
24
+ isReported() {
25
+ return this._reported
26
+ }
27
+
28
+ getDetails() {
29
+ return this.details
30
+ }
31
+ }
32
+
33
+ export class SyncStoreError extends SyncError {
34
+ constructor(message: string, store: SyncStore, details?: Without<ErrorDetails, 'store'>) {
35
+ super(message, { ...details, store })
36
+ this.name = 'SyncStoreError'
37
+ Object.setPrototypeOf(this, new.target.prototype)
38
+ }
39
+ }
40
+
41
+ // useful for transforming non-sync-related errors into one
42
+ // that captures surrounding details (e.g., table name)
43
+ export class SyncUnexpectedError extends SyncError {
44
+ constructor(message: string, details?: ErrorDetails) {
45
+ super(message, details)
46
+ this.name = 'SyncUnexpectedError'
47
+ Object.setPrototypeOf(this, new.target.prototype)
48
+ }
49
+ }