@vue-skuilder/db 0.1.16 → 0.1.18

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 (80) hide show
  1. package/dist/{userDB-DNa0XPtn.d.ts → classroomDB-BgfrVb8d.d.ts} +357 -103
  2. package/dist/{userDB-BqwxtJ_7.d.mts → classroomDB-CTOenngH.d.cts} +358 -104
  3. package/dist/core/index.d.cts +230 -0
  4. package/dist/core/index.d.ts +161 -23
  5. package/dist/core/index.js +1964 -154
  6. package/dist/core/index.js.map +1 -1
  7. package/dist/core/index.mjs +1925 -121
  8. package/dist/core/index.mjs.map +1 -1
  9. package/dist/{dataLayerProvider-BV5iZqt_.d.ts → dataLayerProvider-CZxC9GtB.d.ts} +1 -1
  10. package/dist/{dataLayerProvider-VlngD19_.d.mts → dataLayerProvider-D6PoCwS6.d.cts} +1 -1
  11. package/dist/impl/couch/{index.d.mts → index.d.cts} +46 -5
  12. package/dist/impl/couch/index.d.ts +44 -3
  13. package/dist/impl/couch/index.js +1971 -171
  14. package/dist/impl/couch/index.js.map +1 -1
  15. package/dist/impl/couch/index.mjs +1933 -134
  16. package/dist/impl/couch/index.mjs.map +1 -1
  17. package/dist/impl/static/{index.d.mts → index.d.cts} +5 -6
  18. package/dist/impl/static/index.d.ts +2 -3
  19. package/dist/impl/static/index.js +1614 -119
  20. package/dist/impl/static/index.js.map +1 -1
  21. package/dist/impl/static/index.mjs +1585 -92
  22. package/dist/impl/static/index.mjs.map +1 -1
  23. package/dist/{index-Bmll7Xse.d.mts → index-D-Fa4Smt.d.cts} +1 -1
  24. package/dist/{index.d.mts → index.d.cts} +97 -13
  25. package/dist/index.d.ts +90 -6
  26. package/dist/index.js +2085 -153
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +2031 -106
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/pouch/index.js +3 -3
  31. package/dist/{types-Dbp5DaRR.d.mts → types-CzPDLAK6.d.cts} +1 -1
  32. package/dist/util/packer/{index.d.mts → index.d.cts} +3 -3
  33. package/dist/util/packer/index.js.map +1 -1
  34. package/dist/util/packer/index.mjs.map +1 -1
  35. package/docs/brainstorm-navigation-paradigm.md +369 -0
  36. package/docs/navigators-architecture.md +265 -0
  37. package/docs/todo-evolutionary-orchestration.md +310 -0
  38. package/docs/todo-nominal-tag-types.md +121 -0
  39. package/docs/todo-pipeline-optimization.md +117 -0
  40. package/docs/todo-strategy-authoring.md +401 -0
  41. package/docs/todo-strategy-state-storage.md +278 -0
  42. package/eslint.config.mjs +1 -1
  43. package/package.json +9 -4
  44. package/src/core/interfaces/contentSource.ts +88 -4
  45. package/src/core/interfaces/navigationStrategyManager.ts +0 -5
  46. package/src/core/navigators/CompositeGenerator.ts +268 -0
  47. package/src/core/navigators/Pipeline.ts +205 -0
  48. package/src/core/navigators/PipelineAssembler.ts +194 -0
  49. package/src/core/navigators/elo.ts +104 -15
  50. package/src/core/navigators/filters/eloDistance.ts +132 -0
  51. package/src/core/navigators/filters/index.ts +6 -0
  52. package/src/core/navigators/filters/types.ts +115 -0
  53. package/src/core/navigators/generators/index.ts +2 -0
  54. package/src/core/navigators/generators/types.ts +107 -0
  55. package/src/core/navigators/hardcodedOrder.ts +111 -12
  56. package/src/core/navigators/hierarchyDefinition.ts +266 -0
  57. package/src/core/navigators/index.ts +345 -3
  58. package/src/core/navigators/interferenceMitigator.ts +367 -0
  59. package/src/core/navigators/relativePriority.ts +267 -0
  60. package/src/core/navigators/srs.ts +195 -0
  61. package/src/impl/couch/classroomDB.ts +51 -0
  62. package/src/impl/couch/courseDB.ts +117 -39
  63. package/src/impl/static/courseDB.ts +0 -4
  64. package/src/study/SessionController.ts +149 -1
  65. package/src/study/TagFilteredContentSource.ts +255 -0
  66. package/src/study/index.ts +1 -0
  67. package/src/util/dataDirectory.test.ts +51 -22
  68. package/src/util/logger.ts +0 -1
  69. package/tests/core/navigators/CompositeGenerator.test.ts +455 -0
  70. package/tests/core/navigators/Pipeline.test.ts +405 -0
  71. package/tests/core/navigators/PipelineAssembler.test.ts +351 -0
  72. package/tests/core/navigators/SRSNavigator.test.ts +344 -0
  73. package/tests/core/navigators/eloDistanceFilter.test.ts +192 -0
  74. package/tests/core/navigators/navigators.test.ts +710 -0
  75. package/tsconfig.json +1 -1
  76. package/vitest.config.ts +29 -0
  77. package/dist/core/index.d.mts +0 -92
  78. /package/dist/{SyncStrategy-CyATpyLQ.d.mts → SyncStrategy-CyATpyLQ.d.cts} +0 -0
  79. /package/dist/pouch/{index.d.mts → index.d.cts} +0 -0
  80. /package/dist/{types-legacy-6ettoclI.d.mts → types-legacy-6ettoclI.d.cts} +0 -0
@@ -0,0 +1,230 @@
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';
6
+ import { DataShape, ParsedCard } from '@vue-skuilder/common';
7
+ import 'moment';
8
+
9
+ declare abstract class Loggable {
10
+ protected abstract readonly _className: string;
11
+ protected log(...args: unknown[]): void;
12
+ protected error(...args: unknown[]): void;
13
+ }
14
+
15
+ /**
16
+ * Shared context available to all filters in a pipeline.
17
+ *
18
+ * Built once per getWeightedCards() call and passed to each filter.
19
+ * This avoids repeated lookups for common data like user ELO.
20
+ */
21
+ interface FilterContext {
22
+ /** User database interface */
23
+ user: UserDBInterface;
24
+ /** Course database interface */
25
+ course: CourseDBInterface;
26
+ /** User's global ELO score for this course */
27
+ userElo: number;
28
+ }
29
+ /**
30
+ * A filter that transforms a list of weighted cards.
31
+ *
32
+ * Filters are pure transforms - they receive cards and context,
33
+ * and return a modified list of cards. No delegate wrapping,
34
+ * no side effects beyond provenance tracking.
35
+ *
36
+ * ## Implementation Guidelines
37
+ *
38
+ * 1. **Append provenance**: Every filter should add a StrategyContribution
39
+ * entry documenting its decision for each card.
40
+ *
41
+ * 2. **Use multipliers**: Adjust scores by multiplying, not replacing.
42
+ * This ensures filter order doesn't matter.
43
+ *
44
+ * 3. **Score 0 for exclusion**: To exclude a card, set score to 0.
45
+ * Don't filter it out - let provenance show why it was excluded.
46
+ *
47
+ * 4. **Don't sort**: The Pipeline handles final sorting.
48
+ * Filters just transform scores.
49
+ *
50
+ * ## Example Implementation
51
+ *
52
+ * ```typescript
53
+ * const myFilter: CardFilter = {
54
+ * name: 'My Filter',
55
+ * async transform(cards, context) {
56
+ * return cards.map(card => {
57
+ * const multiplier = computeMultiplier(card, context);
58
+ * const newScore = card.score * multiplier;
59
+ * return {
60
+ * ...card,
61
+ * score: newScore,
62
+ * provenance: [...card.provenance, {
63
+ * strategy: 'myFilter',
64
+ * strategyName: 'My Filter',
65
+ * strategyId: 'MY_FILTER',
66
+ * action: multiplier < 1 ? 'penalized' : 'passed',
67
+ * score: newScore,
68
+ * reason: 'Explanation of decision'
69
+ * }]
70
+ * };
71
+ * });
72
+ * }
73
+ * };
74
+ * ```
75
+ */
76
+ interface CardFilter {
77
+ /** Human-readable name for this filter */
78
+ name: string;
79
+ /**
80
+ * Transform a list of weighted cards.
81
+ *
82
+ * @param cards - Cards to transform (already scored by generator)
83
+ * @param context - Shared context (user, course, userElo, etc.)
84
+ * @returns Transformed cards with updated scores and provenance
85
+ */
86
+ transform(cards: WeightedCard[], context: FilterContext): Promise<WeightedCard[]>;
87
+ }
88
+ /**
89
+ * Factory function type for creating filters from configuration.
90
+ *
91
+ * Used by PipelineAssembler to instantiate filters from strategy documents.
92
+ */
93
+ type CardFilterFactory<TConfig = unknown> = (config: TConfig) => CardFilter;
94
+
95
+ /**
96
+ * Context available to generators when producing candidates.
97
+ *
98
+ * Built once per getWeightedCards() call by the Pipeline.
99
+ */
100
+ interface GeneratorContext {
101
+ /** User database interface */
102
+ user: UserDBInterface;
103
+ /** Course database interface */
104
+ course: CourseDBInterface;
105
+ /** User's global ELO score for this course */
106
+ userElo: number;
107
+ }
108
+ /**
109
+ * A generator that produces candidate cards with initial scores.
110
+ *
111
+ * Generators are the "source" stage of a navigation pipeline.
112
+ * They query the database for eligible cards and assign initial
113
+ * suitability scores based on their strategy (ELO proximity,
114
+ * review urgency, fixed order, etc.).
115
+ *
116
+ * ## Implementation Guidelines
117
+ *
118
+ * 1. **Create provenance**: Each card should have a provenance entry
119
+ * with action='generated' documenting why it was selected.
120
+ *
121
+ * 2. **Score semantics**: Higher scores = more suitable for presentation.
122
+ * Scores should be in [0, 1] range for composability.
123
+ *
124
+ * 3. **Limit handling**: Respect the limit parameter, but may over-fetch
125
+ * internally if needed for scoring accuracy.
126
+ *
127
+ * 4. **Sort before returning**: Return cards sorted by score descending.
128
+ *
129
+ * ## Example Implementation
130
+ *
131
+ * ```typescript
132
+ * const myGenerator: CardGenerator = {
133
+ * name: 'My Generator',
134
+ * async getWeightedCards(limit, context) {
135
+ * const candidates = await fetchCandidates(context.course, limit);
136
+ * return candidates.map(c => ({
137
+ * cardId: c.id,
138
+ * courseId: context.course.getCourseID(),
139
+ * score: computeScore(c, context),
140
+ * provenance: [{
141
+ * strategy: 'myGenerator',
142
+ * strategyName: 'My Generator',
143
+ * strategyId: 'MY_GENERATOR',
144
+ * action: 'generated',
145
+ * score: computeScore(c, context),
146
+ * reason: 'Explanation of selection'
147
+ * }]
148
+ * }));
149
+ * }
150
+ * };
151
+ * ```
152
+ */
153
+ interface CardGenerator {
154
+ /** Human-readable name for this generator */
155
+ name: string;
156
+ /**
157
+ * Produce candidate cards with initial scores.
158
+ *
159
+ * @param limit - Maximum number of cards to return
160
+ * @param context - Shared context (user, course, userElo, etc.)
161
+ * @returns Cards sorted by score descending, with provenance
162
+ */
163
+ getWeightedCards(limit: number, context: GeneratorContext): Promise<WeightedCard[]>;
164
+ }
165
+ /**
166
+ * Factory function type for creating generators from configuration.
167
+ *
168
+ * Used by PipelineAssembler to instantiate generators from strategy documents.
169
+ */
170
+ type CardGeneratorFactory<TConfig = unknown> = (config: TConfig) => CardGenerator;
171
+
172
+ declare function areQuestionRecords(h: CardHistory<CardRecord>): h is CardHistory<QuestionRecord>;
173
+ declare function isQuestionRecord(c: CardRecord): c is QuestionRecord;
174
+ declare function getCardHistoryID(courseID: string, cardID: string): PouchDB.Core.DocumentId;
175
+ declare function parseCardHistoryID(id: string): {
176
+ courseID: string;
177
+ cardID: string;
178
+ };
179
+ interface PouchDBError extends Error {
180
+ error?: string;
181
+ reason?: string;
182
+ }
183
+ declare function docIsDeleted(e: PouchDBError): boolean;
184
+
185
+ /**
186
+ * Interface representing the result of a bulk import operation for a single card
187
+ */
188
+ interface ImportResult {
189
+ /** The original text input for the card */
190
+ originalText: string;
191
+ /** Status of the import operation */
192
+ status: 'success' | 'error';
193
+ /** Message describing the result or error */
194
+ message: string;
195
+ /** ID of the newly created card (only for success) */
196
+ cardId?: string;
197
+ }
198
+ /**
199
+ * Configuration for the bulk card processor
200
+ */
201
+ interface BulkCardProcessorConfig {
202
+ /** The data shape to use for the cards */
203
+ dataShape: DataShape;
204
+ /** The course code used for adding notes */
205
+ courseCode: string;
206
+ /** The username of the current user */
207
+ userName: string;
208
+ }
209
+
210
+ /**
211
+ * Processes multiple cards from bulk text input
212
+ *
213
+ * @param parsedCards - Array of parsed cards to import
214
+ * @param courseDB - Course database interface
215
+ * @param config - Configuration for the card processor
216
+ * @returns Array of import results
217
+ */
218
+ declare function importParsedCards(parsedCards: ParsedCard[], courseDB: CourseDBInterface, config: BulkCardProcessorConfig): Promise<ImportResult[]>;
219
+ /**
220
+ * Validates the configuration for bulk card processing
221
+ *
222
+ * @param config - Configuration to validate
223
+ * @returns Object with validation result and error message if any
224
+ */
225
+ declare function validateProcessorConfig(config: Partial<BulkCardProcessorConfig>): {
226
+ isValid: boolean;
227
+ errorMessage?: string;
228
+ };
229
+
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 };
@@ -1,6 +1,6 @@
1
- import { i as StudyContentSource, U as UserDBInterface, C as CourseDBInterface, e as ContentNavigationStrategyData, f as StudySessionReviewItem, g as ScheduledCard, S as StudySessionNewItem } from '../userDB-DNa0XPtn.js';
2
- export { I as ActivityRecord, A as AdminDBInterface, u as AssignedCard, h as AssignedContent, t as AssignedCourse, s as AssignedTag, c as ClassroomDBInterface, E as ClassroomRegistration, B as ClassroomRegistrationDesignation, F as ClassroomRegistrationDoc, p as ContentSourceID, d as CourseInfo, J as CourseRegistration, r as CourseRegistrationDoc, b as CoursesDBInterface, G as SessionTrackingData, j as StudentClassroomDBInterface, l as StudySessionFailedItem, m as StudySessionFailedNewItem, n as StudySessionFailedReviewItem, k as StudySessionItem, T as TeacherClassroomDBInterface, H as UserConfig, y as UserCourseSetting, x as UserCourseSettings, w as UserDBAuthenticator, a as UserDBReader, v as UserDBWriter, z as UsrCrsDataInterface, q as getStudySource, o as isReview } from '../userDB-DNa0XPtn.js';
3
- export { D as DataLayerProvider } from '../dataLayerProvider-BV5iZqt_.js';
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
4
  import { C as CardHistory, c as CardRecord, i as QuestionRecord } from '../types-legacy-6ettoclI.js';
5
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';
6
6
  import { DataShape, ParsedCard } from '@vue-skuilder/common';
@@ -12,6 +12,163 @@ declare abstract class Loggable {
12
12
  protected error(...args: unknown[]): void;
13
13
  }
14
14
 
15
+ /**
16
+ * Shared context available to all filters in a pipeline.
17
+ *
18
+ * Built once per getWeightedCards() call and passed to each filter.
19
+ * This avoids repeated lookups for common data like user ELO.
20
+ */
21
+ interface FilterContext {
22
+ /** User database interface */
23
+ user: UserDBInterface;
24
+ /** Course database interface */
25
+ course: CourseDBInterface;
26
+ /** User's global ELO score for this course */
27
+ userElo: number;
28
+ }
29
+ /**
30
+ * A filter that transforms a list of weighted cards.
31
+ *
32
+ * Filters are pure transforms - they receive cards and context,
33
+ * and return a modified list of cards. No delegate wrapping,
34
+ * no side effects beyond provenance tracking.
35
+ *
36
+ * ## Implementation Guidelines
37
+ *
38
+ * 1. **Append provenance**: Every filter should add a StrategyContribution
39
+ * entry documenting its decision for each card.
40
+ *
41
+ * 2. **Use multipliers**: Adjust scores by multiplying, not replacing.
42
+ * This ensures filter order doesn't matter.
43
+ *
44
+ * 3. **Score 0 for exclusion**: To exclude a card, set score to 0.
45
+ * Don't filter it out - let provenance show why it was excluded.
46
+ *
47
+ * 4. **Don't sort**: The Pipeline handles final sorting.
48
+ * Filters just transform scores.
49
+ *
50
+ * ## Example Implementation
51
+ *
52
+ * ```typescript
53
+ * const myFilter: CardFilter = {
54
+ * name: 'My Filter',
55
+ * async transform(cards, context) {
56
+ * return cards.map(card => {
57
+ * const multiplier = computeMultiplier(card, context);
58
+ * const newScore = card.score * multiplier;
59
+ * return {
60
+ * ...card,
61
+ * score: newScore,
62
+ * provenance: [...card.provenance, {
63
+ * strategy: 'myFilter',
64
+ * strategyName: 'My Filter',
65
+ * strategyId: 'MY_FILTER',
66
+ * action: multiplier < 1 ? 'penalized' : 'passed',
67
+ * score: newScore,
68
+ * reason: 'Explanation of decision'
69
+ * }]
70
+ * };
71
+ * });
72
+ * }
73
+ * };
74
+ * ```
75
+ */
76
+ interface CardFilter {
77
+ /** Human-readable name for this filter */
78
+ name: string;
79
+ /**
80
+ * Transform a list of weighted cards.
81
+ *
82
+ * @param cards - Cards to transform (already scored by generator)
83
+ * @param context - Shared context (user, course, userElo, etc.)
84
+ * @returns Transformed cards with updated scores and provenance
85
+ */
86
+ transform(cards: WeightedCard[], context: FilterContext): Promise<WeightedCard[]>;
87
+ }
88
+ /**
89
+ * Factory function type for creating filters from configuration.
90
+ *
91
+ * Used by PipelineAssembler to instantiate filters from strategy documents.
92
+ */
93
+ type CardFilterFactory<TConfig = unknown> = (config: TConfig) => CardFilter;
94
+
95
+ /**
96
+ * Context available to generators when producing candidates.
97
+ *
98
+ * Built once per getWeightedCards() call by the Pipeline.
99
+ */
100
+ interface GeneratorContext {
101
+ /** User database interface */
102
+ user: UserDBInterface;
103
+ /** Course database interface */
104
+ course: CourseDBInterface;
105
+ /** User's global ELO score for this course */
106
+ userElo: number;
107
+ }
108
+ /**
109
+ * A generator that produces candidate cards with initial scores.
110
+ *
111
+ * Generators are the "source" stage of a navigation pipeline.
112
+ * They query the database for eligible cards and assign initial
113
+ * suitability scores based on their strategy (ELO proximity,
114
+ * review urgency, fixed order, etc.).
115
+ *
116
+ * ## Implementation Guidelines
117
+ *
118
+ * 1. **Create provenance**: Each card should have a provenance entry
119
+ * with action='generated' documenting why it was selected.
120
+ *
121
+ * 2. **Score semantics**: Higher scores = more suitable for presentation.
122
+ * Scores should be in [0, 1] range for composability.
123
+ *
124
+ * 3. **Limit handling**: Respect the limit parameter, but may over-fetch
125
+ * internally if needed for scoring accuracy.
126
+ *
127
+ * 4. **Sort before returning**: Return cards sorted by score descending.
128
+ *
129
+ * ## Example Implementation
130
+ *
131
+ * ```typescript
132
+ * const myGenerator: CardGenerator = {
133
+ * name: 'My Generator',
134
+ * async getWeightedCards(limit, context) {
135
+ * const candidates = await fetchCandidates(context.course, limit);
136
+ * return candidates.map(c => ({
137
+ * cardId: c.id,
138
+ * courseId: context.course.getCourseID(),
139
+ * score: computeScore(c, context),
140
+ * provenance: [{
141
+ * strategy: 'myGenerator',
142
+ * strategyName: 'My Generator',
143
+ * strategyId: 'MY_GENERATOR',
144
+ * action: 'generated',
145
+ * score: computeScore(c, context),
146
+ * reason: 'Explanation of selection'
147
+ * }]
148
+ * }));
149
+ * }
150
+ * };
151
+ * ```
152
+ */
153
+ interface CardGenerator {
154
+ /** Human-readable name for this generator */
155
+ name: string;
156
+ /**
157
+ * Produce candidate cards with initial scores.
158
+ *
159
+ * @param limit - Maximum number of cards to return
160
+ * @param context - Shared context (user, course, userElo, etc.)
161
+ * @returns Cards sorted by score descending, with provenance
162
+ */
163
+ getWeightedCards(limit: number, context: GeneratorContext): Promise<WeightedCard[]>;
164
+ }
165
+ /**
166
+ * Factory function type for creating generators from configuration.
167
+ *
168
+ * Used by PipelineAssembler to instantiate generators from strategy documents.
169
+ */
170
+ type CardGeneratorFactory<TConfig = unknown> = (config: TConfig) => CardGenerator;
171
+
15
172
  declare function areQuestionRecords(h: CardHistory<CardRecord>): h is CardHistory<QuestionRecord>;
16
173
  declare function isQuestionRecord(c: CardRecord): c is QuestionRecord;
17
174
  declare function getCardHistoryID(courseID: string, cardID: string): PouchDB.Core.DocumentId;
@@ -25,25 +182,6 @@ interface PouchDBError extends Error {
25
182
  }
26
183
  declare function docIsDeleted(e: PouchDBError): boolean;
27
184
 
28
- declare enum Navigators {
29
- ELO = "elo",
30
- HARDCODED = "hardcodedOrder"
31
- }
32
- /**
33
- * A content-navigator provides runtime steering of study sessions.
34
- */
35
- declare abstract class ContentNavigator implements StudyContentSource {
36
- /**
37
- *
38
- * @param user
39
- * @param strategyData
40
- * @returns the runtime object used to steer a study session.
41
- */
42
- static create(user: UserDBInterface, course: CourseDBInterface, strategyData: ContentNavigationStrategyData): Promise<ContentNavigator>;
43
- abstract getPendingReviews(): Promise<(StudySessionReviewItem & ScheduledCard)[]>;
44
- abstract getNewCards(n?: number): Promise<StudySessionNewItem[]>;
45
- }
46
-
47
185
  /**
48
186
  * Interface representing the result of a bulk import operation for a single card
49
187
  */
@@ -89,4 +227,4 @@ declare function validateProcessorConfig(config: Partial<BulkCardProcessorConfig
89
227
  errorMessage?: string;
90
228
  };
91
229
 
92
- export { type BulkCardProcessorConfig, CardHistory, CardRecord, ContentNavigator, CourseDBInterface, type ImportResult, Loggable, Navigators, QuestionRecord, ScheduledCard, StudyContentSource, StudySessionNewItem, StudySessionReviewItem, UserDBInterface, areQuestionRecords, docIsDeleted, getCardHistoryID, importParsedCards, isQuestionRecord, parseCardHistoryID, validateProcessorConfig };
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 };