@vue-skuilder/db 0.1.18 → 0.1.20

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 (59) hide show
  1. package/dist/{classroomDB-BgfrVb8d.d.ts → classroomDB-CZdMBiTU.d.ts} +71 -2
  2. package/dist/{classroomDB-CTOenngH.d.cts → classroomDB-PxDZTky3.d.cts} +71 -2
  3. package/dist/core/index.d.cts +80 -6
  4. package/dist/core/index.d.ts +80 -6
  5. package/dist/core/index.js +370 -52
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +369 -52
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-D6PoCwS6.d.cts → dataLayerProvider-D0MoZMjH.d.cts} +1 -1
  10. package/dist/{dataLayerProvider-CZxC9GtB.d.ts → dataLayerProvider-D8o6ZnKW.d.ts} +1 -1
  11. package/dist/impl/couch/index.d.cts +4 -3
  12. package/dist/impl/couch/index.d.ts +4 -3
  13. package/dist/impl/couch/index.js +371 -55
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +371 -55
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/index.d.cts +5 -4
  18. package/dist/impl/static/index.d.ts +5 -4
  19. package/dist/impl/static/index.js +356 -44
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +356 -44
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/{index-D-Fa4Smt.d.cts → index-B_j6u5E4.d.cts} +1 -1
  24. package/dist/{index-CD8BZz2k.d.ts → index-Dj0SEgk3.d.ts} +1 -1
  25. package/dist/index.d.cts +10 -10
  26. package/dist/index.d.ts +10 -10
  27. package/dist/index.js +382 -55
  28. package/dist/index.js.map +1 -1
  29. package/dist/index.mjs +381 -55
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/{types-CzPDLAK6.d.cts → types-Bn0itutr.d.cts} +1 -1
  32. package/dist/{types-CewsN87z.d.ts → types-DQaXnuoc.d.ts} +1 -1
  33. package/dist/{types-legacy-6ettoclI.d.cts → types-legacy-DDY4N-Uq.d.cts} +3 -1
  34. package/dist/{types-legacy-6ettoclI.d.ts → types-legacy-DDY4N-Uq.d.ts} +3 -1
  35. package/dist/util/packer/index.d.cts +3 -3
  36. package/dist/util/packer/index.d.ts +3 -3
  37. package/docs/navigators-architecture.md +115 -10
  38. package/package.json +4 -4
  39. package/src/core/index.ts +1 -0
  40. package/src/core/interfaces/courseDB.ts +13 -0
  41. package/src/core/interfaces/userDB.ts +32 -0
  42. package/src/core/navigators/Pipeline.ts +127 -14
  43. package/src/core/navigators/filters/index.ts +3 -0
  44. package/src/core/navigators/filters/userTagPreference.ts +232 -0
  45. package/src/core/navigators/hierarchyDefinition.ts +4 -4
  46. package/src/core/navigators/index.ts +59 -0
  47. package/src/core/navigators/inferredPreference.ts +107 -0
  48. package/src/core/navigators/interferenceMitigator.ts +1 -13
  49. package/src/core/navigators/relativePriority.ts +2 -14
  50. package/src/core/navigators/userGoal.ts +136 -0
  51. package/src/core/types/strategyState.ts +84 -0
  52. package/src/core/types/types-legacy.ts +2 -0
  53. package/src/impl/common/BaseUserDB.ts +74 -7
  54. package/src/impl/couch/adminDB.ts +1 -2
  55. package/src/impl/couch/courseDB.ts +30 -10
  56. package/src/impl/static/courseDB.ts +11 -0
  57. package/tests/core/navigators/Pipeline.test.ts +1 -0
  58. package/docs/todo-pipeline-optimization.md +0 -117
  59. package/docs/todo-strategy-state-storage.md +0 -278
@@ -1,6 +1,6 @@
1
1
  import { CourseConfig, ClassroomConfig, CourseElo, Status, SkuilderCourseData as SkuilderCourseData$1, DataShape, TagFilter } from '@vue-skuilder/common';
2
2
  import { Moment } from 'moment';
3
- import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-6ettoclI.js';
3
+ import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-DDY4N-Uq.js';
4
4
 
5
5
  /**
6
6
  * Admin functionality
@@ -222,6 +222,18 @@ interface CourseDBInterface extends NavigationStrategyManager {
222
222
  * Get tags for a card
223
223
  */
224
224
  getAppliedTags(cardId: string): Promise<PouchDB.Query.Response<TagStub>>;
225
+ /**
226
+ * Get tags for multiple cards in a single batch query.
227
+ * More efficient than calling getAppliedTags() for each card.
228
+ *
229
+ * This method reduces redundant database operations when multiple filters
230
+ * need tag data for the same cards. The Pipeline uses this to pre-hydrate
231
+ * tags on WeightedCard objects before filters run.
232
+ *
233
+ * @param cardIds - Array of card IDs to fetch tags for
234
+ * @returns Map from cardId to array of tag names
235
+ */
236
+ getAppliedTagsBatch(cardIds: string[]): Promise<Map<string, string[]>>;
225
237
  /**
226
238
  * Add a tag to a card
227
239
  */
@@ -323,6 +335,17 @@ interface UserDBReader {
323
335
  */
324
336
  getPendingReviews(courseId?: string): Promise<ScheduledCard[]>;
325
337
  getActivityRecords(): Promise<ActivityRecord[]>;
338
+ /**
339
+ * Get strategy-specific state for a course.
340
+ *
341
+ * Strategies use this to persist preferences, learned patterns, or temporal
342
+ * tracking data across sessions. Each strategy owns its own namespace.
343
+ *
344
+ * @param courseId - The course this state applies to
345
+ * @param strategyKey - Unique key identifying the strategy (typically class name)
346
+ * @returns The strategy's data payload, or null if no state exists
347
+ */
348
+ getStrategyState<T>(courseId: string, strategyKey: string): Promise<T | null>;
326
349
  /**
327
350
  * Get user's classroom registrations
328
351
  */
@@ -387,6 +410,24 @@ interface UserDBWriter extends DocumentUpdater {
387
410
  status: Status;
388
411
  error?: string;
389
412
  }>;
413
+ /**
414
+ * Store strategy-specific state for a course.
415
+ *
416
+ * Strategies use this to persist preferences, learned patterns, or temporal
417
+ * tracking data across sessions. Each strategy owns its own namespace.
418
+ *
419
+ * @param courseId - The course this state applies to
420
+ * @param strategyKey - Unique key identifying the strategy (typically class name)
421
+ * @param data - The strategy's data payload to store
422
+ */
423
+ putStrategyState<T>(courseId: string, strategyKey: string, data: T): Promise<void>;
424
+ /**
425
+ * Delete strategy-specific state for a course.
426
+ *
427
+ * @param courseId - The course this state applies to
428
+ * @param strategyKey - Unique key identifying the strategy (typically class name)
429
+ */
430
+ deleteStrategyState(courseId: string, strategyKey: string): Promise<void>;
390
431
  }
391
432
  /**
392
433
  * Authentication and account management operations
@@ -524,6 +565,11 @@ interface WeightedCard {
524
565
  * First entry is from the generator, subsequent entries from filters.
525
566
  */
526
567
  provenance: StrategyContribution[];
568
+ /**
569
+ * Pre-fetched tags. Populated by Pipeline before filters run.
570
+ * Filters should use this instead of querying getAppliedTags() individually.
571
+ */
572
+ tags?: string[];
527
573
  }
528
574
  /**
529
575
  * Extract card origin from provenance trail.
@@ -542,7 +588,8 @@ declare enum Navigators {
542
588
  HARDCODED = "hardcodedOrder",
543
589
  HIERARCHY = "hierarchyDefinition",
544
590
  INTERFERENCE = "interferenceMitigator",
545
- RELATIVE_PRIORITY = "relativePriority"
591
+ RELATIVE_PRIORITY = "relativePriority",
592
+ USER_TAG_PREFERENCE = "userTagPreference"
546
593
  }
547
594
  /**
548
595
  * Role classification for navigation strategies.
@@ -598,6 +645,28 @@ declare abstract class ContentNavigator implements StudyContentSource {
598
645
  * Note: CompositeGenerator doesn't use this pattern and should call super() without args.
599
646
  */
600
647
  constructor(user?: UserDBInterface, course?: CourseDBInterface, strategyData?: ContentNavigationStrategyData);
648
+ /**
649
+ * Unique key identifying this strategy for state storage.
650
+ *
651
+ * Defaults to the constructor name (e.g., "UserTagPreferenceFilter").
652
+ * Override in subclasses if multiple instances of the same strategy type
653
+ * need separate state storage.
654
+ */
655
+ protected get strategyKey(): string;
656
+ /**
657
+ * Get this strategy's persisted state for the current course.
658
+ *
659
+ * @returns The strategy's data payload, or null if no state exists
660
+ * @throws Error if user or course is not initialized
661
+ */
662
+ protected getStrategyState<T>(): Promise<T | null>;
663
+ /**
664
+ * Persist this strategy's state for the current course.
665
+ *
666
+ * @param data - The strategy's data payload to store
667
+ * @throws Error if user or course is not initialized
668
+ */
669
+ protected putStrategyState<T>(data: T): Promise<void>;
601
670
  /**
602
671
  * Factory method to create navigator instances dynamically.
603
672
  *
@@ -1,6 +1,6 @@
1
1
  import { CourseConfig, ClassroomConfig, CourseElo, Status, SkuilderCourseData as SkuilderCourseData$1, DataShape, TagFilter } from '@vue-skuilder/common';
2
2
  import { Moment } from 'moment';
3
- import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-6ettoclI.cjs';
3
+ import { S as SkuilderCourseData, b as DocTypePrefixes, D as DocType, Q as QualifiedCardID, T as TagStub, a as Tag, C as CardHistory, c as CardRecord } from './types-legacy-DDY4N-Uq.cjs';
4
4
 
5
5
  /**
6
6
  * Admin functionality
@@ -222,6 +222,18 @@ interface CourseDBInterface extends NavigationStrategyManager {
222
222
  * Get tags for a card
223
223
  */
224
224
  getAppliedTags(cardId: string): Promise<PouchDB.Query.Response<TagStub>>;
225
+ /**
226
+ * Get tags for multiple cards in a single batch query.
227
+ * More efficient than calling getAppliedTags() for each card.
228
+ *
229
+ * This method reduces redundant database operations when multiple filters
230
+ * need tag data for the same cards. The Pipeline uses this to pre-hydrate
231
+ * tags on WeightedCard objects before filters run.
232
+ *
233
+ * @param cardIds - Array of card IDs to fetch tags for
234
+ * @returns Map from cardId to array of tag names
235
+ */
236
+ getAppliedTagsBatch(cardIds: string[]): Promise<Map<string, string[]>>;
225
237
  /**
226
238
  * Add a tag to a card
227
239
  */
@@ -323,6 +335,17 @@ interface UserDBReader {
323
335
  */
324
336
  getPendingReviews(courseId?: string): Promise<ScheduledCard[]>;
325
337
  getActivityRecords(): Promise<ActivityRecord[]>;
338
+ /**
339
+ * Get strategy-specific state for a course.
340
+ *
341
+ * Strategies use this to persist preferences, learned patterns, or temporal
342
+ * tracking data across sessions. Each strategy owns its own namespace.
343
+ *
344
+ * @param courseId - The course this state applies to
345
+ * @param strategyKey - Unique key identifying the strategy (typically class name)
346
+ * @returns The strategy's data payload, or null if no state exists
347
+ */
348
+ getStrategyState<T>(courseId: string, strategyKey: string): Promise<T | null>;
326
349
  /**
327
350
  * Get user's classroom registrations
328
351
  */
@@ -387,6 +410,24 @@ interface UserDBWriter extends DocumentUpdater {
387
410
  status: Status;
388
411
  error?: string;
389
412
  }>;
413
+ /**
414
+ * Store strategy-specific state for a course.
415
+ *
416
+ * Strategies use this to persist preferences, learned patterns, or temporal
417
+ * tracking data across sessions. Each strategy owns its own namespace.
418
+ *
419
+ * @param courseId - The course this state applies to
420
+ * @param strategyKey - Unique key identifying the strategy (typically class name)
421
+ * @param data - The strategy's data payload to store
422
+ */
423
+ putStrategyState<T>(courseId: string, strategyKey: string, data: T): Promise<void>;
424
+ /**
425
+ * Delete strategy-specific state for a course.
426
+ *
427
+ * @param courseId - The course this state applies to
428
+ * @param strategyKey - Unique key identifying the strategy (typically class name)
429
+ */
430
+ deleteStrategyState(courseId: string, strategyKey: string): Promise<void>;
390
431
  }
391
432
  /**
392
433
  * Authentication and account management operations
@@ -524,6 +565,11 @@ interface WeightedCard {
524
565
  * First entry is from the generator, subsequent entries from filters.
525
566
  */
526
567
  provenance: StrategyContribution[];
568
+ /**
569
+ * Pre-fetched tags. Populated by Pipeline before filters run.
570
+ * Filters should use this instead of querying getAppliedTags() individually.
571
+ */
572
+ tags?: string[];
527
573
  }
528
574
  /**
529
575
  * Extract card origin from provenance trail.
@@ -542,7 +588,8 @@ declare enum Navigators {
542
588
  HARDCODED = "hardcodedOrder",
543
589
  HIERARCHY = "hierarchyDefinition",
544
590
  INTERFERENCE = "interferenceMitigator",
545
- RELATIVE_PRIORITY = "relativePriority"
591
+ RELATIVE_PRIORITY = "relativePriority",
592
+ USER_TAG_PREFERENCE = "userTagPreference"
546
593
  }
547
594
  /**
548
595
  * Role classification for navigation strategies.
@@ -598,6 +645,28 @@ declare abstract class ContentNavigator implements StudyContentSource {
598
645
  * Note: CompositeGenerator doesn't use this pattern and should call super() without args.
599
646
  */
600
647
  constructor(user?: UserDBInterface, course?: CourseDBInterface, strategyData?: ContentNavigationStrategyData);
648
+ /**
649
+ * Unique key identifying this strategy for state storage.
650
+ *
651
+ * Defaults to the constructor name (e.g., "UserTagPreferenceFilter").
652
+ * Override in subclasses if multiple instances of the same strategy type
653
+ * need separate state storage.
654
+ */
655
+ protected get strategyKey(): string;
656
+ /**
657
+ * Get this strategy's persisted state for the current course.
658
+ *
659
+ * @returns The strategy's data payload, or null if no state exists
660
+ * @throws Error if user or course is not initialized
661
+ */
662
+ protected getStrategyState<T>(): Promise<T | null>;
663
+ /**
664
+ * Persist this strategy's state for the current course.
665
+ *
666
+ * @param data - The strategy's data payload to store
667
+ * @throws Error if user or course is not initialized
668
+ */
669
+ protected putStrategyState<T>(data: T): Promise<void>;
601
670
  /**
602
671
  * Factory method to create navigator instances dynamically.
603
672
  *
@@ -1,8 +1,8 @@
1
- import { W as WeightedCard, U as UserDBInterface, C as CourseDBInterface } from '../classroomDB-CTOenngH.cjs';
2
- export { J as ActivityRecord, A as AdminDBInterface, v as AssignedCard, h as AssignedContent, u as AssignedCourse, t as AssignedTag, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, G as ClassroomRegistrationDoc, k as ContentNavigator, q as ContentSourceID, d as CourseInfo, K as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, O as NavigatorRole, P as NavigatorRoles, N as Navigators, g as ScheduledCard, H as SessionTrackingData, L as StrategyContribution, j as StudentClassroomDBInterface, i as StudyContentSource, m as StudySessionFailedItem, n as StudySessionFailedNewItem, o as StudySessionFailedReviewItem, l as StudySessionItem, S as StudySessionNewItem, f as StudySessionReviewItem, T as TeacherClassroomDBInterface, I as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, M as getCardOrigin, r as getStudySource, R as isFilter, Q as isGenerator, p as isReview } from '../classroomDB-CTOenngH.cjs';
3
- export { D as DataLayerProvider } from '../dataLayerProvider-D6PoCwS6.cjs';
4
- import { C as CardHistory, c as CardRecord, i as QuestionRecord } from '../types-legacy-6ettoclI.cjs';
5
- export { d as CardData, e as CourseListData, g as DataShapeData, f as DisplayableData, D as DocType, b as DocTypePrefixes, F as Field, G as GuestUsername, Q as QualifiedCardID, h as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-6ettoclI.cjs';
1
+ import { W as WeightedCard, U as UserDBInterface, C as CourseDBInterface } from '../classroomDB-PxDZTky3.cjs';
2
+ export { J as ActivityRecord, A as AdminDBInterface, v as AssignedCard, h as AssignedContent, u as AssignedCourse, t as AssignedTag, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, G as ClassroomRegistrationDoc, k as ContentNavigator, q as ContentSourceID, d as CourseInfo, K as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, O as NavigatorRole, P as NavigatorRoles, N as Navigators, g as ScheduledCard, H as SessionTrackingData, L as StrategyContribution, j as StudentClassroomDBInterface, i as StudyContentSource, m as StudySessionFailedItem, n as StudySessionFailedNewItem, o as StudySessionFailedReviewItem, l as StudySessionItem, S as StudySessionNewItem, f as StudySessionReviewItem, T as TeacherClassroomDBInterface, I as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, M as getCardOrigin, r as getStudySource, R as isFilter, Q as isGenerator, p as isReview } from '../classroomDB-PxDZTky3.cjs';
3
+ export { D as DataLayerProvider } from '../dataLayerProvider-D0MoZMjH.cjs';
4
+ import { b as DocTypePrefixes, D as DocType, C as CardHistory, c as CardRecord, i as QuestionRecord } from '../types-legacy-DDY4N-Uq.cjs';
5
+ export { d as CardData, e as CourseListData, g as DataShapeData, f as DisplayableData, F as Field, G as GuestUsername, Q as QualifiedCardID, h as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-DDY4N-Uq.cjs';
6
6
  import { DataShape, ParsedCard } from '@vue-skuilder/common';
7
7
  import 'moment';
8
8
 
@@ -169,6 +169,80 @@ interface CardGenerator {
169
169
  */
170
170
  type CardGeneratorFactory<TConfig = unknown> = (config: TConfig) => CardGenerator;
171
171
 
172
+ /**
173
+ * Template literal type for strategy state document IDs.
174
+ *
175
+ * Format: `STRATEGY_STATE-{courseId}-{strategyKey}`
176
+ */
177
+ type StrategyStateId = `${(typeof DocTypePrefixes)[DocType.STRATEGY_STATE]}::${string}::${string}`;
178
+ /**
179
+ * Document storing strategy-specific state in the user database.
180
+ *
181
+ * Each strategy can persist its own state (user preferences, learned patterns,
182
+ * temporal tracking, etc.) using this document type. The state is scoped to
183
+ * a (user, course, strategy) tuple.
184
+ *
185
+ * ## Use Cases
186
+ *
187
+ * 1. **Explicit user preferences**: User configures tag filters, difficulty
188
+ * preferences, or learning goals. UI writes to strategy state.
189
+ *
190
+ * 2. **Learned/temporal state**: Strategy tracks patterns over time, e.g.,
191
+ * "when did I last introduce confusable concepts together?"
192
+ *
193
+ * 3. **Adaptive personalization**: Strategy infers user preferences from
194
+ * behavior and stores them for future sessions.
195
+ *
196
+ * ## Storage Location
197
+ *
198
+ * These documents live in the **user database**, not the course database.
199
+ * They sync with the user's data across devices.
200
+ *
201
+ * ## Document ID Format
202
+ *
203
+ * `STRATEGY_STATE::{courseId}::{strategyKey}`
204
+ *
205
+ * Example: `STRATEGY_STATE::piano-basics::UserTagPreferenceFilter`
206
+ *
207
+ * @template T - The shape of the strategy-specific data payload
208
+ */
209
+ interface StrategyStateDoc<T = unknown> {
210
+ _id: StrategyStateId;
211
+ _rev?: string;
212
+ docType: DocType.STRATEGY_STATE;
213
+ /**
214
+ * The course this state applies to.
215
+ */
216
+ courseId: string;
217
+ /**
218
+ * Unique key identifying the strategy instance.
219
+ * Typically the strategy class name (e.g., "UserTagPreferenceFilter",
220
+ * "InterferenceMitigatorNavigator").
221
+ *
222
+ * If a course has multiple instances of the same strategy type with
223
+ * different configurations, use a more specific key.
224
+ */
225
+ strategyKey: string;
226
+ /**
227
+ * Strategy-specific data payload.
228
+ * Each strategy defines its own schema for this field.
229
+ */
230
+ data: T;
231
+ /**
232
+ * ISO timestamp of last update.
233
+ * Use `moment.utc(updatedAt)` to parse into a Moment object.
234
+ */
235
+ updatedAt: string;
236
+ }
237
+ /**
238
+ * Build the document ID for a strategy state document.
239
+ *
240
+ * @param courseId - The course ID
241
+ * @param strategyKey - The strategy key (typically class name)
242
+ * @returns The document ID in format `STRATEGY_STATE::{courseId}::{strategyKey}`
243
+ */
244
+ declare function buildStrategyStateId(courseId: string, strategyKey: string): StrategyStateId;
245
+
172
246
  declare function areQuestionRecords(h: CardHistory<CardRecord>): h is CardHistory<QuestionRecord>;
173
247
  declare function isQuestionRecord(c: CardRecord): c is QuestionRecord;
174
248
  declare function getCardHistoryID(courseID: string, cardID: string): PouchDB.Core.DocumentId;
@@ -227,4 +301,4 @@ declare function validateProcessorConfig(config: Partial<BulkCardProcessorConfig
227
301
  errorMessage?: string;
228
302
  };
229
303
 
230
- export { type BulkCardProcessorConfig, type CardFilter, type CardFilterFactory, type CardGenerator, type CardGeneratorFactory, CardHistory, CardRecord, CourseDBInterface, type FilterContext, type GeneratorContext, type ImportResult, Loggable, QuestionRecord, UserDBInterface, WeightedCard, areQuestionRecords, docIsDeleted, getCardHistoryID, importParsedCards, isQuestionRecord, parseCardHistoryID, validateProcessorConfig };
304
+ export { type BulkCardProcessorConfig, type CardFilter, type CardFilterFactory, type CardGenerator, type CardGeneratorFactory, CardHistory, CardRecord, CourseDBInterface, DocType, DocTypePrefixes, type FilterContext, type GeneratorContext, type ImportResult, Loggable, QuestionRecord, type StrategyStateDoc, type StrategyStateId, UserDBInterface, WeightedCard, areQuestionRecords, buildStrategyStateId, docIsDeleted, getCardHistoryID, importParsedCards, isQuestionRecord, parseCardHistoryID, validateProcessorConfig };
@@ -1,8 +1,8 @@
1
- import { W as WeightedCard, U as UserDBInterface, C as CourseDBInterface } from '../classroomDB-BgfrVb8d.js';
2
- export { J as ActivityRecord, A as AdminDBInterface, v as AssignedCard, h as AssignedContent, u as AssignedCourse, t as AssignedTag, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, G as ClassroomRegistrationDoc, k as ContentNavigator, q as ContentSourceID, d as CourseInfo, K as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, O as NavigatorRole, P as NavigatorRoles, N as Navigators, g as ScheduledCard, H as SessionTrackingData, L as StrategyContribution, j as StudentClassroomDBInterface, i as StudyContentSource, m as StudySessionFailedItem, n as StudySessionFailedNewItem, o as StudySessionFailedReviewItem, l as StudySessionItem, S as StudySessionNewItem, f as StudySessionReviewItem, T as TeacherClassroomDBInterface, I as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, M as getCardOrigin, r as getStudySource, R as isFilter, Q as isGenerator, p as isReview } from '../classroomDB-BgfrVb8d.js';
3
- export { D as DataLayerProvider } from '../dataLayerProvider-CZxC9GtB.js';
4
- import { C as CardHistory, c as CardRecord, i as QuestionRecord } from '../types-legacy-6ettoclI.js';
5
- export { d as CardData, e as CourseListData, g as DataShapeData, f as DisplayableData, D as DocType, b as DocTypePrefixes, F as Field, G as GuestUsername, Q as QualifiedCardID, h as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-6ettoclI.js';
1
+ import { W as WeightedCard, U as UserDBInterface, C as CourseDBInterface } from '../classroomDB-CZdMBiTU.js';
2
+ export { J as ActivityRecord, A as AdminDBInterface, v as AssignedCard, h as AssignedContent, u as AssignedCourse, t as AssignedTag, c as ClassroomDBInterface, F as ClassroomRegistration, E as ClassroomRegistrationDesignation, G as ClassroomRegistrationDoc, k as ContentNavigator, q as ContentSourceID, d as CourseInfo, K as CourseRegistration, s as CourseRegistrationDoc, b as CoursesDBInterface, O as NavigatorRole, P as NavigatorRoles, N as Navigators, g as ScheduledCard, H as SessionTrackingData, L as StrategyContribution, j as StudentClassroomDBInterface, i as StudyContentSource, m as StudySessionFailedItem, n as StudySessionFailedNewItem, o as StudySessionFailedReviewItem, l as StudySessionItem, S as StudySessionNewItem, f as StudySessionReviewItem, T as TeacherClassroomDBInterface, I as UserConfig, z as UserCourseSetting, y as UserCourseSettings, x as UserDBAuthenticator, a as UserDBReader, w as UserDBWriter, B as UsrCrsDataInterface, M as getCardOrigin, r as getStudySource, R as isFilter, Q as isGenerator, p as isReview } from '../classroomDB-CZdMBiTU.js';
3
+ export { D as DataLayerProvider } from '../dataLayerProvider-D8o6ZnKW.js';
4
+ import { b as DocTypePrefixes, D as DocType, C as CardHistory, c as CardRecord, i as QuestionRecord } from '../types-legacy-DDY4N-Uq.js';
5
+ export { d as CardData, e as CourseListData, g as DataShapeData, f as DisplayableData, F as Field, G as GuestUsername, Q as QualifiedCardID, h as QuestionData, S as SkuilderCourseData, a as Tag, T as TagStub, l as log } from '../types-legacy-DDY4N-Uq.js';
6
6
  import { DataShape, ParsedCard } from '@vue-skuilder/common';
7
7
  import 'moment';
8
8
 
@@ -169,6 +169,80 @@ interface CardGenerator {
169
169
  */
170
170
  type CardGeneratorFactory<TConfig = unknown> = (config: TConfig) => CardGenerator;
171
171
 
172
+ /**
173
+ * Template literal type for strategy state document IDs.
174
+ *
175
+ * Format: `STRATEGY_STATE-{courseId}-{strategyKey}`
176
+ */
177
+ type StrategyStateId = `${(typeof DocTypePrefixes)[DocType.STRATEGY_STATE]}::${string}::${string}`;
178
+ /**
179
+ * Document storing strategy-specific state in the user database.
180
+ *
181
+ * Each strategy can persist its own state (user preferences, learned patterns,
182
+ * temporal tracking, etc.) using this document type. The state is scoped to
183
+ * a (user, course, strategy) tuple.
184
+ *
185
+ * ## Use Cases
186
+ *
187
+ * 1. **Explicit user preferences**: User configures tag filters, difficulty
188
+ * preferences, or learning goals. UI writes to strategy state.
189
+ *
190
+ * 2. **Learned/temporal state**: Strategy tracks patterns over time, e.g.,
191
+ * "when did I last introduce confusable concepts together?"
192
+ *
193
+ * 3. **Adaptive personalization**: Strategy infers user preferences from
194
+ * behavior and stores them for future sessions.
195
+ *
196
+ * ## Storage Location
197
+ *
198
+ * These documents live in the **user database**, not the course database.
199
+ * They sync with the user's data across devices.
200
+ *
201
+ * ## Document ID Format
202
+ *
203
+ * `STRATEGY_STATE::{courseId}::{strategyKey}`
204
+ *
205
+ * Example: `STRATEGY_STATE::piano-basics::UserTagPreferenceFilter`
206
+ *
207
+ * @template T - The shape of the strategy-specific data payload
208
+ */
209
+ interface StrategyStateDoc<T = unknown> {
210
+ _id: StrategyStateId;
211
+ _rev?: string;
212
+ docType: DocType.STRATEGY_STATE;
213
+ /**
214
+ * The course this state applies to.
215
+ */
216
+ courseId: string;
217
+ /**
218
+ * Unique key identifying the strategy instance.
219
+ * Typically the strategy class name (e.g., "UserTagPreferenceFilter",
220
+ * "InterferenceMitigatorNavigator").
221
+ *
222
+ * If a course has multiple instances of the same strategy type with
223
+ * different configurations, use a more specific key.
224
+ */
225
+ strategyKey: string;
226
+ /**
227
+ * Strategy-specific data payload.
228
+ * Each strategy defines its own schema for this field.
229
+ */
230
+ data: T;
231
+ /**
232
+ * ISO timestamp of last update.
233
+ * Use `moment.utc(updatedAt)` to parse into a Moment object.
234
+ */
235
+ updatedAt: string;
236
+ }
237
+ /**
238
+ * Build the document ID for a strategy state document.
239
+ *
240
+ * @param courseId - The course ID
241
+ * @param strategyKey - The strategy key (typically class name)
242
+ * @returns The document ID in format `STRATEGY_STATE::{courseId}::{strategyKey}`
243
+ */
244
+ declare function buildStrategyStateId(courseId: string, strategyKey: string): StrategyStateId;
245
+
172
246
  declare function areQuestionRecords(h: CardHistory<CardRecord>): h is CardHistory<QuestionRecord>;
173
247
  declare function isQuestionRecord(c: CardRecord): c is QuestionRecord;
174
248
  declare function getCardHistoryID(courseID: string, cardID: string): PouchDB.Core.DocumentId;
@@ -227,4 +301,4 @@ declare function validateProcessorConfig(config: Partial<BulkCardProcessorConfig
227
301
  errorMessage?: string;
228
302
  };
229
303
 
230
- export { type BulkCardProcessorConfig, type CardFilter, type CardFilterFactory, type CardGenerator, type CardGeneratorFactory, CardHistory, CardRecord, CourseDBInterface, type FilterContext, type GeneratorContext, type ImportResult, Loggable, QuestionRecord, UserDBInterface, WeightedCard, areQuestionRecords, docIsDeleted, getCardHistoryID, importParsedCards, isQuestionRecord, parseCardHistoryID, validateProcessorConfig };
304
+ export { type BulkCardProcessorConfig, type CardFilter, type CardFilterFactory, type CardGenerator, type CardGeneratorFactory, CardHistory, CardRecord, CourseDBInterface, DocType, DocTypePrefixes, type FilterContext, type GeneratorContext, type ImportResult, Loggable, QuestionRecord, type StrategyStateDoc, type StrategyStateId, UserDBInterface, WeightedCard, areQuestionRecords, buildStrategyStateId, docIsDeleted, getCardHistoryID, importParsedCards, isQuestionRecord, parseCardHistoryID, validateProcessorConfig };