@contractspec/module.learning-journey 1.57.0 → 1.59.0

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 (140) hide show
  1. package/dist/browser/contracts/index.js +578 -0
  2. package/dist/browser/contracts/models.js +193 -0
  3. package/dist/browser/contracts/onboarding.js +417 -0
  4. package/dist/browser/contracts/operations.js +326 -0
  5. package/dist/browser/contracts/shared.js +5 -0
  6. package/dist/browser/docs/index.js +124 -0
  7. package/dist/browser/docs/learning-journey.docblock.js +124 -0
  8. package/dist/browser/engines/index.js +526 -0
  9. package/dist/browser/engines/srs.js +198 -0
  10. package/dist/browser/engines/streak.js +159 -0
  11. package/dist/browser/engines/xp.js +171 -0
  12. package/dist/browser/entities/ai.js +343 -0
  13. package/dist/browser/entities/course.js +276 -0
  14. package/dist/browser/entities/flashcard.js +222 -0
  15. package/dist/browser/entities/gamification.js +340 -0
  16. package/dist/browser/entities/index.js +2136 -0
  17. package/dist/browser/entities/learner.js +329 -0
  18. package/dist/browser/entities/onboarding.js +301 -0
  19. package/dist/browser/entities/quiz.js +304 -0
  20. package/dist/browser/events.js +423 -0
  21. package/dist/browser/index.js +3833 -0
  22. package/dist/browser/learning-journey.capability.js +40 -0
  23. package/dist/browser/learning-journey.feature.js +56 -0
  24. package/dist/browser/track-spec.js +0 -0
  25. package/dist/contracts/index.d.ts +5 -5
  26. package/dist/contracts/index.d.ts.map +1 -0
  27. package/dist/contracts/index.js +578 -5
  28. package/dist/contracts/models.d.ts +426 -431
  29. package/dist/contracts/models.d.ts.map +1 -1
  30. package/dist/contracts/models.js +178 -372
  31. package/dist/contracts/onboarding.d.ts +621 -627
  32. package/dist/contracts/onboarding.d.ts.map +1 -1
  33. package/dist/contracts/onboarding.js +404 -388
  34. package/dist/contracts/operations.d.ts +243 -249
  35. package/dist/contracts/operations.d.ts.map +1 -1
  36. package/dist/contracts/operations.js +324 -148
  37. package/dist/contracts/shared.d.ts +1 -4
  38. package/dist/contracts/shared.d.ts.map +1 -1
  39. package/dist/contracts/shared.js +6 -6
  40. package/dist/docs/index.d.ts +2 -1
  41. package/dist/docs/index.d.ts.map +1 -0
  42. package/dist/docs/index.js +125 -1
  43. package/dist/docs/learning-journey.docblock.d.ts +2 -1
  44. package/dist/docs/learning-journey.docblock.d.ts.map +1 -0
  45. package/dist/docs/learning-journey.docblock.js +47 -58
  46. package/dist/engines/index.d.ts +4 -4
  47. package/dist/engines/index.d.ts.map +1 -0
  48. package/dist/engines/index.js +526 -4
  49. package/dist/engines/srs.d.ts +89 -92
  50. package/dist/engines/srs.d.ts.map +1 -1
  51. package/dist/engines/srs.js +197 -217
  52. package/dist/engines/streak.d.ts +84 -87
  53. package/dist/engines/streak.d.ts.map +1 -1
  54. package/dist/engines/streak.js +158 -192
  55. package/dist/engines/xp.d.ts +80 -83
  56. package/dist/engines/xp.d.ts.map +1 -1
  57. package/dist/engines/xp.js +170 -211
  58. package/dist/entities/ai.d.ts +199 -204
  59. package/dist/entities/ai.d.ts.map +1 -1
  60. package/dist/entities/ai.js +336 -368
  61. package/dist/entities/course.d.ts +149 -154
  62. package/dist/entities/course.d.ts.map +1 -1
  63. package/dist/entities/course.js +267 -306
  64. package/dist/entities/flashcard.d.ts +144 -149
  65. package/dist/entities/flashcard.d.ts.map +1 -1
  66. package/dist/entities/flashcard.js +217 -243
  67. package/dist/entities/gamification.d.ts +197 -202
  68. package/dist/entities/gamification.d.ts.map +1 -1
  69. package/dist/entities/gamification.js +331 -382
  70. package/dist/entities/index.d.ts +613 -618
  71. package/dist/entities/index.d.ts.map +1 -1
  72. package/dist/entities/index.js +2135 -43
  73. package/dist/entities/learner.d.ts +191 -196
  74. package/dist/entities/learner.d.ts.map +1 -1
  75. package/dist/entities/learner.js +322 -357
  76. package/dist/entities/onboarding.d.ts +164 -169
  77. package/dist/entities/onboarding.d.ts.map +1 -1
  78. package/dist/entities/onboarding.js +296 -301
  79. package/dist/entities/quiz.d.ts +184 -189
  80. package/dist/entities/quiz.d.ts.map +1 -1
  81. package/dist/entities/quiz.js +296 -361
  82. package/dist/events.d.ts +608 -614
  83. package/dist/events.d.ts.map +1 -1
  84. package/dist/events.js +421 -687
  85. package/dist/index.d.ts +8 -20
  86. package/dist/index.d.ts.map +1 -0
  87. package/dist/index.js +3834 -22
  88. package/dist/learning-journey.capability.d.ts +3 -8
  89. package/dist/learning-journey.capability.d.ts.map +1 -1
  90. package/dist/learning-journey.capability.js +41 -46
  91. package/dist/learning-journey.feature.d.ts +1 -6
  92. package/dist/learning-journey.feature.d.ts.map +1 -1
  93. package/dist/learning-journey.feature.js +55 -155
  94. package/dist/node/contracts/index.js +578 -0
  95. package/dist/node/contracts/models.js +193 -0
  96. package/dist/node/contracts/onboarding.js +417 -0
  97. package/dist/node/contracts/operations.js +326 -0
  98. package/dist/node/contracts/shared.js +5 -0
  99. package/dist/node/docs/index.js +124 -0
  100. package/dist/node/docs/learning-journey.docblock.js +124 -0
  101. package/dist/node/engines/index.js +526 -0
  102. package/dist/node/engines/srs.js +198 -0
  103. package/dist/node/engines/streak.js +159 -0
  104. package/dist/node/engines/xp.js +171 -0
  105. package/dist/node/entities/ai.js +343 -0
  106. package/dist/node/entities/course.js +276 -0
  107. package/dist/node/entities/flashcard.js +222 -0
  108. package/dist/node/entities/gamification.js +340 -0
  109. package/dist/node/entities/index.js +2136 -0
  110. package/dist/node/entities/learner.js +329 -0
  111. package/dist/node/entities/onboarding.js +301 -0
  112. package/dist/node/entities/quiz.js +304 -0
  113. package/dist/node/events.js +423 -0
  114. package/dist/node/index.js +3833 -0
  115. package/dist/node/learning-journey.capability.js +40 -0
  116. package/dist/node/learning-journey.feature.js +56 -0
  117. package/dist/node/track-spec.js +0 -0
  118. package/dist/track-spec.d.ts +115 -118
  119. package/dist/track-spec.d.ts.map +1 -1
  120. package/dist/track-spec.js +1 -0
  121. package/package.json +237 -60
  122. package/dist/contracts/models.js.map +0 -1
  123. package/dist/contracts/onboarding.js.map +0 -1
  124. package/dist/contracts/operations.js.map +0 -1
  125. package/dist/contracts/shared.js.map +0 -1
  126. package/dist/docs/learning-journey.docblock.js.map +0 -1
  127. package/dist/engines/srs.js.map +0 -1
  128. package/dist/engines/streak.js.map +0 -1
  129. package/dist/engines/xp.js.map +0 -1
  130. package/dist/entities/ai.js.map +0 -1
  131. package/dist/entities/course.js.map +0 -1
  132. package/dist/entities/flashcard.js.map +0 -1
  133. package/dist/entities/gamification.js.map +0 -1
  134. package/dist/entities/index.js.map +0 -1
  135. package/dist/entities/learner.js.map +0 -1
  136. package/dist/entities/onboarding.js.map +0 -1
  137. package/dist/entities/quiz.js.map +0 -1
  138. package/dist/events.js.map +0 -1
  139. package/dist/learning-journey.capability.js.map +0 -1
  140. package/dist/learning-journey.feature.js.map +0 -1
@@ -0,0 +1,276 @@
1
+ // src/entities/course.ts
2
+ import {
3
+ defineEntity,
4
+ defineEntityEnum,
5
+ field,
6
+ index
7
+ } from "@contractspec/lib.schema";
8
+ var CourseDifficultyEnum = defineEntityEnum({
9
+ name: "CourseDifficulty",
10
+ values: ["BEGINNER", "INTERMEDIATE", "ADVANCED", "EXPERT"],
11
+ schema: "lssm_learning",
12
+ description: "Difficulty level of a course."
13
+ });
14
+ var CourseStatusEnum = defineEntityEnum({
15
+ name: "CourseStatus",
16
+ values: ["DRAFT", "PUBLISHED", "ARCHIVED"],
17
+ schema: "lssm_learning",
18
+ description: "Publication status of a course."
19
+ });
20
+ var LessonTypeEnum = defineEntityEnum({
21
+ name: "LessonType",
22
+ values: [
23
+ "CONTENT",
24
+ "VIDEO",
25
+ "INTERACTIVE",
26
+ "QUIZ",
27
+ "PRACTICE",
28
+ "PROJECT"
29
+ ],
30
+ schema: "lssm_learning",
31
+ description: "Type of lesson content."
32
+ });
33
+ var ContentTypeEnum = defineEntityEnum({
34
+ name: "ContentType",
35
+ values: ["MARKDOWN", "VIDEO", "AUDIO", "EMBED", "SCORM", "CUSTOM"],
36
+ schema: "lssm_learning",
37
+ description: "Type of lesson content format."
38
+ });
39
+ var CourseEntity = defineEntity({
40
+ name: "Course",
41
+ description: "A structured learning course.",
42
+ schema: "lssm_learning",
43
+ map: "course",
44
+ fields: {
45
+ id: field.id({ description: "Unique course identifier" }),
46
+ title: field.string({ description: "Course title" }),
47
+ slug: field.string({ isUnique: true, description: "URL-friendly slug" }),
48
+ description: field.string({
49
+ isOptional: true,
50
+ description: "Course description"
51
+ }),
52
+ summary: field.string({ isOptional: true, description: "Short summary" }),
53
+ difficulty: field.enum("CourseDifficulty", {
54
+ default: "BEGINNER",
55
+ description: "Difficulty level"
56
+ }),
57
+ category: field.string({
58
+ isOptional: true,
59
+ description: "Course category"
60
+ }),
61
+ tags: field.json({ isOptional: true, description: "Tags for discovery" }),
62
+ prerequisites: field.json({
63
+ isOptional: true,
64
+ description: "Required course IDs"
65
+ }),
66
+ requiredSkills: field.json({
67
+ isOptional: true,
68
+ description: "Required skill levels"
69
+ }),
70
+ estimatedDuration: field.int({
71
+ isOptional: true,
72
+ description: "Estimated duration in minutes"
73
+ }),
74
+ thumbnailUrl: field.string({
75
+ isOptional: true,
76
+ description: "Thumbnail image URL"
77
+ }),
78
+ coverImageUrl: field.string({
79
+ isOptional: true,
80
+ description: "Cover image URL"
81
+ }),
82
+ promoVideoUrl: field.string({
83
+ isOptional: true,
84
+ description: "Promo video URL"
85
+ }),
86
+ status: field.enum("CourseStatus", {
87
+ default: "DRAFT",
88
+ description: "Publication status"
89
+ }),
90
+ publishedAt: field.dateTime({
91
+ isOptional: true,
92
+ description: "When published"
93
+ }),
94
+ authorId: field.string({ description: "Author user ID" }),
95
+ orgId: field.string({
96
+ isOptional: true,
97
+ description: "Organization scope"
98
+ }),
99
+ isPublic: field.boolean({
100
+ default: false,
101
+ description: "Whether course is publicly accessible"
102
+ }),
103
+ isFeatured: field.boolean({
104
+ default: false,
105
+ description: "Whether course is featured"
106
+ }),
107
+ certificateEnabled: field.boolean({
108
+ default: false,
109
+ description: "Award certificate on completion"
110
+ }),
111
+ metadata: field.json({
112
+ isOptional: true,
113
+ description: "Additional metadata"
114
+ }),
115
+ createdAt: field.createdAt(),
116
+ updatedAt: field.updatedAt(),
117
+ modules: field.hasMany("CourseModule"),
118
+ enrollments: field.hasMany("Enrollment")
119
+ },
120
+ indexes: [
121
+ index.on(["orgId", "status"]),
122
+ index.on(["category"]),
123
+ index.on(["difficulty"]),
124
+ index.on(["authorId"])
125
+ ],
126
+ enums: [CourseDifficultyEnum, CourseStatusEnum]
127
+ });
128
+ var CourseModuleEntity = defineEntity({
129
+ name: "CourseModule",
130
+ description: "A module (section) within a course.",
131
+ schema: "lssm_learning",
132
+ map: "course_module",
133
+ fields: {
134
+ id: field.id({ description: "Unique module identifier" }),
135
+ courseId: field.foreignKey({ description: "Parent course" }),
136
+ title: field.string({ description: "Module title" }),
137
+ description: field.string({
138
+ isOptional: true,
139
+ description: "Module description"
140
+ }),
141
+ order: field.int({ default: 0, description: "Display order" }),
142
+ unlockCondition: field.json({
143
+ isOptional: true,
144
+ description: "Conditions to unlock module"
145
+ }),
146
+ prerequisiteModuleIds: field.json({
147
+ isOptional: true,
148
+ description: "Required modules to complete first"
149
+ }),
150
+ estimatedDuration: field.int({
151
+ isOptional: true,
152
+ description: "Estimated duration in minutes"
153
+ }),
154
+ createdAt: field.createdAt(),
155
+ updatedAt: field.updatedAt(),
156
+ course: field.belongsTo("Course", ["courseId"], ["id"], {
157
+ onDelete: "Cascade"
158
+ }),
159
+ lessons: field.hasMany("Lesson"),
160
+ completions: field.hasMany("ModuleCompletion")
161
+ },
162
+ indexes: [index.on(["courseId", "order"])]
163
+ });
164
+ var LessonEntity = defineEntity({
165
+ name: "Lesson",
166
+ description: "An individual lesson within a module.",
167
+ schema: "lssm_learning",
168
+ map: "lesson",
169
+ fields: {
170
+ id: field.id({ description: "Unique lesson identifier" }),
171
+ moduleId: field.foreignKey({ description: "Parent module" }),
172
+ title: field.string({ description: "Lesson title" }),
173
+ description: field.string({
174
+ isOptional: true,
175
+ description: "Lesson description"
176
+ }),
177
+ type: field.enum("LessonType", {
178
+ default: "CONTENT",
179
+ description: "Lesson type"
180
+ }),
181
+ order: field.int({ default: 0, description: "Display order" }),
182
+ estimatedDuration: field.int({
183
+ isOptional: true,
184
+ description: "Estimated duration in minutes"
185
+ }),
186
+ xpReward: field.int({
187
+ default: 10,
188
+ description: "XP awarded on completion"
189
+ }),
190
+ isFree: field.boolean({
191
+ default: false,
192
+ description: "Whether lesson is free preview"
193
+ }),
194
+ isRequired: field.boolean({
195
+ default: true,
196
+ description: "Whether lesson is required for completion"
197
+ }),
198
+ metadata: field.json({
199
+ isOptional: true,
200
+ description: "Additional metadata"
201
+ }),
202
+ createdAt: field.createdAt(),
203
+ updatedAt: field.updatedAt(),
204
+ module: field.belongsTo("CourseModule", ["moduleId"], ["id"], {
205
+ onDelete: "Cascade"
206
+ }),
207
+ contents: field.hasMany("LessonContent"),
208
+ progress: field.hasMany("LessonProgress"),
209
+ quizzes: field.hasMany("Quiz")
210
+ },
211
+ indexes: [index.on(["moduleId", "order"]), index.on(["type"])],
212
+ enums: [LessonTypeEnum]
213
+ });
214
+ var LessonContentEntity = defineEntity({
215
+ name: "LessonContent",
216
+ description: "Content block within a lesson.",
217
+ schema: "lssm_learning",
218
+ map: "lesson_content",
219
+ fields: {
220
+ id: field.id({ description: "Unique content identifier" }),
221
+ lessonId: field.foreignKey({ description: "Parent lesson" }),
222
+ contentType: field.enum("ContentType", { description: "Content format" }),
223
+ content: field.string({
224
+ isOptional: true,
225
+ description: "Text content (markdown, etc.)"
226
+ }),
227
+ mediaUrl: field.string({
228
+ isOptional: true,
229
+ description: "Media URL for video/audio"
230
+ }),
231
+ embedData: field.json({
232
+ isOptional: true,
233
+ description: "Embed data for external content"
234
+ }),
235
+ order: field.int({ default: 0, description: "Display order" }),
236
+ duration: field.int({
237
+ isOptional: true,
238
+ description: "Content duration in seconds"
239
+ }),
240
+ metadata: field.json({
241
+ isOptional: true,
242
+ description: "Additional metadata"
243
+ }),
244
+ createdAt: field.createdAt(),
245
+ updatedAt: field.updatedAt(),
246
+ lesson: field.belongsTo("Lesson", ["lessonId"], ["id"], {
247
+ onDelete: "Cascade"
248
+ })
249
+ },
250
+ indexes: [index.on(["lessonId", "order"])],
251
+ enums: [ContentTypeEnum]
252
+ });
253
+ var courseEntities = [
254
+ CourseEntity,
255
+ CourseModuleEntity,
256
+ LessonEntity,
257
+ LessonContentEntity
258
+ ];
259
+ var courseEnums = [
260
+ CourseDifficultyEnum,
261
+ CourseStatusEnum,
262
+ LessonTypeEnum,
263
+ ContentTypeEnum
264
+ ];
265
+ export {
266
+ courseEnums,
267
+ courseEntities,
268
+ LessonTypeEnum,
269
+ LessonEntity,
270
+ LessonContentEntity,
271
+ CourseStatusEnum,
272
+ CourseModuleEntity,
273
+ CourseEntity,
274
+ CourseDifficultyEnum,
275
+ ContentTypeEnum
276
+ };
@@ -0,0 +1,222 @@
1
+ // src/entities/flashcard.ts
2
+ import {
3
+ defineEntity,
4
+ defineEntityEnum,
5
+ field,
6
+ index
7
+ } from "@contractspec/lib.schema";
8
+ var CardRatingEnum = defineEntityEnum({
9
+ name: "CardRating",
10
+ values: ["AGAIN", "HARD", "GOOD", "EASY"],
11
+ schema: "lssm_learning",
12
+ description: "Rating for a flashcard review."
13
+ });
14
+ var DeckEntity = defineEntity({
15
+ name: "Deck",
16
+ description: "A collection of flashcards.",
17
+ schema: "lssm_learning",
18
+ map: "deck",
19
+ fields: {
20
+ id: field.id({ description: "Unique deck identifier" }),
21
+ ownerId: field.foreignKey({ description: "Deck owner (learner)" }),
22
+ title: field.string({ description: "Deck title" }),
23
+ description: field.string({
24
+ isOptional: true,
25
+ description: "Deck description"
26
+ }),
27
+ category: field.string({ isOptional: true, description: "Deck category" }),
28
+ tags: field.json({ isOptional: true, description: "Tags for discovery" }),
29
+ isPublic: field.boolean({
30
+ default: false,
31
+ description: "Whether deck is publicly visible"
32
+ }),
33
+ cardCount: field.int({ default: 0, description: "Number of cards" }),
34
+ coverImageUrl: field.string({
35
+ isOptional: true,
36
+ description: "Cover image URL"
37
+ }),
38
+ orgId: field.string({
39
+ isOptional: true,
40
+ description: "Organization scope"
41
+ }),
42
+ newCardsPerDay: field.int({
43
+ default: 20,
44
+ description: "New cards to introduce per day"
45
+ }),
46
+ reviewsPerDay: field.int({
47
+ default: 100,
48
+ description: "Maximum reviews per day"
49
+ }),
50
+ metadata: field.json({
51
+ isOptional: true,
52
+ description: "Additional metadata"
53
+ }),
54
+ createdAt: field.createdAt(),
55
+ updatedAt: field.updatedAt(),
56
+ owner: field.belongsTo("Learner", ["ownerId"], ["id"], {
57
+ onDelete: "Cascade"
58
+ }),
59
+ cards: field.hasMany("Card")
60
+ },
61
+ indexes: [
62
+ index.on(["ownerId"]),
63
+ index.on(["isPublic", "category"]),
64
+ index.on(["orgId"])
65
+ ]
66
+ });
67
+ var CardEntity = defineEntity({
68
+ name: "Card",
69
+ description: "An individual flashcard.",
70
+ schema: "lssm_learning",
71
+ map: "card",
72
+ fields: {
73
+ id: field.id({ description: "Unique card identifier" }),
74
+ deckId: field.foreignKey({ description: "Parent deck" }),
75
+ front: field.string({ description: "Front of card (question)" }),
76
+ back: field.string({ description: "Back of card (answer)" }),
77
+ hints: field.json({ isOptional: true, description: "Hints for the card" }),
78
+ explanation: field.string({
79
+ isOptional: true,
80
+ description: "Detailed explanation"
81
+ }),
82
+ frontMediaUrl: field.string({
83
+ isOptional: true,
84
+ description: "Media for front"
85
+ }),
86
+ backMediaUrl: field.string({
87
+ isOptional: true,
88
+ description: "Media for back"
89
+ }),
90
+ audioUrl: field.string({
91
+ isOptional: true,
92
+ description: "Audio pronunciation"
93
+ }),
94
+ tags: field.json({ isOptional: true, description: "Card tags" }),
95
+ difficulty: field.int({ default: 0, description: "Card difficulty (0-5)" }),
96
+ order: field.int({ default: 0, description: "Card order in deck" }),
97
+ isSuspended: field.boolean({
98
+ default: false,
99
+ description: "Whether card is suspended"
100
+ }),
101
+ metadata: field.json({
102
+ isOptional: true,
103
+ description: "Additional metadata"
104
+ }),
105
+ createdAt: field.createdAt(),
106
+ updatedAt: field.updatedAt(),
107
+ deck: field.belongsTo("Deck", ["deckId"], ["id"], { onDelete: "Cascade" }),
108
+ reviews: field.hasMany("CardReview"),
109
+ schedules: field.hasMany("CardSchedule")
110
+ },
111
+ indexes: [index.on(["deckId", "order"]), index.on(["isSuspended"])]
112
+ });
113
+ var CardReviewEntity = defineEntity({
114
+ name: "CardReview",
115
+ description: "A single review of a flashcard.",
116
+ schema: "lssm_learning",
117
+ map: "card_review",
118
+ fields: {
119
+ id: field.id({ description: "Unique review identifier" }),
120
+ learnerId: field.foreignKey({ description: "Reviewer" }),
121
+ cardId: field.foreignKey({ description: "Reviewed card" }),
122
+ rating: field.enum("CardRating", { description: "Review rating" }),
123
+ responseTimeMs: field.int({
124
+ isOptional: true,
125
+ description: "Time to respond in ms"
126
+ }),
127
+ intervalBefore: field.int({ description: "Interval before review (days)" }),
128
+ easeFactorBefore: field.decimal({
129
+ description: "Ease factor before review"
130
+ }),
131
+ intervalAfter: field.int({ description: "Interval after review (days)" }),
132
+ easeFactorAfter: field.decimal({ description: "Ease factor after review" }),
133
+ reviewType: field.string({
134
+ default: '"review"',
135
+ description: "Type: new, learning, review, relearning"
136
+ }),
137
+ reviewedAt: field.dateTime({ description: "When reviewed" }),
138
+ createdAt: field.createdAt(),
139
+ learner: field.belongsTo("Learner", ["learnerId"], ["id"], {
140
+ onDelete: "Cascade"
141
+ }),
142
+ card: field.belongsTo("Card", ["cardId"], ["id"], { onDelete: "Cascade" })
143
+ },
144
+ indexes: [
145
+ index.on(["learnerId", "reviewedAt"]),
146
+ index.on(["cardId", "reviewedAt"]),
147
+ index.on(["rating"])
148
+ ],
149
+ enums: [CardRatingEnum]
150
+ });
151
+ var CardScheduleEntity = defineEntity({
152
+ name: "CardSchedule",
153
+ description: "SRS schedule for a learner/card pair.",
154
+ schema: "lssm_learning",
155
+ map: "card_schedule",
156
+ fields: {
157
+ id: field.id({ description: "Unique schedule identifier" }),
158
+ learnerId: field.foreignKey({ description: "Learner" }),
159
+ cardId: field.foreignKey({ description: "Card" }),
160
+ interval: field.int({
161
+ default: 0,
162
+ description: "Current interval in days"
163
+ }),
164
+ easeFactor: field.decimal({
165
+ default: 2.5,
166
+ description: "Ease factor (SM-2)"
167
+ }),
168
+ repetitions: field.int({
169
+ default: 0,
170
+ description: "Number of successful repetitions"
171
+ }),
172
+ nextReviewAt: field.dateTime({ description: "When next review is due" }),
173
+ lastReviewAt: field.dateTime({
174
+ isOptional: true,
175
+ description: "When last reviewed"
176
+ }),
177
+ learningStep: field.int({
178
+ default: 0,
179
+ description: "Current learning step"
180
+ }),
181
+ isGraduated: field.boolean({
182
+ default: false,
183
+ description: "Whether card has graduated"
184
+ }),
185
+ isRelearning: field.boolean({
186
+ default: false,
187
+ description: "Whether card is being relearned"
188
+ }),
189
+ lapses: field.int({
190
+ default: 0,
191
+ description: "Number of times card was forgotten"
192
+ }),
193
+ reviewCount: field.int({ default: 0, description: "Total review count" }),
194
+ createdAt: field.createdAt(),
195
+ updatedAt: field.updatedAt(),
196
+ learner: field.belongsTo("Learner", ["learnerId"], ["id"], {
197
+ onDelete: "Cascade"
198
+ }),
199
+ card: field.belongsTo("Card", ["cardId"], ["id"], { onDelete: "Cascade" })
200
+ },
201
+ indexes: [
202
+ index.unique(["learnerId", "cardId"], { name: "card_schedule_unique" }),
203
+ index.on(["learnerId", "nextReviewAt"]),
204
+ index.on(["nextReviewAt"])
205
+ ]
206
+ });
207
+ var flashcardEntities = [
208
+ DeckEntity,
209
+ CardEntity,
210
+ CardReviewEntity,
211
+ CardScheduleEntity
212
+ ];
213
+ var flashcardEnums = [CardRatingEnum];
214
+ export {
215
+ flashcardEnums,
216
+ flashcardEntities,
217
+ DeckEntity,
218
+ CardScheduleEntity,
219
+ CardReviewEntity,
220
+ CardRatingEnum,
221
+ CardEntity
222
+ };