@vue-skuilder/db 0.2.13 → 0.2.15

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 (37) hide show
  1. package/dist/{contentSource-C-0t0y0V.d.ts → contentSource-B1p-vdz7.d.ts} +7 -0
  2. package/dist/{contentSource-jSkcOt2s.d.cts → contentSource-Brz42x7n.d.cts} +7 -0
  3. package/dist/core/index.d.cts +3 -3
  4. package/dist/core/index.d.ts +3 -3
  5. package/dist/core/index.js +6 -3
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +6 -3
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-BB0oi9T0.d.ts → dataLayerProvider-BWayUIoK.d.ts} +1 -1
  10. package/dist/{dataLayerProvider-BDClIrFC.d.cts → dataLayerProvider-CpwpT1rM.d.cts} +1 -1
  11. package/dist/impl/couch/index.d.cts +2 -2
  12. package/dist/impl/couch/index.d.ts +2 -2
  13. package/dist/impl/couch/index.js +6 -3
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +6 -3
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/index.d.cts +2 -2
  18. package/dist/impl/static/index.d.ts +2 -2
  19. package/dist/impl/static/index.js +6 -3
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +6 -3
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/index.d.cts +47 -5
  24. package/dist/index.d.ts +47 -5
  25. package/dist/index.js +50 -30
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +45 -30
  28. package/dist/index.mjs.map +1 -1
  29. package/package.json +3 -3
  30. package/src/core/interfaces/userDB.ts +8 -0
  31. package/src/impl/common/BaseUserDB.ts +2 -2
  32. package/src/impl/common/userDBHelpers.ts +14 -1
  33. package/src/index.ts +6 -0
  34. package/src/study/SessionOverlay.ts +16 -3
  35. package/src/study/index.ts +4 -0
  36. package/src/study/services/EloService.ts +20 -8
  37. package/src/study/services/ResponseProcessor.ts +62 -50
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-jSkcOt2s.cjs';
2
- export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ad as DocumentUpdater, a5 as GeneratorContext, a7 as LearnableWeight, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, a8 as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, Z as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, M as UserOutcomeRecord, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, ae as newInterval, O as registerNavigator } from './contentSource-jSkcOt2s.cjs';
3
- import { D as DataLayerProvider } from './dataLayerProvider-BDClIrFC.cjs';
1
+ import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-Brz42x7n.cjs';
2
+ export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ad as DocumentUpdater, a5 as GeneratorContext, a7 as LearnableWeight, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, a8 as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, Z as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, M as UserOutcomeRecord, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, ae as newInterval, O as registerNavigator } from './contentSource-Brz42x7n.cjs';
3
+ import { D as DataLayerProvider } from './dataLayerProvider-CpwpT1rM.cjs';
4
4
  import { C as CardHistory, c as CardRecord, d as QuestionRecord } from './types-legacy-4tlwHnXo.cjs';
5
5
  export { e as CardData, f as CourseListData, h as DataShapeData, g as DisplayableData, D as DocType, b as DocTypePrefixes, F as Field, G as GuestUsername, Q as QualifiedCardID, i as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from './types-legacy-4tlwHnXo.cjs';
6
6
  import { SrsBacklogDebug, Loggable } from './core/index.cjs';
@@ -10,7 +10,7 @@ import { S as StaticCourseManifest } from './types-BFUa1pa3.cjs';
10
10
  export { A as AttachmentData, C as ChunkMetadata, D as DesignDocument, I as IndexMetadata, a as PackedCourseData, P as PackerConfig } from './types-BFUa1pa3.cjs';
11
11
  import { F as FileSystemAdapter } from './index-k9NFHpS1.cjs';
12
12
  export { C as CouchDBToStaticPacker, a as FileStats, b as FileSystemError } from './index-k9NFHpS1.cjs';
13
- import 'moment';
13
+ import moment from 'moment';
14
14
 
15
15
  /**
16
16
  * Service responsible for ELO rating calculations and updates.
@@ -70,6 +70,12 @@ declare class ResponseProcessor {
70
70
  private srsService;
71
71
  private eloService;
72
72
  constructor(srsService: SrsService, eloService: EloService);
73
+ /**
74
+ * ELO updates are fired without awaiting so response handling isn't blocked
75
+ * on DB writes — but an unhandled rejection silently drops the update.
76
+ * This produces a catch handler that surfaces the failure in logs.
77
+ */
78
+ private logEloFailure;
73
79
  /**
74
80
  * Parses performance data into global score and optional per-tag scores.
75
81
  *
@@ -362,6 +368,30 @@ interface SessionDebugSnapshot {
362
368
  /** Every card the learner has interacted with this session, draw order. */
363
369
  drawnCards: SessionDrawnCardDebug[];
364
370
  }
371
+ /** The narrow surface the overlay needs from a SessionController. */
372
+ interface SessionDebugTarget {
373
+ getDebugSnapshot(): SessionDebugSnapshot;
374
+ }
375
+ /**
376
+ * The live-session surface available to out-of-tree callers through the
377
+ * active-controller registry: the debug snapshot, plus the small set of
378
+ * user-facing session controls a view may need without holding a ref into
379
+ * the `<StudySession>` component's internals (e.g. wiring a timer's
380
+ * add-time button from a composable). Keep this narrow — it is not a
381
+ * general escape hatch to `SessionController`.
382
+ */
383
+ interface ActiveSessionHandle extends SessionDebugTarget {
384
+ /** Extend the session clock by `seconds`. */
385
+ addTime(seconds: number): void;
386
+ }
387
+ /** Called by SessionController's constructor. Pass `null` to deregister. */
388
+ declare function registerActiveController(controller: ActiveSessionHandle | null): void;
389
+ declare function getActiveController(): ActiveSessionHandle | null;
390
+ /**
391
+ * Toggle the pinned overlay on/off. No-ops (with a console hint) when there is
392
+ * no DOM, so it is safe to call from any host environment.
393
+ */
394
+ declare function toggleSessionOverlay(): void;
365
395
 
366
396
  /**
367
397
  * Options for requesting a mid-session replan.
@@ -1460,6 +1490,18 @@ declare function getDataLayer(): DataLayerProvider;
1460
1490
  */
1461
1491
  declare function _resetDataLayer(): Promise<void>;
1462
1492
 
1493
+ declare const REVIEW_TIME_FORMAT: string;
1494
+ /**
1495
+ * Build a ScheduledCard `_id` for the given review time.
1496
+ *
1497
+ * The id suffix is not a mere uniquifier: `getReviewstoDate` PARSES the id
1498
+ * (with {@link REVIEW_TIME_FORMAT}) to decide due-ness, so the id is the
1499
+ * datum. Anything that writes or rewrites scheduled-card docs (scheduling,
1500
+ * user-state snapshot rebasing) must construct ids through this helper —
1501
+ * a hand-rolled formatter will get moment's `kk` token (01-24 hours) wrong.
1502
+ */
1503
+ declare function makeScheduledCardId(reviewTime: string | Date | moment.Moment): string;
1504
+
1463
1505
  type UserAccountStatus = 'pending_verification' | 'verified' | 'suspended';
1464
1506
  interface Entitlement {
1465
1507
  status: 'trial' | 'paid';
@@ -1479,4 +1521,4 @@ interface CouchDbUserDoc extends PouchDB.Authentication.User {
1479
1521
  entitlements: UserEntitlements;
1480
1522
  }
1481
1523
 
1482
- export { type AggregatedDocument, type AttachmentUploadResult, CardHistory, type CardPresentation, CardRecord, type CouchDbUserDoc, CourseDBInterface, CourseLookup, CourseRegistrationDoc, type CustomQuestionsData, DEFAULT_MIGRATION_OPTIONS, type DataLayerConfig, DataLayerProvider, type DocumentCounts, ENV, type Entitlement, FileSystemAdapter, GeneratorResult, Loggable, type MigrationOptions, type MigrationResult, type MixerCardInfo, type MixerRunReport, NOT_SET, type OutcomeObserver, type ProcessedDataShape, type ProcessedQuestionData, QuestionRecord, type QueueSnapshot, QuotaRoundRobinMixer, ReplanHints, type ReplanOptions, type ResponseResult, type RestoreProgress, type SessionAction, SessionController, type SessionControls, type SessionOutcome, type SessionRunReport, type SourceBatch, type SourceMixer, type SourceSelectionBreakdown, type SourceSummary, SrsBacklogDebug, StaticCourseManifest, type StaticCourseValidation, StaticToCouchDBMigrator, StudyContentSource, StudySessionItem, type StudySessionRecord, TagFilteredContentSource, type UserAccountStatus, UserDBInterface, type UserEntitlements, type ValidationIssue, type ValidationResult, WeightedCard, _resetDataLayer, captureMixerRun, clearStaleSessionDebugState, endSessionTracking, ensureAppDataDirectory, getAppDataDirectory, getDataLayer, getDbPath, initializeDataDirectory, initializeDataLayer, isDataShapeRegistered, isDataShapeSchemaAvailable, isQuestionTypeRegistered, mixerDebugAPI, mountMixerDebugger, mountSessionDebugger, processCustomQuestionsData, recordCardPresentation, registerBlanksCard, registerCustomQuestionTypes, registerDataShape, registerQuestionType, registerSeedData, removeCustomQuestionTypes, removeDataShape, removeQuestionType, sessionDebugAPI, snapshotQueues, startSessionTracking, validateMigration, validateStaticCourse };
1524
+ export { type ActiveSessionHandle, type AggregatedDocument, type AttachmentUploadResult, CardHistory, type CardPresentation, CardRecord, type CouchDbUserDoc, CourseDBInterface, CourseLookup, CourseRegistrationDoc, type CustomQuestionsData, DEFAULT_MIGRATION_OPTIONS, type DataLayerConfig, DataLayerProvider, type DocumentCounts, ENV, type Entitlement, FileSystemAdapter, GeneratorResult, Loggable, type MigrationOptions, type MigrationResult, type MixerCardInfo, type MixerRunReport, NOT_SET, type OutcomeObserver, type ProcessedDataShape, type ProcessedQuestionData, QuestionRecord, type QueueSnapshot, QuotaRoundRobinMixer, REVIEW_TIME_FORMAT, ReplanHints, type ReplanOptions, type ResponseResult, type RestoreProgress, type SessionAction, SessionController, type SessionControls, type SessionDebugSnapshot, type SessionDebugTarget, type SessionDrawnCardDebug, type SessionOutcome, type SessionQueueDebug, type SessionQueueItemDebug, type SessionRunReport, type SourceBatch, type SourceMixer, type SourceSelectionBreakdown, type SourceSummary, SrsBacklogDebug, StaticCourseManifest, type StaticCourseValidation, StaticToCouchDBMigrator, StudyContentSource, StudySessionItem, type StudySessionRecord, TagFilteredContentSource, type UserAccountStatus, UserDBInterface, type UserEntitlements, type ValidationIssue, type ValidationResult, WeightedCard, _resetDataLayer, captureMixerRun, clearStaleSessionDebugState, endSessionTracking, ensureAppDataDirectory, getActiveController, getAppDataDirectory, getDataLayer, getDbPath, initializeDataDirectory, initializeDataLayer, isDataShapeRegistered, isDataShapeSchemaAvailable, isQuestionTypeRegistered, makeScheduledCardId, mixerDebugAPI, mountMixerDebugger, mountSessionDebugger, processCustomQuestionsData, recordCardPresentation, registerActiveController, registerBlanksCard, registerCustomQuestionTypes, registerDataShape, registerQuestionType, registerSeedData, removeCustomQuestionTypes, removeDataShape, removeQuestionType, sessionDebugAPI, snapshotQueues, startSessionTracking, toggleSessionOverlay, validateMigration, validateStaticCourse };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-C-0t0y0V.js';
2
- export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ad as DocumentUpdater, a5 as GeneratorContext, a7 as LearnableWeight, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, a8 as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, Z as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, M as UserOutcomeRecord, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, ae as newInterval, O as registerNavigator } from './contentSource-C-0t0y0V.js';
3
- import { D as DataLayerProvider } from './dataLayerProvider-BB0oi9T0.js';
1
+ import { U as UserDBInterface, s as CourseRegistrationDoc, S as StudySessionItem, W as WeightedCard, R as ReplanHints, h as StudyContentSource, G as GeneratorResult, C as CourseDBInterface } from './contentSource-B1p-vdz7.js';
2
+ export { K as ActivityRecord, A as AdminDBInterface, v as AssignedCard, g as AssignedContent, u as AssignedCourse, t as AssignedTag, a4 as CardGenerator, a6 as CardGeneratorFactory, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, H as ClassroomRegistrationDoc, e as ContentNavigationStrategyData, f as ContentNavigator, q as ContentSourceID, d as CourseInfo, L as CourseRegistration, b as CoursesDBInterface, ad as DocumentUpdater, a5 as GeneratorContext, a7 as LearnableWeight, N as NavigatorConstructor, a0 as NavigatorRole, a1 as NavigatorRoles, $ as Navigators, a8 as OrchestrationContext, j as ScheduledCard, I as SessionTrackingData, Z as StrategyContribution, i as StudentClassroomDBInterface, k as StudySessionFailedItem, l as StudySessionFailedNewItem, m as StudySessionFailedReviewItem, n as StudySessionNewItem, o as StudySessionReviewItem, T as TeacherClassroomDBInterface, J as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, M as UserOutcomeRecord, B as UsrCrsDataInterface, a9 as computeDeviation, ab as computeEffectiveWeight, aa as computeSpread, ac as createOrchestrationContext, _ as getCardOrigin, P as getRegisteredNavigator, X as getRegisteredNavigatorNames, V as getRegisteredNavigatorRole, r as getStudySource, Q as hasRegisteredNavigator, Y as initializeNavigatorRegistry, a3 as isFilter, a2 as isGenerator, p as isReview, ae as newInterval, O as registerNavigator } from './contentSource-B1p-vdz7.js';
3
+ import { D as DataLayerProvider } from './dataLayerProvider-BWayUIoK.js';
4
4
  import { C as CardHistory, c as CardRecord, d as QuestionRecord } from './types-legacy-4tlwHnXo.js';
5
5
  export { e as CardData, f as CourseListData, h as DataShapeData, g as DisplayableData, D as DocType, b as DocTypePrefixes, F as Field, G as GuestUsername, Q as QualifiedCardID, i as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from './types-legacy-4tlwHnXo.js';
6
6
  import { SrsBacklogDebug, Loggable } from './core/index.js';
@@ -10,7 +10,7 @@ import { S as StaticCourseManifest } from './types-CHgpWQAY.js';
10
10
  export { A as AttachmentData, C as ChunkMetadata, D as DesignDocument, I as IndexMetadata, a as PackedCourseData, P as PackerConfig } from './types-CHgpWQAY.js';
11
11
  import { F as FileSystemAdapter } from './index-BLLT5BYE.js';
12
12
  export { C as CouchDBToStaticPacker, a as FileStats, b as FileSystemError } from './index-BLLT5BYE.js';
13
- import 'moment';
13
+ import moment from 'moment';
14
14
 
15
15
  /**
16
16
  * Service responsible for ELO rating calculations and updates.
@@ -70,6 +70,12 @@ declare class ResponseProcessor {
70
70
  private srsService;
71
71
  private eloService;
72
72
  constructor(srsService: SrsService, eloService: EloService);
73
+ /**
74
+ * ELO updates are fired without awaiting so response handling isn't blocked
75
+ * on DB writes — but an unhandled rejection silently drops the update.
76
+ * This produces a catch handler that surfaces the failure in logs.
77
+ */
78
+ private logEloFailure;
73
79
  /**
74
80
  * Parses performance data into global score and optional per-tag scores.
75
81
  *
@@ -362,6 +368,30 @@ interface SessionDebugSnapshot {
362
368
  /** Every card the learner has interacted with this session, draw order. */
363
369
  drawnCards: SessionDrawnCardDebug[];
364
370
  }
371
+ /** The narrow surface the overlay needs from a SessionController. */
372
+ interface SessionDebugTarget {
373
+ getDebugSnapshot(): SessionDebugSnapshot;
374
+ }
375
+ /**
376
+ * The live-session surface available to out-of-tree callers through the
377
+ * active-controller registry: the debug snapshot, plus the small set of
378
+ * user-facing session controls a view may need without holding a ref into
379
+ * the `<StudySession>` component's internals (e.g. wiring a timer's
380
+ * add-time button from a composable). Keep this narrow — it is not a
381
+ * general escape hatch to `SessionController`.
382
+ */
383
+ interface ActiveSessionHandle extends SessionDebugTarget {
384
+ /** Extend the session clock by `seconds`. */
385
+ addTime(seconds: number): void;
386
+ }
387
+ /** Called by SessionController's constructor. Pass `null` to deregister. */
388
+ declare function registerActiveController(controller: ActiveSessionHandle | null): void;
389
+ declare function getActiveController(): ActiveSessionHandle | null;
390
+ /**
391
+ * Toggle the pinned overlay on/off. No-ops (with a console hint) when there is
392
+ * no DOM, so it is safe to call from any host environment.
393
+ */
394
+ declare function toggleSessionOverlay(): void;
365
395
 
366
396
  /**
367
397
  * Options for requesting a mid-session replan.
@@ -1460,6 +1490,18 @@ declare function getDataLayer(): DataLayerProvider;
1460
1490
  */
1461
1491
  declare function _resetDataLayer(): Promise<void>;
1462
1492
 
1493
+ declare const REVIEW_TIME_FORMAT: string;
1494
+ /**
1495
+ * Build a ScheduledCard `_id` for the given review time.
1496
+ *
1497
+ * The id suffix is not a mere uniquifier: `getReviewstoDate` PARSES the id
1498
+ * (with {@link REVIEW_TIME_FORMAT}) to decide due-ness, so the id is the
1499
+ * datum. Anything that writes or rewrites scheduled-card docs (scheduling,
1500
+ * user-state snapshot rebasing) must construct ids through this helper —
1501
+ * a hand-rolled formatter will get moment's `kk` token (01-24 hours) wrong.
1502
+ */
1503
+ declare function makeScheduledCardId(reviewTime: string | Date | moment.Moment): string;
1504
+
1463
1505
  type UserAccountStatus = 'pending_verification' | 'verified' | 'suspended';
1464
1506
  interface Entitlement {
1465
1507
  status: 'trial' | 'paid';
@@ -1479,4 +1521,4 @@ interface CouchDbUserDoc extends PouchDB.Authentication.User {
1479
1521
  entitlements: UserEntitlements;
1480
1522
  }
1481
1523
 
1482
- export { type AggregatedDocument, type AttachmentUploadResult, CardHistory, type CardPresentation, CardRecord, type CouchDbUserDoc, CourseDBInterface, CourseLookup, CourseRegistrationDoc, type CustomQuestionsData, DEFAULT_MIGRATION_OPTIONS, type DataLayerConfig, DataLayerProvider, type DocumentCounts, ENV, type Entitlement, FileSystemAdapter, GeneratorResult, Loggable, type MigrationOptions, type MigrationResult, type MixerCardInfo, type MixerRunReport, NOT_SET, type OutcomeObserver, type ProcessedDataShape, type ProcessedQuestionData, QuestionRecord, type QueueSnapshot, QuotaRoundRobinMixer, ReplanHints, type ReplanOptions, type ResponseResult, type RestoreProgress, type SessionAction, SessionController, type SessionControls, type SessionOutcome, type SessionRunReport, type SourceBatch, type SourceMixer, type SourceSelectionBreakdown, type SourceSummary, SrsBacklogDebug, StaticCourseManifest, type StaticCourseValidation, StaticToCouchDBMigrator, StudyContentSource, StudySessionItem, type StudySessionRecord, TagFilteredContentSource, type UserAccountStatus, UserDBInterface, type UserEntitlements, type ValidationIssue, type ValidationResult, WeightedCard, _resetDataLayer, captureMixerRun, clearStaleSessionDebugState, endSessionTracking, ensureAppDataDirectory, getAppDataDirectory, getDataLayer, getDbPath, initializeDataDirectory, initializeDataLayer, isDataShapeRegistered, isDataShapeSchemaAvailable, isQuestionTypeRegistered, mixerDebugAPI, mountMixerDebugger, mountSessionDebugger, processCustomQuestionsData, recordCardPresentation, registerBlanksCard, registerCustomQuestionTypes, registerDataShape, registerQuestionType, registerSeedData, removeCustomQuestionTypes, removeDataShape, removeQuestionType, sessionDebugAPI, snapshotQueues, startSessionTracking, validateMigration, validateStaticCourse };
1524
+ export { type ActiveSessionHandle, type AggregatedDocument, type AttachmentUploadResult, CardHistory, type CardPresentation, CardRecord, type CouchDbUserDoc, CourseDBInterface, CourseLookup, CourseRegistrationDoc, type CustomQuestionsData, DEFAULT_MIGRATION_OPTIONS, type DataLayerConfig, DataLayerProvider, type DocumentCounts, ENV, type Entitlement, FileSystemAdapter, GeneratorResult, Loggable, type MigrationOptions, type MigrationResult, type MixerCardInfo, type MixerRunReport, NOT_SET, type OutcomeObserver, type ProcessedDataShape, type ProcessedQuestionData, QuestionRecord, type QueueSnapshot, QuotaRoundRobinMixer, REVIEW_TIME_FORMAT, ReplanHints, type ReplanOptions, type ResponseResult, type RestoreProgress, type SessionAction, SessionController, type SessionControls, type SessionDebugSnapshot, type SessionDebugTarget, type SessionDrawnCardDebug, type SessionOutcome, type SessionQueueDebug, type SessionQueueItemDebug, type SessionRunReport, type SourceBatch, type SourceMixer, type SourceSelectionBreakdown, type SourceSummary, SrsBacklogDebug, StaticCourseManifest, type StaticCourseValidation, StaticToCouchDBMigrator, StudyContentSource, StudySessionItem, type StudySessionRecord, TagFilteredContentSource, type UserAccountStatus, UserDBInterface, type UserEntitlements, type ValidationIssue, type ValidationResult, WeightedCard, _resetDataLayer, captureMixerRun, clearStaleSessionDebugState, endSessionTracking, ensureAppDataDirectory, getActiveController, getAppDataDirectory, getDataLayer, getDbPath, initializeDataDirectory, initializeDataLayer, isDataShapeRegistered, isDataShapeSchemaAvailable, isQuestionTypeRegistered, makeScheduledCardId, mixerDebugAPI, mountMixerDebugger, mountSessionDebugger, processCustomQuestionsData, recordCardPresentation, registerActiveController, registerBlanksCard, registerCustomQuestionTypes, registerDataShape, registerQuestionType, registerSeedData, removeCustomQuestionTypes, removeDataShape, removeQuestionType, sessionDebugAPI, snapshotQueues, startSessionTracking, toggleSessionOverlay, validateMigration, validateStaticCourse };
package/dist/index.js CHANGED
@@ -243,6 +243,9 @@ var init_dataDirectory = __esm({
243
243
  });
244
244
 
245
245
  // src/impl/common/userDBHelpers.ts
246
+ function makeScheduledCardId(reviewTime) {
247
+ return DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */] + import_moment.default.utc(reviewTime).format(REVIEW_TIME_FORMAT);
248
+ }
246
249
  function hexEncode(str) {
247
250
  let hex;
248
251
  let returnStr = "";
@@ -298,7 +301,7 @@ function scheduleCardReviewLocal(userDB, review) {
298
301
  const now = import_moment.default.utc();
299
302
  logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
300
303
  void userDB.put({
301
- _id: DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */] + review.time.format(REVIEW_TIME_FORMAT),
304
+ _id: makeScheduledCardId(review.time),
302
305
  cardId: review.card_id,
303
306
  reviewTime: review.time.toISOString(),
304
307
  courseId: review.course_id,
@@ -8008,9 +8011,9 @@ Currently logged-in as ${this._username}.`
8008
8011
  }
8009
8012
  }).map((r) => r.doc);
8010
8013
  }
8011
- async getReviewsForcast(daysCount) {
8014
+ async getReviewsForcast(daysCount, course_id) {
8012
8015
  const time = import_moment6.default.utc().add(daysCount, "days");
8013
- return this.getReviewstoDate(time);
8016
+ return this.getReviewstoDate(time, course_id);
8014
8017
  }
8015
8018
  async getPendingReviews(course_id) {
8016
8019
  const now = import_moment6.default.utc();
@@ -10577,6 +10580,7 @@ __export(index_exports, {
10577
10580
  NavigatorRoles: () => NavigatorRoles,
10578
10581
  Navigators: () => Navigators,
10579
10582
  QuotaRoundRobinMixer: () => QuotaRoundRobinMixer,
10583
+ REVIEW_TIME_FORMAT: () => REVIEW_TIME_FORMAT,
10580
10584
  SessionController: () => SessionController,
10581
10585
  StaticToCouchDBMigrator: () => StaticToCouchDBMigrator,
10582
10586
  TagFilteredContentSource: () => TagFilteredContentSource,
@@ -10597,6 +10601,7 @@ __export(index_exports, {
10597
10601
  docIsDeleted: () => docIsDeleted,
10598
10602
  endSessionTracking: () => endSessionTracking,
10599
10603
  ensureAppDataDirectory: () => ensureAppDataDirectory,
10604
+ getActiveController: () => getActiveController,
10600
10605
  getActivePipeline: () => getActivePipeline,
10601
10606
  getAppDataDirectory: () => getAppDataDirectory,
10602
10607
  getCardHistoryID: () => getCardHistoryID,
@@ -10622,6 +10627,7 @@ __export(index_exports, {
10622
10627
  isQuestionTypeRegistered: () => isQuestionTypeRegistered,
10623
10628
  isReview: () => isReview,
10624
10629
  log: () => log,
10630
+ makeScheduledCardId: () => makeScheduledCardId,
10625
10631
  mixerDebugAPI: () => mixerDebugAPI,
10626
10632
  mountMixerDebugger: () => mountMixerDebugger,
10627
10633
  mountPipelineDebugger: () => mountPipelineDebugger,
@@ -10633,6 +10639,7 @@ __export(index_exports, {
10633
10639
  processCustomQuestionsData: () => processCustomQuestionsData,
10634
10640
  recordCardPresentation: () => recordCardPresentation,
10635
10641
  recordUserOutcome: () => recordUserOutcome,
10642
+ registerActiveController: () => registerActiveController,
10636
10643
  registerBlanksCard: () => registerBlanksCard,
10637
10644
  registerCustomQuestionTypes: () => registerCustomQuestionTypes,
10638
10645
  registerDataShape: () => registerDataShape,
@@ -10647,6 +10654,7 @@ __export(index_exports, {
10647
10654
  sessionDebugAPI: () => sessionDebugAPI,
10648
10655
  snapshotQueues: () => snapshotQueues,
10649
10656
  startSessionTracking: () => startSessionTracking,
10657
+ toggleSessionOverlay: () => toggleSessionOverlay,
10650
10658
  updateLearningState: () => updateLearningState,
10651
10659
  updateStrategyWeight: () => updateStrategyWeight,
10652
10660
  userDBDebugAPI: () => userDBDebugAPI,
@@ -11109,13 +11117,18 @@ var EloService = class {
11109
11117
  logger.warn(`k value interpretation not currently implemented`);
11110
11118
  }
11111
11119
  const courseDB = this.dataLayer.getCourseDB(currentCard.card.course_id);
11112
- const userElo = (0, import_common23.toCourseElo)(
11113
- userCourseRegDoc.courses.find((c) => c.courseID === course_id).elo
11114
- );
11120
+ const courseReg = userCourseRegDoc.courses.find((c) => c.courseID === course_id);
11121
+ if (!courseReg) {
11122
+ logger.error(
11123
+ `[EloService] No registration for course ${course_id} on user's registration doc \u2014 skipping ELO update for card ${card_id}. (Is the user registered for this course?)`
11124
+ );
11125
+ return;
11126
+ }
11127
+ const userElo = (0, import_common23.toCourseElo)(courseReg.elo);
11115
11128
  const cardElo = (await courseDB.getCardEloData([currentCard.card.card_id]))[0];
11116
11129
  if (cardElo && userElo) {
11117
11130
  const eloUpdate = (0, import_common23.adjustCourseScores)(userElo, cardElo, userScore);
11118
- userCourseRegDoc.courses.find((c) => c.courseID === course_id).elo = eloUpdate.userElo;
11131
+ courseReg.elo = eloUpdate.userElo;
11119
11132
  const results = await Promise.allSettled([
11120
11133
  this.user.updateUserElo(course_id, eloUpdate.userElo),
11121
11134
  courseDB.updateCardElo(card_id, eloUpdate.cardElo)
@@ -11160,9 +11173,14 @@ var EloService = class {
11160
11173
  */
11161
11174
  async updateUserAndCardEloPerTag(taggedPerformance, course_id, card_id, userCourseRegDoc, currentCard) {
11162
11175
  const courseDB = this.dataLayer.getCourseDB(currentCard.card.course_id);
11163
- const userElo = (0, import_common23.toCourseElo)(
11164
- userCourseRegDoc.courses.find((c) => c.courseID === course_id).elo
11165
- );
11176
+ const courseReg = userCourseRegDoc.courses.find((c) => c.courseID === course_id);
11177
+ if (!courseReg) {
11178
+ logger.error(
11179
+ `[EloService] No registration for course ${course_id} on user's registration doc \u2014 skipping per-tag ELO update for card ${card_id}. (Is the user registered for this course?)`
11180
+ );
11181
+ return;
11182
+ }
11183
+ const userElo = (0, import_common23.toCourseElo)(courseReg.elo);
11166
11184
  const [cardEloResults, cardTagsMap] = await Promise.all([
11167
11185
  courseDB.getCardEloData([currentCard.card.card_id]),
11168
11186
  courseDB.getAppliedTagsBatch([card_id])
@@ -11178,7 +11196,7 @@ var EloService = class {
11178
11196
  }
11179
11197
  if (cardElo && userElo) {
11180
11198
  const eloUpdate = (0, import_common23.adjustCourseScoresPerTag)(userElo, cardElo, enriched);
11181
- userCourseRegDoc.courses.find((c) => c.courseID === course_id).elo = eloUpdate.userElo;
11199
+ courseReg.elo = eloUpdate.userElo;
11182
11200
  const results = await Promise.allSettled([
11183
11201
  this.user.updateUserElo(course_id, eloUpdate.userElo),
11184
11202
  courseDB.updateCardElo(card_id, eloUpdate.cardElo)
@@ -11225,6 +11243,14 @@ var ResponseProcessor = class {
11225
11243
  this.srsService = srsService;
11226
11244
  this.eloService = eloService;
11227
11245
  }
11246
+ /**
11247
+ * ELO updates are fired without awaiting so response handling isn't blocked
11248
+ * on DB writes — but an unhandled rejection silently drops the update.
11249
+ * This produces a catch handler that surfaces the failure in logs.
11250
+ */
11251
+ logEloFailure(context, cardId) {
11252
+ return (e) => logger.error(`[ResponseProcessor] ELO update failed (${context}) for ${cardId}:`, e);
11253
+ }
11228
11254
  /**
11229
11255
  * Parses performance data into global score and optional per-tag scores.
11230
11256
  *
@@ -11342,17 +11368,11 @@ var ResponseProcessor = class {
11342
11368
  cardId,
11343
11369
  courseRegistrationDoc,
11344
11370
  currentCard
11345
- );
11371
+ ).catch(this.logEloFailure("correct per-tag", cardId));
11346
11372
  } else {
11347
11373
  const userScore = 0.5 + globalScore / 2;
11348
11374
  if (history.records.length === 1) {
11349
- void this.eloService.updateUserAndCardElo(
11350
- userScore,
11351
- courseId,
11352
- cardId,
11353
- courseRegistrationDoc,
11354
- currentCard
11355
- );
11375
+ void this.eloService.updateUserAndCardElo(userScore, courseId, cardId, courseRegistrationDoc, currentCard).catch(this.logEloFailure("correct", cardId));
11356
11376
  } else {
11357
11377
  const k = Math.ceil(32 / history.records.length);
11358
11378
  void this.eloService.updateUserAndCardElo(
@@ -11362,7 +11382,7 @@ var ResponseProcessor = class {
11362
11382
  courseRegistrationDoc,
11363
11383
  currentCard,
11364
11384
  k
11365
- );
11385
+ ).catch(this.logEloFailure("correct repeat-view", cardId));
11366
11386
  }
11367
11387
  logger.info(
11368
11388
  `[FirstContactElo] correct first-attempt ELO update (score=${userScore.toFixed(3)}) for ${cardId} (historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps})`
@@ -11403,7 +11423,7 @@ var ResponseProcessor = class {
11403
11423
  cardId,
11404
11424
  courseRegistrationDoc,
11405
11425
  currentCard
11406
- );
11426
+ ).catch(this.logEloFailure("incorrect per-tag", cardId));
11407
11427
  logger.info(
11408
11428
  `[FirstContactElo] incorrect first-attempt per-tag ELO update for ${cardId} (historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps}, tags=${Object.keys(taggedPerformance).length - 1})`
11409
11429
  );
@@ -11415,7 +11435,7 @@ var ResponseProcessor = class {
11415
11435
  cardId,
11416
11436
  courseRegistrationDoc,
11417
11437
  currentCard
11418
- );
11438
+ ).catch(this.logEloFailure("incorrect", cardId));
11419
11439
  logger.info(
11420
11440
  `[FirstContactElo] incorrect first-attempt ELO update (score=0) for ${cardId} (historyLen=${history.records.length}, priorAttemps=${cardRecord.priorAttemps})`
11421
11441
  );
@@ -11436,15 +11456,9 @@ var ResponseProcessor = class {
11436
11456
  cardId,
11437
11457
  courseRegistrationDoc,
11438
11458
  currentCard
11439
- );
11459
+ ).catch(this.logEloFailure("dismiss-failed per-tag", cardId));
11440
11460
  } else {
11441
- void this.eloService.updateUserAndCardElo(
11442
- 0,
11443
- courseId,
11444
- cardId,
11445
- courseRegistrationDoc,
11446
- currentCard
11447
- );
11461
+ void this.eloService.updateUserAndCardElo(0, courseId, cardId, courseRegistrationDoc, currentCard).catch(this.logEloFailure("dismiss-failed", cardId));
11448
11462
  }
11449
11463
  logger.info(
11450
11464
  `[FirstContactElo] dismiss-failed final ELO penalty for ${cardId} (historyLen=${history.records.length}, sessionViews=${sessionViews})`
@@ -15263,6 +15277,7 @@ init_TagFilteredContentSource();
15263
15277
 
15264
15278
  // src/index.ts
15265
15279
  init_factory();
15280
+ init_userDBHelpers();
15266
15281
  // Annotate the CommonJS export names for ESM import in node:
15267
15282
  0 && (module.exports = {
15268
15283
  ContentNavigator,
@@ -15281,6 +15296,7 @@ init_factory();
15281
15296
  NavigatorRoles,
15282
15297
  Navigators,
15283
15298
  QuotaRoundRobinMixer,
15299
+ REVIEW_TIME_FORMAT,
15284
15300
  SessionController,
15285
15301
  StaticToCouchDBMigrator,
15286
15302
  TagFilteredContentSource,
@@ -15301,6 +15317,7 @@ init_factory();
15301
15317
  docIsDeleted,
15302
15318
  endSessionTracking,
15303
15319
  ensureAppDataDirectory,
15320
+ getActiveController,
15304
15321
  getActivePipeline,
15305
15322
  getAppDataDirectory,
15306
15323
  getCardHistoryID,
@@ -15326,6 +15343,7 @@ init_factory();
15326
15343
  isQuestionTypeRegistered,
15327
15344
  isReview,
15328
15345
  log,
15346
+ makeScheduledCardId,
15329
15347
  mixerDebugAPI,
15330
15348
  mountMixerDebugger,
15331
15349
  mountPipelineDebugger,
@@ -15337,6 +15355,7 @@ init_factory();
15337
15355
  processCustomQuestionsData,
15338
15356
  recordCardPresentation,
15339
15357
  recordUserOutcome,
15358
+ registerActiveController,
15340
15359
  registerBlanksCard,
15341
15360
  registerCustomQuestionTypes,
15342
15361
  registerDataShape,
@@ -15351,6 +15370,7 @@ init_factory();
15351
15370
  sessionDebugAPI,
15352
15371
  snapshotQueues,
15353
15372
  startSessionTracking,
15373
+ toggleSessionOverlay,
15354
15374
  updateLearningState,
15355
15375
  updateStrategyWeight,
15356
15376
  userDBDebugAPI,