@lssm/module.learning-journey 0.0.0-canary-20251217062943 → 0.0.0-canary-20251217072406

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 (156) hide show
  1. package/dist/contracts/index.js +6 -1
  2. package/dist/contracts/models.d.ts +118 -118
  3. package/dist/contracts/models.js +389 -1
  4. package/dist/contracts/onboarding.d.ts +168 -168
  5. package/dist/contracts/onboarding.js +404 -1
  6. package/dist/contracts/operations.d.ts +71 -71
  7. package/dist/contracts/operations.js +151 -1
  8. package/dist/contracts/shared.js +5 -1
  9. package/dist/docs/index.js +1 -1
  10. package/dist/docs/learning-journey.docblock.js +61 -5
  11. package/dist/engines/index.js +5 -1
  12. package/dist/engines/srs.js +219 -1
  13. package/dist/engines/streak.js +193 -1
  14. package/dist/engines/xp.js +212 -1
  15. package/dist/entities/ai.d.ts +200 -200
  16. package/dist/entities/ai.js +376 -1
  17. package/dist/entities/course.d.ts +150 -150
  18. package/dist/entities/course.js +316 -1
  19. package/dist/entities/flashcard.d.ts +145 -145
  20. package/dist/entities/flashcard.js +249 -1
  21. package/dist/entities/gamification.d.ts +198 -198
  22. package/dist/entities/gamification.js +392 -1
  23. package/dist/entities/index.d.ts +605 -605
  24. package/dist/entities/index.js +44 -1
  25. package/dist/entities/learner.d.ts +192 -192
  26. package/dist/entities/learner.js +365 -1
  27. package/dist/entities/onboarding.d.ts +165 -165
  28. package/dist/entities/onboarding.js +307 -1
  29. package/dist/entities/quiz.d.ts +185 -185
  30. package/dist/entities/quiz.js +370 -1
  31. package/dist/events.d.ts +213 -213
  32. package/dist/events.js +583 -1
  33. package/dist/index.js +22 -1
  34. package/dist/learning-journey.feature.js +148 -1
  35. package/dist/libs/contracts/dist/capabilities/openbanking.js +88 -1
  36. package/dist/libs/contracts/dist/client/index.js +5 -1
  37. package/dist/libs/contracts/dist/client/react/feature-render.js +2 -1
  38. package/dist/libs/contracts/dist/client/react/form-render.js +4 -1
  39. package/dist/libs/contracts/dist/client/react/index.js +4 -1
  40. package/dist/libs/contracts/dist/contract-registry/index.js +1 -1
  41. package/dist/libs/contracts/dist/contract-registry/schemas.js +60 -1
  42. package/dist/libs/contracts/dist/docs/PUBLISHING.docblock.js +16 -76
  43. package/dist/libs/contracts/dist/docs/accessibility_wcag_compliance_specs.docblock.js +16 -350
  44. package/dist/libs/contracts/dist/docs/index.js +29 -1
  45. package/dist/libs/contracts/dist/docs/presentations.js +71 -1
  46. package/dist/libs/contracts/dist/docs/registry.js +44 -1
  47. package/dist/libs/contracts/dist/docs/tech/PHASE_1_QUICKSTART.docblock.js +16 -383
  48. package/dist/libs/contracts/dist/docs/tech/PHASE_2_AI_NATIVE_OPERATIONS.docblock.js +16 -68
  49. package/dist/libs/contracts/dist/docs/tech/PHASE_3_AUTO_EVOLUTION.docblock.js +16 -140
  50. package/dist/libs/contracts/dist/docs/tech/PHASE_4_PERSONALIZATION_ENGINE.docblock.js +16 -86
  51. package/dist/libs/contracts/dist/docs/tech/PHASE_5_ZERO_TOUCH_OPERATIONS.docblock.js +16 -1
  52. package/dist/libs/contracts/dist/docs/tech/auth/better-auth-nextjs.docblock.js +24 -2
  53. package/dist/libs/contracts/dist/docs/tech/contracts/openapi-export.docblock.js +21 -2
  54. package/dist/libs/contracts/dist/docs/tech/lifecycle-stage-system.docblock.js +16 -213
  55. package/dist/libs/contracts/dist/docs/tech/llm/llm-integration.docblock.js +73 -5
  56. package/dist/libs/contracts/dist/docs/tech/mcp-endpoints.docblock.js +37 -1
  57. package/dist/libs/contracts/dist/docs/tech/presentation-runtime.docblock.js +16 -1
  58. package/dist/libs/contracts/dist/docs/tech/schema/README.docblock.js +20 -262
  59. package/dist/libs/contracts/dist/docs/tech/studio/learning-events.docblock.js +48 -1
  60. package/dist/libs/contracts/dist/docs/tech/studio/learning-journeys.docblock.js +24 -2
  61. package/dist/libs/contracts/dist/docs/tech/studio/platform-admin-panel.docblock.js +23 -2
  62. package/dist/libs/contracts/dist/docs/tech/studio/project-access-teams.docblock.js +25 -16
  63. package/dist/libs/contracts/dist/docs/tech/studio/project-routing.docblock.js +67 -1
  64. package/dist/libs/contracts/dist/docs/tech/studio/sandbox-unlogged.docblock.js +22 -2
  65. package/dist/libs/contracts/dist/docs/tech/studio/team-invitations.docblock.js +40 -36
  66. package/dist/libs/contracts/dist/docs/tech/studio/workspace-ops.docblock.js +47 -1
  67. package/dist/libs/contracts/dist/docs/tech/studio/workspaces.docblock.js +23 -2
  68. package/dist/libs/contracts/dist/docs/tech/telemetry-ingest.docblock.js +36 -3
  69. package/dist/libs/contracts/dist/docs/tech/templates/runtime.docblock.js +20 -1
  70. package/dist/libs/contracts/dist/docs/tech/vscode-extension.docblock.js +36 -3
  71. package/dist/libs/contracts/dist/docs/tech/workflows/overview.docblock.js +20 -1
  72. package/dist/libs/contracts/dist/events.js +10 -1
  73. package/dist/libs/contracts/dist/experiments/evaluator.js +1 -1
  74. package/dist/libs/contracts/dist/index.js +71 -1
  75. package/dist/libs/contracts/dist/install.js +2 -1
  76. package/dist/libs/contracts/dist/integrations/contracts.js +377 -1
  77. package/dist/libs/contracts/dist/integrations/index.js +18 -1
  78. package/dist/libs/contracts/dist/integrations/openbanking/contracts/accounts.js +228 -1
  79. package/dist/libs/contracts/dist/integrations/openbanking/contracts/balances.js +159 -1
  80. package/dist/libs/contracts/dist/integrations/openbanking/contracts/index.js +3 -1
  81. package/dist/libs/contracts/dist/integrations/openbanking/contracts/transactions.js +210 -1
  82. package/dist/libs/contracts/dist/integrations/openbanking/models.js +242 -1
  83. package/dist/libs/contracts/dist/integrations/openbanking/telemetry.js +13 -1
  84. package/dist/libs/contracts/dist/integrations/providers/elevenlabs.js +52 -1
  85. package/dist/libs/contracts/dist/integrations/providers/gcs-storage.js +75 -1
  86. package/dist/libs/contracts/dist/integrations/providers/gmail.js +87 -1
  87. package/dist/libs/contracts/dist/integrations/providers/google-calendar.js +66 -1
  88. package/dist/libs/contracts/dist/integrations/providers/index.js +11 -1
  89. package/dist/libs/contracts/dist/integrations/providers/mistral.js +68 -1
  90. package/dist/libs/contracts/dist/integrations/providers/postmark.js +68 -1
  91. package/dist/libs/contracts/dist/integrations/providers/powens.js +116 -1
  92. package/dist/libs/contracts/dist/integrations/providers/qdrant.js +73 -1
  93. package/dist/libs/contracts/dist/integrations/providers/registry.js +10 -1
  94. package/dist/libs/contracts/dist/integrations/providers/stripe.js +83 -1
  95. package/dist/libs/contracts/dist/integrations/providers/twilio-sms.js +61 -1
  96. package/dist/libs/contracts/dist/jsonschema.js +1 -1
  97. package/dist/libs/contracts/dist/knowledge/contracts.js +306 -1
  98. package/dist/libs/contracts/dist/knowledge/index.js +7 -1
  99. package/dist/libs/contracts/dist/knowledge/spaces/email-threads.js +34 -1
  100. package/dist/libs/contracts/dist/knowledge/spaces/financial-docs.js +34 -1
  101. package/dist/libs/contracts/dist/knowledge/spaces/financial-overview.js +38 -1
  102. package/dist/libs/contracts/dist/knowledge/spaces/index.js +6 -1
  103. package/dist/libs/contracts/dist/knowledge/spaces/product-canon.js +34 -1
  104. package/dist/libs/contracts/dist/knowledge/spaces/support-faq.js +37 -1
  105. package/dist/libs/contracts/dist/knowledge/spaces/uploaded-docs.js +34 -1
  106. package/dist/libs/contracts/dist/llm/exporters.js +19 -1
  107. package/dist/libs/contracts/dist/llm/index.js +2 -1
  108. package/dist/libs/contracts/dist/llm/prompts.js +1 -1
  109. package/dist/libs/contracts/dist/onboarding-base.js +196 -1
  110. package/dist/libs/contracts/dist/openapi.js +1 -1
  111. package/dist/libs/contracts/dist/ownership.js +21 -1
  112. package/dist/libs/contracts/dist/presentations.js +1 -1
  113. package/dist/libs/contracts/dist/presentations.v2.js +11 -1
  114. package/dist/libs/contracts/dist/prompt.js +1 -1
  115. package/dist/libs/contracts/dist/promptRegistry.js +1 -1
  116. package/dist/libs/contracts/dist/regenerator/index.js +1 -1
  117. package/dist/libs/contracts/dist/regenerator/service.js +6 -1
  118. package/dist/libs/contracts/dist/registry.js +2 -1
  119. package/dist/libs/contracts/dist/resources.js +1 -1
  120. package/dist/libs/contracts/dist/schema/dist/EnumType.js +2 -1
  121. package/dist/libs/contracts/dist/schema/dist/FieldType.js +49 -1
  122. package/dist/libs/contracts/dist/schema/dist/ScalarTypeEnum.js +236 -1
  123. package/dist/libs/contracts/dist/schema/dist/SchemaModel.js +34 -1
  124. package/dist/libs/contracts/dist/schema/dist/entity/defineEntity.js +1 -1
  125. package/dist/libs/contracts/dist/schema/dist/entity/index.js +2 -1
  126. package/dist/libs/contracts/dist/schema/dist/entity/types.js +1 -1
  127. package/dist/libs/contracts/dist/schema/dist/index.js +6 -1
  128. package/dist/libs/contracts/dist/server/graphql-pothos.js +6 -1
  129. package/dist/libs/contracts/dist/server/index.js +8 -1
  130. package/dist/libs/contracts/dist/server/mcp/createMcpServer.js +4 -1
  131. package/dist/libs/contracts/dist/server/mcp/registerPresentations.js +2 -1
  132. package/dist/libs/contracts/dist/server/mcp/registerPrompts.js +1 -1
  133. package/dist/libs/contracts/dist/server/mcp/registerResources.js +2 -1
  134. package/dist/libs/contracts/dist/server/mcp/registerTools.js +1 -1
  135. package/dist/libs/contracts/dist/server/provider-mcp.js +1 -1
  136. package/dist/libs/contracts/dist/server/rest-elysia.js +1 -1
  137. package/dist/libs/contracts/dist/server/rest-express.js +1 -1
  138. package/dist/libs/contracts/dist/server/rest-generic.js +1 -1
  139. package/dist/libs/contracts/dist/server/rest-next-app.js +1 -1
  140. package/dist/libs/contracts/dist/server/rest-next-pages.js +1 -1
  141. package/dist/libs/contracts/dist/spec.js +34 -1
  142. package/dist/libs/contracts/dist/telemetry/index.js +1 -1
  143. package/dist/libs/contracts/dist/telemetry/tracker.js +1 -1
  144. package/dist/libs/contracts/dist/tests/index.js +1 -1
  145. package/dist/libs/contracts/dist/tests/runner.js +2 -1
  146. package/dist/libs/contracts/dist/workflow/index.js +1 -1
  147. package/dist/libs/contracts/dist/workflow/runner.js +1 -1
  148. package/dist/libs/schema/dist/EnumType.js +2 -1
  149. package/dist/libs/schema/dist/FieldType.js +49 -1
  150. package/dist/libs/schema/dist/ScalarTypeEnum.js +236 -1
  151. package/dist/libs/schema/dist/SchemaModel.js +39 -1
  152. package/dist/libs/schema/dist/entity/defineEntity.js +236 -1
  153. package/dist/libs/schema/dist/entity/index.js +2 -1
  154. package/dist/libs/schema/dist/entity/types.js +1 -1
  155. package/dist/libs/schema/dist/index.js +6 -1
  156. package/package.json +5 -5
@@ -1 +1,219 @@
1
- const e={learningSteps:[1,10],graduatingInterval:1,easyInterval:4,relearningSteps:[10],minEaseFactor:1.3,maxInterval:365,intervalModifier:1,newIntervalModifier:.5,hardIntervalModifier:1.2,easyBonus:1.3};var t=class{config;constructor(t={}){this.config={...e,...t}}calculateNextReview(e,t,n=new Date){return!e.isGraduated&&!e.isRelearning?this.handleLearningCard(e,t,n):e.isRelearning?this.handleRelearningCard(e,t,n):this.handleReviewCard(e,t,n)}handleLearningCard(e,t,n){let r=this.config.learningSteps,i=e.learningStep,a=!1,o=0,s;switch(t){case`AGAIN`:i=0,o=r[0]??1,s=this.addMinutes(n,o);break;case`HARD`:o=r[i]??r[0]??1,s=this.addMinutes(n,o);break;case`GOOD`:i++,i>=r.length?(a=!0,o=this.config.graduatingInterval,s=this.addDays(n,o)):(o=r[i]??10,s=this.addMinutes(n,o));break;case`EASY`:a=!0,o=this.config.easyInterval,s=this.addDays(n,o);break}return{interval:a?o:0,easeFactor:e.easeFactor,repetitions:a?1:0,nextReviewAt:s,learningStep:i,isGraduated:a,isRelearning:!1,lapses:e.lapses}}handleRelearningCard(e,t,n){let r=this.config.relearningSteps,i=e.learningStep,a=!0,o=0,s;switch(t){case`AGAIN`:i=0,o=r[0]??10,s=this.addMinutes(n,o);break;case`HARD`:o=r[i]??r[0]??10,s=this.addMinutes(n,o);break;case`GOOD`:i++,i>=r.length?(a=!1,o=Math.max(1,Math.floor(e.interval*this.config.newIntervalModifier)),s=this.addDays(n,o)):(o=r[i]??10,s=this.addMinutes(n,o));break;case`EASY`:a=!1,o=Math.max(1,Math.floor(e.interval*this.config.newIntervalModifier*1.5)),s=this.addDays(n,o);break}return{interval:a?e.interval:o,easeFactor:e.easeFactor,repetitions:a?e.repetitions:e.repetitions+1,nextReviewAt:s,learningStep:i,isGraduated:!0,isRelearning:a,lapses:e.lapses}}handleReviewCard(e,t,n){let r,i=e.easeFactor,a=e.repetitions,o=!1,s=0,c=e.lapses;switch(t){case`AGAIN`:c++,o=!0,s=0,i=Math.max(this.config.minEaseFactor,i-.2),r=e.interval;let t=this.config.relearningSteps[0]??10;return{interval:r,easeFactor:i,repetitions:a,nextReviewAt:this.addMinutes(n,t),learningStep:s,isGraduated:!0,isRelearning:!0,lapses:c};case`HARD`:i=Math.max(this.config.minEaseFactor,i-.15),r=Math.max(e.interval+1,e.interval*this.config.hardIntervalModifier);break;case`GOOD`:r=e.interval*i*this.config.intervalModifier,a++;break;case`EASY`:i+=.15,r=e.interval*i*this.config.easyBonus*this.config.intervalModifier,a++;break}return r=Math.min(Math.round(r),this.config.maxInterval),r=Math.max(1,r),{interval:r,easeFactor:i,repetitions:a,nextReviewAt:this.addDays(n,r),learningStep:s,isGraduated:!0,isRelearning:o,lapses:c}}getInitialState(){return{interval:0,easeFactor:2.5,repetitions:0,learningStep:0,isGraduated:!1,isRelearning:!1,lapses:0}}isDue(e,t=new Date){return e<=t}getOverdueDays(e,t=new Date){let n=t.getTime()-e.getTime();return Math.floor(n/(1e3*60*60*24))}addMinutes(e,t){return new Date(e.getTime()+t*60*1e3)}addDays(e,t){return new Date(e.getTime()+t*24*60*60*1e3)}};const n=new t;export{e as DEFAULT_SRS_CONFIG,t as SRSEngine,n as srsEngine};
1
+ //#region src/engines/srs.ts
2
+ const DEFAULT_SRS_CONFIG = {
3
+ learningSteps: [1, 10],
4
+ graduatingInterval: 1,
5
+ easyInterval: 4,
6
+ relearningSteps: [10],
7
+ minEaseFactor: 1.3,
8
+ maxInterval: 365,
9
+ intervalModifier: 1,
10
+ newIntervalModifier: .5,
11
+ hardIntervalModifier: 1.2,
12
+ easyBonus: 1.3
13
+ };
14
+ var SRSEngine = class {
15
+ config;
16
+ constructor(config = {}) {
17
+ this.config = {
18
+ ...DEFAULT_SRS_CONFIG,
19
+ ...config
20
+ };
21
+ }
22
+ /**
23
+ * Calculate the next review state based on rating.
24
+ */
25
+ calculateNextReview(state, rating, now = /* @__PURE__ */ new Date()) {
26
+ if (!state.isGraduated && !state.isRelearning) return this.handleLearningCard(state, rating, now);
27
+ if (state.isRelearning) return this.handleRelearningCard(state, rating, now);
28
+ return this.handleReviewCard(state, rating, now);
29
+ }
30
+ /**
31
+ * Handle learning phase (new cards).
32
+ */
33
+ handleLearningCard(state, rating, now) {
34
+ const steps = this.config.learningSteps;
35
+ let newStep = state.learningStep;
36
+ let isGraduated = false;
37
+ let interval = 0;
38
+ let nextReviewAt;
39
+ switch (rating) {
40
+ case "AGAIN":
41
+ newStep = 0;
42
+ interval = steps[0] ?? 1;
43
+ nextReviewAt = this.addMinutes(now, interval);
44
+ break;
45
+ case "HARD":
46
+ interval = steps[newStep] ?? steps[0] ?? 1;
47
+ nextReviewAt = this.addMinutes(now, interval);
48
+ break;
49
+ case "GOOD":
50
+ newStep++;
51
+ if (newStep >= steps.length) {
52
+ isGraduated = true;
53
+ interval = this.config.graduatingInterval;
54
+ nextReviewAt = this.addDays(now, interval);
55
+ } else {
56
+ interval = steps[newStep] ?? 10;
57
+ nextReviewAt = this.addMinutes(now, interval);
58
+ }
59
+ break;
60
+ case "EASY":
61
+ isGraduated = true;
62
+ interval = this.config.easyInterval;
63
+ nextReviewAt = this.addDays(now, interval);
64
+ break;
65
+ }
66
+ return {
67
+ interval: isGraduated ? interval : 0,
68
+ easeFactor: state.easeFactor,
69
+ repetitions: isGraduated ? 1 : 0,
70
+ nextReviewAt,
71
+ learningStep: newStep,
72
+ isGraduated,
73
+ isRelearning: false,
74
+ lapses: state.lapses
75
+ };
76
+ }
77
+ /**
78
+ * Handle relearning phase (lapsed cards).
79
+ */
80
+ handleRelearningCard(state, rating, now) {
81
+ const steps = this.config.relearningSteps;
82
+ let newStep = state.learningStep;
83
+ let isRelearning = true;
84
+ let interval = 0;
85
+ let nextReviewAt;
86
+ switch (rating) {
87
+ case "AGAIN":
88
+ newStep = 0;
89
+ interval = steps[0] ?? 10;
90
+ nextReviewAt = this.addMinutes(now, interval);
91
+ break;
92
+ case "HARD":
93
+ interval = steps[newStep] ?? steps[0] ?? 10;
94
+ nextReviewAt = this.addMinutes(now, interval);
95
+ break;
96
+ case "GOOD":
97
+ newStep++;
98
+ if (newStep >= steps.length) {
99
+ isRelearning = false;
100
+ interval = Math.max(1, Math.floor(state.interval * this.config.newIntervalModifier));
101
+ nextReviewAt = this.addDays(now, interval);
102
+ } else {
103
+ interval = steps[newStep] ?? 10;
104
+ nextReviewAt = this.addMinutes(now, interval);
105
+ }
106
+ break;
107
+ case "EASY":
108
+ isRelearning = false;
109
+ interval = Math.max(1, Math.floor(state.interval * this.config.newIntervalModifier * 1.5));
110
+ nextReviewAt = this.addDays(now, interval);
111
+ break;
112
+ }
113
+ return {
114
+ interval: isRelearning ? state.interval : interval,
115
+ easeFactor: state.easeFactor,
116
+ repetitions: isRelearning ? state.repetitions : state.repetitions + 1,
117
+ nextReviewAt,
118
+ learningStep: newStep,
119
+ isGraduated: true,
120
+ isRelearning,
121
+ lapses: state.lapses
122
+ };
123
+ }
124
+ /**
125
+ * Handle review phase (graduated cards).
126
+ */
127
+ handleReviewCard(state, rating, now) {
128
+ let newInterval;
129
+ let newEaseFactor = state.easeFactor;
130
+ let repetitions = state.repetitions;
131
+ let isRelearning = false;
132
+ let learningStep = 0;
133
+ let lapses = state.lapses;
134
+ switch (rating) {
135
+ case "AGAIN":
136
+ lapses++;
137
+ isRelearning = true;
138
+ learningStep = 0;
139
+ newEaseFactor = Math.max(this.config.minEaseFactor, newEaseFactor - .2);
140
+ newInterval = state.interval;
141
+ const relearnStep = this.config.relearningSteps[0] ?? 10;
142
+ return {
143
+ interval: newInterval,
144
+ easeFactor: newEaseFactor,
145
+ repetitions,
146
+ nextReviewAt: this.addMinutes(now, relearnStep),
147
+ learningStep,
148
+ isGraduated: true,
149
+ isRelearning: true,
150
+ lapses
151
+ };
152
+ case "HARD":
153
+ newEaseFactor = Math.max(this.config.minEaseFactor, newEaseFactor - .15);
154
+ newInterval = Math.max(state.interval + 1, state.interval * this.config.hardIntervalModifier);
155
+ break;
156
+ case "GOOD":
157
+ newInterval = state.interval * newEaseFactor * this.config.intervalModifier;
158
+ repetitions++;
159
+ break;
160
+ case "EASY":
161
+ newEaseFactor = newEaseFactor + .15;
162
+ newInterval = state.interval * newEaseFactor * this.config.easyBonus * this.config.intervalModifier;
163
+ repetitions++;
164
+ break;
165
+ }
166
+ newInterval = Math.min(Math.round(newInterval), this.config.maxInterval);
167
+ newInterval = Math.max(1, newInterval);
168
+ return {
169
+ interval: newInterval,
170
+ easeFactor: newEaseFactor,
171
+ repetitions,
172
+ nextReviewAt: this.addDays(now, newInterval),
173
+ learningStep,
174
+ isGraduated: true,
175
+ isRelearning,
176
+ lapses
177
+ };
178
+ }
179
+ /**
180
+ * Get initial SRS state for a new card.
181
+ */
182
+ getInitialState() {
183
+ return {
184
+ interval: 0,
185
+ easeFactor: 2.5,
186
+ repetitions: 0,
187
+ learningStep: 0,
188
+ isGraduated: false,
189
+ isRelearning: false,
190
+ lapses: 0
191
+ };
192
+ }
193
+ /**
194
+ * Check if a card is due for review.
195
+ */
196
+ isDue(nextReviewAt, now = /* @__PURE__ */ new Date()) {
197
+ return nextReviewAt <= now;
198
+ }
199
+ /**
200
+ * Calculate overdue days (negative if not yet due).
201
+ */
202
+ getOverdueDays(nextReviewAt, now = /* @__PURE__ */ new Date()) {
203
+ const diff = now.getTime() - nextReviewAt.getTime();
204
+ return Math.floor(diff / (1e3 * 60 * 60 * 24));
205
+ }
206
+ addMinutes(date, minutes) {
207
+ return new Date(date.getTime() + minutes * 60 * 1e3);
208
+ }
209
+ addDays(date, days) {
210
+ return new Date(date.getTime() + days * 24 * 60 * 60 * 1e3);
211
+ }
212
+ };
213
+ /**
214
+ * Default SRS engine instance.
215
+ */
216
+ const srsEngine = new SRSEngine();
217
+
218
+ //#endregion
219
+ export { DEFAULT_SRS_CONFIG, SRSEngine, srsEngine };
@@ -1 +1,193 @@
1
- const e={timezone:`UTC`,freezesPerMonth:2,maxFreezes:5,gracePeriodHours:4};var t=class{config;constructor(t={}){this.config={...e,...t}}update(e,t=new Date){let n=this.getDateString(t),r={state:{...e},streakMaintained:!1,streakLost:!1,freezeUsed:!1,newStreak:!1,daysMissed:0};if(!e.lastActivityDate)return r.state.currentStreak=1,r.state.longestStreak=Math.max(1,e.longestStreak),r.state.lastActivityAt=t,r.state.lastActivityDate=n,r.newStreak=!0,r.streakMaintained=!0,r;if(e.lastActivityDate===n)return r.state.lastActivityAt=t,r.streakMaintained=!0,r;let i=this.getDaysBetween(e.lastActivityDate,n);if(i===1)return r.state.currentStreak=e.currentStreak+1,r.state.longestStreak=Math.max(r.state.currentStreak,e.longestStreak),r.state.lastActivityAt=t,r.state.lastActivityDate=n,r.streakMaintained=!0,r;r.daysMissed=i-1;let a=r.daysMissed;return a<=e.freezesRemaining?(r.state.freezesRemaining=e.freezesRemaining-a,r.state.freezeUsedAt=t,r.state.currentStreak=e.currentStreak+1,r.state.longestStreak=Math.max(r.state.currentStreak,e.longestStreak),r.state.lastActivityAt=t,r.state.lastActivityDate=n,r.freezeUsed=!0,r.streakMaintained=!0,r):(r.streakLost=!0,r.state.currentStreak=1,r.state.lastActivityAt=t,r.state.lastActivityDate=n,r.newStreak=!0,r)}checkStatus(e,t=new Date){if(!e.lastActivityDate)return{isActive:!1,willExpireAt:null,canUseFreeze:!1,daysUntilExpiry:0};let n=this.getDateString(t),r=this.getDaysBetween(e.lastActivityDate,n);if(r===0){let n=this.addDays(t,1);return n.setHours(23,59,59,999),{isActive:!0,willExpireAt:n,canUseFreeze:e.freezesRemaining>0,daysUntilExpiry:1}}if(r===1){let n=new Date(t);return n.setHours(23+this.config.gracePeriodHours,59,59,999),{isActive:!0,willExpireAt:n,canUseFreeze:e.freezesRemaining>0,daysUntilExpiry:0}}let i=r-1;return{isActive:i<=e.freezesRemaining,willExpireAt:null,canUseFreeze:i<=e.freezesRemaining,daysUntilExpiry:-i}}useFreeze(e,t=new Date){return e.freezesRemaining<=0?null:{...e,freezesRemaining:e.freezesRemaining-1,freezeUsedAt:t}}awardMonthlyFreezes(e){return{...e,freezesRemaining:Math.min(e.freezesRemaining+this.config.freezesPerMonth,this.config.maxFreezes)}}getInitialState(){return{currentStreak:0,longestStreak:0,lastActivityAt:null,lastActivityDate:null,freezesRemaining:this.config.freezesPerMonth,freezeUsedAt:null}}getMilestones(e){let t=[3,7,14,30,60,90,180,365,500,1e3];return{achieved:t.filter(t=>e>=t),next:t.find(t=>e<t)??null}}getDateString(e){return`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`}getDaysBetween(e,t){let n=new Date(e),r=new Date(t).getTime()-n.getTime();return Math.floor(r/(1e3*60*60*24))}addDays(e,t){return new Date(e.getTime()+t*24*60*60*1e3)}};const n=new t;export{e as DEFAULT_STREAK_CONFIG,t as StreakEngine,n as streakEngine};
1
+ //#region src/engines/streak.ts
2
+ const DEFAULT_STREAK_CONFIG = {
3
+ timezone: "UTC",
4
+ freezesPerMonth: 2,
5
+ maxFreezes: 5,
6
+ gracePeriodHours: 4
7
+ };
8
+ var StreakEngine = class {
9
+ config;
10
+ constructor(config = {}) {
11
+ this.config = {
12
+ ...DEFAULT_STREAK_CONFIG,
13
+ ...config
14
+ };
15
+ }
16
+ /**
17
+ * Update streak based on new activity.
18
+ */
19
+ update(state, now = /* @__PURE__ */ new Date()) {
20
+ const todayDate = this.getDateString(now);
21
+ const result = {
22
+ state: { ...state },
23
+ streakMaintained: false,
24
+ streakLost: false,
25
+ freezeUsed: false,
26
+ newStreak: false,
27
+ daysMissed: 0
28
+ };
29
+ if (!state.lastActivityDate) {
30
+ result.state.currentStreak = 1;
31
+ result.state.longestStreak = Math.max(1, state.longestStreak);
32
+ result.state.lastActivityAt = now;
33
+ result.state.lastActivityDate = todayDate;
34
+ result.newStreak = true;
35
+ result.streakMaintained = true;
36
+ return result;
37
+ }
38
+ if (state.lastActivityDate === todayDate) {
39
+ result.state.lastActivityAt = now;
40
+ result.streakMaintained = true;
41
+ return result;
42
+ }
43
+ const daysSinceActivity = this.getDaysBetween(state.lastActivityDate, todayDate);
44
+ if (daysSinceActivity === 1) {
45
+ result.state.currentStreak = state.currentStreak + 1;
46
+ result.state.longestStreak = Math.max(result.state.currentStreak, state.longestStreak);
47
+ result.state.lastActivityAt = now;
48
+ result.state.lastActivityDate = todayDate;
49
+ result.streakMaintained = true;
50
+ return result;
51
+ }
52
+ result.daysMissed = daysSinceActivity - 1;
53
+ const freezesNeeded = result.daysMissed;
54
+ if (freezesNeeded <= state.freezesRemaining) {
55
+ result.state.freezesRemaining = state.freezesRemaining - freezesNeeded;
56
+ result.state.freezeUsedAt = now;
57
+ result.state.currentStreak = state.currentStreak + 1;
58
+ result.state.longestStreak = Math.max(result.state.currentStreak, state.longestStreak);
59
+ result.state.lastActivityAt = now;
60
+ result.state.lastActivityDate = todayDate;
61
+ result.freezeUsed = true;
62
+ result.streakMaintained = true;
63
+ return result;
64
+ }
65
+ result.streakLost = true;
66
+ result.state.currentStreak = 1;
67
+ result.state.lastActivityAt = now;
68
+ result.state.lastActivityDate = todayDate;
69
+ result.newStreak = true;
70
+ return result;
71
+ }
72
+ /**
73
+ * Check streak status without recording activity.
74
+ */
75
+ checkStatus(state, now = /* @__PURE__ */ new Date()) {
76
+ if (!state.lastActivityDate) return {
77
+ isActive: false,
78
+ willExpireAt: null,
79
+ canUseFreeze: false,
80
+ daysUntilExpiry: 0
81
+ };
82
+ const todayDate = this.getDateString(now);
83
+ const daysSinceActivity = this.getDaysBetween(state.lastActivityDate, todayDate);
84
+ if (daysSinceActivity === 0) {
85
+ const tomorrow = this.addDays(now, 1);
86
+ tomorrow.setHours(23, 59, 59, 999);
87
+ return {
88
+ isActive: true,
89
+ willExpireAt: tomorrow,
90
+ canUseFreeze: state.freezesRemaining > 0,
91
+ daysUntilExpiry: 1
92
+ };
93
+ }
94
+ if (daysSinceActivity === 1) {
95
+ const endOfDay = new Date(now);
96
+ endOfDay.setHours(23 + this.config.gracePeriodHours, 59, 59, 999);
97
+ return {
98
+ isActive: true,
99
+ willExpireAt: endOfDay,
100
+ canUseFreeze: state.freezesRemaining > 0,
101
+ daysUntilExpiry: 0
102
+ };
103
+ }
104
+ const missedDays = daysSinceActivity - 1;
105
+ return {
106
+ isActive: missedDays <= state.freezesRemaining,
107
+ willExpireAt: null,
108
+ canUseFreeze: missedDays <= state.freezesRemaining,
109
+ daysUntilExpiry: -missedDays
110
+ };
111
+ }
112
+ /**
113
+ * Manually use a freeze to protect streak.
114
+ */
115
+ useFreeze(state, now = /* @__PURE__ */ new Date()) {
116
+ if (state.freezesRemaining <= 0) return null;
117
+ return {
118
+ ...state,
119
+ freezesRemaining: state.freezesRemaining - 1,
120
+ freezeUsedAt: now
121
+ };
122
+ }
123
+ /**
124
+ * Award monthly freezes.
125
+ */
126
+ awardMonthlyFreezes(state) {
127
+ return {
128
+ ...state,
129
+ freezesRemaining: Math.min(state.freezesRemaining + this.config.freezesPerMonth, this.config.maxFreezes)
130
+ };
131
+ }
132
+ /**
133
+ * Get initial streak state.
134
+ */
135
+ getInitialState() {
136
+ return {
137
+ currentStreak: 0,
138
+ longestStreak: 0,
139
+ lastActivityAt: null,
140
+ lastActivityDate: null,
141
+ freezesRemaining: this.config.freezesPerMonth,
142
+ freezeUsedAt: null
143
+ };
144
+ }
145
+ /**
146
+ * Calculate streak milestones.
147
+ */
148
+ getMilestones(currentStreak) {
149
+ const milestones = [
150
+ 3,
151
+ 7,
152
+ 14,
153
+ 30,
154
+ 60,
155
+ 90,
156
+ 180,
157
+ 365,
158
+ 500,
159
+ 1e3
160
+ ];
161
+ return {
162
+ achieved: milestones.filter((m) => currentStreak >= m),
163
+ next: milestones.find((m) => currentStreak < m) ?? null
164
+ };
165
+ }
166
+ /**
167
+ * Get date string in YYYY-MM-DD format.
168
+ */
169
+ getDateString(date) {
170
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
171
+ }
172
+ /**
173
+ * Get number of days between two date strings.
174
+ */
175
+ getDaysBetween(dateStr1, dateStr2) {
176
+ const date1 = new Date(dateStr1);
177
+ const diffTime = new Date(dateStr2).getTime() - date1.getTime();
178
+ return Math.floor(diffTime / (1e3 * 60 * 60 * 24));
179
+ }
180
+ /**
181
+ * Add days to a date.
182
+ */
183
+ addDays(date, days) {
184
+ return new Date(date.getTime() + days * 24 * 60 * 60 * 1e3);
185
+ }
186
+ };
187
+ /**
188
+ * Default streak engine instance.
189
+ */
190
+ const streakEngine = new StreakEngine();
191
+
192
+ //#endregion
193
+ export { DEFAULT_STREAK_CONFIG, StreakEngine, streakEngine };
@@ -1 +1,212 @@
1
- const e={baseValues:{lesson_complete:10,quiz_pass:20,quiz_perfect:50,flashcard_review:1,course_complete:200,module_complete:50,streak_bonus:5,achievement_unlock:0,daily_goal_complete:15,first_lesson:25,onboarding_step:5,onboarding_complete:50},scoreThresholds:[{min:90,multiplier:1.5},{min:80,multiplier:1.25},{min:70,multiplier:1},{min:60,multiplier:.75},{min:0,multiplier:.5}],streakTiers:[{days:365,bonus:50},{days:180,bonus:30},{days:90,bonus:20},{days:30,bonus:15},{days:14,bonus:10},{days:7,bonus:5},{days:3,bonus:2},{days:1,bonus:0}],perfectScoreMultiplier:1.5,firstAttemptBonus:10,retryPenalty:.5,speedBonusMultiplier:1.2,speedBonusThreshold:.8};var t=class{config;constructor(t={}){this.config={...e,...t,baseValues:{...e.baseValues,...t.baseValues},scoreThresholds:t.scoreThresholds||e.scoreThresholds,streakTiers:t.streakTiers||e.streakTiers}}calculate(e){let t=[],n=e.baseXp??this.config.baseValues[e.activity],r=n;if(t.push({source:`base`,amount:n}),e.score!==void 0){let i=this.getScoreMultiplier(e.score);if(i!==1){let e=Math.round(n*(i-1));r+=e,t.push({source:`score_bonus`,amount:e,multiplier:i})}if(e.score===100){let e=Math.round(n*(this.config.perfectScoreMultiplier-1));r+=e,t.push({source:`perfect_score`,amount:e,multiplier:this.config.perfectScoreMultiplier})}}if(e.attemptNumber===1&&!e.isRetry&&(r+=this.config.firstAttemptBonus,t.push({source:`first_attempt`,amount:this.config.firstAttemptBonus})),e.isRetry){let e=Math.round(r*(1-this.config.retryPenalty));r-=e,t.push({source:`retry_penalty`,amount:-e,multiplier:this.config.retryPenalty})}if(e.currentStreak&&e.currentStreak>0){let n=this.getStreakBonus(e.currentStreak);n>0&&(r+=n,t.push({source:`streak_bonus`,amount:n}))}return n>0&&(r=Math.max(1,r)),{totalXp:Math.round(r),baseXp:n,breakdown:t}}calculateStreakBonus(e){let t=this.getStreakBonus(e);return{totalXp:t,baseXp:t,breakdown:[{source:`streak_bonus`,amount:t}]}}getXpForLevel(e){return e<=1?0:Math.round(100*(e-1)**1.5)}getLevelFromXp(e){let t=1,n=this.getXpForLevel(t+1);for(;e>=n&&t<1e3;)t++,n=this.getXpForLevel(t+1);let r=this.getXpForLevel(t),i=this.getXpForLevel(t+1);return{level:t,xpInLevel:e-r,xpForNextLevel:i-r}}getScoreMultiplier(e){for(let t of this.config.scoreThresholds)if(e>=t.min)return t.multiplier;return 1}getStreakBonus(e){for(let t of this.config.streakTiers)if(e>=t.days)return t.bonus;return 0}};const n=new t;export{e as DEFAULT_XP_CONFIG,t as XPEngine,n as xpEngine};
1
+ //#region src/engines/xp.ts
2
+ const DEFAULT_XP_CONFIG = {
3
+ baseValues: {
4
+ lesson_complete: 10,
5
+ quiz_pass: 20,
6
+ quiz_perfect: 50,
7
+ flashcard_review: 1,
8
+ course_complete: 200,
9
+ module_complete: 50,
10
+ streak_bonus: 5,
11
+ achievement_unlock: 0,
12
+ daily_goal_complete: 15,
13
+ first_lesson: 25,
14
+ onboarding_step: 5,
15
+ onboarding_complete: 50
16
+ },
17
+ scoreThresholds: [
18
+ {
19
+ min: 90,
20
+ multiplier: 1.5
21
+ },
22
+ {
23
+ min: 80,
24
+ multiplier: 1.25
25
+ },
26
+ {
27
+ min: 70,
28
+ multiplier: 1
29
+ },
30
+ {
31
+ min: 60,
32
+ multiplier: .75
33
+ },
34
+ {
35
+ min: 0,
36
+ multiplier: .5
37
+ }
38
+ ],
39
+ streakTiers: [
40
+ {
41
+ days: 365,
42
+ bonus: 50
43
+ },
44
+ {
45
+ days: 180,
46
+ bonus: 30
47
+ },
48
+ {
49
+ days: 90,
50
+ bonus: 20
51
+ },
52
+ {
53
+ days: 30,
54
+ bonus: 15
55
+ },
56
+ {
57
+ days: 14,
58
+ bonus: 10
59
+ },
60
+ {
61
+ days: 7,
62
+ bonus: 5
63
+ },
64
+ {
65
+ days: 3,
66
+ bonus: 2
67
+ },
68
+ {
69
+ days: 1,
70
+ bonus: 0
71
+ }
72
+ ],
73
+ perfectScoreMultiplier: 1.5,
74
+ firstAttemptBonus: 10,
75
+ retryPenalty: .5,
76
+ speedBonusMultiplier: 1.2,
77
+ speedBonusThreshold: .8
78
+ };
79
+ var XPEngine = class {
80
+ config;
81
+ constructor(config = {}) {
82
+ this.config = {
83
+ ...DEFAULT_XP_CONFIG,
84
+ ...config,
85
+ baseValues: {
86
+ ...DEFAULT_XP_CONFIG.baseValues,
87
+ ...config.baseValues
88
+ },
89
+ scoreThresholds: config.scoreThresholds || DEFAULT_XP_CONFIG.scoreThresholds,
90
+ streakTiers: config.streakTiers || DEFAULT_XP_CONFIG.streakTiers
91
+ };
92
+ }
93
+ /**
94
+ * Calculate XP for an activity.
95
+ */
96
+ calculate(input) {
97
+ const breakdown = [];
98
+ const baseXp = input.baseXp ?? this.config.baseValues[input.activity];
99
+ let totalXp = baseXp;
100
+ breakdown.push({
101
+ source: "base",
102
+ amount: baseXp
103
+ });
104
+ if (input.score !== void 0) {
105
+ const scoreMultiplier = this.getScoreMultiplier(input.score);
106
+ if (scoreMultiplier !== 1) {
107
+ const scoreBonus = Math.round(baseXp * (scoreMultiplier - 1));
108
+ totalXp += scoreBonus;
109
+ breakdown.push({
110
+ source: "score_bonus",
111
+ amount: scoreBonus,
112
+ multiplier: scoreMultiplier
113
+ });
114
+ }
115
+ if (input.score === 100) {
116
+ const perfectBonus = Math.round(baseXp * (this.config.perfectScoreMultiplier - 1));
117
+ totalXp += perfectBonus;
118
+ breakdown.push({
119
+ source: "perfect_score",
120
+ amount: perfectBonus,
121
+ multiplier: this.config.perfectScoreMultiplier
122
+ });
123
+ }
124
+ }
125
+ if (input.attemptNumber === 1 && !input.isRetry) {
126
+ totalXp += this.config.firstAttemptBonus;
127
+ breakdown.push({
128
+ source: "first_attempt",
129
+ amount: this.config.firstAttemptBonus
130
+ });
131
+ }
132
+ if (input.isRetry) {
133
+ const penalty = Math.round(totalXp * (1 - this.config.retryPenalty));
134
+ totalXp -= penalty;
135
+ breakdown.push({
136
+ source: "retry_penalty",
137
+ amount: -penalty,
138
+ multiplier: this.config.retryPenalty
139
+ });
140
+ }
141
+ if (input.currentStreak && input.currentStreak > 0) {
142
+ const streakBonus = this.getStreakBonus(input.currentStreak);
143
+ if (streakBonus > 0) {
144
+ totalXp += streakBonus;
145
+ breakdown.push({
146
+ source: "streak_bonus",
147
+ amount: streakBonus
148
+ });
149
+ }
150
+ }
151
+ if (baseXp > 0) totalXp = Math.max(1, totalXp);
152
+ return {
153
+ totalXp: Math.round(totalXp),
154
+ baseXp,
155
+ breakdown
156
+ };
157
+ }
158
+ /**
159
+ * Calculate streak bonus XP.
160
+ */
161
+ calculateStreakBonus(currentStreak) {
162
+ const bonus = this.getStreakBonus(currentStreak);
163
+ return {
164
+ totalXp: bonus,
165
+ baseXp: bonus,
166
+ breakdown: [{
167
+ source: "streak_bonus",
168
+ amount: bonus
169
+ }]
170
+ };
171
+ }
172
+ /**
173
+ * Calculate XP needed for a level.
174
+ */
175
+ getXpForLevel(level) {
176
+ if (level <= 1) return 0;
177
+ return Math.round(100 * Math.pow(level - 1, 1.5));
178
+ }
179
+ /**
180
+ * Get level from total XP.
181
+ */
182
+ getLevelFromXp(totalXp) {
183
+ let level = 1;
184
+ let xpRequired = this.getXpForLevel(level + 1);
185
+ while (totalXp >= xpRequired && level < 1e3) {
186
+ level++;
187
+ xpRequired = this.getXpForLevel(level + 1);
188
+ }
189
+ const xpForCurrentLevel = this.getXpForLevel(level);
190
+ const xpForNextLevel = this.getXpForLevel(level + 1);
191
+ return {
192
+ level,
193
+ xpInLevel: totalXp - xpForCurrentLevel,
194
+ xpForNextLevel: xpForNextLevel - xpForCurrentLevel
195
+ };
196
+ }
197
+ getScoreMultiplier(score) {
198
+ for (const threshold of this.config.scoreThresholds) if (score >= threshold.min) return threshold.multiplier;
199
+ return 1;
200
+ }
201
+ getStreakBonus(streak) {
202
+ for (const tier of this.config.streakTiers) if (streak >= tier.days) return tier.bonus;
203
+ return 0;
204
+ }
205
+ };
206
+ /**
207
+ * Default XP engine instance.
208
+ */
209
+ const xpEngine = new XPEngine();
210
+
211
+ //#endregion
212
+ export { DEFAULT_XP_CONFIG, XPEngine, xpEngine };