@lessonkit/core 1.5.0 → 1.6.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.
@@ -21,7 +21,7 @@ declare const ID_PATTERN: RegExp;
21
21
  declare const ID_MAX_LENGTH = 64;
22
22
 
23
23
  /** H5P-aligned interaction kinds for assessment telemetry and xAPI. */
24
- type AssessmentInteractionType = "mcq" | "trueFalse" | "fillInBlanks" | "markTheWords" | "dragTheWords" | "dragAndDrop" | "assessmentSequence" | "findHotspot" | "findMultipleHotspots" | "summary" | "imagePairing" | "imageSequencing" | "essay" | "arithmeticQuiz" | "memoryGame";
24
+ type AssessmentInteractionType = "mcq" | "trueFalse" | "fillInBlanks" | "markTheWords" | "dragTheWords" | "dragAndDrop" | "assessmentSequence" | "findHotspot" | "findMultipleHotspots" | "summary" | "imagePairing" | "imageSequencing" | "essay" | "arithmeticQuiz" | "memoryGame" | "combinationLock" | "crossword" | "wordSearch";
25
25
  /** Serializable resume blob for a single assessment block. */
26
26
  type AssessmentResumeState = Record<string, unknown>;
27
27
  /** Behaviour flags aligned with H5P question types. */
@@ -65,7 +65,7 @@ type McqAssessmentProps = AssessmentBaseProps & {
65
65
  answer: string;
66
66
  };
67
67
 
68
- type TelemetryEventName = "course_started" | "course_completed" | "lesson_started" | "lesson_completed" | "lesson_time_on_task" | "quiz_answered" | "quiz_completed" | "assessment_answered" | "assessment_completed" | "interaction" | "book_page_viewed" | "slide_viewed" | "compound_page_viewed" | "hotspot_opened" | "accordion_section_toggled" | "flashcard_flipped" | "image_slider_changed" | "video_cue_reached" | "video_segment_completed" | "memory_card_flipped" | "information_wall_search" | "parallax_slide_viewed" | "questionnaire_submitted" | "branch_node_viewed" | "branch_selected";
68
+ type TelemetryEventName = "course_started" | "course_completed" | "lesson_started" | "lesson_completed" | "lesson_time_on_task" | "quiz_answered" | "quiz_completed" | "assessment_answered" | "assessment_completed" | "interaction" | "book_page_viewed" | "slide_viewed" | "compound_page_viewed" | "hotspot_opened" | "accordion_section_toggled" | "flashcard_flipped" | "image_slider_changed" | "video_cue_reached" | "video_segment_completed" | "memory_card_flipped" | "information_wall_search" | "parallax_slide_viewed" | "questionnaire_submitted" | "branch_node_viewed" | "branch_selected" | "image_juxtaposition_changed" | "timeline_event_viewed" | "image_sequence_changed" | "audio_recording_started" | "audio_recording_completed" | "qr_content_revealed" | "advent_door_opened" | "map_stage_viewed" | "map_exit_selected";
69
69
  type TelemetryUser = {
70
70
  id?: string;
71
71
  email?: string;
@@ -196,6 +196,42 @@ type BranchSelectedData = {
196
196
  label: string;
197
197
  scoreWeight?: number;
198
198
  };
199
+ type ImageJuxtapositionChangedData = {
200
+ blockId: BlockId;
201
+ position: number;
202
+ };
203
+ type TimelineEventViewedData = {
204
+ blockId: BlockId;
205
+ eventId: string;
206
+ };
207
+ type ImageSequenceChangedData = {
208
+ blockId: BlockId;
209
+ frameIndex: number;
210
+ };
211
+ type AudioRecordingData = {
212
+ blockId: BlockId;
213
+ };
214
+ type QrContentRevealedData = {
215
+ blockId: BlockId;
216
+ };
217
+ type AdventDoorOpenedData = {
218
+ blockId: BlockId;
219
+ doorId: string;
220
+ day: number;
221
+ };
222
+ type MapStageViewedData = {
223
+ blockId: BlockId;
224
+ stageId: string;
225
+ stageIndex: number;
226
+ stageLabel?: string;
227
+ };
228
+ type MapExitSelectedData = {
229
+ blockId: BlockId;
230
+ fromStageId: string;
231
+ toStageId: string;
232
+ label: string;
233
+ scoreWeight?: number;
234
+ };
199
235
  type TelemetryEvent = (TelemetryEventBase & {
200
236
  name: "course_started";
201
237
  lessonId?: LessonId;
@@ -296,6 +332,42 @@ type TelemetryEvent = (TelemetryEventBase & {
296
332
  name: "branch_selected";
297
333
  lessonId: LessonId;
298
334
  data: BranchSelectedData;
335
+ }) | (TelemetryEventBase & {
336
+ name: "image_juxtaposition_changed";
337
+ lessonId?: LessonId;
338
+ data: ImageJuxtapositionChangedData;
339
+ }) | (TelemetryEventBase & {
340
+ name: "timeline_event_viewed";
341
+ lessonId?: LessonId;
342
+ data: TimelineEventViewedData;
343
+ }) | (TelemetryEventBase & {
344
+ name: "image_sequence_changed";
345
+ lessonId?: LessonId;
346
+ data: ImageSequenceChangedData;
347
+ }) | (TelemetryEventBase & {
348
+ name: "audio_recording_started";
349
+ lessonId?: LessonId;
350
+ data: AudioRecordingData;
351
+ }) | (TelemetryEventBase & {
352
+ name: "audio_recording_completed";
353
+ lessonId?: LessonId;
354
+ data: AudioRecordingData;
355
+ }) | (TelemetryEventBase & {
356
+ name: "qr_content_revealed";
357
+ lessonId?: LessonId;
358
+ data: QrContentRevealedData;
359
+ }) | (TelemetryEventBase & {
360
+ name: "advent_door_opened";
361
+ lessonId?: LessonId;
362
+ data: AdventDoorOpenedData;
363
+ }) | (TelemetryEventBase & {
364
+ name: "map_stage_viewed";
365
+ lessonId: LessonId;
366
+ data: MapStageViewedData;
367
+ }) | (TelemetryEventBase & {
368
+ name: "map_exit_selected";
369
+ lessonId: LessonId;
370
+ data: MapExitSelectedData;
299
371
  });
300
372
  /** Payload shape for a telemetry event name. */
301
373
  type TelemetryDataFor<N extends TelemetryEventName> = Extract<TelemetryEvent, {
@@ -446,6 +518,42 @@ type BuildTelemetryEventInput = (BuildTelemetryEventContext & {
446
518
  name: "branch_selected";
447
519
  lessonId?: LessonId;
448
520
  data: BranchSelectedData;
521
+ }) | (BuildTelemetryEventContext & {
522
+ name: "image_juxtaposition_changed";
523
+ lessonId?: LessonId;
524
+ data: ImageJuxtapositionChangedData;
525
+ }) | (BuildTelemetryEventContext & {
526
+ name: "timeline_event_viewed";
527
+ lessonId?: LessonId;
528
+ data: TimelineEventViewedData;
529
+ }) | (BuildTelemetryEventContext & {
530
+ name: "image_sequence_changed";
531
+ lessonId?: LessonId;
532
+ data: ImageSequenceChangedData;
533
+ }) | (BuildTelemetryEventContext & {
534
+ name: "audio_recording_started";
535
+ lessonId?: LessonId;
536
+ data: AudioRecordingData;
537
+ }) | (BuildTelemetryEventContext & {
538
+ name: "audio_recording_completed";
539
+ lessonId?: LessonId;
540
+ data: AudioRecordingData;
541
+ }) | (BuildTelemetryEventContext & {
542
+ name: "qr_content_revealed";
543
+ lessonId?: LessonId;
544
+ data: QrContentRevealedData;
545
+ }) | (BuildTelemetryEventContext & {
546
+ name: "advent_door_opened";
547
+ lessonId?: LessonId;
548
+ data: AdventDoorOpenedData;
549
+ }) | (BuildTelemetryEventContext & {
550
+ name: "map_stage_viewed";
551
+ lessonId?: LessonId;
552
+ data: MapStageViewedData;
553
+ }) | (BuildTelemetryEventContext & {
554
+ name: "map_exit_selected";
555
+ lessonId?: LessonId;
556
+ data: MapExitSelectedData;
449
557
  });
450
558
 
451
559
  /** Reset dev-warning state (tests only). */
@@ -453,6 +561,18 @@ declare function resetTelemetryBuilderWarningsForTests(): void;
453
561
  /**
454
562
  * Build a typed telemetry event from a catalog event name and context.
455
563
  * Validates lesson-scoped events require `lessonId`.
564
+ *
565
+ * @example
566
+ * ```ts
567
+ * import { buildTelemetryEvent } from "@lessonkit/core";
568
+ *
569
+ * const event = buildTelemetryEvent({
570
+ * name: "lesson_completed",
571
+ * courseId: "sec-101",
572
+ * lessonId: "phishing-101",
573
+ * sessionId: "tab-abc",
574
+ * });
575
+ * ```
456
576
  */
457
577
  declare function buildTelemetryEvent(opts: BuildTelemetryEventInput): TelemetryEvent;
458
578
  /**
@@ -592,12 +712,38 @@ declare function tryEmitCourseStarted(ctx: CourseLifecycleContext, deps: CourseL
592
712
  }>;
593
713
  declare function buildCourseStartedTelemetryEvent(ctx: CourseLifecycleContext): TelemetryEvent;
594
714
  type LessonCompletionEmitter = (lessonId: LessonId, durationMs?: number) => void;
715
+ /**
716
+ * Mark a lesson complete in progress state and emit `lesson_completed` when newly completed.
717
+ *
718
+ * @example
719
+ * ```ts
720
+ * completeLessonWithTelemetry({
721
+ * progress,
722
+ * lessonId: "lesson-1",
723
+ * nowMs: Date.now(),
724
+ * emitLessonCompleted: (id, durationMs) => track("lesson_completed", { lessonId: id, durationMs }),
725
+ * });
726
+ * ```
727
+ */
595
728
  declare function completeLessonWithTelemetry(opts: {
596
729
  progress: ProgressController;
597
730
  lessonId: LessonId;
598
731
  nowMs: number;
599
732
  emitLessonCompleted: LessonCompletionEmitter;
600
733
  }): boolean;
734
+ /**
735
+ * Complete the active lesson (if any), then mark the course complete and emit `course_completed`.
736
+ *
737
+ * @example
738
+ * ```ts
739
+ * completeCourseWithTelemetry({
740
+ * progress,
741
+ * nowMs: Date.now(),
742
+ * emitLessonCompleted: (id) => track("lesson_completed", { lessonId: id }),
743
+ * emitCourseCompleted: () => track("course_completed", {}),
744
+ * });
745
+ * ```
746
+ */
601
747
  declare function completeCourseWithTelemetry(opts: {
602
748
  progress: ProgressController;
603
749
  nowMs: number;
@@ -1 +1 @@
1
- export { aE as resetCourseStartedEmitFlightForTests, ay as resetSharedVolatileSessionIdForTests, az as resetStoragePortForTests, aA as resetTelemetryBuilderWarningsForTests } from './testing-BFr8oEfw.cjs';
1
+ export { aE as resetCourseStartedEmitFlightForTests, ay as resetSharedVolatileSessionIdForTests, az as resetStoragePortForTests, aA as resetTelemetryBuilderWarningsForTests } from './testing-CzgxF1Ru.cjs';
package/dist/testing.d.ts CHANGED
@@ -1 +1 @@
1
- export { aE as resetCourseStartedEmitFlightForTests, ay as resetSharedVolatileSessionIdForTests, az as resetStoragePortForTests, aA as resetTelemetryBuilderWarningsForTests } from './testing-BFr8oEfw.js';
1
+ export { aE as resetCourseStartedEmitFlightForTests, ay as resetSharedVolatileSessionIdForTests, az as resetStoragePortForTests, aA as resetTelemetryBuilderWarningsForTests } from './testing-CzgxF1Ru.js';
package/dist/testing.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  resetSharedVolatileSessionIdForTests,
4
4
  resetStoragePortForTests,
5
5
  resetTelemetryBuilderWarningsForTests
6
- } from "./chunk-KFXFQ6B2.js";
6
+ } from "./chunk-NGCHHJSM.js";
7
7
  export {
8
8
  resetCourseStartedEmitFlightForTests,
9
9
  resetSharedVolatileSessionIdForTests,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessonkit/core",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "private": false,
5
5
  "description": "Shared types and telemetry primitives for LessonKit.",
6
6
  "license": "Apache-2.0",
@@ -249,6 +249,146 @@
249
249
  ],
250
250
  "xapiVerb": "http://adlnet.gov/expapi/verbs/answered",
251
251
  "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}:node:{toNodeId}"
252
+ },
253
+ {
254
+ "name": "image_juxtaposition_changed",
255
+ "description": "Learner adjusted the before/after divider",
256
+ "requiredFields": [
257
+ "courseId",
258
+ "sessionId",
259
+ "timestamp"
260
+ ],
261
+ "dataFields": [
262
+ "blockId",
263
+ "position"
264
+ ],
265
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/interacted",
266
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
267
+ },
268
+ {
269
+ "name": "timeline_event_viewed",
270
+ "description": "Learner focused a timeline event",
271
+ "requiredFields": [
272
+ "courseId",
273
+ "sessionId",
274
+ "timestamp"
275
+ ],
276
+ "dataFields": [
277
+ "blockId",
278
+ "eventId"
279
+ ],
280
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/experienced",
281
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
282
+ },
283
+ {
284
+ "name": "image_sequence_changed",
285
+ "description": "Learner changed the image sequence frame",
286
+ "requiredFields": [
287
+ "courseId",
288
+ "sessionId",
289
+ "timestamp"
290
+ ],
291
+ "dataFields": [
292
+ "blockId",
293
+ "frameIndex"
294
+ ],
295
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/interacted",
296
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
297
+ },
298
+ {
299
+ "name": "audio_recording_started",
300
+ "description": "Learner started an audio recording",
301
+ "requiredFields": [
302
+ "courseId",
303
+ "sessionId",
304
+ "timestamp"
305
+ ],
306
+ "dataFields": [
307
+ "blockId"
308
+ ],
309
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/interacted",
310
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
311
+ },
312
+ {
313
+ "name": "audio_recording_completed",
314
+ "description": "Learner completed an audio recording",
315
+ "requiredFields": [
316
+ "courseId",
317
+ "sessionId",
318
+ "timestamp"
319
+ ],
320
+ "dataFields": [
321
+ "blockId"
322
+ ],
323
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/completed",
324
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
325
+ },
326
+ {
327
+ "name": "qr_content_revealed",
328
+ "description": "Learner revealed QR hidden content",
329
+ "requiredFields": [
330
+ "courseId",
331
+ "sessionId",
332
+ "timestamp"
333
+ ],
334
+ "dataFields": [
335
+ "blockId"
336
+ ],
337
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/experienced",
338
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
339
+ },
340
+ {
341
+ "name": "advent_door_opened",
342
+ "description": "Learner opened an advent calendar door",
343
+ "requiredFields": [
344
+ "courseId",
345
+ "sessionId",
346
+ "timestamp"
347
+ ],
348
+ "dataFields": [
349
+ "blockId",
350
+ "doorId",
351
+ "day"
352
+ ],
353
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/interacted",
354
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
355
+ },
356
+ {
357
+ "name": "map_stage_viewed",
358
+ "description": "Learner viewed a stage in a GameMap",
359
+ "requiredFields": [
360
+ "courseId",
361
+ "lessonId",
362
+ "sessionId",
363
+ "timestamp"
364
+ ],
365
+ "dataFields": [
366
+ "blockId",
367
+ "stageId",
368
+ "stageIndex",
369
+ "stageLabel"
370
+ ],
371
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/experienced",
372
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}:stage:{stageId}"
373
+ },
374
+ {
375
+ "name": "map_exit_selected",
376
+ "description": "Learner selected a map exit in a GameMap",
377
+ "requiredFields": [
378
+ "courseId",
379
+ "lessonId",
380
+ "sessionId",
381
+ "timestamp"
382
+ ],
383
+ "dataFields": [
384
+ "blockId",
385
+ "fromStageId",
386
+ "toStageId",
387
+ "label",
388
+ "scoreWeight"
389
+ ],
390
+ "xapiVerb": "http://adlnet.gov/expapi/verbs/answered",
391
+ "urnPattern": "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}:stage:{toStageId}"
252
392
  }
253
393
  ]
254
394
  }