@intellectif/lk-core 0.15.0 → 0.16.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/{activity-C7cR8U9n.d.cts → activity-DcxtpX7Z.d.cts} +5 -1
  3. package/dist/{activity-C7cR8U9n.d.ts → activity-DcxtpX7Z.d.ts} +5 -1
  4. package/dist/{chunk-5KWF34LJ.cjs → chunk-6P6XK2LI.cjs} +31 -31
  5. package/dist/{chunk-5KWF34LJ.cjs.map → chunk-6P6XK2LI.cjs.map} +1 -1
  6. package/dist/{chunk-QFWEF5ST.cjs → chunk-F7UOLKWV.cjs} +34 -23
  7. package/dist/chunk-F7UOLKWV.cjs.map +1 -0
  8. package/dist/{chunk-W7AQP6AV.cjs → chunk-FJJ2GVBA.cjs} +24 -22
  9. package/dist/chunk-FJJ2GVBA.cjs.map +1 -0
  10. package/dist/{chunk-EEMVWBPP.js → chunk-GOZGBVJP.js} +10 -8
  11. package/dist/chunk-GOZGBVJP.js.map +1 -0
  12. package/dist/{chunk-DPHX4E7X.js → chunk-IRUD73IQ.js} +34 -23
  13. package/dist/chunk-IRUD73IQ.js.map +1 -0
  14. package/dist/{chunk-54XYQ3CS.js → chunk-OXF7VT7S.js} +2 -2
  15. package/dist/{chunk-L6YBQXSB.js → chunk-S63IOKFP.js} +2 -2
  16. package/dist/{chunk-7ETU6EXQ.cjs → chunk-VFTVZJO5.cjs} +4 -4
  17. package/dist/{chunk-7ETU6EXQ.cjs.map → chunk-VFTVZJO5.cjs.map} +1 -1
  18. package/dist/{index-D10NaztZ.d.ts → index-BWpwZRQw.d.cts} +1 -1
  19. package/dist/{index-BWyMDlUS.d.cts → index-DRJqwO2y.d.ts} +1 -1
  20. package/dist/index.cjs +112 -111
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +17 -4
  23. package/dist/index.d.ts +17 -4
  24. package/dist/index.js +8 -7
  25. package/dist/index.js.map +1 -1
  26. package/dist/schemas.cjs +3 -3
  27. package/dist/schemas.d.cts +2 -2
  28. package/dist/schemas.d.ts +2 -2
  29. package/dist/schemas.js +2 -2
  30. package/dist/scoring.cjs +3 -3
  31. package/dist/scoring.d.cts +2 -2
  32. package/dist/scoring.d.ts +2 -2
  33. package/dist/scoring.js +2 -2
  34. package/dist/xapi.cjs +3 -3
  35. package/dist/xapi.d.cts +1 -1
  36. package/dist/xapi.d.ts +1 -1
  37. package/dist/xapi.js +2 -2
  38. package/package.json +1 -1
  39. package/dist/chunk-DPHX4E7X.js.map +0 -1
  40. package/dist/chunk-EEMVWBPP.js.map +0 -1
  41. package/dist/chunk-QFWEF5ST.cjs.map +0 -1
  42. package/dist/chunk-W7AQP6AV.cjs.map +0 -1
  43. /package/dist/{chunk-54XYQ3CS.js.map → chunk-OXF7VT7S.js.map} +0 -0
  44. /package/dist/{chunk-L6YBQXSB.js.map → chunk-S63IOKFP.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @intellectif/lk-core
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7992fac: A dictation's captions are refused whatever form they take: `tracks` count as captions, as
8
+ `captionsUrl` always did.
9
+
10
+ **This tightens validation.** A dictation whose `media` or `slowMedia` carries a non-empty `tracks`
11
+ list, and a group whose stimulus carries one while a dictation without its own recording plays that
12
+ stimulus, were valid until now — and were not safe. A caption track transcribes the words a dictation
13
+ asks the learner to type; a subtitle track translates them, which gives them away just as surely.
14
+ Redaction keeps tracks, because on any other recording they are what a learner is meant to see, so
15
+ before this release such a track reached an exam client.
16
+
17
+ `validateActivity`, `validateItemGroup`, `assertRedacted`, `assertRedactedItemGroup`, `redact()` and
18
+ `redactItemGroup()` now refuse it with the message they give for `captionsUrl`, at `media.tracks`,
19
+ `slowMedia.tracks` or `stimulus.media.tracks`; `validateDraft` and `validateItemGroupDraft` report
20
+ `dc_captions_not_allowed` there. With both `captionsUrl` and `tracks` on a group's stimulus, the one
21
+ refusal stays at `stimulus.media.captionsUrl`, where it has always been. Check stored content before
22
+ upgrading: remove the tracks, or give the dictation a recording of its own without them.
23
+
24
+ Also:
25
+
26
+ - `ThemeTokens` gains four optional tokens for the interactive video's caption lines:
27
+ `--lk-iv-caption-color`, `--lk-iv-caption-secondary-color`, `--lk-iv-caption-secondary-scale` and
28
+ `--lk-iv-caption-gap`.
29
+ - The `video-captions-changed` interaction is documented with its new payload: `srclang` for the
30
+ first caption line and `secondary` for the second, each `null` when none shows.
31
+
3
32
  ## 0.15.0
4
33
 
5
34
  ### Minor Changes
@@ -1315,7 +1315,11 @@ type InteractionKind = 'option-selected' | 'option-deselected' | 'blank-filled'
1315
1315
  | 'video-quiz-opened'
1316
1316
  /** Interactive video: one question of an open quiz was shown (`cueId`, `slotId`). */
1317
1317
  | 'video-quiz-question-shown' | 'video-quiz-skipped' | 'video-quiz-closed'
1318
- /** Interactive video: captions turned on (the track's `srclang`) or off (`null`). */
1318
+ /**
1319
+ * Interactive video: the captions a learner chose. `payload.srclang` is the
1320
+ * primary line's language, or `null` with captions off; `payload.secondary` is
1321
+ * the second line's, or `null` with none showing. Fires when either changes.
1322
+ */
1319
1323
  | 'video-captions-changed' | 'video-fullscreen-changed' | 'video-pip-changed' | (string & {});
1320
1324
  /** Fired by activity components on every discrete learner interaction. */
1321
1325
  interface InteractionEvent {
@@ -1315,7 +1315,11 @@ type InteractionKind = 'option-selected' | 'option-deselected' | 'blank-filled'
1315
1315
  | 'video-quiz-opened'
1316
1316
  /** Interactive video: one question of an open quiz was shown (`cueId`, `slotId`). */
1317
1317
  | 'video-quiz-question-shown' | 'video-quiz-skipped' | 'video-quiz-closed'
1318
- /** Interactive video: captions turned on (the track's `srclang`) or off (`null`). */
1318
+ /**
1319
+ * Interactive video: the captions a learner chose. `payload.srclang` is the
1320
+ * primary line's language, or `null` with captions off; `payload.secondary` is
1321
+ * the second line's, or `null` with none showing. Fires when either changes.
1322
+ */
1319
1323
  | 'video-captions-changed' | 'video-fullscreen-changed' | 'video-pip-changed' | (string & {});
1320
1324
  /** Fired by activity components on every discrete learner interaction. */
1321
1325
  interface InteractionEvent {
@@ -14,7 +14,7 @@
14
14
 
15
15
 
16
16
 
17
- var _chunkQFWEF5STcjs = require('./chunk-QFWEF5ST.cjs');
17
+ var _chunkF7UOLKWVcjs = require('./chunk-F7UOLKWV.cjs');
18
18
 
19
19
  // src/is-redacted.ts
20
20
  function isRedacted(data) {
@@ -299,12 +299,12 @@ function composeAssessmentScore(sections, policy) {
299
299
  function diffDictationChars(reference, attempt) {
300
300
  const referencePoints = Array.from(reference);
301
301
  const attemptPoints = Array.from(attempt);
302
- return _chunkQFWEF5STcjs.alignSequences.call(void 0, referencePoints, attemptPoints).map(
302
+ return _chunkF7UOLKWVcjs.alignSequences.call(void 0, referencePoints, attemptPoints).map(
303
303
  ({ step, referenceIndex, attemptIndex }) => {
304
- if (step === _chunkQFWEF5STcjs.STEP.missing) {
304
+ if (step === _chunkF7UOLKWVcjs.STEP.missing) {
305
305
  return { op: "missing", reference: referencePoints[referenceIndex], attempt: "" };
306
306
  }
307
- if (step === _chunkQFWEF5STcjs.STEP.extra) {
307
+ if (step === _chunkF7UOLKWVcjs.STEP.extra) {
308
308
  return { op: "extra", reference: "", attempt: attemptPoints[attemptIndex] };
309
309
  }
310
310
  const referencePoint = referencePoints[referenceIndex];
@@ -326,14 +326,14 @@ function heardTokens(words) {
326
326
  const tokens = [];
327
327
  let length = 0;
328
328
  for (const [wordIndex, word] of words.entries()) {
329
- const normalized = _chunkQFWEF5STcjs.normalizeDictationText.call(void 0, word.text);
329
+ const normalized = _chunkF7UOLKWVcjs.normalizeDictationText.call(void 0, word.text);
330
330
  if (normalized === "") {
331
331
  continue;
332
332
  }
333
333
  const inserted = word.error === "insertion";
334
334
  for (const text of normalized.split(" ")) {
335
- length += _chunkQFWEF5STcjs.codePointLength.call(void 0, text) + (length === 0 ? 0 : 1);
336
- if (!inserted && length <= _chunkQFWEF5STcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
335
+ length += _chunkF7UOLKWVcjs.codePointLength.call(void 0, text) + (length === 0 ? 0 : 1);
336
+ if (!inserted && length <= _chunkF7UOLKWVcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
337
337
  tokens.push({ text, wordIndex });
338
338
  }
339
339
  }
@@ -384,7 +384,7 @@ var SpeechAssessmentSchema = _v4.z.strictObject({
384
384
  assessmentVersion: _v4.z.literal("1.0"),
385
385
  status: _v4.z.enum(["assessed", "no_speech"]),
386
386
  task: _v4.z.enum(["scripted", "unscripted"]),
387
- locale: _v4.z.string().regex(_chunkQFWEF5STcjs.CANONICAL_LOCALE_RE),
387
+ locale: _v4.z.string().regex(_chunkF7UOLKWVcjs.CANONICAL_LOCALE_RE),
388
388
  referenceText: _v4.z.string().max(8e3).optional(),
389
389
  recordingKey: _v4.z.string().min(1).max(1024).optional(),
390
390
  assessor: AssessorSchema,
@@ -399,7 +399,7 @@ var SpeechAssessmentSchema = _v4.z.strictObject({
399
399
  recognizedText: _v4.z.string().max(8e3).optional(),
400
400
  miscue: _v4.z.enum(["assessor", "none"]),
401
401
  phonemeAlphabet: _v4.z.enum(["ipa", "sapi"]).optional(),
402
- words: _v4.z.array(WordSchema).max(_chunkQFWEF5STcjs.SPEECH_ASSESSMENT_MAX_WORDS),
402
+ words: _v4.z.array(WordSchema).max(_chunkF7UOLKWVcjs.SPEECH_ASSESSMENT_MAX_WORDS),
403
403
  prosody: _v4.z.strictObject({ monotoneConfidence: Confidence.optional() }).optional(),
404
404
  signal: _v4.z.strictObject({ snrDb: _v4.z.number().optional() }).optional()
405
405
  });
@@ -421,21 +421,21 @@ function rawTextLength(words) {
421
421
  }
422
422
  function totalTextErrors(words) {
423
423
  const raw = rawTextLength(words);
424
- if (raw > _chunkQFWEF5STcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
424
+ if (raw > _chunkF7UOLKWVcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
425
425
  return [
426
426
  {
427
427
  path: ["words"],
428
- message: `The words of an assessment carry ${raw} characters between them, and at most ${_chunkQFWEF5STcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH} can be aligned against the text of an item.`,
428
+ message: `The words of an assessment carry ${raw} characters between them, and at most ${_chunkF7UOLKWVcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH} can be aligned against the text of an item.`,
429
429
  code: "too_big"
430
430
  }
431
431
  ];
432
432
  }
433
433
  const normalized = heardTokens(words).length;
434
- if (normalized > _chunkQFWEF5STcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
434
+ if (normalized > _chunkF7UOLKWVcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH) {
435
435
  return [
436
436
  {
437
437
  path: ["words"],
438
- message: `The words of an assessment spell ${normalized} characters between them once they are normalised, and at most ${_chunkQFWEF5STcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH} can be aligned against the text of an item. One character can stand for several.`,
438
+ message: `The words of an assessment spell ${normalized} characters between them once they are normalised, and at most ${_chunkF7UOLKWVcjs.SPEECH_ASSESSMENT_MAX_TEXT_LENGTH} can be aligned against the text of an item. One character can stand for several.`,
439
439
  code: "too_big"
440
440
  }
441
441
  ];
@@ -505,16 +505,16 @@ function alignReadAloud(data, assessment) {
505
505
  return alignValidated(data, checked.data);
506
506
  }
507
507
  function alignValidated(data, assessment) {
508
- const reference = _chunkQFWEF5STcjs.dictationReferenceWords.call(void 0, {
508
+ const reference = _chunkF7UOLKWVcjs.dictationReferenceWords.call(void 0, {
509
509
  transcript: data.referenceText
510
510
  })[0];
511
511
  const heard = heardTokens(assessment.words).tokens;
512
512
  const entries = [];
513
- for (const { step, referenceIndex, attemptIndex } of _chunkQFWEF5STcjs.alignSequences.call(void 0,
513
+ for (const { step, referenceIndex, attemptIndex } of _chunkF7UOLKWVcjs.alignSequences.call(void 0,
514
514
  reference.map((word) => word.word),
515
515
  heard.map((token) => token.text)
516
516
  )) {
517
- if (step === _chunkQFWEF5STcjs.STEP.extra) {
517
+ if (step === _chunkF7UOLKWVcjs.STEP.extra) {
518
518
  const token2 = heard[attemptIndex];
519
519
  entries.push({
520
520
  reference: "",
@@ -526,7 +526,7 @@ function alignValidated(data, assessment) {
526
526
  continue;
527
527
  }
528
528
  const referenceWord = reference[referenceIndex];
529
- if (step === _chunkQFWEF5STcjs.STEP.missing) {
529
+ if (step === _chunkF7UOLKWVcjs.STEP.missing) {
530
530
  entries.push({
531
531
  itemId: referenceWord.itemId,
532
532
  reference: referenceWord.word,
@@ -579,7 +579,7 @@ function placeInsertions(assessment, entries) {
579
579
  reference: "",
580
580
  // Every insertion is accounted for, including one whose text normalises
581
581
  // to nothing: a reader of `wordIndex` must find each word it looks for.
582
- heard: _chunkQFWEF5STcjs.normalizeDictationText.call(void 0, word.text),
582
+ heard: _chunkF7UOLKWVcjs.normalizeDictationText.call(void 0, word.text),
583
583
  state: "inserted",
584
584
  ...accuracyOf(word),
585
585
  wordIndex
@@ -616,9 +616,9 @@ function readResponseRecording(value) {
616
616
  function spokenWordCount(assessment) {
617
617
  const recognized = assessment.recognizedText;
618
618
  if (recognized !== void 0 && recognized.trim() !== "") {
619
- return _chunkQFWEF5STcjs.countWords.call(void 0, recognized);
619
+ return _chunkF7UOLKWVcjs.countWords.call(void 0, recognized);
620
620
  }
621
- return _chunkQFWEF5STcjs.countWords.call(void 0,
621
+ return _chunkF7UOLKWVcjs.countWords.call(void 0,
622
622
  assessment.words.filter((word) => word.error !== "omission" && word.error !== "insertion").map((word) => word.text).join(" ")
623
623
  );
624
624
  }
@@ -642,9 +642,9 @@ function gradeReadAloud(data, response, assessment, options) {
642
642
  );
643
643
  }
644
644
  const rounding = roundingPolicyOf(_optionalChain([options, 'optionalAccess', _6 => _6.rounding]));
645
- const parsed = _chunkQFWEF5STcjs.ReadAloudDataSchema.safeParse(data);
645
+ const parsed = _chunkF7UOLKWVcjs.ReadAloudDataSchema.safeParse(data);
646
646
  if (!parsed.success) {
647
- throw new (0, _chunkQFWEF5STcjs.ActivitySchemaError)(
647
+ throw new (0, _chunkF7UOLKWVcjs.ActivitySchemaError)(
648
648
  "read-aloud",
649
649
  parsed.error.issues.map((issue) => ({
650
650
  path: issue.path.map(String),
@@ -654,7 +654,7 @@ function gradeReadAloud(data, response, assessment, options) {
654
654
  );
655
655
  }
656
656
  if (isRedacted(data) || isRedacted(parsed.data)) {
657
- throw new (0, _chunkQFWEF5STcjs.RedactedScoringError)("read-aloud");
657
+ throw new (0, _chunkF7UOLKWVcjs.RedactedScoringError)("read-aloud");
658
658
  }
659
659
  const item = parsed.data;
660
660
  const checkedResponse = readResponseRecording(response);
@@ -807,7 +807,7 @@ function gradeReadAloud(data, response, assessment, options) {
807
807
  };
808
808
  }
809
809
  function referenceWordsOf(data) {
810
- return _chunkQFWEF5STcjs.dictationReferenceWords.call(void 0, { transcript: data.referenceText })[0];
810
+ return _chunkF7UOLKWVcjs.dictationReferenceWords.call(void 0, { transcript: data.referenceText })[0];
811
811
  }
812
812
 
813
813
  // src/scoring/speech/wav.ts
@@ -949,19 +949,19 @@ function outcomeFromUnscorable(result) {
949
949
  // src/scoring/index.ts
950
950
  function score(activityType, activityData, learnerResponse, options) {
951
951
  const rounding = roundingPolicyOf(_optionalChain([options, 'optionalAccess', _9 => _9.rounding]));
952
- const descriptor = _chunkQFWEF5STcjs.getActivityTypeDescriptor.call(void 0, activityType);
952
+ const descriptor = _chunkF7UOLKWVcjs.getActivityTypeDescriptor.call(void 0, activityType);
953
953
  if (descriptor === void 0) {
954
- throw new (0, _chunkQFWEF5STcjs.UnknownActivityTypeError)(String(activityType));
954
+ throw new (0, _chunkF7UOLKWVcjs.UnknownActivityTypeError)(String(activityType));
955
955
  }
956
956
  if (descriptor.scoring.kind === "deferred") {
957
- throw new (0, _chunkQFWEF5STcjs.DeferredScoringError)(descriptor.type);
957
+ throw new (0, _chunkF7UOLKWVcjs.DeferredScoringError)(descriptor.type);
958
958
  }
959
959
  if (isRedacted(activityData)) {
960
- throw new (0, _chunkQFWEF5STcjs.RedactedScoringError)(descriptor.type);
960
+ throw new (0, _chunkF7UOLKWVcjs.RedactedScoringError)(descriptor.type);
961
961
  }
962
962
  const result = descriptor.scoring.score(activityData, learnerResponse);
963
963
  if (!Number.isFinite(result.score)) {
964
- throw new (0, _chunkQFWEF5STcjs.RedactedScoringError)(descriptor.type);
964
+ throw new (0, _chunkF7UOLKWVcjs.RedactedScoringError)(descriptor.type);
965
965
  }
966
966
  const passed = computePassThreshold(activityData, result.score, rounding);
967
967
  return { ...result, passed, feedback: _nullishCoalesce(result.feedback, () => ( selectFeedback(activityData, passed))) };
@@ -969,7 +969,7 @@ function score(activityType, activityData, learnerResponse, options) {
969
969
  function evaluate(data, response, options) {
970
970
  const rounding = roundingPolicyOf(_optionalChain([options, 'optionalAccess', _10 => _10.rounding]));
971
971
  const type = data.type;
972
- const descriptor = typeof type === "string" ? _chunkQFWEF5STcjs.getActivityTypeDescriptor.call(void 0, type) : void 0;
972
+ const descriptor = typeof type === "string" ? _chunkF7UOLKWVcjs.getActivityTypeDescriptor.call(void 0, type) : void 0;
973
973
  if (descriptor === void 0) {
974
974
  return {
975
975
  status: "unscorable",
@@ -1031,4 +1031,4 @@ function evaluate(data, response, options) {
1031
1031
 
1032
1032
 
1033
1033
  exports.roundGrade = roundGrade; exports.gte = gte; exports.classifyBand = classifyBand; exports.DEFAULT_PASS_THRESHOLD = DEFAULT_PASS_THRESHOLD; exports.computePassThreshold = computePassThreshold; exports.gradeFromRubric = gradeFromRubric; exports.outcomeFromGrade = outcomeFromGrade; exports.hasGrade = hasGrade; exports.composeAssessmentScore = composeAssessmentScore; exports.diffDictationChars = diffDictationChars; exports.validateSpeechAssessment = validateSpeechAssessment; exports.alignReadAloud = alignReadAloud; exports.gradeReadAloud = gradeReadAloud; exports.inspectWav = inspectWav; exports.outcomeFromUnscorable = outcomeFromUnscorable; exports.score = score; exports.evaluate = evaluate;
1034
- //# sourceMappingURL=chunk-5KWF34LJ.cjs.map
1034
+ //# sourceMappingURL=chunk-6P6XK2LI.cjs.map