@lessonkit/core 1.5.0 → 1.7.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.
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ import {
36
36
  tryEmitCourseStarted,
37
37
  validateId,
38
38
  warnDev
39
- } from "./chunk-KFXFQ6B2.js";
39
+ } from "./chunk-DTJGIMUU.js";
40
40
 
41
41
  // src/assertNever.ts
42
42
  function assertNever(value, message = "Unexpected value") {
@@ -117,7 +117,7 @@ function buildLessonkitUrn(parts) {
117
117
  urn += `:block:${blockId}`;
118
118
  }
119
119
  if (parts.nodeId !== void 0) {
120
- const nodeId = assertValidId(parts.nodeId, "blockId");
120
+ const nodeId = assertValidId(parts.nodeId, "nodeId");
121
121
  if (parts.blockId === void 0) {
122
122
  throw new Error("buildLessonkitUrn: nodeId requires blockId");
123
123
  }
@@ -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 = {}) {
@@ -187,9 +250,11 @@ function parseCompoundResumeState(raw, opts) {
187
250
  opts?.onDroppedChildKeys?.(droppedChildKeys);
188
251
  }
189
252
  const activeChapterIndex = typeof obj.activeChapterIndex === "number" && Number.isFinite(obj.activeChapterIndex) ? obj.activeChapterIndex : void 0;
253
+ const rawPageIndex = Math.max(0, Math.floor(obj.activePageIndex));
254
+ const activePageIndex = typeof opts?.pageCount === "number" && opts.pageCount > 0 ? clampCompoundPageIndex(rawPageIndex, opts.pageCount) : rawPageIndex;
190
255
  return {
191
256
  schemaVersion: COMPOUND_RESUME_SCHEMA_VERSION,
192
- activePageIndex: Math.max(0, Math.floor(obj.activePageIndex)),
257
+ activePageIndex,
193
258
  ...activeChapterIndex !== void 0 ? { activeChapterIndex: Math.max(0, Math.floor(activeChapterIndex)) } : {},
194
259
  childStates
195
260
  };
@@ -229,6 +294,11 @@ function clearCompoundState(storage, courseId, compoundId) {
229
294
  }
230
295
 
231
296
  // src/compoundAllowlists.ts
297
+ var ASSESSMENT_17_CHILD_TYPES = [
298
+ "SortParagraphs",
299
+ "GuessTheAnswer",
300
+ "MultimediaChoice"
301
+ ];
232
302
  var PAGE_AND_SLIDE_14_BLOCKS = [
233
303
  "Video",
234
304
  "Summary",
@@ -273,7 +343,18 @@ var PAGE_ALLOWED_CHILD_TYPES = [
273
343
  "ImageSlider",
274
344
  "Embed",
275
345
  "Chart",
276
- "ProgressTracker"
346
+ "Table",
347
+ "ImageJuxtaposition",
348
+ "Timeline",
349
+ "ImageSequence",
350
+ "Collage",
351
+ "AudioRecorder",
352
+ "CombinationLock",
353
+ "QrContent",
354
+ "Crossword",
355
+ "AdventCalendar",
356
+ "ProgressTracker",
357
+ ...ASSESSMENT_17_CHILD_TYPES
277
358
  ];
278
359
  var BRANCH_NODE_ALLOWED_CHILD_TYPES = [
279
360
  "Text",
@@ -307,9 +388,66 @@ var BRANCH_NODE_ALLOWED_CHILD_TYPES = [
307
388
  "ImageSlider",
308
389
  "Embed",
309
390
  "Chart",
310
- "BranchChoice"
391
+ "Table",
392
+ "ImageJuxtaposition",
393
+ "Timeline",
394
+ "ImageSequence",
395
+ "Collage",
396
+ "AudioRecorder",
397
+ "CombinationLock",
398
+ "QrContent",
399
+ "Crossword",
400
+ "AdventCalendar",
401
+ "BranchChoice",
402
+ ...ASSESSMENT_17_CHILD_TYPES
311
403
  ];
312
404
  var BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES = ["BranchNode"];
405
+ var GAME_MAP_ALLOWED_CHILD_TYPES = ["MapStage"];
406
+ var MAP_STAGE_ALLOWED_CHILD_TYPES = [
407
+ "Text",
408
+ "Heading",
409
+ "Image",
410
+ "Video",
411
+ "Scenario",
412
+ "Reflection",
413
+ "Quiz",
414
+ "KnowledgeCheck",
415
+ "TrueFalse",
416
+ "FillInTheBlanks",
417
+ "DragAndDrop",
418
+ "DragTheWords",
419
+ "MarkTheWords",
420
+ "Summary",
421
+ "ImagePairing",
422
+ "ImageSequencing",
423
+ "MemoryGame",
424
+ "InformationWall",
425
+ "ParallaxSlideshow",
426
+ "Questionnaire",
427
+ "Essay",
428
+ "ArithmeticQuiz",
429
+ "Accordion",
430
+ "DialogCards",
431
+ "Flashcards",
432
+ "ImageHotspots",
433
+ "FindHotspot",
434
+ "FindMultipleHotspots",
435
+ "ImageSlider",
436
+ "Embed",
437
+ "Chart",
438
+ "Table",
439
+ "ImageJuxtaposition",
440
+ "Timeline",
441
+ "ImageSequence",
442
+ "Collage",
443
+ "AudioRecorder",
444
+ "CombinationLock",
445
+ "QrContent",
446
+ "Crossword",
447
+ "AdventCalendar",
448
+ "MapExit",
449
+ ...ASSESSMENT_17_CHILD_TYPES
450
+ ];
313
451
  var INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES = ["Page"];
314
452
  var SLIDE_ALLOWED_CHILD_TYPES = [
315
453
  "Text",
@@ -342,7 +480,18 @@ var SLIDE_ALLOWED_CHILD_TYPES = [
342
480
  "FindMultipleHotspots",
343
481
  "ImageSlider",
344
482
  "Embed",
345
- "Chart"
483
+ "Chart",
484
+ "Table",
485
+ "ImageJuxtaposition",
486
+ "Timeline",
487
+ "ImageSequence",
488
+ "Collage",
489
+ "AudioRecorder",
490
+ "CombinationLock",
491
+ "QrContent",
492
+ "Crossword",
493
+ "AdventCalendar",
494
+ ...ASSESSMENT_17_CHILD_TYPES
346
495
  ];
347
496
  var SLIDE_DECK_ALLOWED_CHILD_TYPES = ["Slide"];
348
497
  var TIMED_CUE_ALLOWED_CHILD_TYPES = [
@@ -358,7 +507,9 @@ var TIMED_CUE_ALLOWED_CHILD_TYPES = [
358
507
  "MemoryGame",
359
508
  "Questionnaire",
360
509
  "Essay",
361
- "ArithmeticQuiz"
510
+ "ArithmeticQuiz",
511
+ "MultimediaChoice",
512
+ "GuessTheAnswer"
362
513
  ];
363
514
  var INTERACTIVE_VIDEO_ALLOWED_CHILD_TYPES = ["TimedCue"];
364
515
  var ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES = [
@@ -375,8 +526,10 @@ var ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES = [
375
526
  "ImagePairing",
376
527
  "ImageSequencing",
377
528
  "ArithmeticQuiz",
378
- "Essay"
529
+ "Essay",
530
+ ...ASSESSMENT_17_CHILD_TYPES
379
531
  ];
532
+ var SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES = ["Quiz", "KnowledgeCheck"];
380
533
  var ALLOWLISTS = {
381
534
  Page: PAGE_ALLOWED_CHILD_TYPES,
382
535
  InteractiveBook: INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES,
@@ -386,7 +539,10 @@ var ALLOWLISTS = {
386
539
  InteractiveVideo: INTERACTIVE_VIDEO_ALLOWED_CHILD_TYPES,
387
540
  AssessmentSequence: ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES,
388
541
  BranchingScenario: BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES,
389
- BranchNode: BRANCH_NODE_ALLOWED_CHILD_TYPES
542
+ BranchNode: BRANCH_NODE_ALLOWED_CHILD_TYPES,
543
+ GameMap: GAME_MAP_ALLOWED_CHILD_TYPES,
544
+ MapStage: MAP_STAGE_ALLOWED_CHILD_TYPES,
545
+ SingleChoiceSet: SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES
390
546
  };
391
547
  var COMPOUND_MAX_NESTING_DEPTH = {
392
548
  Page: 1,
@@ -397,7 +553,10 @@ var COMPOUND_MAX_NESTING_DEPTH = {
397
553
  InteractiveVideo: 2,
398
554
  AssessmentSequence: 1,
399
555
  BranchingScenario: 2,
400
- BranchNode: 1
556
+ BranchNode: 1,
557
+ GameMap: 2,
558
+ MapStage: 1,
559
+ SingleChoiceSet: 1
401
560
  };
402
561
  function getAllowedChildTypes(parent) {
403
562
  return ALLOWLISTS[parent];
@@ -702,6 +861,78 @@ var TELEMETRY_EVENT_CATALOG_V3 = [
702
861
  dataFields: ["blockId", "fromNodeId", "toNodeId", "label", "scoreWeight"],
703
862
  xapiVerb: "http://adlnet.gov/expapi/verbs/answered",
704
863
  urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}:node:{toNodeId}"
864
+ },
865
+ {
866
+ name: "image_juxtaposition_changed",
867
+ description: "Learner adjusted the before/after divider",
868
+ requiredFields: ["courseId", "sessionId", "timestamp"],
869
+ dataFields: ["blockId", "position"],
870
+ xapiVerb: "http://adlnet.gov/expapi/verbs/interacted",
871
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
872
+ },
873
+ {
874
+ name: "timeline_event_viewed",
875
+ description: "Learner focused a timeline event",
876
+ requiredFields: ["courseId", "sessionId", "timestamp"],
877
+ dataFields: ["blockId", "eventId"],
878
+ xapiVerb: "http://adlnet.gov/expapi/verbs/experienced",
879
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
880
+ },
881
+ {
882
+ name: "image_sequence_changed",
883
+ description: "Learner changed the image sequence frame",
884
+ requiredFields: ["courseId", "sessionId", "timestamp"],
885
+ dataFields: ["blockId", "frameIndex"],
886
+ xapiVerb: "http://adlnet.gov/expapi/verbs/interacted",
887
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
888
+ },
889
+ {
890
+ name: "audio_recording_started",
891
+ description: "Learner started an audio recording",
892
+ requiredFields: ["courseId", "sessionId", "timestamp"],
893
+ dataFields: ["blockId"],
894
+ xapiVerb: "http://adlnet.gov/expapi/verbs/interacted",
895
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
896
+ },
897
+ {
898
+ name: "audio_recording_completed",
899
+ description: "Learner completed an audio recording",
900
+ requiredFields: ["courseId", "sessionId", "timestamp"],
901
+ dataFields: ["blockId"],
902
+ xapiVerb: "http://adlnet.gov/expapi/verbs/completed",
903
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
904
+ },
905
+ {
906
+ name: "qr_content_revealed",
907
+ description: "Learner revealed QR hidden content",
908
+ requiredFields: ["courseId", "sessionId", "timestamp"],
909
+ dataFields: ["blockId"],
910
+ xapiVerb: "http://adlnet.gov/expapi/verbs/experienced",
911
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
912
+ },
913
+ {
914
+ name: "advent_door_opened",
915
+ description: "Learner opened an advent calendar door",
916
+ requiredFields: ["courseId", "sessionId", "timestamp"],
917
+ dataFields: ["blockId", "doorId", "day"],
918
+ xapiVerb: "http://adlnet.gov/expapi/verbs/interacted",
919
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}"
920
+ },
921
+ {
922
+ name: "map_stage_viewed",
923
+ description: "Learner viewed a stage in a GameMap",
924
+ requiredFields: ["courseId", "lessonId", "sessionId", "timestamp"],
925
+ dataFields: ["blockId", "stageId", "stageIndex", "stageLabel"],
926
+ xapiVerb: "http://adlnet.gov/expapi/verbs/experienced",
927
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}:stage:{stageId}"
928
+ },
929
+ {
930
+ name: "map_exit_selected",
931
+ description: "Learner selected a map exit in a GameMap",
932
+ requiredFields: ["courseId", "lessonId", "sessionId", "timestamp"],
933
+ dataFields: ["blockId", "fromStageId", "toStageId", "label", "scoreWeight"],
934
+ xapiVerb: "http://adlnet.gov/expapi/verbs/answered",
935
+ urnPattern: "urn:lessonkit:course:{courseId}:lesson:{lessonId}:block:{blockId}:stage:{toStageId}"
705
936
  }
706
937
  ];
707
938
  function buildTelemetryCatalogV3() {
@@ -798,6 +1029,10 @@ function createTrackingClient(opts) {
798
1029
  const runFlush = () => {
799
1030
  if (!buffer.length) return Promise.resolve(true);
800
1031
  const events = buffer.splice(0, buffer.length);
1032
+ for (const event of events) {
1033
+ const key = eventDedupKey(event);
1034
+ if (key) pendingDeliverIds.add(key);
1035
+ }
801
1036
  let succeeded = false;
802
1037
  return Promise.resolve().then(async () => {
803
1038
  if (batchSink) {
@@ -807,7 +1042,7 @@ function createTrackingClient(opts) {
807
1042
  try {
808
1043
  await sink?.(events[i]);
809
1044
  } catch {
810
- buffer.unshift(...events.slice(i));
1045
+ buffer.unshift(...events);
811
1046
  return;
812
1047
  }
813
1048
  }
@@ -1142,6 +1377,10 @@ function resolvePluginHost(plugins) {
1142
1377
  if (Array.isArray(plugins) && plugins.length > 0) return createPluginRegistry(plugins);
1143
1378
  return null;
1144
1379
  }
1380
+ function pluginListFingerprint(plugins) {
1381
+ if (!plugins || !Array.isArray(plugins)) return null;
1382
+ return plugins.map((p) => `${p.id}\0${p.version ?? ""}`).join("\n");
1383
+ }
1145
1384
  function warnRuntimeV1Deprecated() {
1146
1385
  const g = globalThis;
1147
1386
  if (typeof g.process !== "undefined" && g.process.env?.NODE_ENV === "production") return;
@@ -1154,12 +1393,19 @@ function createLessonkitRuntime(config, ports = {}) {
1154
1393
  const storage = ports.storage ?? createSessionStoragePort();
1155
1394
  const clock = ports.clock ?? createDefaultClock();
1156
1395
  const configSnapshot = { ...config };
1396
+ const hasExplicitSessionId = Boolean(configSnapshot.session?.sessionId?.trim());
1397
+ let autoSessionId;
1157
1398
  let sessionId = resolveSessionId(storage, configSnapshot.session?.sessionId);
1399
+ if (!hasExplicitSessionId) {
1400
+ autoSessionId = sessionId;
1401
+ }
1158
1402
  let attemptId = configSnapshot.session?.attemptId;
1159
1403
  let user = configSnapshot.session?.user;
1160
1404
  let courseId = configSnapshot.courseId;
1405
+ let configuredSessionId = configSnapshot.session?.sessionId;
1161
1406
  let progress = createProgressController();
1162
1407
  let pluginHost = resolvePluginHost(configSnapshot.plugins);
1408
+ let pluginFingerprint = pluginListFingerprint(configSnapshot.plugins);
1163
1409
  let disposed = false;
1164
1410
  const getPluginCtx = () => buildPluginContext({
1165
1411
  courseId,
@@ -1171,12 +1417,6 @@ function createLessonkitRuntime(config, ports = {}) {
1171
1417
  pluginHost?.setupAll(getPluginCtx());
1172
1418
  }
1173
1419
  const getSession = () => ({ sessionId, attemptId, user });
1174
- const syncSessionFromConfig = (next) => {
1175
- sessionId = resolveSessionId(storage, next.session?.sessionId);
1176
- attemptId = next.session?.attemptId;
1177
- user = next.session?.user;
1178
- courseId = next.courseId;
1179
- };
1180
1420
  const applyPluginsToEvent = (event) => {
1181
1421
  if (!pluginHost) return event;
1182
1422
  return pluginHost.runTelemetry(event, getPluginCtx());
@@ -1198,7 +1438,6 @@ function createLessonkitRuntime(config, ports = {}) {
1198
1438
  const event = buildAndApply(name, data, lessonId);
1199
1439
  if (event) emitFn(event);
1200
1440
  };
1201
- syncSessionFromConfig(configSnapshot);
1202
1441
  const track = (name, data, emit, lessonId) => {
1203
1442
  if (disposed) return;
1204
1443
  const event = buildAndApply(name, data, lessonId);
@@ -1240,21 +1479,52 @@ function createLessonkitRuntime(config, ports = {}) {
1240
1479
  if (next.autoCompleteOnLessonSwitch !== void 0) {
1241
1480
  configSnapshot.autoCompleteOnLessonSwitch = next.autoCompleteOnLessonSwitch;
1242
1481
  }
1482
+ if (next.courseId !== void 0) {
1483
+ courseId = next.courseId;
1484
+ }
1243
1485
  if (next.session !== void 0) {
1486
+ const previousSessionId = sessionId;
1244
1487
  configSnapshot.session = { ...configSnapshot.session, ...next.session };
1488
+ const explicitSessionId = configSnapshot.session?.sessionId?.trim();
1489
+ if (explicitSessionId) {
1490
+ sessionId = resolveSessionId(storage, explicitSessionId);
1491
+ autoSessionId = void 0;
1492
+ } else {
1493
+ sessionId = autoSessionId ?? resolveSessionId(storage, void 0);
1494
+ if (!autoSessionId) autoSessionId = sessionId;
1495
+ }
1496
+ attemptId = configSnapshot.session?.attemptId;
1497
+ user = configSnapshot.session?.user;
1498
+ if (previousSessionId !== sessionId) {
1499
+ const prevExplicit = configuredSessionId?.trim();
1500
+ const nextExplicit = configSnapshot.session?.sessionId?.trim();
1501
+ const isExplicitLearnerSwap = Boolean(prevExplicit) && Boolean(nextExplicit) && prevExplicit !== nextExplicit;
1502
+ if (!isExplicitLearnerSwap) {
1503
+ migrateCourseStartedMark(storage, previousSessionId, sessionId, courseId);
1504
+ }
1505
+ }
1506
+ configuredSessionId = configSnapshot.session?.sessionId;
1245
1507
  }
1246
- syncSessionFromConfig(configSnapshot);
1247
1508
  const sessionKeyAfter = JSON.stringify({ sessionId, attemptId, user });
1248
1509
  if (next.courseId !== void 0 && next.courseId !== previousCourseId) {
1249
1510
  progress = createProgressController();
1250
- }
1251
- if (next.plugins !== void 0 && next.plugins !== configSnapshot.plugins) {
1252
- pluginHost?.disposeAll();
1253
- configSnapshot.plugins = next.plugins;
1254
- pluginHost = resolvePluginHost(configSnapshot.plugins);
1255
1511
  if (!configSnapshot.deferPluginSetup) {
1512
+ pluginHost?.disposeAll();
1256
1513
  pluginHost?.setupAll(getPluginCtx());
1257
1514
  }
1515
+ }
1516
+ if (next.plugins !== void 0) {
1517
+ const nextFingerprint = pluginListFingerprint(next.plugins);
1518
+ const pluginsChanged = next.plugins !== configSnapshot.plugins || nextFingerprint !== null && nextFingerprint !== pluginFingerprint;
1519
+ if (pluginsChanged) {
1520
+ pluginHost?.disposeAll();
1521
+ configSnapshot.plugins = next.plugins;
1522
+ pluginFingerprint = nextFingerprint;
1523
+ pluginHost = resolvePluginHost(configSnapshot.plugins);
1524
+ if (!configSnapshot.deferPluginSetup) {
1525
+ pluginHost?.setupAll(getPluginCtx());
1526
+ }
1527
+ }
1258
1528
  } else if (next.session !== void 0 && sessionKeyBefore !== sessionKeyAfter && pluginHost && !configSnapshot.deferPluginSetup) {
1259
1529
  pluginHost.disposeAll();
1260
1530
  pluginHost.setupAll(getPluginCtx());
@@ -1309,15 +1579,17 @@ function createLessonkitRuntime(config, ports = {}) {
1309
1579
  configSnapshot.courseId = nextCourseId;
1310
1580
  courseId = nextCourseId;
1311
1581
  progress = createProgressController();
1312
- pluginHost?.disposeAll();
1313
1582
  if (!configSnapshot.deferPluginSetup) {
1583
+ pluginHost?.disposeAll();
1314
1584
  pluginHost?.setupAll(getPluginCtx());
1315
1585
  }
1316
1586
  },
1317
1587
  dispose() {
1318
1588
  if (disposed) return;
1319
1589
  disposed = true;
1320
- pluginHost?.disposeAll();
1590
+ if (!configSnapshot.deferPluginSetup) {
1591
+ pluginHost?.disposeAll();
1592
+ }
1321
1593
  }
1322
1594
  };
1323
1595
  }
@@ -1334,18 +1606,22 @@ function defineLifecyclePlugin(plugin) {
1334
1606
  }
1335
1607
  export {
1336
1608
  ACCORDION_FORBIDDEN_CHILD_TYPES,
1609
+ ASSESSMENT_17_CHILD_TYPES,
1337
1610
  ASSESSMENT_SEQUENCE_ALLOWED_CHILD_TYPES,
1338
1611
  BLOCKS_14_PAGE_SLIDE,
1339
1612
  BRANCHING_SCENARIO_ALLOWED_CHILD_TYPES,
1340
1613
  BRANCH_NODE_ALLOWED_CHILD_TYPES,
1341
1614
  COMPOUND_MAX_NESTING_DEPTH,
1342
1615
  COMPOUND_RESUME_SCHEMA_VERSION,
1616
+ GAME_MAP_ALLOWED_CHILD_TYPES,
1343
1617
  ID_MAX_LENGTH,
1344
1618
  ID_PATTERN,
1345
1619
  INTERACTIVE_BOOK_ALLOWED_CHILD_TYPES,
1346
1620
  INTERACTIVE_VIDEO_ALLOWED_CHILD_TYPES,
1621
+ MAP_STAGE_ALLOWED_CHILD_TYPES,
1347
1622
  PAGE_ALLOWED_CHILD_TYPES,
1348
1623
  SESSION_STORAGE_KEY,
1624
+ SINGLE_CHOICE_SET_ALLOWED_CHILD_TYPES,
1349
1625
  SLIDE_ALLOWED_CHILD_TYPES,
1350
1626
  SLIDE_DECK_ALLOWED_CHILD_TYPES,
1351
1627
  TELEMETRY_EVENT_CATALOG,
@@ -1390,6 +1666,7 @@ export {
1390
1666
  hasCourseStartedXapiSent,
1391
1667
  isChildTypeAllowed,
1392
1668
  isLifecycleTelemetryEvent,
1669
+ isMultiSelectMcq,
1393
1670
  loadCompoundState,
1394
1671
  markCourseStarted,
1395
1672
  markCourseStartedEmittedToTracking,
@@ -1397,6 +1674,7 @@ export {
1397
1674
  markCourseStartedXapiSent,
1398
1675
  migrateCourseStartedMark,
1399
1676
  nowIso,
1677
+ orderChoicesByIndices,
1400
1678
  parseBlockId,
1401
1679
  parseCheckId,
1402
1680
  parseCompoundResumeState,
@@ -1405,8 +1683,12 @@ export {
1405
1683
  resetSharedVolatileSessionIdForTests,
1406
1684
  resetStoragePortForTests,
1407
1685
  resetTelemetryBuilderWarningsForTests,
1686
+ resolveMcqCorrectAnswers,
1687
+ resolveMcqShuffleSeed,
1408
1688
  resolveSessionId,
1409
1689
  saveCompoundState,
1690
+ scoreMcqSelection,
1691
+ shuffleChoiceIndices,
1410
1692
  slugifyId,
1411
1693
  telemetryCatalogV2Version,
1412
1694
  telemetryCatalogV3Version,