@lessonkit/core 1.6.0 → 1.7.1

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.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as CourseId, L as LessonId, a as CheckId, B as BlockId, I as IdentityIdPath, b as IdentityValidationResult, c as LessonkitUrn, A as AssessmentResumeState, S as StoragePort, T as TelemetryEventName, d as TelemetrySink, e as TelemetryBatchSink, f as TrackingClient, g as TelemetryEvent, h as TelemetryUser, i as LessonkitPlugin, P as PluginRegistry, j as ProgressController, k as PluginHost, l as ProgressState, m as TelemetryDataFor, n as AssessmentScoreInput, o as AssessmentScoreResult, p as ClockPort, q as AssessmentPlugin, r as LifecyclePlugin, s as TelemetryPlugin, t as LessonkitPluginContext } from './testing-CzgxF1Ru.js';
2
- export { u as AccordionSectionToggledData, v as AssessmentAnsweredData, w as AssessmentBaseProps, x as AssessmentBehaviour, y as AssessmentCompletedData, z as AssessmentHandle, D as AssessmentInteractionType, E as AssessmentXAPIData, F as BookPageViewedData, G as BranchNodeViewedData, H as BranchSelectedData, J as BuildTelemetryEventInput, K as CompoundPageViewedData, M as CourseLifecycleContext, N as CourseLifecycleDeps, O as FlashcardFlippedData, Q as HotspotOpenedData, R as ID_MAX_LENGTH, U as ID_PATTERN, V as IdentityValidationIssue, W as ImageSliderChangedData, X as InformationWallSearchData, Y as InteractionBlockRegistration, Z as InteractionData, _ as InteractionPlugin, $ as LessonCompletionEmitter, a0 as LessonLifecycleData, a1 as LessonkitPluginKind, a2 as McqAssessmentProps, a3 as MemoryCardFlippedData, a4 as ParallaxSlideViewedData, a5 as PluginIdentity, a6 as QuestionnaireSubmittedData, a7 as QuizAnsweredData, a8 as QuizCompletedData, a9 as SESSION_STORAGE_KEY, aa as SlideViewedData, ab as TelemetryEventBase, ac as TimerPort, ad as VideoCueReachedData, ae as VideoSegmentCompletedData, af as buildCourseStartedTelemetryEvent, ag as buildTelemetryEvent, ah as completeCourseWithTelemetry, ai as completeLessonWithTelemetry, aj as createDefaultClock, ak as createGlobalTimer, al as createNoopStorage, am as createProgressController, an as createSessionStoragePort, ao as getTabSessionId, ap as hasCourseStarted, aq as hasCourseStartedEmittedToTracking, ar as hasCourseStartedPipelineDelivered, as as hasCourseStartedXapiSent, at as markCourseStarted, au as markCourseStartedEmittedToTracking, av as markCourseStartedPipelineDelivered, aw as markCourseStartedXapiSent, ax as migrateCourseStartedMark, ay as resetSharedVolatileSessionIdForTests, az as resetStoragePortForTests, aA as resetTelemetryBuilderWarningsForTests, aB as resolveSessionId, aC as tryBuildTelemetryEvent, aD as tryEmitCourseStarted } from './testing-CzgxF1Ru.js';
1
+ import { C as CourseId, L as LessonId, a as CheckId, B as BlockId, I as IdentityIdPath, b as IdentityValidationResult, c as LessonkitUrn, M as McqAssessmentProps, A as AssessmentResumeState, S as StoragePort, T as TelemetryEventName, d as TelemetrySink, e as TelemetryBatchSink, f as TrackingClient, g as TelemetryEvent, h as TelemetryUser, i as LessonkitPlugin, P as PluginRegistry, j as ProgressController, k as PluginHost, l as ProgressState, m as TelemetryDataFor, n as AssessmentScoreInput, o as AssessmentScoreResult, p as ClockPort, q as AssessmentPlugin, r as LifecyclePlugin, s as TelemetryPlugin, t as LessonkitPluginContext } from './testing-CQ-ZsT7D.js';
2
+ export { u as AccordionSectionToggledData, v as AssessmentAnsweredData, w as AssessmentBaseProps, x as AssessmentBehaviour, y as AssessmentCompletedData, z as AssessmentHandle, D as AssessmentInteractionType, E as AssessmentXAPIData, F as BookPageViewedData, G as BranchNodeViewedData, H as BranchSelectedData, J as BuildTelemetryEventInput, K as CompoundPageViewedData, N as CourseLifecycleContext, O as CourseLifecycleDeps, Q as FlashcardFlippedData, R as HotspotOpenedData, U as ID_MAX_LENGTH, V as ID_PATTERN, W as IdentityValidationIssue, X as ImageSliderChangedData, Y as InformationWallSearchData, Z as InteractionBlockRegistration, _ as InteractionData, $ as InteractionPlugin, a0 as InvalidSessionIdContext, a1 as LessonCompletionEmitter, a2 as LessonLifecycleData, a3 as LessonkitPluginKind, a4 as MemoryCardFlippedData, a5 as ParallaxSlideViewedData, a6 as PluginIdentity, a7 as QuestionnaireSubmittedData, a8 as QuizAnsweredData, a9 as QuizCompletedData, aa as ResolveSessionIdOptions, ab as SESSION_STORAGE_KEY, ac as SlideViewedData, ad as TelemetryEventBase, ae as TimerPort, af as VideoCueReachedData, ag as VideoSegmentCompletedData, ah as buildCourseStartedTelemetryEvent, ai as buildTelemetryEvent, aj as completeCourseWithTelemetry, ak as completeLessonWithTelemetry, al as createDefaultClock, am as createGlobalTimer, an as createNoopStorage, ao as createProgressController, ap as createSessionStoragePort, aq as getTabSessionId, ar as hasCourseStarted, as as hasCourseStartedEmittedToTracking, at as hasCourseStartedPipelineDelivered, au as hasCourseStartedXapiSent, av as markCourseStarted, aw as markCourseStartedEmittedToTracking, ax as markCourseStartedPipelineDelivered, ay as markCourseStartedXapiSent, az as migrateCourseStartedMark, aA as resetSharedVolatileSessionIdForTests, aB as resetStoragePortForTests, aC as resetTelemetryBuilderWarningsForTests, aD as resolveSessionId, aE as tryBuildTelemetryEvent, aF as tryEmitCourseStarted } from './testing-CQ-ZsT7D.js';
3
3
 
4
4
  /**
5
5
  * Exhaustiveness helper for switch/default branches.
@@ -36,6 +36,22 @@ type LessonkitUrnParts = {
36
36
  */
37
37
  declare function buildLessonkitUrn(parts: LessonkitUrnParts): LessonkitUrn;
38
38
 
39
+ type McqScoreResult = {
40
+ score: number;
41
+ maxScore: number;
42
+ exactMatch: boolean;
43
+ hasWrongSelection: boolean;
44
+ passedThreshold: boolean;
45
+ };
46
+ /** Correct answer labels for scoring (multi-select uses `answers` when length > 1). */
47
+ declare function resolveMcqCorrectAnswers(props: Pick<McqAssessmentProps, "answer" | "answers">): Set<string>;
48
+ declare function isMultiSelectMcq(props: Pick<McqAssessmentProps, "answers">): boolean;
49
+ declare function scoreMcqSelection(selected: string | string[] | null | undefined, correct: Set<string>, multi: boolean, passingScore?: number): McqScoreResult;
50
+ /** Deterministic Fisher–Yates shuffle; returns display order indices. */
51
+ declare function shuffleChoiceIndices(length: number, seed: string | number): number[];
52
+ declare function resolveMcqShuffleSeed(props: Pick<McqAssessmentProps, "checkId" | "shuffleSeed">): string | number;
53
+ declare function orderChoicesByIndices(choices: string[], orderIndices: number[]): string[];
54
+
39
55
  /** LMS parent-bridge forwarding mode for packaged course runtimes. */
40
56
  type LmsBridgeMode = "auto" | "off";
41
57
 
@@ -88,21 +104,24 @@ declare function saveCompoundState(storage: StoragePort, courseId: CourseId, com
88
104
  declare function clearCompoundState(storage: StoragePort, courseId: CourseId, compoundId: BlockId): void;
89
105
 
90
106
  /** Canonical compound child allowlists (H5P sub-content curation). */
91
- declare const PAGE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar", "ProgressTracker"];
107
+ /** Tier B P1 assessments shipped in framework 1.7.0. */
108
+ declare const ASSESSMENT_17_CHILD_TYPES: readonly ["SortParagraphs", "GuessTheAnswer", "MultimediaChoice"];
109
+ declare const PAGE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar", "ProgressTracker", "SortParagraphs", "GuessTheAnswer", "MultimediaChoice"];
92
110
  /** Branch node content (Page-like minus ProgressTracker). */
93
- declare const BRANCH_NODE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar", "BranchChoice"];
111
+ declare const BRANCH_NODE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar", "BranchChoice", "SortParagraphs", "GuessTheAnswer", "MultimediaChoice"];
94
112
  declare const BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES: readonly ["BranchNode"];
95
113
  declare const GAME_MAP_ALLOWED_CHILD_TYPES: readonly ["MapStage"];
96
114
  /** Map stage content (BranchNode parity; WordSearch excluded from compounds). */
97
- declare const MAP_STAGE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar", "MapExit"];
115
+ declare const MAP_STAGE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar", "MapExit", "SortParagraphs", "GuessTheAnswer", "MultimediaChoice"];
98
116
  declare const INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES: readonly ["Page"];
99
117
  /** Per-slide content (H5P Course Presentation slide row). Excludes ProgressTracker. */
100
- declare const SLIDE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar"];
118
+ declare const SLIDE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Video", "Scenario", "Reflection", "Quiz", "KnowledgeCheck", "TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "InformationWall", "ParallaxSlideshow", "Questionnaire", "Essay", "ArithmeticQuiz", "Accordion", "DialogCards", "Flashcards", "ImageHotspots", "FindHotspot", "FindMultipleHotspots", "ImageSlider", "Embed", "Chart", "Table", "ImageJuxtaposition", "Timeline", "ImageSequence", "Collage", "AudioRecorder", "CombinationLock", "QrContent", "Crossword", "AdventCalendar", "SortParagraphs", "GuessTheAnswer", "MultimediaChoice"];
101
119
  declare const SLIDE_DECK_ALLOWED_CHILD_TYPES: readonly ["Slide"];
102
- declare const TIMED_CUE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Quiz", "TrueFalse", "FillInTheBlanks", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "Questionnaire", "Essay", "ArithmeticQuiz"];
120
+ declare const TIMED_CUE_ALLOWED_CHILD_TYPES: readonly ["Text", "Heading", "Image", "Quiz", "TrueFalse", "FillInTheBlanks", "Summary", "ImagePairing", "ImageSequencing", "MemoryGame", "Questionnaire", "Essay", "ArithmeticQuiz", "MultimediaChoice", "GuessTheAnswer"];
103
121
  declare const INTERACTIVE_VIDEO_ALLOWED_CHILD_TYPES: readonly ["TimedCue"];
104
- declare const ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES: readonly ["TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Quiz", "KnowledgeCheck", "FindHotspot", "FindMultipleHotspots", "Summary", "ImagePairing", "ImageSequencing", "ArithmeticQuiz", "Essay"];
105
- type CompoundParentType = "Page" | "InteractiveBook" | "Slide" | "SlideDeck" | "TimedCue" | "InteractiveVideo" | "AssessmentSequence" | "BranchingScenario" | "BranchNode" | "GameMap" | "MapStage";
122
+ declare const ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES: readonly ["TrueFalse", "FillInTheBlanks", "DragAndDrop", "DragTheWords", "MarkTheWords", "Quiz", "KnowledgeCheck", "FindHotspot", "FindMultipleHotspots", "Summary", "ImagePairing", "ImageSequencing", "ArithmeticQuiz", "Essay", "SortParagraphs", "GuessTheAnswer", "MultimediaChoice"];
123
+ declare const SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES: readonly ["Quiz", "KnowledgeCheck"];
124
+ type CompoundParentType = "Page" | "InteractiveBook" | "Slide" | "SlideDeck" | "TimedCue" | "InteractiveVideo" | "AssessmentSequence" | "BranchingScenario" | "BranchNode" | "GameMap" | "MapStage" | "SingleChoiceSet";
106
125
  declare const COMPOUND_MAX_NESTING_DEPTH: Record<CompoundParentType, number>;
107
126
  declare function getAllowedChildTypes(parent: CompoundParentType): readonly string[];
108
127
  declare function isChildTypeAllowed(parent: CompoundParentType, childType: string): boolean;
@@ -290,9 +309,23 @@ type HeadlessLessonkitRuntime = {
290
309
  * runtime.setActiveLesson("lesson-1");
291
310
  * runtime.track("interaction", { label: "opened" }, { lessonId: "lesson-1" });
292
311
  * ```
312
+ *
313
+ * @throws When plugin registry setup fails or tracking batch config is invalid (same rules as React provider).
293
314
  */
294
315
  declare function createLessonkitRuntime(config: HeadlessLessonkitConfig, ports?: HeadlessRuntimePorts): HeadlessLessonkitRuntime;
295
316
 
317
+ /**
318
+ * Register framework plugins (telemetry, assessment scoring, lifecycle hooks).
319
+ *
320
+ * @example
321
+ * ```ts
322
+ * import { createPluginRegistry, defineTelemetryPlugin } from "@lessonkit/core";
323
+ *
324
+ * const plugins = createPluginRegistry([
325
+ * defineTelemetryPlugin({ id: "analytics-bridge", setup: () => {} }),
326
+ * ]);
327
+ * ```
328
+ */
296
329
  declare function createPluginRegistry(plugins?: readonly LessonkitPlugin[]): PluginRegistry;
297
330
 
298
331
  /**
@@ -354,4 +387,4 @@ declare function buildPluginContext(opts: {
354
387
  user?: TelemetryUser;
355
388
  }): LessonkitPluginContext;
356
389
 
357
- export { ACCORDION_FORBIDDEN_CHILD_TYPES, ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES, AssessmentPlugin, AssessmentResumeState, AssessmentScoreInput, AssessmentScoreResult, BLOCKS_14_PAGE_SLIDE, BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES, BRANCH_NODE_ALLOWED_CHILD_TYPES, BlockId, type BranchGraphNodeInput, type BranchGraphValidationIssue, type BranchGraphValidationResult, COMPOUND_MAX_NESTING_DEPTH, COMPOUND_RESUME_SCHEMA_VERSION, CheckId, ClockPort, type CompoundBaseProps, type CompoundHandle, type CompoundParentType, type CompoundResumeInput, type CompoundResumeState, CourseId, type EmitContext, GAME_MAP_ALLOWED_CHILD_TYPES, type HeadlessLessonkitConfig, type HeadlessLessonkitRuntime, type HeadlessRuntimePorts, INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES, INTERACTIVE_VIDEO_ALLOWED_CHILD_TYPES, IdentityIdPath, IdentityValidationResult, LessonId, LessonkitPlugin, LessonkitPluginContext, type LessonkitRuntimeVersion, LessonkitUrn, type LessonkitUrnParts, LifecyclePlugin, type LmsBridgeMode, type LoadCompoundStateOptions, MAP_STAGE_ALLOWED_CHILD_TYPES, PAGE_ALLOWED_CHILD_TYPES, type ParseCompoundResumeStateOptions, PluginHost, PluginRegistry, ProgressController, ProgressState, SLIDE_ALLOWED_CHILD_TYPES, SLIDE_DECK_ALLOWED_CHILD_TYPES, StoragePort, TELEMETRY_EVENT_CATALOG, TELEMETRY_EVENT_CATALOG_V2, TELEMETRY_EVENT_CATALOG_V3, TIMED_CUE_ALLOWED_CHILD_TYPES, TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryCatalogV2Entry, type TelemetryCatalogV3Entry, TelemetryDataFor, type TelemetryEmitFn, TelemetryEvent, TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, TelemetryPlugin, TelemetrySink, TelemetryUser, TrackingClient, assertNever, assertValidId, buildLessonkitUrn, buildPluginContext, buildTelemetryCatalog, buildTelemetryCatalogV2, buildTelemetryCatalogV3, clampCompoundPageIndex, clearCompoundState, compoundStateStorageKey, createCompoundResumeState, createLessonkitRuntime, createPluginRegistry, createSessionId, createTelemetryPipeline, createTrackingClient, createTrackingPipelineSink, defineAssessmentPlugin, defineLifecyclePlugin, defineTelemetryPlugin, deriveId, getAllowedChildTypes, isChildTypeAllowed, isLifecycleTelemetryEvent, loadCompoundState, nowIso, parseBlockId, parseCheckId, parseCompoundResumeState, parseCourseId, parseLessonId, saveCompoundState, slugifyId, telemetryCatalogV2Version, telemetryCatalogV3Version, telemetryCatalogVersion, validateBranchGraph, validateId };
390
+ export { ACCORDION_FORBIDDEN_CHILD_TYPES, ASSESSMENT_17_CHILD_TYPES, ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES, AssessmentPlugin, AssessmentResumeState, AssessmentScoreInput, AssessmentScoreResult, BLOCKS_14_PAGE_SLIDE, BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES, BRANCH_NODE_ALLOWED_CHILD_TYPES, BlockId, type BranchGraphNodeInput, type BranchGraphValidationIssue, type BranchGraphValidationResult, COMPOUND_MAX_NESTING_DEPTH, COMPOUND_RESUME_SCHEMA_VERSION, CheckId, ClockPort, type CompoundBaseProps, type CompoundHandle, type CompoundParentType, type CompoundResumeInput, type CompoundResumeState, CourseId, type EmitContext, GAME_MAP_ALLOWED_CHILD_TYPES, type HeadlessLessonkitConfig, type HeadlessLessonkitRuntime, type HeadlessRuntimePorts, INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES, INTERACTIVE_VIDEO_ALLOWED_CHILD_TYPES, IdentityIdPath, IdentityValidationResult, LessonId, LessonkitPlugin, LessonkitPluginContext, type LessonkitRuntimeVersion, LessonkitUrn, type LessonkitUrnParts, LifecyclePlugin, type LmsBridgeMode, type LoadCompoundStateOptions, MAP_STAGE_ALLOWED_CHILD_TYPES, McqAssessmentProps, type McqScoreResult, PAGE_ALLOWED_CHILD_TYPES, type ParseCompoundResumeStateOptions, PluginHost, PluginRegistry, ProgressController, ProgressState, SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES, SLIDE_ALLOWED_CHILD_TYPES, SLIDE_DECK_ALLOWED_CHILD_TYPES, StoragePort, TELEMETRY_EVENT_CATALOG, TELEMETRY_EVENT_CATALOG_V2, TELEMETRY_EVENT_CATALOG_V3, TIMED_CUE_ALLOWED_CHILD_TYPES, TelemetryBatchSink, type TelemetryCatalogEntry, type TelemetryCatalogV2Entry, type TelemetryCatalogV3Entry, TelemetryDataFor, type TelemetryEmitFn, TelemetryEvent, TelemetryEventName, type TelemetryPipeline, type TelemetryPipelineSink, TelemetryPlugin, TelemetrySink, TelemetryUser, TrackingClient, assertNever, assertValidId, buildLessonkitUrn, buildPluginContext, buildTelemetryCatalog, buildTelemetryCatalogV2, buildTelemetryCatalogV3, clampCompoundPageIndex, clearCompoundState, compoundStateStorageKey, createCompoundResumeState, createLessonkitRuntime, createPluginRegistry, createSessionId, createTelemetryPipeline, createTrackingClient, createTrackingPipelineSink, defineAssessmentPlugin, defineLifecyclePlugin, defineTelemetryPlugin, deriveId, getAllowedChildTypes, isChildTypeAllowed, isLifecycleTelemetryEvent, isMultiSelectMcq, loadCompoundState, nowIso, orderChoicesByIndices, parseBlockId, parseCheckId, parseCompoundResumeState, parseCourseId, parseLessonId, resolveMcqCorrectAnswers, resolveMcqShuffleSeed, saveCompoundState, scoreMcqSelection, shuffleChoiceIndices, slugifyId, telemetryCatalogV2Version, telemetryCatalogV3Version, telemetryCatalogVersion, validateBranchGraph, validateId };
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  tryEmitCourseStarted,
37
37
  validateId,
38
38
  warnDev
39
- } from "./chunk-NGCHHJSM.js";
39
+ } from "./chunk-DTJGIMUU.js";
40
40
 
41
41
  // src/assertNever.ts
42
42
  function assertNever(value, message = "Unexpected value") {
@@ -126,6 +126,69 @@ function buildLessonkitUrn(parts) {
126
126
  return urn;
127
127
  }
128
128
 
129
+ // src/mcqAssessment.ts
130
+ function resolveMcqCorrectAnswers(props) {
131
+ if (props.answers && props.answers.length > 1) {
132
+ return new Set(props.answers.map((a) => a.trim()).filter(Boolean));
133
+ }
134
+ const single = props.answers?.[0]?.trim() ?? props.answer.trim();
135
+ return new Set(single ? [single] : []);
136
+ }
137
+ function isMultiSelectMcq(props) {
138
+ return (props.answers?.length ?? 0) > 1;
139
+ }
140
+ function scoreMcqSelection(selected, correct, multi, passingScore) {
141
+ const maxScore = multi ? Math.max(correct.size, 1) : 1;
142
+ if (!selected || Array.isArray(selected) && selected.length === 0) {
143
+ return {
144
+ score: 0,
145
+ maxScore,
146
+ exactMatch: false,
147
+ hasWrongSelection: false,
148
+ passedThreshold: false
149
+ };
150
+ }
151
+ const selectedSet = new Set(
152
+ (Array.isArray(selected) ? selected : [selected]).map((s) => s.trim()).filter(Boolean)
153
+ );
154
+ let score = 0;
155
+ for (const label of selectedSet) {
156
+ if (correct.has(label)) score += 1;
157
+ }
158
+ const hasWrongSelection = [...selectedSet].some((label) => !correct.has(label));
159
+ const exactMatch = !hasWrongSelection && selectedSet.size === correct.size && [...correct].every((label) => selectedSet.has(label));
160
+ const threshold = passingScore ?? maxScore;
161
+ const passedThreshold = score >= threshold && !hasWrongSelection;
162
+ return { score, maxScore, exactMatch, hasWrongSelection, passedThreshold };
163
+ }
164
+ function hashSeedToNumber(seed) {
165
+ if (typeof seed === "number" && Number.isFinite(seed)) return Math.abs(Math.trunc(seed)) || 1;
166
+ let hash = 2166136261;
167
+ const text = String(seed);
168
+ for (let i = 0; i < text.length; i += 1) {
169
+ hash ^= text.charCodeAt(i);
170
+ hash = Math.imul(hash, 16777619);
171
+ }
172
+ return hash >>> 0 || 1;
173
+ }
174
+ function shuffleChoiceIndices(length, seed) {
175
+ const indices = Array.from({ length }, (_, i) => i);
176
+ if (length <= 1) return indices;
177
+ let state = hashSeedToNumber(seed);
178
+ for (let i = length - 1; i > 0; i -= 1) {
179
+ state = Math.imul(state, 1664525) + 1013904223 >>> 0;
180
+ const j = state % (i + 1);
181
+ [indices[i], indices[j]] = [indices[j], indices[i]];
182
+ }
183
+ return indices;
184
+ }
185
+ function resolveMcqShuffleSeed(props) {
186
+ return props.shuffleSeed ?? props.checkId;
187
+ }
188
+ function orderChoicesByIndices(choices, orderIndices) {
189
+ return orderIndices.map((index) => choices[index] ?? "").filter((c) => c.length > 0);
190
+ }
191
+
129
192
  // src/compound.ts
130
193
  var COMPOUND_RESUME_SCHEMA_VERSION = 1;
131
194
  function createCompoundResumeState(input = {}) {
@@ -231,6 +294,11 @@ function clearCompoundState(storage, courseId, compoundId) {
231
294
  }
232
295
 
233
296
  // src/compoundAllowlists.ts
297
+ var ASSESSMENT_17_CHILD_TYPES = [
298
+ "SortParagraphs",
299
+ "GuessTheAnswer",
300
+ "MultimediaChoice"
301
+ ];
234
302
  var PAGE_AND_SLIDE_14_BLOCKS = [
235
303
  "Video",
236
304
  "Summary",
@@ -285,7 +353,8 @@ var PAGE_ALLOWED_CHILD_TYPES = [
285
353
  "QrContent",
286
354
  "Crossword",
287
355
  "AdventCalendar",
288
- "ProgressTracker"
356
+ "ProgressTracker",
357
+ ...ASSESSMENT_17_CHILD_TYPES
289
358
  ];
290
359
  var BRANCH_NODE_ALLOWED_CHILD_TYPES = [
291
360
  "Text",
@@ -329,7 +398,8 @@ var BRANCH_NODE_ALLOWED_CHILD_TYPES = [
329
398
  "QrContent",
330
399
  "Crossword",
331
400
  "AdventCalendar",
332
- "BranchChoice"
401
+ "BranchChoice",
402
+ ...ASSESSMENT_17_CHILD_TYPES
333
403
  ];
334
404
  var BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES = ["BranchNode"];
335
405
  var GAME_MAP_ALLOWED_CHILD_TYPES = ["MapStage"];
@@ -375,7 +445,8 @@ var MAP_STAGE_ALLOWED_CHILD_TYPES = [
375
445
  "QrContent",
376
446
  "Crossword",
377
447
  "AdventCalendar",
378
- "MapExit"
448
+ "MapExit",
449
+ ...ASSESSMENT_17_CHILD_TYPES
379
450
  ];
380
451
  var INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES = ["Page"];
381
452
  var SLIDE_ALLOWED_CHILD_TYPES = [
@@ -419,7 +490,8 @@ var SLIDE_ALLOWED_CHILD_TYPES = [
419
490
  "CombinationLock",
420
491
  "QrContent",
421
492
  "Crossword",
422
- "AdventCalendar"
493
+ "AdventCalendar",
494
+ ...ASSESSMENT_17_CHILD_TYPES
423
495
  ];
424
496
  var SLIDE_DECK_ALLOWED_CHILD_TYPES = ["Slide"];
425
497
  var TIMED_CUE_ALLOWED_CHILD_TYPES = [
@@ -435,7 +507,9 @@ var TIMED_CUE_ALLOWED_CHILD_TYPES = [
435
507
  "MemoryGame",
436
508
  "Questionnaire",
437
509
  "Essay",
438
- "ArithmeticQuiz"
510
+ "ArithmeticQuiz",
511
+ "MultimediaChoice",
512
+ "GuessTheAnswer"
439
513
  ];
440
514
  var INTERACTIVE_VIDEO_ALLOWED_CHILD_TYPES = ["TimedCue"];
441
515
  var ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES = [
@@ -452,8 +526,10 @@ var ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES = [
452
526
  "ImagePairing",
453
527
  "ImageSequencing",
454
528
  "ArithmeticQuiz",
455
- "Essay"
529
+ "Essay",
530
+ ...ASSESSMENT_17_CHILD_TYPES
456
531
  ];
532
+ var SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES = ["Quiz", "KnowledgeCheck"];
457
533
  var ALLOWLISTS = {
458
534
  Page: PAGE_ALLOWED_CHILD_TYPES,
459
535
  InteractiveBook: INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES,
@@ -465,7 +541,8 @@ var ALLOWLISTS = {
465
541
  BranchingScenario: BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES,
466
542
  BranchNode: BRANCH_NODE_ALLOWED_CHILD_TYPES,
467
543
  GameMap: GAME_MAP_ALLOWED_CHILD_TYPES,
468
- MapStage: MAP_STAGE_ALLOWED_CHILD_TYPES
544
+ MapStage: MAP_STAGE_ALLOWED_CHILD_TYPES,
545
+ SingleChoiceSet: SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES
469
546
  };
470
547
  var COMPOUND_MAX_NESTING_DEPTH = {
471
548
  Page: 1,
@@ -478,7 +555,8 @@ var COMPOUND_MAX_NESTING_DEPTH = {
478
555
  BranchingScenario: 2,
479
556
  BranchNode: 1,
480
557
  GameMap: 2,
481
- MapStage: 1
558
+ MapStage: 1,
559
+ SingleChoiceSet: 1
482
560
  };
483
561
  function getAllowedChildTypes(parent) {
484
562
  return ALLOWLISTS[parent];
@@ -1528,6 +1606,7 @@ function defineLifecyclePlugin(plugin) {
1528
1606
  }
1529
1607
  export {
1530
1608
  ACCORDION_FORBIDDEN_CHILD_TYPES,
1609
+ ASSESSMENT_17_CHILD_TYPES,
1531
1610
  ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES,
1532
1611
  BLOCKS_14_PAGE_SLIDE,
1533
1612
  BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES,
@@ -1542,6 +1621,7 @@ export {
1542
1621
  MAP_STAGE_ALLOWED_CHILD_TYPES,
1543
1622
  PAGE_ALLOWED_CHILD_TYPES,
1544
1623
  SESSION_STORAGE_KEY,
1624
+ SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES,
1545
1625
  SLIDE_ALLOWED_CHILD_TYPES,
1546
1626
  SLIDE_DECK_ALLOWED_CHILD_TYPES,
1547
1627
  TELEMETRY_EVENT_CATALOG,
@@ -1586,6 +1666,7 @@ export {
1586
1666
  hasCourseStartedXapiSent,
1587
1667
  isChildTypeAllowed,
1588
1668
  isLifecycleTelemetryEvent,
1669
+ isMultiSelectMcq,
1589
1670
  loadCompoundState,
1590
1671
  markCourseStarted,
1591
1672
  markCourseStartedEmittedToTracking,
@@ -1593,6 +1674,7 @@ export {
1593
1674
  markCourseStartedXapiSent,
1594
1675
  migrateCourseStartedMark,
1595
1676
  nowIso,
1677
+ orderChoicesByIndices,
1596
1678
  parseBlockId,
1597
1679
  parseCheckId,
1598
1680
  parseCompoundResumeState,
@@ -1601,8 +1683,12 @@ export {
1601
1683
  resetSharedVolatileSessionIdForTests,
1602
1684
  resetStoragePortForTests,
1603
1685
  resetTelemetryBuilderWarningsForTests,
1686
+ resolveMcqCorrectAnswers,
1687
+ resolveMcqShuffleSeed,
1604
1688
  resolveSessionId,
1605
1689
  saveCompoundState,
1690
+ scoreMcqSelection,
1691
+ shuffleChoiceIndices,
1606
1692
  slugifyId,
1607
1693
  telemetryCatalogV2Version,
1608
1694
  telemetryCatalogV3Version,
@@ -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" | "combinationLock" | "crossword" | "wordSearch";
24
+ type AssessmentInteractionType = "mcq" | "trueFalse" | "fillInBlanks" | "markTheWords" | "dragTheWords" | "dragAndDrop" | "assessmentSequence" | "findHotspot" | "findMultipleHotspots" | "summary" | "imagePairing" | "imageSequencing" | "essay" | "arithmeticQuiz" | "memoryGame" | "combinationLock" | "crossword" | "wordSearch" | "sortParagraphs" | "guessTheAnswer";
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. */
@@ -57,12 +57,34 @@ type AssessmentBaseProps = AssessmentBehaviour & {
57
57
  checkId: CheckId;
58
58
  passingScore?: number;
59
59
  };
60
- /** MCQ assessment props shared by React components and LMS packaging descriptors. */
60
+ /**
61
+ * MCQ assessment props shared by React components and LMS packaging descriptors.
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const props: McqAssessmentProps = {
66
+ * checkId: "verify-sender",
67
+ * question: "First step for a suspicious email?",
68
+ * choices: ["Open attachment", "Verify sender"],
69
+ * answer: "Verify sender",
70
+ * passingScore: 1,
71
+ * };
72
+ * ```
73
+ */
61
74
  type McqAssessmentProps = AssessmentBaseProps & {
62
75
  kind?: "mcq";
63
76
  question: string;
64
77
  choices: string[];
78
+ /** Single correct choice (required for backward compatibility). */
65
79
  answer: string;
80
+ /** When length > 1, enables multi-select checkbox mode. */
81
+ answers?: string[];
82
+ /** Randomize choice display order in the SPA (stable when `shuffleSeed` set). */
83
+ shuffleChoices?: boolean;
84
+ /** Seed for deterministic shuffle; defaults to `checkId`. */
85
+ shuffleSeed?: string | number;
86
+ /** Per-choice feedback announced on selection; keys match choice labels. */
87
+ choiceFeedback?: Record<string, string>;
66
88
  };
67
89
 
68
90
  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";
@@ -602,8 +624,20 @@ type ProgressController = {
602
624
  declare function createProgressController(): ProgressController;
603
625
 
604
626
  declare const SESSION_STORAGE_KEY = "lessonkit:sessionId";
627
+ type InvalidSessionIdContext = {
628
+ /** The invalid id that was rejected. */
629
+ invalidId: string;
630
+ /** Id actually used after fallback. */
631
+ fallbackId: string;
632
+ /** Whether the invalid id came from config or from stored tab state. */
633
+ source: "provided" | "stored";
634
+ };
635
+ type ResolveSessionIdOptions = {
636
+ /** Invoked when an invalid session id is replaced by a tab or generated id. */
637
+ onInvalidSessionId?: (ctx: InvalidSessionIdContext) => void;
638
+ };
605
639
  declare function getTabSessionId(storage: StoragePort): string | null;
606
- declare function resolveSessionId(storage: StoragePort, provided?: string): string;
640
+ declare function resolveSessionId(storage: StoragePort, provided?: string, options?: ResolveSessionIdOptions): string;
607
641
  declare function hasCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
608
642
  declare function markCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
609
643
  declare function hasCourseStartedEmittedToTracking(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
@@ -751,4 +785,4 @@ declare function completeCourseWithTelemetry(opts: {
751
785
  emitCourseCompleted: () => void;
752
786
  }): boolean;
753
787
 
754
- export { type LessonCompletionEmitter as $, type AssessmentResumeState as A, type BlockId as B, type CourseId as C, type AssessmentInteractionType as D, type AssessmentXAPIData as E, type BookPageViewedData as F, type BranchNodeViewedData as G, type BranchSelectedData as H, type IdentityIdPath as I, type BuildTelemetryEventInput as J, type CompoundPageViewedData as K, type LessonId as L, type CourseLifecycleContext as M, type CourseLifecycleDeps as N, type FlashcardFlippedData as O, type PluginRegistry as P, type HotspotOpenedData as Q, ID_MAX_LENGTH as R, type StoragePort as S, type TelemetryEventName as T, ID_PATTERN as U, type IdentityValidationIssue as V, type ImageSliderChangedData as W, type InformationWallSearchData as X, type InteractionBlockRegistration as Y, type InteractionData as Z, type InteractionPlugin as _, type CheckId as a, type LessonLifecycleData as a0, type LessonkitPluginKind as a1, type McqAssessmentProps as a2, type MemoryCardFlippedData as a3, type ParallaxSlideViewedData as a4, type PluginIdentity as a5, type QuestionnaireSubmittedData as a6, type QuizAnsweredData as a7, type QuizCompletedData as a8, SESSION_STORAGE_KEY as a9, resetTelemetryBuilderWarningsForTests as aA, resolveSessionId as aB, tryBuildTelemetryEvent as aC, tryEmitCourseStarted as aD, resetCourseStartedEmitFlightForTests as aE, type SlideViewedData as aa, type TelemetryEventBase as ab, type TimerPort as ac, type VideoCueReachedData as ad, type VideoSegmentCompletedData as ae, buildCourseStartedTelemetryEvent as af, buildTelemetryEvent as ag, completeCourseWithTelemetry as ah, completeLessonWithTelemetry as ai, createDefaultClock as aj, createGlobalTimer as ak, createNoopStorage as al, createProgressController as am, createSessionStoragePort as an, getTabSessionId as ao, hasCourseStarted as ap, hasCourseStartedEmittedToTracking as aq, hasCourseStartedPipelineDelivered as ar, hasCourseStartedXapiSent as as, markCourseStarted as at, markCourseStartedEmittedToTracking as au, markCourseStartedPipelineDelivered as av, markCourseStartedXapiSent as aw, migrateCourseStartedMark as ax, resetSharedVolatileSessionIdForTests as ay, resetStoragePortForTests as az, type IdentityValidationResult as b, type LessonkitUrn as c, type TelemetrySink as d, type TelemetryBatchSink as e, type TrackingClient as f, type TelemetryEvent as g, type TelemetryUser as h, type LessonkitPlugin as i, type ProgressController as j, type PluginHost as k, type ProgressState as l, type TelemetryDataFor as m, type AssessmentScoreInput as n, type AssessmentScoreResult as o, type ClockPort as p, type AssessmentPlugin as q, type LifecyclePlugin as r, type TelemetryPlugin as s, type LessonkitPluginContext as t, type AccordionSectionToggledData as u, type AssessmentAnsweredData as v, type AssessmentBaseProps as w, type AssessmentBehaviour as x, type AssessmentCompletedData as y, type AssessmentHandle as z };
788
+ export { type InteractionPlugin as $, type AssessmentResumeState as A, type BlockId as B, type CourseId as C, type AssessmentInteractionType as D, type AssessmentXAPIData as E, type BookPageViewedData as F, type BranchNodeViewedData as G, type BranchSelectedData as H, type IdentityIdPath as I, type BuildTelemetryEventInput as J, type CompoundPageViewedData as K, type LessonId as L, type McqAssessmentProps as M, type CourseLifecycleContext as N, type CourseLifecycleDeps as O, type PluginRegistry as P, type FlashcardFlippedData as Q, type HotspotOpenedData as R, type StoragePort as S, type TelemetryEventName as T, ID_MAX_LENGTH as U, ID_PATTERN as V, type IdentityValidationIssue as W, type ImageSliderChangedData as X, type InformationWallSearchData as Y, type InteractionBlockRegistration as Z, type InteractionData as _, type CheckId as a, type InvalidSessionIdContext as a0, type LessonCompletionEmitter as a1, type LessonLifecycleData as a2, type LessonkitPluginKind as a3, type MemoryCardFlippedData as a4, type ParallaxSlideViewedData as a5, type PluginIdentity as a6, type QuestionnaireSubmittedData as a7, type QuizAnsweredData as a8, type QuizCompletedData as a9, resetSharedVolatileSessionIdForTests as aA, resetStoragePortForTests as aB, resetTelemetryBuilderWarningsForTests as aC, resolveSessionId as aD, tryBuildTelemetryEvent as aE, tryEmitCourseStarted as aF, resetCourseStartedEmitFlightForTests as aG, type ResolveSessionIdOptions as aa, SESSION_STORAGE_KEY as ab, type SlideViewedData as ac, type TelemetryEventBase as ad, type TimerPort as ae, type VideoCueReachedData as af, type VideoSegmentCompletedData as ag, buildCourseStartedTelemetryEvent as ah, buildTelemetryEvent as ai, completeCourseWithTelemetry as aj, completeLessonWithTelemetry as ak, createDefaultClock as al, createGlobalTimer as am, createNoopStorage as an, createProgressController as ao, createSessionStoragePort as ap, getTabSessionId as aq, hasCourseStarted as ar, hasCourseStartedEmittedToTracking as as, hasCourseStartedPipelineDelivered as at, hasCourseStartedXapiSent as au, markCourseStarted as av, markCourseStartedEmittedToTracking as aw, markCourseStartedPipelineDelivered as ax, markCourseStartedXapiSent as ay, migrateCourseStartedMark as az, type IdentityValidationResult as b, type LessonkitUrn as c, type TelemetrySink as d, type TelemetryBatchSink as e, type TrackingClient as f, type TelemetryEvent as g, type TelemetryUser as h, type LessonkitPlugin as i, type ProgressController as j, type PluginHost as k, type ProgressState as l, type TelemetryDataFor as m, type AssessmentScoreInput as n, type AssessmentScoreResult as o, type ClockPort as p, type AssessmentPlugin as q, type LifecyclePlugin as r, type TelemetryPlugin as s, type LessonkitPluginContext as t, type AccordionSectionToggledData as u, type AssessmentAnsweredData as v, type AssessmentBaseProps as w, type AssessmentBehaviour as x, type AssessmentCompletedData as y, type AssessmentHandle as z };
@@ -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" | "combinationLock" | "crossword" | "wordSearch";
24
+ type AssessmentInteractionType = "mcq" | "trueFalse" | "fillInBlanks" | "markTheWords" | "dragTheWords" | "dragAndDrop" | "assessmentSequence" | "findHotspot" | "findMultipleHotspots" | "summary" | "imagePairing" | "imageSequencing" | "essay" | "arithmeticQuiz" | "memoryGame" | "combinationLock" | "crossword" | "wordSearch" | "sortParagraphs" | "guessTheAnswer";
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. */
@@ -57,12 +57,34 @@ type AssessmentBaseProps = AssessmentBehaviour & {
57
57
  checkId: CheckId;
58
58
  passingScore?: number;
59
59
  };
60
- /** MCQ assessment props shared by React components and LMS packaging descriptors. */
60
+ /**
61
+ * MCQ assessment props shared by React components and LMS packaging descriptors.
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const props: McqAssessmentProps = {
66
+ * checkId: "verify-sender",
67
+ * question: "First step for a suspicious email?",
68
+ * choices: ["Open attachment", "Verify sender"],
69
+ * answer: "Verify sender",
70
+ * passingScore: 1,
71
+ * };
72
+ * ```
73
+ */
61
74
  type McqAssessmentProps = AssessmentBaseProps & {
62
75
  kind?: "mcq";
63
76
  question: string;
64
77
  choices: string[];
78
+ /** Single correct choice (required for backward compatibility). */
65
79
  answer: string;
80
+ /** When length > 1, enables multi-select checkbox mode. */
81
+ answers?: string[];
82
+ /** Randomize choice display order in the SPA (stable when `shuffleSeed` set). */
83
+ shuffleChoices?: boolean;
84
+ /** Seed for deterministic shuffle; defaults to `checkId`. */
85
+ shuffleSeed?: string | number;
86
+ /** Per-choice feedback announced on selection; keys match choice labels. */
87
+ choiceFeedback?: Record<string, string>;
66
88
  };
67
89
 
68
90
  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";
@@ -602,8 +624,20 @@ type ProgressController = {
602
624
  declare function createProgressController(): ProgressController;
603
625
 
604
626
  declare const SESSION_STORAGE_KEY = "lessonkit:sessionId";
627
+ type InvalidSessionIdContext = {
628
+ /** The invalid id that was rejected. */
629
+ invalidId: string;
630
+ /** Id actually used after fallback. */
631
+ fallbackId: string;
632
+ /** Whether the invalid id came from config or from stored tab state. */
633
+ source: "provided" | "stored";
634
+ };
635
+ type ResolveSessionIdOptions = {
636
+ /** Invoked when an invalid session id is replaced by a tab or generated id. */
637
+ onInvalidSessionId?: (ctx: InvalidSessionIdContext) => void;
638
+ };
605
639
  declare function getTabSessionId(storage: StoragePort): string | null;
606
- declare function resolveSessionId(storage: StoragePort, provided?: string): string;
640
+ declare function resolveSessionId(storage: StoragePort, provided?: string, options?: ResolveSessionIdOptions): string;
607
641
  declare function hasCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
608
642
  declare function markCourseStarted(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
609
643
  declare function hasCourseStartedEmittedToTracking(storage: StoragePort, sessionId: string, courseId?: CourseId): boolean;
@@ -751,4 +785,4 @@ declare function completeCourseWithTelemetry(opts: {
751
785
  emitCourseCompleted: () => void;
752
786
  }): boolean;
753
787
 
754
- export { type LessonCompletionEmitter as $, type AssessmentResumeState as A, type BlockId as B, type CourseId as C, type AssessmentInteractionType as D, type AssessmentXAPIData as E, type BookPageViewedData as F, type BranchNodeViewedData as G, type BranchSelectedData as H, type IdentityIdPath as I, type BuildTelemetryEventInput as J, type CompoundPageViewedData as K, type LessonId as L, type CourseLifecycleContext as M, type CourseLifecycleDeps as N, type FlashcardFlippedData as O, type PluginRegistry as P, type HotspotOpenedData as Q, ID_MAX_LENGTH as R, type StoragePort as S, type TelemetryEventName as T, ID_PATTERN as U, type IdentityValidationIssue as V, type ImageSliderChangedData as W, type InformationWallSearchData as X, type InteractionBlockRegistration as Y, type InteractionData as Z, type InteractionPlugin as _, type CheckId as a, type LessonLifecycleData as a0, type LessonkitPluginKind as a1, type McqAssessmentProps as a2, type MemoryCardFlippedData as a3, type ParallaxSlideViewedData as a4, type PluginIdentity as a5, type QuestionnaireSubmittedData as a6, type QuizAnsweredData as a7, type QuizCompletedData as a8, SESSION_STORAGE_KEY as a9, resetTelemetryBuilderWarningsForTests as aA, resolveSessionId as aB, tryBuildTelemetryEvent as aC, tryEmitCourseStarted as aD, resetCourseStartedEmitFlightForTests as aE, type SlideViewedData as aa, type TelemetryEventBase as ab, type TimerPort as ac, type VideoCueReachedData as ad, type VideoSegmentCompletedData as ae, buildCourseStartedTelemetryEvent as af, buildTelemetryEvent as ag, completeCourseWithTelemetry as ah, completeLessonWithTelemetry as ai, createDefaultClock as aj, createGlobalTimer as ak, createNoopStorage as al, createProgressController as am, createSessionStoragePort as an, getTabSessionId as ao, hasCourseStarted as ap, hasCourseStartedEmittedToTracking as aq, hasCourseStartedPipelineDelivered as ar, hasCourseStartedXapiSent as as, markCourseStarted as at, markCourseStartedEmittedToTracking as au, markCourseStartedPipelineDelivered as av, markCourseStartedXapiSent as aw, migrateCourseStartedMark as ax, resetSharedVolatileSessionIdForTests as ay, resetStoragePortForTests as az, type IdentityValidationResult as b, type LessonkitUrn as c, type TelemetrySink as d, type TelemetryBatchSink as e, type TrackingClient as f, type TelemetryEvent as g, type TelemetryUser as h, type LessonkitPlugin as i, type ProgressController as j, type PluginHost as k, type ProgressState as l, type TelemetryDataFor as m, type AssessmentScoreInput as n, type AssessmentScoreResult as o, type ClockPort as p, type AssessmentPlugin as q, type LifecyclePlugin as r, type TelemetryPlugin as s, type LessonkitPluginContext as t, type AccordionSectionToggledData as u, type AssessmentAnsweredData as v, type AssessmentBaseProps as w, type AssessmentBehaviour as x, type AssessmentCompletedData as y, type AssessmentHandle as z };
788
+ export { type InteractionPlugin as $, type AssessmentResumeState as A, type BlockId as B, type CourseId as C, type AssessmentInteractionType as D, type AssessmentXAPIData as E, type BookPageViewedData as F, type BranchNodeViewedData as G, type BranchSelectedData as H, type IdentityIdPath as I, type BuildTelemetryEventInput as J, type CompoundPageViewedData as K, type LessonId as L, type McqAssessmentProps as M, type CourseLifecycleContext as N, type CourseLifecycleDeps as O, type PluginRegistry as P, type FlashcardFlippedData as Q, type HotspotOpenedData as R, type StoragePort as S, type TelemetryEventName as T, ID_MAX_LENGTH as U, ID_PATTERN as V, type IdentityValidationIssue as W, type ImageSliderChangedData as X, type InformationWallSearchData as Y, type InteractionBlockRegistration as Z, type InteractionData as _, type CheckId as a, type InvalidSessionIdContext as a0, type LessonCompletionEmitter as a1, type LessonLifecycleData as a2, type LessonkitPluginKind as a3, type MemoryCardFlippedData as a4, type ParallaxSlideViewedData as a5, type PluginIdentity as a6, type QuestionnaireSubmittedData as a7, type QuizAnsweredData as a8, type QuizCompletedData as a9, resetSharedVolatileSessionIdForTests as aA, resetStoragePortForTests as aB, resetTelemetryBuilderWarningsForTests as aC, resolveSessionId as aD, tryBuildTelemetryEvent as aE, tryEmitCourseStarted as aF, resetCourseStartedEmitFlightForTests as aG, type ResolveSessionIdOptions as aa, SESSION_STORAGE_KEY as ab, type SlideViewedData as ac, type TelemetryEventBase as ad, type TimerPort as ae, type VideoCueReachedData as af, type VideoSegmentCompletedData as ag, buildCourseStartedTelemetryEvent as ah, buildTelemetryEvent as ai, completeCourseWithTelemetry as aj, completeLessonWithTelemetry as ak, createDefaultClock as al, createGlobalTimer as am, createNoopStorage as an, createProgressController as ao, createSessionStoragePort as ap, getTabSessionId as aq, hasCourseStarted as ar, hasCourseStartedEmittedToTracking as as, hasCourseStartedPipelineDelivered as at, hasCourseStartedXapiSent as au, markCourseStarted as av, markCourseStartedEmittedToTracking as aw, markCourseStartedPipelineDelivered as ax, markCourseStartedXapiSent as ay, migrateCourseStartedMark as az, type IdentityValidationResult as b, type LessonkitUrn as c, type TelemetrySink as d, type TelemetryBatchSink as e, type TrackingClient as f, type TelemetryEvent as g, type TelemetryUser as h, type LessonkitPlugin as i, type ProgressController as j, type PluginHost as k, type ProgressState as l, type TelemetryDataFor as m, type AssessmentScoreInput as n, type AssessmentScoreResult as o, type ClockPort as p, type AssessmentPlugin as q, type LifecyclePlugin as r, type TelemetryPlugin as s, type LessonkitPluginContext as t, type AccordionSectionToggledData as u, type AssessmentAnsweredData as v, type AssessmentBaseProps as w, type AssessmentBehaviour as x, type AssessmentCompletedData as y, type AssessmentHandle as z };
package/dist/testing.cjs CHANGED
@@ -46,8 +46,10 @@ function resetSharedVolatileSessionIdForTests() {
46
46
 
47
47
  // src/runtime/courseLifecycle.ts
48
48
  var courseStartedEmitFlights = /* @__PURE__ */ new Map();
49
+ var courseStartedEmittedInTab = /* @__PURE__ */ new Set();
49
50
  function resetCourseStartedEmitFlightForTests() {
50
51
  courseStartedEmitFlights.clear();
52
+ courseStartedEmittedInTab.clear();
51
53
  }
52
54
  // Annotate the CommonJS export names for ESM import in node:
53
55
  0 && (module.exports = {
@@ -1 +1 @@
1
- export { aE as resetCourseStartedEmitFlightForTests, ay as resetSharedVolatileSessionIdForTests, az as resetStoragePortForTests, aA as resetTelemetryBuilderWarningsForTests } from './testing-CzgxF1Ru.cjs';
1
+ export { aG as resetCourseStartedEmitFlightForTests, aA as resetSharedVolatileSessionIdForTests, aB as resetStoragePortForTests, aC as resetTelemetryBuilderWarningsForTests } from './testing-CQ-ZsT7D.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-CzgxF1Ru.js';
1
+ export { aG as resetCourseStartedEmitFlightForTests, aA as resetSharedVolatileSessionIdForTests, aB as resetStoragePortForTests, aC as resetTelemetryBuilderWarningsForTests } from './testing-CQ-ZsT7D.js';
package/dist/testing.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  resetSharedVolatileSessionIdForTests,
4
4
  resetStoragePortForTests,
5
5
  resetTelemetryBuilderWarningsForTests
6
- } from "./chunk-NGCHHJSM.js";
6
+ } from "./chunk-DTJGIMUU.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.6.0",
3
+ "version": "1.7.1",
4
4
  "private": false,
5
5
  "description": "Shared types and telemetry primitives for LessonKit.",
6
6
  "license": "Apache-2.0",
@@ -48,8 +48,8 @@
48
48
  "identity-contract.v1.json"
49
49
  ],
50
50
  "scripts": {
51
- "build": "tsup src/index.ts src/testing.ts --format esm,cjs --dts",
52
- "dev": "tsup src/index.ts src/testing.ts --format esm,cjs --dts --watch",
51
+ "build": "tsup src/index.ts src/testing.ts --format esm,cjs --dts --tsconfig tsconfig.build.json",
52
+ "dev": "tsup src/index.ts src/testing.ts --format esm,cjs --dts --watch --tsconfig tsconfig.build.json",
53
53
  "prepublishOnly": "npm run build",
54
54
  "typecheck": "tsc -p tsconfig.json",
55
55
  "test": "vitest run --passWithNoTests",