@lssm/example.learning-patterns 0.0.0-canary-20251213172311 → 0.0.0-canary-20251215231151

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.
@@ -46,7 +46,8 @@ const getAvailability = (
46
46
  const baseTime = startedAt.getTime();
47
47
  let unlockTime = baseTime;
48
48
  if (availability.unlockOnDay !== undefined) {
49
- unlockTime = baseTime + (availability.unlockOnDay - 1) * 24 * 60 * 60 * 1000;
49
+ unlockTime =
50
+ baseTime + (availability.unlockOnDay - 1) * 24 * 60 * 60 * 1000;
50
51
  }
51
52
  if (availability.unlockAfterHours !== undefined) {
52
53
  unlockTime = baseTime + availability.unlockAfterHours * 60 * 60 * 1000;
@@ -54,7 +55,9 @@ const getAvailability = (
54
55
  const availableAt = new Date(unlockTime);
55
56
  const dueAt =
56
57
  availability.dueWithinHours !== undefined
57
- ? new Date(availableAt.getTime() + availability.dueWithinHours * 60 * 60 * 1000)
58
+ ? new Date(
59
+ availableAt.getTime() + availability.dueWithinHours * 60 * 60 * 1000
60
+ )
58
61
  : undefined;
59
62
  return { availableAt, dueAt };
60
63
  };
@@ -66,18 +69,21 @@ const matchesCondition = (
66
69
  ): { matched: boolean; occurrences?: number; masteryCount?: number } => {
67
70
  if ((condition.kind ?? 'event') === 'event') {
68
71
  if (condition.eventName !== event.name) return { matched: false };
69
- if (!matchesFilter(condition.payloadFilter, event.payload)) return { matched: false };
72
+ if (!matchesFilter(condition.payloadFilter, event.payload))
73
+ return { matched: false };
70
74
  return { matched: true };
71
75
  }
72
76
  if (condition.kind === 'count') {
73
77
  if (condition.eventName !== event.name) return { matched: false };
74
- if (!matchesFilter(condition.payloadFilter, event.payload)) return { matched: false };
78
+ if (!matchesFilter(condition.payloadFilter, event.payload))
79
+ return { matched: false };
75
80
  const occurrences = step.occurrences + 1;
76
81
  return { matched: occurrences >= condition.atLeast, occurrences };
77
82
  }
78
83
  if (condition.kind === 'srs_mastery') {
79
84
  if (condition.eventName !== event.name) return { matched: false };
80
- if (!matchesFilter(condition.payloadFilter, event.payload)) return { matched: false };
85
+ if (!matchesFilter(condition.payloadFilter, event.payload))
86
+ return { matched: false };
81
87
  const masteryKey = condition.masteryField ?? 'mastery';
82
88
  const masteryValue = event.payload?.[masteryKey];
83
89
  if (typeof masteryValue !== 'number') return { matched: false };
@@ -103,7 +109,10 @@ function initProgress(track: LearningJourneyTrackSpec): StepState[] {
103
109
  }));
104
110
  }
105
111
 
106
- function applyEvents(track: LearningJourneyTrackSpec, events: LearningEvent[]): StepState[] {
112
+ function applyEvents(
113
+ track: LearningJourneyTrackSpec,
114
+ events: LearningEvent[]
115
+ ): StepState[] {
107
116
  const steps = initProgress(track);
108
117
  let startedAt: Date | undefined;
109
118
  for (const event of events) {
@@ -114,7 +123,10 @@ function applyEvents(track: LearningJourneyTrackSpec, events: LearningEvent[]):
114
123
  const state = steps[index];
115
124
  if (!spec || !state) continue;
116
125
  if (state.status === 'COMPLETED') continue;
117
- const { availableAt, dueAt } = getAvailability(spec.availability, startedAt);
126
+ const { availableAt, dueAt } = getAvailability(
127
+ spec.availability,
128
+ startedAt
129
+ );
118
130
  state.availableAt = availableAt;
119
131
  state.dueAt = dueAt;
120
132
  if (availableAt && eventTime < availableAt) continue;
@@ -132,9 +144,18 @@ describe('@lssm/example.learning-patterns tracks', () => {
132
144
  it('drills track progresses via session count + mastery', () => {
133
145
  const events: LearningEvent[] = [
134
146
  { name: LEARNING_EVENTS.DRILL_SESSION_COMPLETED },
135
- { name: LEARNING_EVENTS.DRILL_SESSION_COMPLETED, payload: { accuracyBucket: 'high' } },
136
- { name: LEARNING_EVENTS.DRILL_SESSION_COMPLETED, payload: { accuracyBucket: 'high' } },
137
- { name: LEARNING_EVENTS.DRILL_SESSION_COMPLETED, payload: { accuracyBucket: 'high' } },
147
+ {
148
+ name: LEARNING_EVENTS.DRILL_SESSION_COMPLETED,
149
+ payload: { accuracyBucket: 'high' },
150
+ },
151
+ {
152
+ name: LEARNING_EVENTS.DRILL_SESSION_COMPLETED,
153
+ payload: { accuracyBucket: 'high' },
154
+ },
155
+ {
156
+ name: LEARNING_EVENTS.DRILL_SESSION_COMPLETED,
157
+ payload: { accuracyBucket: 'high' },
158
+ },
138
159
  ...Array.from({ length: 5 }).map(() => ({
139
160
  name: LEARNING_EVENTS.DRILL_CARD_MASTERED,
140
161
  payload: { skillId: 's1', mastery: 0.9 },
@@ -179,8 +200,18 @@ describe('@lssm/example.learning-patterns tracks', () => {
179
200
  describe('@lssm/example.learning-patterns XP + streak + SRS determinism', () => {
180
201
  it('XP engine produces deterministic results for streak bonus inputs', () => {
181
202
  const xp = new XPEngine();
182
- const r1 = xp.calculate({ activity: 'lesson_complete', score: 90, attemptNumber: 1, currentStreak: 7 });
183
- const r2 = xp.calculate({ activity: 'lesson_complete', score: 90, attemptNumber: 1, currentStreak: 7 });
203
+ const r1 = xp.calculate({
204
+ activity: 'lesson_complete',
205
+ score: 90,
206
+ attemptNumber: 1,
207
+ currentStreak: 7,
208
+ });
209
+ const r2 = xp.calculate({
210
+ activity: 'lesson_complete',
211
+ score: 90,
212
+ attemptNumber: 1,
213
+ currentStreak: 7,
214
+ });
184
215
  expect(r1.totalXp).toBe(r2.totalXp);
185
216
  expect(r1.totalXp).toBeGreaterThan(0);
186
217
  });
@@ -195,8 +226,14 @@ describe('@lssm/example.learning-patterns XP + streak + SRS determinism', () =>
195
226
  freezesRemaining: 0,
196
227
  freezeUsedAt: null,
197
228
  };
198
- const day1 = streak.update(initial, new Date('2026-01-01T10:00:00.000Z')).state;
199
- const day2 = streak.update(day1, new Date('2026-01-02T10:00:00.000Z')).state;
229
+ const day1 = streak.update(
230
+ initial,
231
+ new Date('2026-01-01T10:00:00.000Z')
232
+ ).state;
233
+ const day2 = streak.update(
234
+ day1,
235
+ new Date('2026-01-02T10:00:00.000Z')
236
+ ).state;
200
237
  expect(day2.currentStreak).toBe(2);
201
238
  });
202
239
 
@@ -217,5 +254,3 @@ describe('@lssm/example.learning-patterns XP + streak + SRS determinism', () =>
217
254
  expect(result.nextReviewAt.toISOString()).toBe('2026-01-01T00:10:00.000Z');
218
255
  });
219
256
  });
220
-
221
-
@@ -39,6 +39,6 @@ export const ambientCoachTrack: LearningJourneyTrackSpec = {
39
39
  ],
40
40
  };
41
41
 
42
- export const ambientCoachTracks: LearningJourneyTrackSpec[] = [ambientCoachTrack];
43
-
44
-
42
+ export const ambientCoachTracks: LearningJourneyTrackSpec[] = [
43
+ ambientCoachTrack,
44
+ ];
@@ -13,7 +13,10 @@ export const drillsTrack: LearningJourneyTrackSpec = {
13
13
  id: 'complete_first_session',
14
14
  title: 'Complete your first session',
15
15
  order: 1,
16
- completion: { kind: 'event', eventName: LEARNING_EVENTS.DRILL_SESSION_COMPLETED },
16
+ completion: {
17
+ kind: 'event',
18
+ eventName: LEARNING_EVENTS.DRILL_SESSION_COMPLETED,
19
+ },
17
20
  xpReward: 10,
18
21
  },
19
22
  {
@@ -46,5 +49,3 @@ export const drillsTrack: LearningJourneyTrackSpec = {
46
49
  };
47
50
 
48
51
  export const drillTracks: LearningJourneyTrackSpec[] = [drillsTrack];
49
-
50
-
@@ -1,5 +1,3 @@
1
1
  export * from './drills';
2
2
  export * from './ambient-coach';
3
3
  export * from './quests';
4
-
5
-
@@ -20,7 +20,10 @@ export const questTrack: LearningJourneyTrackSpec = {
20
20
  id: 'day1_complete',
21
21
  title: 'Complete day 1 step',
22
22
  order: 2,
23
- completion: { kind: 'event', eventName: LEARNING_EVENTS.QUEST_STEP_COMPLETED },
23
+ completion: {
24
+ kind: 'event',
25
+ eventName: LEARNING_EVENTS.QUEST_STEP_COMPLETED,
26
+ },
24
27
  availability: { unlockOnDay: 1, dueWithinHours: 48 },
25
28
  xpReward: 10,
26
29
  },
@@ -28,7 +31,10 @@ export const questTrack: LearningJourneyTrackSpec = {
28
31
  id: 'day2_complete',
29
32
  title: 'Complete day 2 step',
30
33
  order: 3,
31
- completion: { kind: 'event', eventName: LEARNING_EVENTS.QUEST_STEP_COMPLETED },
34
+ completion: {
35
+ kind: 'event',
36
+ eventName: LEARNING_EVENTS.QUEST_STEP_COMPLETED,
37
+ },
32
38
  availability: { unlockOnDay: 2, dueWithinHours: 48 },
33
39
  xpReward: 10,
34
40
  },
@@ -36,5 +42,3 @@ export const questTrack: LearningJourneyTrackSpec = {
36
42
  };
37
43
 
38
44
  export const questTracks: LearningJourneyTrackSpec[] = [questTrack];
39
-
40
-
package/tsconfig.json CHANGED
@@ -9,3 +9,4 @@
9
9
  }
10
10
 
11
11
 
12
+