@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.
- package/CHANGELOG.md +29 -0
- package/dist/{activity-C7cR8U9n.d.cts → activity-DcxtpX7Z.d.cts} +5 -1
- package/dist/{activity-C7cR8U9n.d.ts → activity-DcxtpX7Z.d.ts} +5 -1
- package/dist/{chunk-5KWF34LJ.cjs → chunk-6P6XK2LI.cjs} +31 -31
- package/dist/{chunk-5KWF34LJ.cjs.map → chunk-6P6XK2LI.cjs.map} +1 -1
- package/dist/{chunk-QFWEF5ST.cjs → chunk-F7UOLKWV.cjs} +34 -23
- package/dist/chunk-F7UOLKWV.cjs.map +1 -0
- package/dist/{chunk-W7AQP6AV.cjs → chunk-FJJ2GVBA.cjs} +24 -22
- package/dist/chunk-FJJ2GVBA.cjs.map +1 -0
- package/dist/{chunk-EEMVWBPP.js → chunk-GOZGBVJP.js} +10 -8
- package/dist/chunk-GOZGBVJP.js.map +1 -0
- package/dist/{chunk-DPHX4E7X.js → chunk-IRUD73IQ.js} +34 -23
- package/dist/chunk-IRUD73IQ.js.map +1 -0
- package/dist/{chunk-54XYQ3CS.js → chunk-OXF7VT7S.js} +2 -2
- package/dist/{chunk-L6YBQXSB.js → chunk-S63IOKFP.js} +2 -2
- package/dist/{chunk-7ETU6EXQ.cjs → chunk-VFTVZJO5.cjs} +4 -4
- package/dist/{chunk-7ETU6EXQ.cjs.map → chunk-VFTVZJO5.cjs.map} +1 -1
- package/dist/{index-D10NaztZ.d.ts → index-BWpwZRQw.d.cts} +1 -1
- package/dist/{index-BWyMDlUS.d.cts → index-DRJqwO2y.d.ts} +1 -1
- package/dist/index.cjs +112 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +8 -7
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +3 -3
- package/dist/schemas.d.cts +2 -2
- package/dist/schemas.d.ts +2 -2
- package/dist/schemas.js +2 -2
- package/dist/scoring.cjs +3 -3
- package/dist/scoring.d.cts +2 -2
- package/dist/scoring.d.ts +2 -2
- package/dist/scoring.js +2 -2
- package/dist/xapi.cjs +3 -3
- package/dist/xapi.d.cts +1 -1
- package/dist/xapi.d.ts +1 -1
- package/dist/xapi.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-DPHX4E7X.js.map +0 -1
- package/dist/chunk-EEMVWBPP.js.map +0 -1
- package/dist/chunk-QFWEF5ST.cjs.map +0 -1
- package/dist/chunk-W7AQP6AV.cjs.map +0 -1
- /package/dist/{chunk-54XYQ3CS.js.map → chunk-OXF7VT7S.js.map} +0 -0
- /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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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
|
|
302
|
+
return _chunkF7UOLKWVcjs.alignSequences.call(void 0, referencePoints, attemptPoints).map(
|
|
303
303
|
({ step, referenceIndex, attemptIndex }) => {
|
|
304
|
-
if (step ===
|
|
304
|
+
if (step === _chunkF7UOLKWVcjs.STEP.missing) {
|
|
305
305
|
return { op: "missing", reference: referencePoints[referenceIndex], attempt: "" };
|
|
306
306
|
}
|
|
307
|
-
if (step ===
|
|
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 =
|
|
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 +=
|
|
336
|
-
if (!inserted && 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(
|
|
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(
|
|
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 >
|
|
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 ${
|
|
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 >
|
|
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 ${
|
|
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 =
|
|
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
|
|
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 ===
|
|
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 ===
|
|
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:
|
|
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
|
|
619
|
+
return _chunkF7UOLKWVcjs.countWords.call(void 0, recognized);
|
|
620
620
|
}
|
|
621
|
-
return
|
|
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 =
|
|
645
|
+
const parsed = _chunkF7UOLKWVcjs.ReadAloudDataSchema.safeParse(data);
|
|
646
646
|
if (!parsed.success) {
|
|
647
|
-
throw new (0,
|
|
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,
|
|
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
|
|
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 =
|
|
952
|
+
const descriptor = _chunkF7UOLKWVcjs.getActivityTypeDescriptor.call(void 0, activityType);
|
|
953
953
|
if (descriptor === void 0) {
|
|
954
|
-
throw new (0,
|
|
954
|
+
throw new (0, _chunkF7UOLKWVcjs.UnknownActivityTypeError)(String(activityType));
|
|
955
955
|
}
|
|
956
956
|
if (descriptor.scoring.kind === "deferred") {
|
|
957
|
-
throw new (0,
|
|
957
|
+
throw new (0, _chunkF7UOLKWVcjs.DeferredScoringError)(descriptor.type);
|
|
958
958
|
}
|
|
959
959
|
if (isRedacted(activityData)) {
|
|
960
|
-
throw new (0,
|
|
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,
|
|
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" ?
|
|
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-
|
|
1034
|
+
//# sourceMappingURL=chunk-6P6XK2LI.cjs.map
|